Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for agent-side ACL token management via API instead of config files. #3324

Merged
merged 7 commits into from
Jul 26, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 17 additions & 23 deletions agent/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ type aclManager struct {
acls *lru.TwoQueueCache

// master is the ACL to use when the agent master token is supplied.
// This may be nil if that option isn't set in the agent config.
master acl.ACL

// down is the ACL to use when the servers are down. This may be nil
Expand All @@ -93,29 +92,24 @@ func newACLManager(config *Config) (*aclManager, error) {
return nil, err
}

// If an agent master token is configured, build a policy and ACL for
// it, otherwise leave it nil.
var master acl.ACL
if len(config.ACLAgentMasterToken) > 0 {
policy := &acl.Policy{
Agents: []*acl.AgentPolicy{
&acl.AgentPolicy{
Node: config.NodeName,
Policy: acl.PolicyWrite,
},
// Build a policy for the agent master token.
policy := &acl.Policy{
Agents: []*acl.AgentPolicy{
&acl.AgentPolicy{
Node: config.NodeName,
Policy: acl.PolicyWrite,
},
Nodes: []*acl.NodePolicy{
&acl.NodePolicy{
Name: "",
Policy: acl.PolicyRead,
},
},
Nodes: []*acl.NodePolicy{
&acl.NodePolicy{
Name: "",
Policy: acl.PolicyRead,
},
}
acl, err := acl.New(acl.DenyAll(), policy)
if err != nil {
return nil, err
}
master = acl
},
}
master, err := acl.New(acl.DenyAll(), policy)
if err != nil {
return nil, err
}

var down acl.ACL
Expand Down Expand Up @@ -155,7 +149,7 @@ func (m *aclManager) lookupACL(a *Agent, id string) (acl.ACL, error) {
id = anonymousToken
} else if acl.RootACL(id) != nil {
return nil, errors.New(rootDenied)
} else if m.master != nil && id == a.config.ACLAgentMasterToken {
} else if a.tokens.IsAgentMasterToken(id) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can agent.tokens be nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - the token store is created as part of constructing an agent, so it will be there even if ACLs aren't enabled.

return m.master, nil
}

Expand Down
15 changes: 13 additions & 2 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ type Agent struct {
// watchPlans tracks all the currently-running watch plans for the
// agent.
watchPlans []*watch.Plan

// tokens holds ACL tokens initially from the configuration, but can
// be updated at runtime, so should always be used instead of going to
// the configuration directly.
tokens *TokenStore
}

func New(c *Config) (*Agent, error) {
Expand Down Expand Up @@ -220,6 +225,7 @@ func New(c *Config) (*Agent, error) {
endpoints: make(map[string]string),
dnsAddrs: dnsAddrs,
httpAddrs: httpAddrs,
tokens: new(TokenStore),
}
if err := a.resolveTmplAddrs(); err != nil {
return nil, err
Expand Down Expand Up @@ -271,6 +277,11 @@ func New(c *Config) (*Agent, error) {
"wan": a.config.AdvertiseAddrWan,
}

// Set up the initial state of the token store based on the config.
a.tokens.UpdateUserToken(a.config.ACLToken)
a.tokens.UpdateAgentToken(a.config.ACLAgentToken)
a.tokens.UpdateAgentMasterToken(a.config.ACLAgentMasterToken)

return a, nil
}

Expand All @@ -292,7 +303,7 @@ func (a *Agent) Start() error {
}

// create the local state
a.state = NewLocalState(c, a.logger)
a.state = NewLocalState(c, a.logger, a.tokens)

// create the config for the rpc server/client
consulCfg, err := a.consulConfig()
Expand Down Expand Up @@ -1344,7 +1355,7 @@ func (a *Agent) sendCoordinate() {
Datacenter: a.config.Datacenter,
Node: a.config.NodeName,
Coord: c,
WriteRequest: structs.WriteRequest{Token: a.config.GetTokenForAgent()},
WriteRequest: structs.WriteRequest{Token: a.tokens.GetTokenForAgent()},
}
var reply struct{}
if err := a.RPC("Coordinate.Update", &req, &reply); err != nil {
Expand Down
21 changes: 21 additions & 0 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,27 @@ func TestAgent_CheckAdvertiseAddrsSettings(t *testing.T) {
}
}

func TestAgent_TokenStore(t *testing.T) {
t.Parallel()

cfg := TestConfig()
cfg.ACLToken = "user"
cfg.ACLAgentToken = "agent"
cfg.ACLAgentMasterToken = "master"
a := NewTestAgent(t.Name(), cfg)
defer a.Shutdown()

if got, want := a.tokens.GetTokenForUser(), "user"; got != want {
t.Fatalf("got %q want %q", got, want)
}
if got, want := a.tokens.GetTokenForAgent(), "agent"; got != want {
t.Fatalf("got %q want %q", got, want)
}
if got, want := a.tokens.IsAgentMasterToken("master"), true; got != want {
t.Fatalf("got %v want %v", got, want)
}
}

func TestAgent_CheckPerformanceSettings(t *testing.T) {
t.Parallel()
// Try a default config.
Expand Down
12 changes: 0 additions & 12 deletions agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1021,18 +1021,6 @@ 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
}
if c.ACLToken != "" {
return c.ACLToken
}
return ""
}

// VerifyUniqueListeners checks to see if an address was used more than once in
// the config
func (c *Config) VerifyUniqueListeners() error {
Expand Down
31 changes: 0 additions & 31 deletions agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1171,37 +1171,6 @@ func TestDecodeConfig(t *testing.T) {
}
}

func TestDecodeConfig_ACLTokenPreference(t *testing.T) {
tests := []struct {
in string
tok string
}{
{
in: `{}`,
tok: "",
},
{
in: `{"acl_token":"a"}`,
tok: "a",
},
{
in: `{"acl_token":"a","acl_agent_token":"b"}`,
tok: "b",
},
}
for _, tt := range tests {
t.Run(tt.in, func(t *testing.T) {
c, err := DecodeConfig(strings.NewReader(tt.in))
if err != nil {
t.Fatalf("got error %v want nil", err)
}
if got, want := c.GetTokenForAgent(), tt.tok; got != want {
t.Fatalf("got token for agent %q want %q", got, want)
}
})
}
}

func TestDecodeConfig_VerifyUniqueListeners(t *testing.T) {
t.Parallel()
tests := []struct {
Expand Down
12 changes: 0 additions & 12 deletions agent/consul/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,15 +460,3 @@ 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
}
if c.ACLToken != "" {
return c.ACLToken
}
return ""
}
21 changes: 0 additions & 21 deletions agent/consul/config_test.go

This file was deleted.

8 changes: 4 additions & 4 deletions agent/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (d *DNSServer) handlePtr(resp dns.ResponseWriter, req *dns.Msg) {
args := structs.DCSpecificRequest{
Datacenter: datacenter,
QueryOptions: structs.QueryOptions{
Token: d.agent.config.ACLToken,
Token: d.agent.tokens.GetTokenForUser(),
AllowStale: *d.config.AllowStale,
},
}
Expand Down Expand Up @@ -388,7 +388,7 @@ func (d *DNSServer) nodeLookup(network, datacenter, node string, req, resp *dns.
Datacenter: datacenter,
Node: node,
QueryOptions: structs.QueryOptions{
Token: d.agent.config.ACLToken,
Token: d.agent.tokens.GetTokenForUser(),
AllowStale: *d.config.AllowStale,
},
}
Expand Down Expand Up @@ -602,7 +602,7 @@ func (d *DNSServer) serviceLookup(network, datacenter, service, tag string, req,
ServiceTag: tag,
TagFilter: tag != "",
QueryOptions: structs.QueryOptions{
Token: d.agent.config.ACLToken,
Token: d.agent.tokens.GetTokenForUser(),
AllowStale: *d.config.AllowStale,
},
}
Expand Down Expand Up @@ -680,7 +680,7 @@ func (d *DNSServer) preparedQueryLookup(network, datacenter, query string, req,
Datacenter: datacenter,
QueryIDOrName: query,
QueryOptions: structs.QueryOptions{
Token: d.agent.config.ACLToken,
Token: d.agent.tokens.GetTokenForUser(),
AllowStale: *d.config.AllowStale,
},

Expand Down
2 changes: 1 addition & 1 deletion agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ func (s *HTTPServer) parseToken(req *http.Request, token *string) {
}

// Set the default ACLToken
*token = s.agent.config.ACLToken
*token = s.agent.tokens.GetTokenForUser()
}

// parseSource is used to parse the ?near=<node> query parameter, used for
Expand Down
4 changes: 2 additions & 2 deletions agent/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,14 +569,14 @@ func TestACLResolution(t *testing.T) {
defer a.Shutdown()

// Check when no token is set
a.Config.ACLToken = ""
a.tokens.UpdateUserToken("")
a.srv.parseToken(req, &token)
if token != "" {
t.Fatalf("bad: %s", token)
}

// Check when ACLToken set
a.Config.ACLToken = "agent"
a.tokens.UpdateUserToken("agent")
a.srv.parseToken(req, &token)
if token != "agent" {
t.Fatalf("bad: %s", token)
Expand Down
16 changes: 7 additions & 9 deletions agent/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,14 @@ type syncStatus struct {
// populated during NewLocalAgent from the agent configuration to avoid
// race conditions with the agent configuration.
type localStateConfig struct {
ACLToken string
AEInterval time.Duration
AdvertiseAddr string
CheckUpdateInterval time.Duration
Datacenter string
NodeID types.NodeID
NodeName string
TaggedAddresses map[string]string
TokenForAgent string
Tokens *TokenStore
}

// localState is used to represent the node's services,
Expand Down Expand Up @@ -89,17 +88,16 @@ type localState struct {
}

// NewLocalState creates a is used to initialize the local state
func NewLocalState(c *Config, lg *log.Logger) *localState {
func NewLocalState(c *Config, lg *log.Logger, tokens *TokenStore) *localState {
lc := localStateConfig{
ACLToken: c.ACLToken,
AEInterval: c.AEInterval,
AdvertiseAddr: c.AdvertiseAddr,
CheckUpdateInterval: c.CheckUpdateInterval,
Datacenter: c.Datacenter,
NodeID: c.NodeID,
NodeName: c.NodeName,
TaggedAddresses: map[string]string{},
TokenForAgent: c.GetTokenForAgent(),
Tokens: tokens,
}
for k, v := range c.TaggedAddresses {
lc.TaggedAddresses[k] = v
Expand Down Expand Up @@ -172,7 +170,7 @@ func (l *localState) ServiceToken(id string) string {
func (l *localState) serviceToken(id string) string {
token := l.serviceTokens[id]
if token == "" {
token = l.config.ACLToken
token = l.config.Tokens.GetTokenForUser()
}
return token
}
Expand Down Expand Up @@ -239,7 +237,7 @@ func (l *localState) CheckToken(checkID types.CheckID) string {
func (l *localState) checkToken(checkID types.CheckID) string {
token := l.checkTokens[checkID]
if token == "" {
token = l.config.ACLToken
token = l.config.Tokens.GetTokenForUser()
}
return token
}
Expand Down Expand Up @@ -449,7 +447,7 @@ func (l *localState) setSyncState() error {
req := structs.NodeSpecificRequest{
Datacenter: l.config.Datacenter,
Node: l.config.NodeName,
QueryOptions: structs.QueryOptions{Token: l.config.TokenForAgent},
QueryOptions: structs.QueryOptions{Token: l.config.Tokens.GetTokenForAgent()},
}
var out1 structs.IndexedNodeServices
var out2 structs.IndexedHealthChecks
Expand Down Expand Up @@ -785,7 +783,7 @@ func (l *localState) syncNodeInfo() error {
Address: l.config.AdvertiseAddr,
TaggedAddresses: l.config.TaggedAddresses,
NodeMeta: l.metadata,
WriteRequest: structs.WriteRequest{Token: l.config.TokenForAgent},
WriteRequest: structs.WriteRequest{Token: l.config.Tokens.GetTokenForAgent()},
}
var out struct{}
err := l.delegate.RPC("Catalog.Register", &req, &out)
Expand Down
Loading