diff --git a/agent/agent_test.go b/agent/agent_test.go index 8e270bd487..887447bb80 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -4594,19 +4594,21 @@ func TestAutoConfig_Integration(t *testing.T) { connect { enabled = true } auto_encrypt { allow_tls = true } auto_config { - authorizer { + authorization { enabled = true - claim_mappings = { - consul_node_name = "node" + static { + claim_mappings = { + consul_node_name = "node" + } + claim_assertions = [ + "value.node == \"${node}\"" + ] + bound_issuer = "consul" + bound_audiences = [ + "consul" + ] + jwt_validation_pub_keys = ["` + strings.ReplaceAll(pub, "\n", "\\n") + `"] } - claim_assertions = [ - "value.node == \"${node}\"" - ] - bound_issuer = "consul" - bound_audiences = [ - "consul" - ] - jwt_validation_pub_keys = ["` + strings.ReplaceAll(pub, "\n", "\\n") + `"] } } ` diff --git a/agent/config/builder.go b/agent/config/builder.go index f02834c306..b9217c1936 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -1937,41 +1937,41 @@ func (b *Builder) autoConfigVal(raw AutoConfigRaw) AutoConfig { val.IPSANs = append(val.IPSANs, ip) } - val.Authorizer = b.autoConfigAuthorizerVal(raw.Authorizer) + val.Authorizer = b.autoConfigAuthorizerVal(raw.Authorization) return val } -func (b *Builder) autoConfigAuthorizerVal(raw AutoConfigAuthorizerRaw) AutoConfigAuthorizer { +func (b *Builder) autoConfigAuthorizerVal(raw AutoConfigAuthorizationRaw) AutoConfigAuthorizer { + // Our config file syntax wraps the static authorizer configuration in a "static" stanza. However + // internally we do not support multiple configured authorization types so the RuntimeConfig just + // inlines the static one. While we can and probably should extend the authorization types in the + // future to support dynamic authorizers (ACL Auth Methods configured via normal APIs) its not + // needed right now so the configuration types will remain simplistic until they need to be otherwise. var val AutoConfigAuthorizer val.Enabled = b.boolValWithDefault(raw.Enabled, false) - val.ClaimAssertions = raw.ClaimAssertions - val.AllowReuse = b.boolValWithDefault(raw.AllowReuse, false) + val.ClaimAssertions = raw.Static.ClaimAssertions + val.AllowReuse = b.boolValWithDefault(raw.Static.AllowReuse, false) val.AuthMethod = structs.ACLAuthMethod{ - Name: "Auto Config Authorizer", - Type: "jwt", - // TODO (autoconf) - Configurable token TTL - MaxTokenTTL: 72 * time.Hour, + Name: "Auto Config Authorizer", + Type: "jwt", EnterpriseMeta: *structs.DefaultEnterpriseMeta(), Config: map[string]interface{}{ - "JWTSupportedAlgs": raw.JWTSupportedAlgs, - "BoundAudiences": raw.BoundAudiences, - "ClaimMappings": raw.ClaimMappings, - "ListClaimMappings": raw.ListClaimMappings, - "OIDCDiscoveryURL": b.stringVal(raw.OIDCDiscoveryURL), - "OIDCDiscoveryCACert": b.stringVal(raw.OIDCDiscoveryCACert), - "JWKSURL": b.stringVal(raw.JWKSURL), - "JWKSCACert": b.stringVal(raw.JWKSCACert), - "JWTValidationPubKeys": raw.JWTValidationPubKeys, - "BoundIssuer": b.stringVal(raw.BoundIssuer), - "ExpirationLeeway": b.durationVal("auto_config.authorizer.expiration_leeway", raw.ExpirationLeeway), - "NotBeforeLeeway": b.durationVal("auto_config.authorizer.not_before_leeway", raw.NotBeforeLeeway), - "ClockSkewLeeway": b.durationVal("auto_config.authorizer.clock_skew_leeway", raw.ClockSkewLeeway), + "JWTSupportedAlgs": raw.Static.JWTSupportedAlgs, + "BoundAudiences": raw.Static.BoundAudiences, + "ClaimMappings": raw.Static.ClaimMappings, + "ListClaimMappings": raw.Static.ListClaimMappings, + "OIDCDiscoveryURL": b.stringVal(raw.Static.OIDCDiscoveryURL), + "OIDCDiscoveryCACert": b.stringVal(raw.Static.OIDCDiscoveryCACert), + "JWKSURL": b.stringVal(raw.Static.JWKSURL), + "JWKSCACert": b.stringVal(raw.Static.JWKSCACert), + "JWTValidationPubKeys": raw.Static.JWTValidationPubKeys, + "BoundIssuer": b.stringVal(raw.Static.BoundIssuer), + "ExpirationLeeway": b.durationVal("auto_config.authorization.static.expiration_leeway", raw.Static.ExpirationLeeway), + "NotBeforeLeeway": b.durationVal("auto_config.authorization.static.not_before_leeway", raw.Static.NotBeforeLeeway), + "ClockSkewLeeway": b.durationVal("auto_config.authorization.static.clock_skew_leeway", raw.Static.ClockSkewLeeway), }, - // should be unnecessary as we aren't using the typical login process to create tokens but this is our - // desired mode regardless so if it ever did matter its probably better to be explicit. - TokenLocality: "local", } return val @@ -2018,7 +2018,7 @@ func (b *Builder) validateAutoConfigAuthorizer(rt RuntimeConfig) error { } // Auto Config Authorization is only supported on servers if !rt.ServerMode { - return fmt.Errorf("auto_config.authorizer.enabled cannot be set to true for client agents") + return fmt.Errorf("auto_config.authorization.enabled cannot be set to true for client agents") } // build out the validator to ensure that the given configuration was valid @@ -2026,7 +2026,7 @@ func (b *Builder) validateAutoConfigAuthorizer(rt RuntimeConfig) error { validator, err := ssoauth.NewValidator(null, &authz.AuthMethod) if err != nil { - return fmt.Errorf("auto_config.authorizer has invalid configuration: %v", err) + return fmt.Errorf("auto_config.authorization.static has invalid configuration: %v", err) } // create a blank identity for use to validate the claim assertions. @@ -2041,14 +2041,14 @@ func (b *Builder) validateAutoConfigAuthorizer(rt RuntimeConfig) error { // validate any HIL filled, err := libtempl.InterpolateHIL(raw, varMap, true) if err != nil { - return fmt.Errorf("auto_config.claim_assertion %q is invalid: %v", raw, err) + return fmt.Errorf("auto_config.authorization.static.claim_assertion %q is invalid: %v", raw, err) } // validate the bexpr syntax - note that for now all the keys mapped by the claim mappings // are not validateable due to them being put inside a map. Some bexpr updates to setup keys // from current map keys would probably be nice here. if _, err := bexpr.CreateEvaluatorForType(filled, nil, blankID.SelectableFields); err != nil { - return fmt.Errorf("auto_config.claim_assertion %q is invalid: %v", raw, err) + return fmt.Errorf("auto_config.authorization.static.claim_assertion %q is invalid: %v", raw, err) } } return nil diff --git a/agent/config/config.go b/agent/config/config.go index 3b2490cd7e..2ed81f6b92 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -689,17 +689,21 @@ type AuditSink struct { } type AutoConfigRaw struct { - Enabled *bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` - IntroToken *string `json:"intro_token,omitempty" hcl:"intro_token" mapstructure:"intro_token"` - IntroTokenFile *string `json:"intro_token_file,omitempty" hcl:"intro_token_file" mapstructure:"intro_token_file"` - ServerAddresses []string `json:"server_addresses,omitempty" hcl:"server_addresses" mapstructure:"server_addresses"` - DNSSANs []string `json:"dns_sans,omitempty" hcl:"dns_sans" mapstructure:"dns_sans"` - IPSANs []string `json:"ip_sans,omitempty" hcl:"ip_sans" mapstructure:"ip_sans"` - Authorizer AutoConfigAuthorizerRaw `json:"authorizer,omitempty" hcl:"authorizer" mapstructure:"authorizer"` + Enabled *bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` + IntroToken *string `json:"intro_token,omitempty" hcl:"intro_token" mapstructure:"intro_token"` + IntroTokenFile *string `json:"intro_token_file,omitempty" hcl:"intro_token_file" mapstructure:"intro_token_file"` + ServerAddresses []string `json:"server_addresses,omitempty" hcl:"server_addresses" mapstructure:"server_addresses"` + DNSSANs []string `json:"dns_sans,omitempty" hcl:"dns_sans" mapstructure:"dns_sans"` + IPSANs []string `json:"ip_sans,omitempty" hcl:"ip_sans" mapstructure:"ip_sans"` + Authorization AutoConfigAuthorizationRaw `json:"authorization,omitempty" hcl:"authorization" mapstructure:"authorization"` +} + +type AutoConfigAuthorizationRaw struct { + Enabled *bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` + Static AutoConfigAuthorizerRaw `json:"static,omitempty" hcl:"static" mapstructure:"static"` } type AutoConfigAuthorizerRaw struct { - Enabled *bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` ClaimAssertions []string `json:"claim_assertions,omitempty" hcl:"claim_assertions" mapstructure:"claim_assertions"` AllowReuse *bool `json:"allow_reuse,omitempty" hcl:"allow_reuse" mapstructure:"allow_reuse"` diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go index bf1e37d9c9..de84f9b6b0 100644 --- a/agent/config/runtime_test.go +++ b/agent/config/runtime_test.go @@ -3918,7 +3918,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { }, hcl: []string{` auto_config { - authorizer { + authorization { enabled = true } } @@ -3926,12 +3926,12 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { json: []string{` { "auto_config": { - "authorizer": { + "authorization": { "enabled": true } } }`}, - err: "auto_config.authorizer.enabled cannot be set to true for client agents", + err: "auto_config.authorization.enabled cannot be set to true for client agents", }, { @@ -3942,7 +3942,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { }, hcl: []string{` auto_config { - authorizer { + authorization { enabled = true } } @@ -3950,12 +3950,12 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { json: []string{` { "auto_config": { - "authorizer": { + "authorization": { "enabled": true } } }`}, - err: `auto_config.authorizer has invalid configuration: exactly one of 'JWTValidationPubKeys', 'JWKSURL', or 'OIDCDiscoveryURL' must be set for type "jwt"`, + err: `auto_config.authorization.static has invalid configuration: exactly one of 'JWTValidationPubKeys', 'JWKSURL', or 'OIDCDiscoveryURL' must be set for type "jwt"`, }, { @@ -3966,24 +3966,28 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { }, hcl: []string{` auto_config { - authorizer { + authorization { enabled = true - jwks_url = "https://fake.uri.local" - oidc_discovery_url = "https://fake.uri.local" + static { + jwks_url = "https://fake.uri.local" + oidc_discovery_url = "https://fake.uri.local" + } } } `}, json: []string{` { "auto_config": { - "authorizer": { + "authorization": { "enabled": true, - "jwks_url": "https://fake.uri.local", - "oidc_discovery_url": "https://fake.uri.local" + "static": { + "jwks_url": "https://fake.uri.local", + "oidc_discovery_url": "https://fake.uri.local" + } } } }`}, - err: `auto_config.authorizer has invalid configuration: exactly one of 'JWTValidationPubKeys', 'JWKSURL', or 'OIDCDiscoveryURL' must be set for type "jwt"`, + err: `auto_config.authorization.static has invalid configuration: exactly one of 'JWTValidationPubKeys', 'JWKSURL', or 'OIDCDiscoveryURL' must be set for type "jwt"`, }, { @@ -3994,28 +3998,32 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { }, hcl: []string{` auto_config { - authorizer { + authorization { enabled = true - jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] - claim_assertions = [ - "values.node == ${node}" - ] + static { + jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] + claim_assertions = [ + "values.node == ${node}" + ] + } } } `}, json: []string{` { "auto_config": { - "authorizer": { + "authorization": { "enabled": true, - "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"], - "claim_assertions": [ - "values.node == ${node}" - ] + "static": { + "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"], + "claim_assertions": [ + "values.node == ${node}" + ] + } } } }`}, - err: `auto_config.claim_assertion "values.node == ${node}" is invalid: Selector "values" is not valid`, + err: `auto_config.authorization.static.claim_assertion "values.node == ${node}" is invalid: Selector "values" is not valid`, }, { desc: "auto config authorizer ok", @@ -4025,14 +4033,16 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { }, hcl: []string{` auto_config { - authorizer { + authorization { enabled = true - jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] - claim_assertions = [ - "value.node == ${node}" - ] - claim_mappings = { - node = "node" + static { + jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] + claim_assertions = [ + "value.node == ${node}" + ] + claim_mappings = { + node = "node" + } } } } @@ -4040,14 +4050,16 @@ func TestConfigFlagsAndEdgecases(t *testing.T) { json: []string{` { "auto_config": { - "authorizer": { + "authorization": { "enabled": true, - "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"], - "claim_assertions": [ - "value.node == ${node}" - ], - "claim_mappings": { - "node": "node" + "static": { + "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"], + "claim_assertions": [ + "value.node == ${node}" + ], + "claim_mappings": { + "node": "node" + } } } } @@ -4304,19 +4316,21 @@ func TestFullConfig(t *testing.T) { "dns_sans": ["6zdaWg9J"], "ip_sans": ["198.18.99.99"], "server_addresses": ["198.18.100.1"], - "authorizer": { + "authorization": { "enabled": true, - "allow_reuse": true, - "claim_mappings": { - "node": "node" - }, - "list_claim_mappings": { - "foo": "bar" - }, - "bound_issuer": "consul", - "bound_audiences": ["consul-cluster-1"], - "claim_assertions": ["value.node == \"${node}\""], - "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] + "static": { + "allow_reuse": true, + "claim_mappings": { + "node": "node" + }, + "list_claim_mappings": { + "foo": "bar" + }, + "bound_issuer": "consul", + "bound_audiences": ["consul-cluster-1"], + "claim_assertions": ["value.node == \"${node}\""], + "jwt_validation_pub_keys": ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] + } } }, "autopilot": { @@ -4962,19 +4976,21 @@ func TestFullConfig(t *testing.T) { dns_sans = ["6zdaWg9J"] ip_sans = ["198.18.99.99"] server_addresses = ["198.18.100.1"] - authorizer = { + authorization = { enabled = true - allow_reuse = true - claim_mappings = { - node = "node" + static { + allow_reuse = true + claim_mappings = { + node = "node" + } + list_claim_mappings = { + foo = "bar" + } + bound_issuer = "consul" + bound_audiences = ["consul-cluster-1"] + claim_assertions = ["value.node == \"${node}\""] + jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] } - list_claim_mappings = { - foo = "bar" - } - bound_issuer = "consul" - bound_audiences = ["consul-cluster-1"] - claim_assertions = ["value.node == \"${node}\""] - jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"] } } autopilot = { @@ -5828,7 +5844,6 @@ func TestFullConfig(t *testing.T) { AuthMethod: structs.ACLAuthMethod{ Name: "Auto Config Authorizer", Type: "jwt", - MaxTokenTTL: 72 * time.Hour, EnterpriseMeta: *structs.DefaultEnterpriseMeta(), Config: map[string]interface{}{ "JWTValidationPubKeys": []string{"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERVchfCZng4mmdvQz1+sJHRN40snC\nYt8NjYOnbnScEXMkyoUmASr88gb7jaVAVt3RYASAbgBjB2Z+EUizWkx5Tg==\n-----END PUBLIC KEY-----"}, @@ -5849,7 +5864,6 @@ func TestFullConfig(t *testing.T) { "ClockSkewLeeway": 0 * time.Second, "JWTSupportedAlgs": []string(nil), }, - TokenLocality: "local", }, }, },