diff --git a/command/agent/config.go b/command/agent/config.go index 9a6a043f8a..5c14ae3641 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -194,6 +194,30 @@ type Config struct { CheckUpdateInterval time.Duration `mapstructure:"-"` CheckUpdateIntervalRaw string `mapstructure:"check_update_interval" json:"-"` + // ACLToken is the default token used to make requests if a per-request + // token is not provided. If not configured the 'anonymous' token is used. + ACLToken string `mapstructure:"acl_token" json:"-"` + + // ACLDatacenter is the central datacenter that holds authoritative + // ACL records. This must be the same for the entire cluster. + // If this is not set, ACLs are not enabled. Off by default. + ACLDatacenter string `mapstructure:"acl_datacenter"` + + // ACLCacheInterval is used to control how long ACLs are cached. This has + // a major impact on performance. By default, it is set to 30 seconds. + ACLCacheInterval time.Duration `mapstructure:"-"` + ACLCacheIntervalRaw string `mapstructure:"acl_cache_interval"` + + // ACLDownPolicy is used to control the ACL interaction when we cannot + // reach the ACLDatacenter and the token is not in the cache. + // There are two modes: + // * deny - Deny all requests + // * extend-cache - Ignore the cache expiration, and allow cached + // ACL's to be used to service requests. This + // is the default. If the ACL is not in the cache, + // this acts like deny. + ACLDownPolicy string `mapstructure:"acl_down_policy"` + // AEInterval controls the anti-entropy interval. This is how often // the agent attempts to reconcile it's local state with the server' // representation of our state. Defaults to every 60s. @@ -246,6 +270,8 @@ func DefaultConfig() *Config { Protocol: consul.ProtocolVersionMax, CheckUpdateInterval: 5 * time.Minute, AEInterval: time.Minute, + ACLCacheInterval: 30 * time.Second, + ACLDownPolicy: "extend-cache", } } @@ -341,6 +367,14 @@ func DecodeConfig(r io.Reader) (*Config, error) { result.CheckUpdateInterval = dur } + if raw := result.ACLCacheIntervalRaw; raw != "" { + dur, err := time.ParseDuration(raw) + if err != nil { + return nil, fmt.Errorf("ACLCacheInterval invalid: %v", err) + } + result.ACLCacheInterval = dur + } + return &result, nil } @@ -583,6 +617,19 @@ func MergeConfig(a, b *Config) *Config { if b.SyslogFacility != "" { result.SyslogFacility = b.SyslogFacility } + if b.ACLToken != "" { + result.ACLToken = b.ACLToken + } + if b.ACLDatacenter != "" { + result.ACLDatacenter = b.ACLDatacenter + } + if b.ACLCacheIntervalRaw != "" { + result.ACLCacheInterval = b.ACLCacheInterval + result.ACLCacheIntervalRaw = b.ACLCacheIntervalRaw + } + if b.ACLDownPolicy != "" { + result.ACLDownPolicy = b.ACLDownPolicy + } // Copy the start join addresses result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin)) diff --git a/command/agent/config_test.go b/command/agent/config_test.go index 0c6db15e1c..17abee7e5b 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -356,6 +356,27 @@ func TestDecodeConfig(t *testing.T) { if config.CheckUpdateInterval != 10*time.Minute { t.Fatalf("bad: %#v", config) } + + // ACLs + input = `{"acl_token": "1234", "acl_datacenter": "dc2", + "acl_cache_interval": "60s", "acl_down_policy": "deny"}` + config, err = DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + + if config.ACLToken != "1234" { + t.Fatalf("bad: %#v", config) + } + if config.ACLDatacenter != "dc2" { + t.Fatalf("bad: %#v", config) + } + if config.ACLCacheInterval != 60*time.Second { + t.Fatalf("bad: %#v", config) + } + if config.ACLDownPolicy != "deny" { + t.Fatalf("bad: %#v", config) + } } func TestDecodeConfig_Service(t *testing.T) { @@ -503,6 +524,11 @@ func TestMergeConfig(t *testing.T) { RejoinAfterLeave: true, CheckUpdateInterval: 8 * time.Minute, CheckUpdateIntervalRaw: "8m", + ACLToken: "1234", + ACLDatacenter: "dc2", + ACLCacheInterval: 15 * time.Second, + ACLCacheIntervalRaw: "15s", + ACLDownPolicy: "deny", } c := MergeConfig(a, b)