Adds support for a new "acl_agent_token" which is used for internal

catalog operations.
This commit is contained in:
James Phillips 2016-12-11 11:24:44 -08:00
parent 0ed6b1bb18
commit 0139bbb963
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
8 changed files with 107 additions and 8 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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,

View File

@ -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)

View File

@ -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 ""
}
}

20
consul/config_test.go Normal file
View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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".
* <a name"acl_agent_token"></a><a href="#acl_agent_token">`acl_agent_token`</a> - Used for clients
and servers to perform internal operations to the service catalog. If this isn't specified, then
the <a href="#acl_token">`acl_token`</a> will be used. This was added in Consul 0.7.2.
<br><br>
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.
* <a name="acl_enforce_version_8"></a><a href="#acl_enforce_version_8">`acl_enforce_version_8`</a> -
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