From 0139bbb9634065a4cbed283d8e5826bfb1e2a135 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Sun, 11 Dec 2016 11:24:44 -0800 Subject: [PATCH] Adds support for a new "acl_agent_token" which is used for internal catalog operations. --- command/agent/agent.go | 7 ++-- command/agent/config.go | 20 ++++++++++++ command/agent/config_test.go | 32 ++++++++++++++++++- command/agent/local.go | 4 +-- consul/config.go | 18 +++++++++++ consul/config_test.go | 20 ++++++++++++ consul/leader.go | 6 ++-- .../source/docs/agent/options.html.markdown | 8 +++++ 8 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 consul/config_test.go diff --git a/command/agent/agent.go b/command/agent/agent.go index 23c7f4d557..7e464b2fa4 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -227,8 +227,8 @@ func Create(config *Config, logOutput io.Writer, logWriter *logger.LogWriter, Port: agent.config.Ports.Server, Tags: []string{}, } - // TODO (slackpad) - Plumb the "acl_agent_token" into here. - agent.state.AddService(&consulService, "") + + agent.state.AddService(&consulService, agent.config.GetTokenForAgent()) } else { err = agent.setupClient() agent.state.SetIface(agent.client) @@ -364,6 +364,9 @@ func (a *Agent) consulConfig() *consul.Config { if a.config.ACLToken != "" { base.ACLToken = a.config.ACLToken } + if a.config.ACLAgentToken != "" { + base.ACLAgentToken = a.config.ACLAgentToken + } if a.config.ACLMasterToken != "" { base.ACLMasterToken = a.config.ACLMasterToken } diff --git a/command/agent/config.go b/command/agent/config.go index fc80b4bf1a..c4c4c06e29 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -488,6 +488,11 @@ type Config struct { // token is not provided. If not configured the 'anonymous' token is used. ACLToken string `mapstructure:"acl_token" json:"-"` + // ACLAgentToken is the default token used to make requests for the agent + // itself, such as for registering itself with the catalog. If not + // configured, the 'acl_token' will be used. + ACLAgentToken string `mapstructure:"acl_agent_token" json:"-"` + // ACLMasterToken is used to bootstrap the ACL system. It should be specified // on the servers in the ACLDatacenter. When the leader comes online, it ensures // that the Master token is available. This provides the initial token. @@ -756,6 +761,18 @@ func (c *Config) ClientListener(override string, port int) (net.Addr, error) { return &net.TCPAddr{IP: ip, Port: port}, nil } +// GetTokenForAgent returns the token the agent should use for its own internal +// operations, such as registering itself with the catalog. +func (c *Config) GetTokenForAgent() string { + if c.ACLAgentToken != "" { + return c.ACLAgentToken + } else if c.ACLToken != "" { + return c.ACLToken + } else { + return "" + } +} + // DecodeConfig reads the configuration from the given reader in JSON // format and decodes it into a proper Config structure. func DecodeConfig(r io.Reader) (*Config, error) { @@ -1466,6 +1483,9 @@ func MergeConfig(a, b *Config) *Config { if b.ACLToken != "" { result.ACLToken = b.ACLToken } + if b.ACLAgentToken != "" { + result.ACLAgentToken = b.ACLAgentToken + } if b.ACLMasterToken != "" { result.ACLMasterToken = b.ACLMasterToken } diff --git a/command/agent/config_test.go b/command/agent/config_test.go index 48a615f974..58f97213b3 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -643,7 +643,7 @@ func TestDecodeConfig(t *testing.T) { } // ACLs - input = `{"acl_token": "1234", "acl_datacenter": "dc2", + input = `{"acl_token": "1234", "acl_agent_token": "5678", "acl_datacenter": "dc2", "acl_ttl": "60s", "acl_down_policy": "deny", "acl_default_policy": "deny", "acl_master_token": "2345", "acl_replication_token": "8675309"}` @@ -655,6 +655,9 @@ func TestDecodeConfig(t *testing.T) { if config.ACLToken != "1234" { t.Fatalf("bad: %#v", config) } + if config.ACLAgentToken != "5678" { + t.Fatalf("bad: %#v", config) + } if config.ACLMasterToken != "2345" { t.Fatalf("bad: %#v", config) } @@ -674,6 +677,32 @@ func TestDecodeConfig(t *testing.T) { t.Fatalf("bad: %#v", config) } + // ACL token precedence. + input = `{}` + config, err = DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + if token := config.GetTokenForAgent(); token != "" { + t.Fatalf("bad: %s", token) + } + input = `{"acl_token": "hello"}` + config, err = DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + if token := config.GetTokenForAgent(); token != "hello" { + t.Fatalf("bad: %s", token) + } + input = `{"acl_agent_token": "world", "acl_token": "hello"}` + config, err = DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + if token := config.GetTokenForAgent(); token != "world" { + t.Fatalf("bad: %s", token) + } + // ACL flag for Consul version 0.8 features (broken out since we will // eventually remove this). We first verify this is opt-out. config = DefaultConfig() @@ -1561,6 +1590,7 @@ func TestMergeConfig(t *testing.T) { CheckUpdateInterval: 8 * time.Minute, CheckUpdateIntervalRaw: "8m", ACLToken: "1234", + ACLAgentToken: "5678", ACLMasterToken: "2345", ACLDatacenter: "dc2", ACLTTL: 15 * time.Second, diff --git a/command/agent/local.go b/command/agent/local.go index 475fe56d9c..c324a15361 100644 --- a/command/agent/local.go +++ b/command/agent/local.go @@ -400,7 +400,7 @@ func (l *localState) setSyncState() error { req := structs.NodeSpecificRequest{ Datacenter: l.config.Datacenter, Node: l.config.NodeName, - QueryOptions: structs.QueryOptions{Token: l.config.ACLToken}, + QueryOptions: structs.QueryOptions{Token: l.config.GetTokenForAgent()}, } var out1 structs.IndexedNodeServices var out2 structs.IndexedHealthChecks @@ -709,7 +709,7 @@ func (l *localState) syncNodeInfo() error { Node: l.config.NodeName, Address: l.config.AdvertiseAddr, TaggedAddresses: l.config.TaggedAddresses, - WriteRequest: structs.WriteRequest{Token: l.config.ACLToken}, + WriteRequest: structs.WriteRequest{Token: l.config.GetTokenForAgent()}, } var out struct{} err := l.iface.RPC("Catalog.Register", &req, &out) diff --git a/consul/config.go b/consul/config.go index 0259b3d31d..c10252ed26 100644 --- a/consul/config.go +++ b/consul/config.go @@ -155,6 +155,11 @@ type Config struct { // backwards compatibility as well. ACLToken string + // ACLAgentToken is the default token used to make requests for the agent + // itself, such as for registering itself with the catalog. If not + // configured, the ACLToken will be used. + ACLAgentToken string + // ACLMasterToken is used to bootstrap the ACL system. It should be specified // on the servers in the ACLDatacenter. When the leader comes online, it ensures // that the Master token is available. This provides the initial token. @@ -370,6 +375,7 @@ func (c *Config) ScaleRaft(raftMultRaw uint) { c.RaftConfig.LeaderLeaseTimeout = raftMult * def.LeaderLeaseTimeout } +// tlsConfig maps this config into a tlsutil config. func (c *Config) tlsConfig() *tlsutil.Config { tlsConf := &tlsutil.Config{ VerifyIncoming: c.VerifyIncoming, @@ -384,3 +390,15 @@ func (c *Config) tlsConfig() *tlsutil.Config { } return tlsConf } + +// GetTokenForAgent returns the token the agent should use for its own internal +// operations, such as registering itself with the catalog. +func (c *Config) GetTokenForAgent() string { + if c.ACLAgentToken != "" { + return c.ACLAgentToken + } else if c.ACLToken != "" { + return c.ACLToken + } else { + return "" + } +} diff --git a/consul/config_test.go b/consul/config_test.go new file mode 100644 index 0000000000..e0d1cb93a8 --- /dev/null +++ b/consul/config_test.go @@ -0,0 +1,20 @@ +package consul + +import ( + "testing" +) + +func TestConfig_GetTokenForAgent(t *testing.T) { + config := DefaultConfig() + if token := config.GetTokenForAgent(); token != "" { + t.Fatalf("bad: %s", token) + } + config.ACLToken = "hello" + if token := config.GetTokenForAgent(); token != "hello" { + t.Fatalf("bad: %s", token) + } + config.ACLAgentToken = "world" + if token := config.GetTokenForAgent(); token != "world" { + t.Fatalf("bad: %s", token) + } +} diff --git a/consul/leader.go b/consul/leader.go index 375b01ae5c..43cde0666a 100644 --- a/consul/leader.go +++ b/consul/leader.go @@ -428,7 +428,7 @@ AFTER_CHECK: Status: structs.HealthPassing, Output: SerfCheckAliveOutput, }, - WriteRequest: structs.WriteRequest{Token: s.config.ACLToken}, + WriteRequest: structs.WriteRequest{Token: s.config.GetTokenForAgent()}, } var out struct{} return s.endpoints.Catalog.Register(&req, &out) @@ -469,7 +469,7 @@ func (s *Server) handleFailedMember(member serf.Member) error { Status: structs.HealthCritical, Output: SerfCheckFailedOutput, }, - WriteRequest: structs.WriteRequest{Token: s.config.ACLToken}, + WriteRequest: structs.WriteRequest{Token: s.config.GetTokenForAgent()}, } var out struct{} return s.endpoints.Catalog.Register(&req, &out) @@ -612,7 +612,7 @@ func (s *Server) reapTombstones(index uint64) { Datacenter: s.config.Datacenter, Op: structs.TombstoneReap, ReapIndex: index, - WriteRequest: structs.WriteRequest{Token: s.config.ACLToken}, + WriteRequest: structs.WriteRequest{Token: s.config.GetTokenForAgent()}, } _, err := s.raftApply(structs.TombstoneRequestType, &req) if err != nil { diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown index ea6fb252c6..59118d9401 100644 --- a/website/source/docs/agent/options.html.markdown +++ b/website/source/docs/agent/options.html.markdown @@ -377,6 +377,14 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass all operations, and "extend-cache" allows any cached ACLs to be used, ignoring their TTL values. If a non-cached ACL is used, "extend-cache" acts like "deny". +* `acl_agent_token` - Used for clients + and servers to perform internal operations to the service catalog. If this isn't specified, then + the `acl_token` will be used. This was added in Consul 0.7.2. +

+ For clients, this token must at least have write access to the node name it will register as. For + servers, this must have write access to all nodes that are expected to join the cluster, as well + as write access to the "consul" service, which will be registered automatically on its behalf. + * `acl_enforce_version_8` - Used for clients and servers to determine if enforcement should occur for new ACL policies being previewed before Consul 0.8. Added in Consul 0.7.2, this will default to false in versions of