Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JWT options for acl_auth_method #448

Merged
merged 2 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 2.2.1 (Unreleased)

IMPROVEMENTS:
* resource/acl_auth_method: add support for configuring a JWT auth-method ([#448](https://github.com/hashicorp/terraform-provider-nomad/pull/448))

## 2.2.0 (March 12, 2024)

IMPROVEMENTS:
Expand Down
129 changes: 112 additions & 17 deletions nomad/resource_acl_auth_method.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func resourceACLAuthMethod() *schema.Resource {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
api.ACLAuthMethodTypeOIDC,
api.ACLAuthMethodTypeJWT,
}, false),
},
"token_locality": {
Expand Down Expand Up @@ -80,20 +81,38 @@ func resourceACLAuthMethod() *schema.Resource {
func resourceACLAuthMethodConfig() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"oidc_discovery_url": {
Description: "The OIDC Discovery URL, without any .well-known component (base path).",
"jwt_validation_pub_keys": {
Description: "List of PEM-encoded public keys to use to authenticate signatures locally.",
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
ExactlyOneOf: []string{"config.0.jwks_url", "config.0.oidc_discovery_url"},
},
"jwks_url": {
Description: "JSON Web Key Sets url for authenticating signatures.",
Type: schema.TypeString,
Required: true,
Optional: true,
},
"jwks_ca_cert": {
Description: "PEM encoded CA cert for use by the TLS client used to talk with the JWKS server.",
Type: schema.TypeString,
Optional: true,
},
"oidc_discovery_url": {
Description: "The OIDC Discovery URL, without any .well-known component (base path).",
Type: schema.TypeString,
Optional: true,
RequiredWith: []string{"config.0.oidc_client_id", "config.0.oidc_client_secret"},
},
"oidc_client_id": {
Description: "The OAuth Client ID configured with the OIDC provider.",
Type: schema.TypeString,
Required: true,
Optional: true,
},
"oidc_client_secret": {
Description: "The OAuth Client Secret configured with the OIDC provider.",
Type: schema.TypeString,
Required: true,
Optional: true,
Sensitive: true,
},
"oidc_disable_userinfo": {
Expand All @@ -114,11 +133,17 @@ func resourceACLAuthMethodConfig() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
"bound_issuer": {
Description: "The value against which to match the iss claim in a JWT.",
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
"allowed_redirect_uris": {
Description: "A list of allowed values that can be used for the redirect URI.",
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
Optional: true,
},
"discovery_ca_pem": {
Description: "PEM encoded CA certs for use by the TLS client used to talk with the OIDC Discovery URL.",
Expand All @@ -132,6 +157,24 @@ func resourceACLAuthMethodConfig() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
"expiration_leeway": {
Description: `Duration of leeway when validating expiration of a JWT in the form of a time duration such as "5m" or "1h".`,
Type: schema.TypeString,
Default: "0s",
Optional: true,
},
"not_before_leeway": {
Description: `Duration of leeway when validating not before values of a token in the form of a time duration such as "5m" or "1h".`,
Type: schema.TypeString,
Default: "0s",
Optional: true,
},
"clock_skew_leeway": {
Description: `Duration of leeway when validating all claims in the form of a time duration such as "5m" or "1h".`,
Type: schema.TypeString,
Default: "0s",
Optional: true,
},
"claim_mappings": {
Description: "Mappings of claims (key) that will be copied to a metadata field (value).",
Type: schema.TypeMap,
Expand Down Expand Up @@ -302,6 +345,16 @@ func generateNomadACLAuthMethodConfig(intf interface{}) (*api.ACLAuthMethodConfi

for k, v := range configMap {
switch k {
case "jwt_validation_pub_keys":
unpacked, err := unpackStringArray(v, "jwt_validation_pub_keys")
if err != nil {
return nil, err
}
authMethodConfig.JWTValidationPubKeys = unpacked
case "jwks_url":
authMethodConfig.JWKSURL = v.(string)
case "jwks_ca_cert":
authMethodConfig.JWKSCACert = v.(string)
case "oidc_discovery_url":
authMethodConfig.OIDCDiscoveryURL = v.(string)
case "oidc_client_id":
Expand All @@ -322,6 +375,12 @@ func generateNomadACLAuthMethodConfig(intf interface{}) (*api.ACLAuthMethodConfi
return nil, err
}
authMethodConfig.BoundAudiences = unpacked
case "bound_issuer":
unpacked, err := unpackStringArray(v, "bound_issuer")
if err != nil {
return nil, err
}
authMethodConfig.BoundIssuer = unpacked
case "allowed_redirect_uris":
unpacked, err := unpackStringArray(v, "allowed_redirect_uris")
if err != nil {
Expand All @@ -340,6 +399,24 @@ func generateNomadACLAuthMethodConfig(intf interface{}) (*api.ACLAuthMethodConfi
return nil, err
}
authMethodConfig.SigningAlgs = unpacked
case "expiration_leeway":
dur, err := parseDuration(v.(string), "expiration_leeway")
if err != nil {
return nil, err
}
authMethodConfig.ExpirationLeeway = dur
case "not_before_leeway":
dur, err := parseDuration(v.(string), "not_before_leeway")
if err != nil {
return nil, err
}
authMethodConfig.NotBeforeLeeway = dur
case "clock_skew_leeway":
dur, err := parseDuration(v.(string), "clock_skew_leeway")
if err != nil {
return nil, err
}
authMethodConfig.ClockSkewLeeway = dur
case "claim_mappings":
unpacked, err := unpackStringMap(v, "claim_mappings")
if err != nil {
Expand All @@ -363,17 +440,24 @@ func flattenACLAuthMethodConfig(cfg *api.ACLAuthMethodConfig) []any {
return nil
}
result := map[string]interface{}{
"oidc_discovery_url": cfg.OIDCDiscoveryURL,
"oidc_client_id": cfg.OIDCClientID,
"oidc_client_secret": cfg.OIDCClientSecret,
"oidc_scopes": packStringArray(cfg.OIDCScopes),
"oidc_disable_userinfo": cfg.OIDCDisableUserInfo,
"bound_audiences": packStringArray(cfg.BoundAudiences),
"allowed_redirect_uris": packStringArray(cfg.AllowedRedirectURIs),
"discovery_ca_pem": packStringArray(cfg.DiscoveryCaPem),
"signing_algs": packStringArray(cfg.SigningAlgs),
"claim_mappings": packStringMap(cfg.ClaimMappings),
"list_claim_mappings": packStringMap(cfg.ListClaimMappings),
"jwt_validation_pub_keys": packStringArray(cfg.JWTValidationPubKeys),
"jwks_url": cfg.JWKSURL,
"jwks_ca_cert": cfg.JWKSCACert,
"oidc_discovery_url": cfg.OIDCDiscoveryURL,
"oidc_client_id": cfg.OIDCClientID,
"oidc_client_secret": cfg.OIDCClientSecret,
"oidc_scopes": packStringArray(cfg.OIDCScopes),
"oidc_disable_userinfo": cfg.OIDCDisableUserInfo,
"bound_audiences": packStringArray(cfg.BoundAudiences),
"bound_issuer": packStringArray(cfg.BoundIssuer),
"allowed_redirect_uris": packStringArray(cfg.AllowedRedirectURIs),
"discovery_ca_pem": packStringArray(cfg.DiscoveryCaPem),
"signing_algs": packStringArray(cfg.SigningAlgs),
"expiration_leeway": cfg.ExpirationLeeway.String(),
"not_before_leeway": cfg.NotBeforeLeeway.String(),
"clock_skew_leeway": cfg.ClockSkewLeeway.String(),
"claim_mappings": packStringMap(cfg.ClaimMappings),
"list_claim_mappings": packStringMap(cfg.ListClaimMappings),
}
return []any{result}
}
Expand Down Expand Up @@ -421,3 +505,14 @@ func packStringMap(stringMap map[string]string) map[string]interface{} {
}
return packed
}

func parseDuration(durStr, name string) (time.Duration, error) {
if durStr == "" {
return 0, nil
}
dur, err := time.ParseDuration(durStr)
if err != nil {
return dur, fmt.Errorf("failed to parse %s duration: %v", name, err)
}
return dur, nil
}
31 changes: 26 additions & 5 deletions website/docs/r/acl_auth_method.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ The following arguments are supported:

- `name` `(string: <required>)` - The identifier of the ACL Auth Method.

- `type` `(string: <required>)` - ACL Auth Method SSO workflow type. Currently,
the only supported type is `OIDC`.
- `type` `(string: <required>)` - ACL Auth Method SSO workflow type. Valid values,
are `OIDC` and `JWT`.

- `token_locality` `(string: <required>)` - Defines whether the ACL Auth Method
creates a local or global token when performing SSO login. This field must be
Expand All @@ -65,13 +65,22 @@ The following arguments are supported:
- `config`: `(block: <required>)` - Configuration specific to the auth method
provider.

- `oidc_discovery_url`: `(string: <required>)` - The OIDC Discovery URL,
- `jwt_validation_pub_keys`: `([]string: <optional>)` - List of PEM-encoded
public keys to use to authenticate signatures locally.

- `jwks_url`: `(string: <optional>)` - JSON Web Key Sets url for authenticating
signatures.

- `jwks_ca_cert`: `(string: <optional>)` - PEM encoded CA cert for use by the
TLS client used to talk with the JWKS server.

- `oidc_discovery_url`: `(string: <optional>)` - The OIDC Discovery URL,
without any .well-known component (base path).

- `oidc_client_id`: `(string: <required>)` - The OAuth Client ID configured
- `oidc_client_id`: `(string: <optional>)` - The OAuth Client ID configured
with the OIDC provider.

- `oidc_client_secret`: `(string: <required>)` - The OAuth Client Secret
- `oidc_client_secret`: `(string: <optional>)` - The OAuth Client Secret
configured with the OIDC provider.

- `oidc_scopes`: `([]string: <optional>)` - List of OIDC scopes.
Expand All @@ -84,6 +93,9 @@ The following arguments are supported:
- `bound_audiences`: `([]string: <optional>)` - List of auth claims that are
valid for login.

- `bound_issuer`: `([]string: <optional>)` - The value against which to match
the iss claim in a JWT.

- `allowed_redirect_uris`: `([]string: <optional>)` - A list of allowed values
that can be used for the redirect URI.

Expand All @@ -93,6 +105,15 @@ The following arguments are supported:
- `signing_algs`: `([]string: <optional>)` - A list of supported signing
algorithms.

- `expiration_leeway`: `(string: <optional>)` - Duration of leeway when validating
expiration of a JWT in the form of a time duration such as "5m" or "1h".

- `not_before_leeway`: `(string: <optional>)` - Duration of leeway when validating
not before values of a token in the form of a time duration such as "5m" or "1h".

- `clock_skew_leeway`: `(string: <optional>)` - Duration of leeway when validating
all claims in the form of a time duration such as "5m" or "1h".

- `claim_mappings`: `(map[string]string: <optional>)` - Mappings of claims (key)
that will be copied to a metadata field (value).

Expand Down