Browse Source

Change auto config authorizer to allow for future extension

The envisioned changes would allow extra settings to enable dynamically defined auth methods to be used instead of  or in addition to the statically defined one in the configuration.
pull/8152/head
Matt Keeler 4 years ago committed by Matt Keeler
parent
commit
b0fcf86140
  1. 24
      agent/agent_test.go
  2. 56
      agent/config/builder.go
  3. 20
      agent/config/config.go
  4. 140
      agent/config/runtime_test.go

24
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") + `"]
}
}
`

56
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

20
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"`

140
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"
}
list_claim_mappings = {
foo = "bar"
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-----"]
}
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",
},
},
},

Loading…
Cancel
Save