agent/local: do not persist the agent tokens

Only default to the user token and agent token for the sync. Change the
exported methods to only return the stored tokens associated with a
specific check or service.
This commit is contained in:
Daniel Nephin 2021-05-04 16:43:44 -04:00
parent 5c7b0e6339
commit 3419d126f1
2 changed files with 75 additions and 54 deletions

View File

@ -227,17 +227,22 @@ func (l *State) SetDiscardCheckOutput(b bool) {
l.discardCheckOutput.Store(b) l.discardCheckOutput.Store(b)
} }
// ServiceToken returns the configured ACL token for the given // ServiceToken returns the ACL token associated with the service. If the service is
// service ID. If none is present, the agent's token is returned. // not found, or does not have a token, the empty string is returned.
func (l *State) ServiceToken(id structs.ServiceID) string { func (l *State) ServiceToken(id structs.ServiceID) string {
l.RLock() l.RLock()
defer l.RUnlock() defer l.RUnlock()
return l.serviceToken(id) if s := l.services[id]; s != nil {
return s.Token
}
return ""
} }
// serviceToken returns an ACL token associated with a service. // aclTokenForServiceSync returns an ACL token associated with a service. If there is
// no ACL token associated with the service, falls back to the agent token, then to the
// user default token.
// This method is not synchronized and the lock must already be held. // This method is not synchronized and the lock must already be held.
func (l *State) serviceToken(id structs.ServiceID) string { func (l *State) aclTokenForServiceSync(id structs.ServiceID) string {
var token string var token string
if s := l.services[id]; s != nil { if s := l.services[id]; s != nil {
token = s.Token token = s.Token
@ -440,17 +445,22 @@ func (l *State) ServiceStates(entMeta *structs.EnterpriseMeta) map[structs.Servi
return m return m
} }
// CheckToken is used to return the configured health check token for a // CheckToken returns the ACL token associated with the check. If the check is
// Check, or if none is configured, the default agent ACL token. // not found, or does not have a token, the empty string is returned.
func (l *State) CheckToken(checkID structs.CheckID) string { func (l *State) CheckToken(id structs.CheckID) string {
l.RLock() l.RLock()
defer l.RUnlock() defer l.RUnlock()
return l.checkToken(checkID) if c := l.checks[id]; c != nil {
return c.Token
}
return ""
} }
// checkToken returns an ACL token associated with a check. // aclTokenForCheckSync returns an ACL token associated with a check. If there is
// no ACL token associated with the check, falls back to the agent token, then to the
// user default token.
// This method is not synchronized and the lock must already be held. // This method is not synchronized and the lock must already be held.
func (l *State) checkToken(id structs.CheckID) string { func (l *State) aclTokenForCheckSync(id structs.CheckID) string {
var token string var token string
c := l.checks[id] c := l.checks[id]
if c != nil { if c != nil {
@ -1132,7 +1142,7 @@ func (l *State) deleteService(key structs.ServiceID) error {
return fmt.Errorf("ServiceID missing") return fmt.Errorf("ServiceID missing")
} }
st := l.serviceToken(key) st := l.aclTokenForServiceSync(key)
req := structs.DeregisterRequest{ req := structs.DeregisterRequest{
Datacenter: l.config.Datacenter, Datacenter: l.config.Datacenter,
@ -1182,7 +1192,7 @@ func (l *State) deleteCheck(key structs.CheckID) error {
return fmt.Errorf("CheckID missing") return fmt.Errorf("CheckID missing")
} }
ct := l.checkToken(key) ct := l.aclTokenForCheckSync(key)
req := structs.DeregisterRequest{ req := structs.DeregisterRequest{
Datacenter: l.config.Datacenter, Datacenter: l.config.Datacenter,
Node: l.config.NodeName, Node: l.config.NodeName,
@ -1226,7 +1236,7 @@ func (l *State) pruneCheck(id structs.CheckID) {
// syncService is used to sync a service to the server // syncService is used to sync a service to the server
func (l *State) syncService(key structs.ServiceID) error { func (l *State) syncService(key structs.ServiceID) error {
st := l.serviceToken(key) st := l.aclTokenForServiceSync(key)
// If the service has associated checks that are out of sync, // If the service has associated checks that are out of sync,
// piggyback them on the service sync so they are part of the // piggyback them on the service sync so they are part of the
@ -1242,7 +1252,7 @@ func (l *State) syncService(key structs.ServiceID) error {
if !key.Matches(c.Check.CompoundServiceID()) { if !key.Matches(c.Check.CompoundServiceID()) {
continue continue
} }
if st != l.checkToken(checkKey) { if st != l.aclTokenForCheckSync(checkKey) {
continue continue
} }
checks = append(checks, c.Check) checks = append(checks, c.Check)
@ -1308,7 +1318,7 @@ func (l *State) syncService(key structs.ServiceID) error {
// syncCheck is used to sync a check to the server // syncCheck is used to sync a check to the server
func (l *State) syncCheck(key structs.CheckID) error { func (l *State) syncCheck(key structs.CheckID) error {
c := l.checks[key] c := l.checks[key]
ct := l.checkToken(key) ct := l.aclTokenForCheckSync(key)
req := structs.RegisterRequest{ req := structs.RegisterRequest{
Datacenter: l.config.Datacenter, Datacenter: l.config.Datacenter,
ID: l.config.NodeID, ID: l.config.NodeID,

View File

@ -1768,33 +1768,38 @@ func TestAgentAntiEntropy_NodeInfo(t *testing.T) {
} }
} }
func TestAgent_ServiceTokens(t *testing.T) { func TestState_ServiceTokens(t *testing.T) {
t.Parallel()
tokens := new(token.Store) tokens := new(token.Store)
tokens.UpdateUserToken("default", token.TokenSourceConfig)
cfg := loadRuntimeConfig(t, `bind_addr = "127.0.0.1" data_dir = "dummy"`) cfg := loadRuntimeConfig(t, `bind_addr = "127.0.0.1" data_dir = "dummy"`)
l := local.NewState(agent.LocalConfig(cfg), nil, tokens) l := local.NewState(agent.LocalConfig(cfg), nil, tokens)
l.TriggerSyncChanges = func() {} l.TriggerSyncChanges = func() {}
l.AddService(&structs.NodeService{ID: "redis"}, "") id := structs.NewServiceID("redis", nil)
// Returns default when no token is set t.Run("defaults to empty string", func(t *testing.T) {
if token := l.ServiceToken(structs.NewServiceID("redis", nil)); token != "default" { require.Equal(t, "", l.ServiceToken(id))
t.Fatalf("bad: %s", token) })
}
// Returns configured token t.Run("empty string when there is no token", func(t *testing.T) {
l.AddService(&structs.NodeService{ID: "redis"}, "abc123") err := l.AddService(&structs.NodeService{ID: "redis"}, "")
if token := l.ServiceToken(structs.NewServiceID("redis", nil)); token != "abc123" { require.NoError(t, err)
t.Fatalf("bad: %s", token)
}
// Keeps token around for the delete require.Equal(t, "", l.ServiceToken(id))
l.RemoveService(structs.NewServiceID("redis", nil)) })
if token := l.ServiceToken(structs.NewServiceID("redis", nil)); token != "abc123" {
t.Fatalf("bad: %s", token) t.Run("returns configured token", func(t *testing.T) {
} err := l.AddService(&structs.NodeService{ID: "redis"}, "abc123")
require.NoError(t, err)
require.Equal(t, "abc123", l.ServiceToken(id))
})
t.Run("RemoveCheck keeps token around for the delete", func(t *testing.T) {
err := l.RemoveService(structs.NewServiceID("redis", nil))
require.NoError(t, err)
require.Equal(t, "abc123", l.ServiceToken(id))
})
} }
func loadRuntimeConfig(t *testing.T, hcl string) *config.RuntimeConfig { func loadRuntimeConfig(t *testing.T, hcl string) *config.RuntimeConfig {
@ -1805,32 +1810,38 @@ func loadRuntimeConfig(t *testing.T, hcl string) *config.RuntimeConfig {
return result.RuntimeConfig return result.RuntimeConfig
} }
func TestAgent_CheckTokens(t *testing.T) { func TestState_CheckTokens(t *testing.T) {
t.Parallel()
tokens := new(token.Store) tokens := new(token.Store)
tokens.UpdateUserToken("default", token.TokenSourceConfig)
cfg := loadRuntimeConfig(t, `bind_addr = "127.0.0.1" data_dir = "dummy"`) cfg := loadRuntimeConfig(t, `bind_addr = "127.0.0.1" data_dir = "dummy"`)
l := local.NewState(agent.LocalConfig(cfg), nil, tokens) l := local.NewState(agent.LocalConfig(cfg), nil, tokens)
l.TriggerSyncChanges = func() {} l.TriggerSyncChanges = func() {}
// Returns default when no token is set id := structs.NewCheckID("mem", nil)
l.AddCheck(&structs.HealthCheck{CheckID: types.CheckID("mem")}, "")
if token := l.CheckToken(structs.NewCheckID("mem", nil)); token != "default" {
t.Fatalf("bad: %s", token)
}
// Returns configured token t.Run("defaults to empty string", func(t *testing.T) {
l.AddCheck(&structs.HealthCheck{CheckID: types.CheckID("mem")}, "abc123") require.Equal(t, "", l.CheckToken(id))
if token := l.CheckToken(structs.NewCheckID("mem", nil)); token != "abc123" { })
t.Fatalf("bad: %s", token)
}
// Keeps token around for the delete t.Run("empty string when there is no token", func(t *testing.T) {
l.RemoveCheck(structs.NewCheckID("mem", nil)) err := l.AddCheck(&structs.HealthCheck{CheckID: "mem"}, "")
if token := l.CheckToken(structs.NewCheckID("mem", nil)); token != "abc123" { require.NoError(t, err)
t.Fatalf("bad: %s", token)
} require.Equal(t, "", l.CheckToken(id))
})
t.Run("returns configured token", func(t *testing.T) {
err := l.AddCheck(&structs.HealthCheck{CheckID: "mem"}, "abc123")
require.NoError(t, err)
require.Equal(t, "abc123", l.CheckToken(id))
})
t.Run("RemoveCheck keeps token around for the delete", func(t *testing.T) {
err := l.RemoveCheck(structs.NewCheckID("mem", nil))
require.NoError(t, err)
require.Equal(t, "abc123", l.CheckToken(id))
})
} }
func TestAgent_CheckCriticalTime(t *testing.T) { func TestAgent_CheckCriticalTime(t *testing.T) {