Skip to content

Commit

Permalink
mention SECONDS in session manager environment variables to make it e…
Browse files Browse the repository at this point in the history
…asier to understand meaning of each variable
  • Loading branch information
alexmt committed Apr 17, 2020
1 parent 1cb2e9f commit 3028f35
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 37 deletions.
26 changes: 26 additions & 0 deletions docs/operator-manual/user-management/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,32 @@ argocd account update-password \
argocd account generate-token --account <username>
```

### Failed logins rate limiting

Argo CD throttles failed login attempts in order to prevent password brute-forcing. The following environments
variables are available to control throttling settings:

* `ARGOCD_SESSION_MAX_FAIL_COUNT`: Maximum number of failed logins before the
delay kicks in. Default: 5.

* `ARGOCD_SESSION_FAILURE_DELAY_START_SECONDS`: Time in seconds the authentication
should be delayed for if the limiter becomes first active. Default: 3

* `ARGOCD_SESSION_FAILURE_DELAY_INCREASE_SECONDS`: Time in seconds the authentication
delay should be increased on consecutive login failures after max fail count
has been reached. Default: 2

* `ARGOCD_SESSION_FAILURE_DELAY_MAX_SECONDS`: Max time in seconds the authentication
delay can be increased to. Default: 30

* `ARGOCD_SESSION_FAILURE_WINDOW_SECONDS`: Number of seconds for the failure window.
Default: 300 (5 minutes). If this is set to 0, the failure window is
disabled and the delay kicks in after 10 consecutive logon failures,
regardless of the time frame they happened.

`ARGOCD_SESSION_MAX_CACHE_SIZE`: Maximum number of entries allowed in the
cache. Default: 1000

## SSO

There are two ways that SSO can be configured:
Expand Down
34 changes: 23 additions & 11 deletions util/session/sessionmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,25 @@ const (
// The default time in seconds for the failure window
defaultFailureWindow = 300

// environment variables to control rate limiter behaviour
envLoginDelayStart = "ARGOCD_SESSION_FAILURE_DELAY_START"
envLoginDelayIncrease = "ARGOCD_SESSION_FAILURE_DELAY_INCREASE"
envLoginDelayMax = "ARGOCD_SESSION_FAILURE_DELAY_MAX"
envLoginMaxFailCount = "ARGOCD_SESSION_FAILURE_MAX_FAIL_COUNT"
envLoginFailureWindow = "ARGOCD_SESSION_FAILURE_WINDOW"
envLoginMaxCacheSize = "ARGOCD_SESSION_MAX_CACHE_SIZE"
// environment variables to control rate limiter behaviour:

// Time in seconds the authentication should be delayed for if the limiter becomes first active. Default: 3
envLoginDelayStartSeconds = "ARGOCD_SESSION_FAILURE_DELAY_START_SECONDS"

// Time in seconds the authentication delay should be increased on consecutive login failures after max fail count has been reached. Default: 2
envLoginDelayIncreaseSeconds = "ARGOCD_SESSION_FAILURE_DELAY_INCREASE_SECONDS"

// Max time in seconds the authentication delay can be increased to. Default: 30
envLoginDelayMaxSeconds = "ARGOCD_SESSION_FAILURE_DELAY_MAX_SECONDS"

// Max number of login failures before login delay kicks in
envLoginMaxFailCount = "ARGOCD_SESSION_FAILURE_MAX_FAIL_COUNT"

// Number of maximum seconds the login is allowed to delay for. Default: 300 (5 minutes).
envLoginFailureWindowSeconds = "ARGOCD_SESSION_FAILURE_WINDOW_SECONDS"

// Max number of stored usernames
envLoginMaxCacheSize = "ARGOCD_SESSION_MAX_CACHE_SIZE"
)

// Helper function to parse a number from an environment variable. Returns a
Expand Down Expand Up @@ -142,22 +154,22 @@ func getMaxLoginFailures() int {

// Returns the number of seconds login should be delayed after maximum number of failures has been reached
func getLoginDelayStart() int {
return parseNumFromEnv(envLoginDelayStart, defaultLoginDelayStart, 1, math.MaxInt32)
return parseNumFromEnv(envLoginDelayStartSeconds, defaultLoginDelayStart, 1, math.MaxInt32)
}

// Returns the number of seconds the delay shall be increased by on consecutive login failures
func getLoginDelayIncrease() int {
return parseNumFromEnv(envLoginDelayIncrease, defaultLoginDelayIncrease, 0, math.MaxInt32)
return parseNumFromEnv(envLoginDelayIncreaseSeconds, defaultLoginDelayIncrease, 0, math.MaxInt32)
}

// Returns the number of maximum seconds the login is allowed to delay for
func getLoginDelayMax() int {
return parseNumFromEnv(envLoginDelayMax, defaultLoginDelayMax, 0, math.MaxInt32)
return parseNumFromEnv(envLoginDelayMaxSeconds, defaultLoginDelayMax, 0, math.MaxInt32)
}

// Returns the number of maximum seconds the login is allowed to delay for
func getLoginFailureWindow() time.Duration {
return time.Duration(parseNumFromEnv(envLoginFailureWindow, defaultFailureWindow, 0, math.MaxInt32))
return time.Duration(parseNumFromEnv(envLoginFailureWindowSeconds, defaultFailureWindow, 0, math.MaxInt32))
}

// NewSessionManager creates a new session manager from Argo CD settings
Expand Down
52 changes: 26 additions & 26 deletions util/session/sessionmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ func TestCacheValueGetters(t *testing.T) {
})

t.Run("Valid environment overrides", func(t *testing.T) {
os.Setenv(envLoginDelayStart, "5")
os.Setenv(envLoginDelayIncrease, "5")
os.Setenv(envLoginDelayMax, "5")
os.Setenv(envLoginDelayStartSeconds, "5")
os.Setenv(envLoginDelayIncreaseSeconds, "5")
os.Setenv(envLoginDelayMaxSeconds, "5")
os.Setenv(envLoginMaxFailCount, "5")
os.Setenv(envLoginMaxCacheSize, "5")

Expand All @@ -198,17 +198,17 @@ func TestCacheValueGetters(t *testing.T) {
mcs := getMaximumCacheSize()
assert.Equal(t, 5, mcs)

os.Setenv(envLoginDelayStart, "")
os.Setenv(envLoginDelayIncrease, "")
os.Setenv(envLoginDelayMax, "")
os.Setenv(envLoginDelayStartSeconds, "")
os.Setenv(envLoginDelayIncreaseSeconds, "")
os.Setenv(envLoginDelayMaxSeconds, "")
os.Setenv(envLoginMaxFailCount, "")
os.Setenv(envLoginMaxCacheSize, "")
})

t.Run("Invalid environment overrides", func(t *testing.T) {
os.Setenv(envLoginDelayStart, "invalid")
os.Setenv(envLoginDelayIncrease, "invalid")
os.Setenv(envLoginDelayMax, "invalid")
os.Setenv(envLoginDelayStartSeconds, "invalid")
os.Setenv(envLoginDelayIncreaseSeconds, "invalid")
os.Setenv(envLoginDelayMaxSeconds, "invalid")
os.Setenv(envLoginMaxFailCount, "invalid")
os.Setenv(envLoginMaxCacheSize, "invalid")

Expand All @@ -227,17 +227,17 @@ func TestCacheValueGetters(t *testing.T) {
mcs := getMaximumCacheSize()
assert.Equal(t, defaultMaxCacheSize, mcs)

os.Setenv(envLoginDelayStart, "")
os.Setenv(envLoginDelayIncrease, "")
os.Setenv(envLoginDelayMax, "")
os.Setenv(envLoginDelayStartSeconds, "")
os.Setenv(envLoginDelayIncreaseSeconds, "")
os.Setenv(envLoginDelayMaxSeconds, "")
os.Setenv(envLoginMaxFailCount, "")
os.Setenv(envLoginMaxCacheSize, "")
})

t.Run("Less than allowed in environment overrides", func(t *testing.T) {
os.Setenv(envLoginDelayStart, "-1")
os.Setenv(envLoginDelayIncrease, "-1")
os.Setenv(envLoginDelayMax, "-1")
os.Setenv(envLoginDelayStartSeconds, "-1")
os.Setenv(envLoginDelayIncreaseSeconds, "-1")
os.Setenv(envLoginDelayMaxSeconds, "-1")
os.Setenv(envLoginMaxFailCount, "-1")
os.Setenv(envLoginMaxCacheSize, "-1")

Expand All @@ -256,17 +256,17 @@ func TestCacheValueGetters(t *testing.T) {
mcs := getMaximumCacheSize()
assert.Equal(t, defaultMaxCacheSize, mcs)

os.Setenv(envLoginDelayStart, "")
os.Setenv(envLoginDelayIncrease, "")
os.Setenv(envLoginDelayMax, "")
os.Setenv(envLoginDelayStartSeconds, "")
os.Setenv(envLoginDelayIncreaseSeconds, "")
os.Setenv(envLoginDelayMaxSeconds, "")
os.Setenv(envLoginMaxFailCount, "")
os.Setenv(envLoginMaxCacheSize, "")
})

t.Run("Greater than allowed in environment overrides", func(t *testing.T) {
os.Setenv(envLoginDelayStart, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginDelayIncrease, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginDelayMax, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginDelayStartSeconds, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginDelayIncreaseSeconds, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginDelayMaxSeconds, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginMaxFailCount, fmt.Sprintf("%d", math.MaxInt32+1))
os.Setenv(envLoginMaxCacheSize, fmt.Sprintf("%d", math.MaxInt32+1))

Expand All @@ -285,9 +285,9 @@ func TestCacheValueGetters(t *testing.T) {
mcs := getMaximumCacheSize()
assert.Equal(t, defaultMaxCacheSize, mcs)

os.Setenv(envLoginDelayStart, "")
os.Setenv(envLoginDelayIncrease, "")
os.Setenv(envLoginDelayMax, "")
os.Setenv(envLoginDelayStartSeconds, "")
os.Setenv(envLoginDelayIncreaseSeconds, "")
os.Setenv(envLoginDelayMaxSeconds, "")
os.Setenv(envLoginMaxFailCount, "")
os.Setenv(envLoginMaxCacheSize, "")
})
Expand Down Expand Up @@ -381,7 +381,7 @@ func TestFailedAttemptsExpiry(t *testing.T) {

invalidUsers := []string{"invalid1", "invalid2", "invalid3", "invalid4", "invalid5", "invalid6", "invalid7"}

os.Setenv(envLoginFailureWindow, "1")
os.Setenv(envLoginFailureWindowSeconds, "1")

for _, user := range invalidUsers {
err := mgr.VerifyUsernamePassword(user, "password")
Expand All @@ -394,5 +394,5 @@ func TestFailedAttemptsExpiry(t *testing.T) {
assert.Error(t, err)
assert.Len(t, mgr.GetLoginFailures(), 1)

os.Setenv(envLoginFailureWindow, "")
os.Setenv(envLoginFailureWindowSeconds, "")
}

0 comments on commit 3028f35

Please sign in to comment.