Adds the ability to bypass Okta MFA checks. (#3944)

* Adds the ability to bypass Okta MFA checks.

Unlike before, the administrator opts-in to this behavior, and is
suitably warned.

Fixes #3872
This commit is contained in:
Jeff Mitchell 2018-02-09 17:03:49 -05:00 committed by GitHub
parent 65328e9c12
commit a9a322aa39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 12 deletions

View File

@ -115,6 +115,11 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
case "PASSWORD_WARN": case "PASSWORD_WARN":
oktaResponse.AddWarning("Your Okta password is in warning state and needs to be changed soon.") oktaResponse.AddWarning("Your Okta password is in warning state and needs to be changed soon.")
case "MFA_REQUIRED", "MFA_ENROLL":
if !cfg.BypassOktaMFA {
return nil, logical.ErrorResponse("okta mfa required for this account but mfa bypass not set in config"), nil, nil
}
case "SUCCESS": case "SUCCESS":
// Do nothing here // Do nothing here
@ -126,7 +131,13 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
} }
// Verify result status again in case a switch case above modifies result // Verify result status again in case a switch case above modifies result
if result.Status != "SUCCESS" && result.Status != "PASSWORD_WARN" { switch {
case result.Status == "SUCCESS",
result.Status == "PASSWORD_WARN",
result.Status == "MFA_REQUIRED" && cfg.BypassOktaMFA,
result.Status == "MFA_ENROLL" && cfg.BypassOktaMFA:
// Allowed
default:
if b.Logger().IsDebug() { if b.Logger().IsDebug() {
b.Logger().Debug("auth/okta: authentication returned a non-success status", "status", result.Status) b.Logger().Debug("auth/okta: authentication returned a non-success status", "status", result.Status)
} }

View File

@ -54,6 +54,10 @@ func pathConfig(b *backend) *framework.Path {
Type: framework.TypeDurationSecond, Type: framework.TypeDurationSecond,
Description: `Maximum duration after which authentication will be expired`, Description: `Maximum duration after which authentication will be expired`,
}, },
"bypass_okta_mfa": &framework.FieldSchema{
Type: framework.TypeBool,
Description: `When set true, requests by Okta for a MFA check will be bypassed. This also disallows certain status checks on the account, such as whether the password is expired.`,
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@ -103,6 +107,7 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f
"org_name": cfg.Org, "org_name": cfg.Org,
"ttl": cfg.TTL.Seconds(), "ttl": cfg.TTL.Seconds(),
"max_ttl": cfg.MaxTTL.Seconds(), "max_ttl": cfg.MaxTTL.Seconds(),
"bypass_okta_mfa": cfg.BypassOktaMFA,
}, },
} }
if cfg.BaseURL != "" { if cfg.BaseURL != "" {
@ -112,6 +117,10 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f
resp.Data["production"] = *cfg.Production resp.Data["production"] = *cfg.Production
} }
if cfg.BypassOktaMFA {
resp.AddWarning("Okta MFA bypass is configured. In addition to ignoring Okta MFA requests, certain other account statuses will not be seen, such as PASSWORD_EXPIRED. Authentication will succeed in these cases.")
}
return resp, nil return resp, nil
} }
@ -175,6 +184,11 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
cfg.Production = nil cfg.Production = nil
} }
bypass, ok := d.GetOk("bypass_okta_mfa")
if ok {
cfg.BypassOktaMFA = bypass.(bool)
}
ttl, ok := d.GetOk("ttl") ttl, ok := d.GetOk("ttl")
if ok { if ok {
cfg.TTL = time.Duration(ttl.(int)) * time.Second cfg.TTL = time.Duration(ttl.(int)) * time.Second
@ -197,7 +211,13 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
return nil, err return nil, err
} }
return nil, nil var resp *logical.Response
if cfg.BypassOktaMFA {
resp = new(logical.Response)
resp.AddWarning("Okta MFA bypass is configured. In addition to ignoring Okta MFA requests, certain other account statuses will not be seen, such as PASSWORD_EXPIRED. Authentication will succeed in these cases.")
}
return resp, nil
} }
func (b *backend) pathConfigExistenceCheck(ctx context.Context, req *logical.Request, d *framework.FieldData) (bool, error) { func (b *backend) pathConfigExistenceCheck(ctx context.Context, req *logical.Request, d *framework.FieldData) (bool, error) {
@ -234,6 +254,7 @@ type ConfigEntry struct {
Production *bool `json:"is_production,omitempty"` Production *bool `json:"is_production,omitempty"`
TTL time.Duration `json:"ttl"` TTL time.Duration `json:"ttl"`
MaxTTL time.Duration `json:"max_ttl"` MaxTTL time.Duration `json:"max_ttl"`
BypassOktaMFA bool `json:"bypass_okta_mfa"`
} }
const pathConfigHelp = ` const pathConfigHelp = `

View File

@ -37,6 +37,9 @@ distinction between the `create` and `update` capabilities inside ACL policies.
- `ttl` `(string: "")` - Duration after which authentication will be expired. - `ttl` `(string: "")` - Duration after which authentication will be expired.
- `max_ttl` `(string: "")` - Maximum duration after which authentication will - `max_ttl` `(string: "")` - Maximum duration after which authentication will
be expired. be expired.
- `bypass_okta_mfa` `(bool: false)` - Whether to bypass an Okta MFA request.
Useful if using one of Vault's built-in MFA mechanisms, but this will also
cause certain other statuses to be ignored, such as `PASSWORD_EXPIRED`.
### Sample Payload ### Sample Payload