Skip to content

Commit

Permalink
Treat second factor SSO as SecondFactor=on; Prevent local user lockou…
Browse files Browse the repository at this point in the history
…t when SSO is the only enabled MFA method; Ensure SecondFactors=[] is disallowed.
  • Loading branch information
Joerger committed Oct 9, 2024
1 parent 12f7439 commit a543532
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 12 deletions.
30 changes: 22 additions & 8 deletions api/types/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@ type AuthPreference interface {
IsSecondFactorEnabled() bool
// IsSecondFactorEnforced checks if second factor is enforced.
IsSecondFactorEnforced() bool
// IsSecondFactorTOTPAllowed checks if users are allowed to register TOTP devices.
// IsSecondFactorTOTPAllowed checks if users can use TOTP as an MFA method.
IsSecondFactorTOTPAllowed() bool
// IsSecondFactorWebauthnAllowed checks if users are allowed to register
// Webauthn devices.
// IsSecondFactorWebauthnAllowed checks if users can use WebAuthn as an MFA method.
IsSecondFactorWebauthnAllowed() bool
// IsSecondFactorSSOAllowed checks if users can use SSO as an MFA method.
IsSecondFactorSSOAllowed() bool
// IsAdminActionMFAEnforced checks if admin action MFA is enforced.
IsAdminActionMFAEnforced() bool

Expand Down Expand Up @@ -383,17 +384,21 @@ func (c *AuthPreferenceV2) IsSecondFactorEnforced() bool {
return len(c.GetSecondFactors()) > 0 && c.Spec.SecondFactor != constants.SecondFactorOptional
}

// IsSecondFactorTOTPAllowed checks if users are allowed to register TOTP devices.
// IsSecondFactorTOTPAllowed checks if users can use TOTP as an MFA method.
func (c *AuthPreferenceV2) IsSecondFactorTOTPAllowed() bool {
return slices.Contains(c.GetSecondFactors(), SecondFactorType_SECOND_FACTOR_TYPE_OTP)
}

// IsSecondFactorWebauthnAllowed checks if users are allowed to register
// Webauthn devices.
// IsSecondFactorWebauthnAllowed checks if users can use WebAuthn as an MFA method.
func (c *AuthPreferenceV2) IsSecondFactorWebauthnAllowed() bool {
return slices.Contains(c.GetSecondFactors(), SecondFactorType_SECOND_FACTOR_TYPE_WEBAUTHN)
}

// IsSecondFactorSSOAllowed checks if users can use SSO as an MFA method.
func (c *AuthPreferenceV2) IsSecondFactorSSOAllowed() bool {
return slices.Contains(c.GetSecondFactors(), SecondFactorType_SECOND_FACTOR_TYPE_SSO)
}

// IsAdminActionMFAEnforced checks if admin action MFA is enforced.
func (c *AuthPreferenceV2) IsAdminActionMFAEnforced() bool {
// OTP is not supported for Admin MFA.
Expand Down Expand Up @@ -679,7 +684,6 @@ func (c *AuthPreferenceV2) CheckAndSetDefaults() error {
if c.Spec.Type == "" {
c.Spec.Type = constants.Local
}

if c.Spec.AllowLocalAuth == nil {
c.Spec.AllowLocalAuth = NewBoolOption(true)
}
Expand Down Expand Up @@ -722,7 +726,7 @@ func (c *AuthPreferenceV2) CheckAndSetDefaults() error {
c.Spec.SecondFactor = constants.SecondFactorWebauthn
case "":
// default to OTP if SecondFactors is also not set.
if len(c.Spec.SecondFactors) == 0 {
if c.Spec.SecondFactors == nil {
c.Spec.SecondFactor = constants.SecondFactorOTP
}
default:
Expand Down Expand Up @@ -771,6 +775,16 @@ func (c *AuthPreferenceV2) CheckAndSetDefaults() error {
return trace.BadParameter("missing required Webauthn configuration for headless=true")
}

// Prevent accidental local lockout by disabling local second factor methods, (likely leaving only sso enabled).
hasTOTP := c.IsSecondFactorTOTPAllowed()
if c.GetAllowLocalAuth() && !hasTOTP && !hasWebauthn {
switch c.Spec.SecondFactor {
case constants.SecondFactorOptional, constants.SecondFactorOff:
default:
return trace.BadParameter("missing a local second factor method for local users (otp, webauthn), either add a local second factor method or disable local auth")
}
}

// Validate connector name for type=local.
if c.Spec.Type == constants.Local {
switch connectorName := c.Spec.ConnectorName; connectorName {
Expand Down
3 changes: 2 additions & 1 deletion api/types/second_factor.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ import (
func legacySecondFactorFromSecondFactors(secondFactors []SecondFactorType) constants.SecondFactorType {
hasOTP := slices.Contains(secondFactors, SecondFactorType_SECOND_FACTOR_TYPE_OTP)
hasWebAuthn := slices.Contains(secondFactors, SecondFactorType_SECOND_FACTOR_TYPE_WEBAUTHN)
hasSSO := slices.Contains(secondFactors, SecondFactorType_SECOND_FACTOR_TYPE_SSO)

switch {
case hasOTP && hasWebAuthn:
case (hasOTP && hasWebAuthn) || hasSSO:
return constants.SecondFactorOn
case hasWebAuthn:
return constants.SecondFactorWebauthn
Expand Down
4 changes: 1 addition & 3 deletions lib/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/accesslist"
"github.com/gravitational/teleport/api/utils/keys"
Expand Down Expand Up @@ -325,8 +324,7 @@ func ValidateResource(res types.Resource) error {
if GetModules().Features().Cloud || !IsInsecureTestMode() {
switch r := res.(type) {
case types.AuthPreference:
switch r.GetSecondFactor() {
case constants.SecondFactorOff, constants.SecondFactorOptional:
if !r.IsSecondFactorEnforced() {
return trace.Wrap(ErrCannotDisableSecondFactor)
}
}
Expand Down

0 comments on commit a543532

Please sign in to comment.