* dns token

fix whitespace for docs and comments

fix test cases

fix test cases

remove tabs in help text

Add changelog

Peering dns test

Peering dns test

Partial implementation of Peered DNS test

Swap to new topology lib

expose dns port for integration tests on client

remove partial test implementation

remove extra port exposure

remove changelog from the ent pr

Add dns token to set-agent-token switch

Add enterprise golden file

Use builtin/dns template in tests

Update ent dns policy

Update ent dns template test

remove local gen certs

fix templated policy specs

* add changelog

* go mod tidy
This commit is contained in:
John Landa 2023-09-20 15:50:06 -06:00 committed by GitHub
parent 0236c48369
commit 9eaa8eb026
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 305 additions and 108 deletions

3
.changelog/17936.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
acl: Add new `acl.tokens.dns` config field which specifies the token used implicitly during dns checks.
```

View File

@ -1531,6 +1531,9 @@ func (s *HTTPHandlers) AgentToken(resp http.ResponseWriter, req *http.Request) (
case "config_file_service_registration": case "config_file_service_registration":
s.agent.tokens.UpdateConfigFileRegistrationToken(args.Token, token_store.TokenSourceAPI) s.agent.tokens.UpdateConfigFileRegistrationToken(args.Token, token_store.TokenSourceAPI)
case "dns_token", "dns":
s.agent.tokens.UpdateDNSToken(args.Token, token_store.TokenSourceAPI)
default: default:
return HTTPError{StatusCode: http.StatusNotFound, Reason: fmt.Sprintf("Token %q is unknown", target)} return HTTPError{StatusCode: http.StatusNotFound, Reason: fmt.Sprintf("Token %q is unknown", target)}
} }

View File

@ -882,6 +882,7 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
ACLAgentRecoveryToken: stringVal(c.ACL.Tokens.AgentRecovery), ACLAgentRecoveryToken: stringVal(c.ACL.Tokens.AgentRecovery),
ACLReplicationToken: stringVal(c.ACL.Tokens.Replication), ACLReplicationToken: stringVal(c.ACL.Tokens.Replication),
ACLConfigFileRegistrationToken: stringVal(c.ACL.Tokens.ConfigFileRegistration), ACLConfigFileRegistrationToken: stringVal(c.ACL.Tokens.ConfigFileRegistration),
ACLDNSToken: stringVal(c.ACL.Tokens.DNS),
}, },
// Autopilot // Autopilot

View File

@ -778,6 +778,7 @@ type Tokens struct {
Default *string `mapstructure:"default"` Default *string `mapstructure:"default"`
Agent *string `mapstructure:"agent"` Agent *string `mapstructure:"agent"`
ConfigFileRegistration *string `mapstructure:"config_file_service_registration"` ConfigFileRegistration *string `mapstructure:"config_file_service_registration"`
DNS *string `mapstructure:"dns"`
// Enterprise Only // Enterprise Only
ManagedServiceProvider []ServiceProviderToken `mapstructure:"managed_service_provider"` ManagedServiceProvider []ServiceProviderToken `mapstructure:"managed_service_provider"`

View File

@ -17,6 +17,7 @@
"ACLAgentRecoveryToken": "hidden", "ACLAgentRecoveryToken": "hidden",
"ACLAgentToken": "hidden", "ACLAgentToken": "hidden",
"ACLConfigFileRegistrationToken": "hidden", "ACLConfigFileRegistrationToken": "hidden",
"ACLDNSToken": "hidden",
"ACLDefaultToken": "hidden", "ACLDefaultToken": "hidden",
"ACLReplicationToken": "hidden", "ACLReplicationToken": "hidden",
"DataDir": "", "DataDir": "",

View File

@ -416,7 +416,7 @@ func (d *DNSServer) handlePtr(resp dns.ResponseWriter, req *dns.Msg) {
args := structs.DCSpecificRequest{ args := structs.DCSpecificRequest{
Datacenter: datacenter, Datacenter: datacenter,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
AllowStale: cfg.AllowStale, AllowStale: cfg.AllowStale,
}, },
} }
@ -452,7 +452,7 @@ func (d *DNSServer) handlePtr(resp dns.ResponseWriter, req *dns.Msg) {
sargs := structs.ServiceSpecificRequest{ sargs := structs.ServiceSpecificRequest{
Datacenter: datacenter, Datacenter: datacenter,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
AllowStale: cfg.AllowStale, AllowStale: cfg.AllowStale,
}, },
ServiceAddress: serviceAddress, ServiceAddress: serviceAddress,
@ -513,7 +513,7 @@ func (d *DNSServer) handleQuery(resp dns.ResponseWriter, req *dns.Msg) {
cfg := d.config.Load().(*dnsConfig) cfg := d.config.Load().(*dnsConfig)
// Setup the message response // Set up the message response
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(req) m.SetReply(req)
m.Compress = !cfg.DisableCompression m.Compress = !cfg.DisableCompression
@ -875,7 +875,7 @@ func (d *DNSServer) dispatch(remoteAddr net.Addr, req, resp *dns.Msg, maxRecursi
ServiceName: queryParts[len(queryParts)-1], ServiceName: queryParts[len(queryParts)-1],
EnterpriseMeta: locality.EnterpriseMeta, EnterpriseMeta: locality.EnterpriseMeta,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
}, },
} }
if args.PeerName == "" { if args.PeerName == "" {
@ -1093,7 +1093,7 @@ func (d *DNSServer) nodeLookup(cfg *dnsConfig, lookup nodeLookup, req, resp *dns
PeerName: lookup.PeerName, PeerName: lookup.PeerName,
Node: lookup.Node, Node: lookup.Node,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
AllowStale: cfg.AllowStale, AllowStale: cfg.AllowStale,
}, },
EnterpriseMeta: lookup.EnterpriseMeta, EnterpriseMeta: lookup.EnterpriseMeta,
@ -1425,7 +1425,7 @@ func (d *DNSServer) lookupServiceNodes(cfg *dnsConfig, lookup serviceLookup) (st
ServiceTags: serviceTags, ServiceTags: serviceTags,
TagFilter: lookup.Tag != "", TagFilter: lookup.Tag != "",
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
AllowStale: cfg.AllowStale, AllowStale: cfg.AllowStale,
MaxAge: cfg.CacheMaxAge, MaxAge: cfg.CacheMaxAge,
UseCache: cfg.UseCache, UseCache: cfg.UseCache,
@ -1503,7 +1503,7 @@ func (d *DNSServer) preparedQueryLookup(cfg *dnsConfig, datacenter, query string
Datacenter: datacenter, Datacenter: datacenter,
QueryIDOrName: query, QueryIDOrName: query,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: d.agent.tokens.UserToken(), Token: d.coalesceDNSToken(),
AllowStale: cfg.AllowStale, AllowStale: cfg.AllowStale,
MaxAge: cfg.CacheMaxAge, MaxAge: cfg.CacheMaxAge,
}, },
@ -2172,3 +2172,11 @@ func (d *DNSServer) resolveCNAME(cfg *dnsConfig, name string, maxRecursionLevel
d.logger.Error("all resolvers failed for name", "name", name) d.logger.Error("all resolvers failed for name", "name", name)
return nil return nil
} }
func (d *DNSServer) coalesceDNSToken() string {
if d.agent.tokens.DNSToken() != "" {
return d.agent.tokens.DNSToken()
} else {
return d.agent.tokens.UserToken()
}
}

View File

@ -10,11 +10,12 @@ import (
"context" "context"
"testing" "testing"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/testrpc" "github.com/hashicorp/consul/testrpc"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
) )
func TestDNS_CE_PeeredServices(t *testing.T) { func TestDNS_CE_PeeredServices(t *testing.T) {

View File

@ -6310,6 +6310,22 @@ func TestDNS_ServiceLookup_SRV_RFC_TCP_Default(t *testing.T) {
} }
func initDNSToken(t *testing.T, rpc RPC) {
t.Helper()
reqToken := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
SecretID: "279d4735-f8ca-4d48-b5cc-c00a9713bbf8",
Policies: nil,
TemplatedPolicies: []*structs.ACLTemplatedPolicy{{TemplateName: "builtin/dns"}},
},
WriteRequest: structs.WriteRequest{Token: "root"},
}
err := rpc.RPC(context.Background(), "ACL.TokenSet", &reqToken, &structs.ACLToken{})
require.NoError(t, err)
}
func TestDNS_ServiceLookup_FilterACL(t *testing.T) { func TestDNS_ServiceLookup_FilterACL(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("too slow for testing.Short") t.Skip("too slow for testing.Short")
@ -6322,10 +6338,11 @@ func TestDNS_ServiceLookup_FilterACL(t *testing.T) {
}{ }{
{"root", 1}, {"root", 1},
{"anonymous", 0}, {"anonymous", 0},
{"dns", 1},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run("ACLToken == "+tt.token, func(t *testing.T) { t.Run("ACLToken == "+tt.token, func(t *testing.T) {
a := NewTestAgent(t, ` hcl := `
primary_datacenter = "dc1" primary_datacenter = "dc1"
acl { acl {
@ -6335,13 +6352,34 @@ func TestDNS_ServiceLookup_FilterACL(t *testing.T) {
tokens { tokens {
initial_management = "root" initial_management = "root"
default = "`+tt.token+`" `
if tt.token == "dns" {
// Create a UUID for dns token since it doesn't have an alias
dnsToken := "279d4735-f8ca-4d48-b5cc-c00a9713bbf8"
hcl = hcl + `
default = "anonymous"
dns = "` + dnsToken + `"
`
} else {
hcl = hcl + `
default = "` + tt.token + `"
`
}
hcl = hcl + `
} }
} }
`) `
a := NewTestAgent(t, hcl)
defer a.Shutdown() defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1") testrpc.WaitForLeader(t, a.RPC, "dc1")
if tt.token == "dns" {
initDNSToken(t, a)
}
// Register a service // Register a service
args := &structs.RegisterRequest{ args := &structs.RegisterRequest{
Datacenter: "dc1", Datacenter: "dc1",
@ -6373,6 +6411,7 @@ func TestDNS_ServiceLookup_FilterACL(t *testing.T) {
}) })
} }
} }
func TestDNS_ServiceLookup_MetaTXT(t *testing.T) { func TestDNS_ServiceLookup_MetaTXT(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("too slow for testing.Short") t.Skip("too slow for testing.Short")

View File

@ -529,6 +529,7 @@ type TestACLConfigParams struct {
DefaultToken string DefaultToken string
AgentRecoveryToken string AgentRecoveryToken string
ReplicationToken string ReplicationToken string
DNSToken string
EnableTokenReplication bool EnableTokenReplication bool
} }
@ -547,7 +548,8 @@ func (p *TestACLConfigParams) HasConfiguredTokens() bool {
p.AgentToken != "" || p.AgentToken != "" ||
p.DefaultToken != "" || p.DefaultToken != "" ||
p.AgentRecoveryToken != "" || p.AgentRecoveryToken != "" ||
p.ReplicationToken != "" p.ReplicationToken != "" ||
p.DNSToken != ""
} }
func TestACLConfigNew() string { func TestACLConfigNew() string {
@ -557,6 +559,7 @@ func TestACLConfigNew() string {
InitialManagementToken: "root", InitialManagementToken: "root",
AgentToken: "root", AgentToken: "root",
AgentRecoveryToken: "towel", AgentRecoveryToken: "towel",
DNSToken: "dns",
}) })
} }

View File

@ -26,6 +26,7 @@ type Config struct {
ACLAgentRecoveryToken string ACLAgentRecoveryToken string
ACLReplicationToken string ACLReplicationToken string
ACLConfigFileRegistrationToken string ACLConfigFileRegistrationToken string
ACLDNSToken string
EnterpriseConfig EnterpriseConfig
} }
@ -77,6 +78,7 @@ type persistedTokens struct {
Default string `json:"default,omitempty"` Default string `json:"default,omitempty"`
Agent string `json:"agent,omitempty"` Agent string `json:"agent,omitempty"`
ConfigFileRegistration string `json:"config_file_service_registration,omitempty"` ConfigFileRegistration string `json:"config_file_service_registration,omitempty"`
DNS string `json:"dns,omitempty"`
} }
type fileStore struct { type fileStore struct {
@ -144,6 +146,16 @@ func loadTokens(s *Store, cfg Config, tokens persistedTokens, logger Logger) {
s.UpdateConfigFileRegistrationToken(cfg.ACLConfigFileRegistrationToken, TokenSourceConfig) s.UpdateConfigFileRegistrationToken(cfg.ACLConfigFileRegistrationToken, TokenSourceConfig)
} }
if tokens.DNS != "" {
s.UpdateDNSToken(tokens.DNS, TokenSourceAPI)
if cfg.ACLDNSToken != "" {
logger.Warn("\"dns\" token present in both the configuration and persisted token store, using the persisted token")
}
} else {
s.UpdateDNSToken(cfg.ACLDNSToken, TokenSourceConfig)
}
loadEnterpriseTokens(s, cfg) loadEnterpriseTokens(s, cfg)
} }
@ -206,6 +218,10 @@ func (p *fileStore) saveToFile(s *Store) error {
tokens.ConfigFileRegistration = tok tokens.ConfigFileRegistration = tok
} }
if tok, source := s.DNSTokenAndSource(); tok != "" && source == TokenSourceAPI {
tokens.DNS = tok
}
data, err := json.Marshal(tokens) data, err := json.Marshal(tokens)
if err != nil { if err != nil {
p.logger.Warn("failed to persist tokens", "error", err) p.logger.Warn("failed to persist tokens", "error", err)

View File

@ -8,9 +8,10 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/hashicorp/consul/sdk/testutil"
) )
func TestStore_Load(t *testing.T) { func TestStore_Load(t *testing.T) {
@ -27,6 +28,7 @@ func TestStore_Load(t *testing.T) {
ACLDefaultToken: "charlie", ACLDefaultToken: "charlie",
ACLReplicationToken: "delta", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
require.NoError(t, store.Load(cfg, logger)) require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "alfa", store.AgentToken()) require.Equal(t, "alfa", store.AgentToken())
@ -34,62 +36,69 @@ func TestStore_Load(t *testing.T) {
require.Equal(t, "charlie", store.UserToken()) require.Equal(t, "charlie", store.UserToken())
require.Equal(t, "delta", store.ReplicationToken()) require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken()) require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
}) })
t.Run("updated from Config", func(t *testing.T) { t.Run("updated from Config", func(t *testing.T) {
cfg := Config{ cfg := Config{
DataDir: dataDir, DataDir: dataDir,
ACLDefaultToken: "echo", ACLDefaultToken: "sierra",
ACLAgentToken: "foxtrot", ACLAgentToken: "tango",
ACLAgentRecoveryToken: "golf", ACLAgentRecoveryToken: "uniform",
ACLReplicationToken: "hotel", ACLReplicationToken: "victor",
ACLConfigFileRegistrationToken: "india", ACLConfigFileRegistrationToken: "xray",
ACLDNSToken: "zulu",
} }
// ensures no error for missing persisted tokens file // ensures no error for missing persisted tokens file
require.NoError(t, store.Load(cfg, logger)) require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "echo", store.UserToken()) require.Equal(t, "sierra", store.UserToken())
require.Equal(t, "foxtrot", store.AgentToken()) require.Equal(t, "tango", store.AgentToken())
require.Equal(t, "golf", store.AgentRecoveryToken()) require.Equal(t, "uniform", store.AgentRecoveryToken())
require.Equal(t, "hotel", store.ReplicationToken()) require.Equal(t, "victor", store.ReplicationToken())
require.Equal(t, "india", store.ConfigFileRegistrationToken()) require.Equal(t, "xray", store.ConfigFileRegistrationToken())
require.Equal(t, "zulu", store.DNSToken())
}) })
t.Run("with persisted tokens", func(t *testing.T) { t.Run("with persisted tokens", func(t *testing.T) {
cfg := Config{ cfg := Config{
DataDir: dataDir, DataDir: dataDir,
ACLDefaultToken: "echo", ACLDefaultToken: "alpha",
ACLAgentToken: "foxtrot", ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "golf", ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "hotel", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "delta", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
tokens := `{ tokens := `{
"agent" : "india", "agent" : "golf",
"agent_recovery" : "juliett", "agent_recovery" : "hotel",
"default": "kilo", "default": "india",
"replication": "lima", "replication": "juliet",
"config_file_service_registration": "mike" "config_file_service_registration": "kilo",
"dns": "lima"
}` }`
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600)) require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger)) require.NoError(t, store.Load(cfg, logger))
// no updates since token persistence is not enabled // no updates since token persistence is not enabled
require.Equal(t, "echo", store.UserToken()) require.Equal(t, "alpha", store.UserToken())
require.Equal(t, "foxtrot", store.AgentToken()) require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "golf", store.AgentRecoveryToken()) require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "hotel", store.ReplicationToken()) require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "delta", store.ConfigFileRegistrationToken()) require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
cfg.EnablePersistence = true cfg.EnablePersistence = true
require.NoError(t, store.Load(cfg, logger)) require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "india", store.AgentToken()) require.Equal(t, "golf", store.AgentToken())
require.Equal(t, "juliett", store.AgentRecoveryToken()) require.Equal(t, "hotel", store.AgentRecoveryToken())
require.Equal(t, "kilo", store.UserToken()) require.Equal(t, "india", store.UserToken())
require.Equal(t, "lima", store.ReplicationToken()) require.Equal(t, "juliet", store.ReplicationToken())
require.Equal(t, "mike", store.ConfigFileRegistrationToken()) require.Equal(t, "kilo", store.ConfigFileRegistrationToken())
require.Equal(t, "lima", store.DNSToken())
// check store persistence was enabled // check store persistence was enabled
require.NotNil(t, store.persistence) require.NotNil(t, store.persistence)
@ -115,7 +124,8 @@ func TestStore_Load(t *testing.T) {
"agent_recovery" : "november", "agent_recovery" : "november",
"default": "oscar", "default": "oscar",
"replication" : "papa", "replication" : "papa",
"config_file_service_registration" : "lima" "config_file_service_registration" : "lima",
"dns": "kilo"
}` }`
cfg := Config{ cfg := Config{
@ -126,6 +136,7 @@ func TestStore_Load(t *testing.T) {
ACLAgentRecoveryToken: "sierra", ACLAgentRecoveryToken: "sierra",
ACLReplicationToken: "tango", ACLReplicationToken: "tango",
ACLConfigFileRegistrationToken: "uniform", ACLConfigFileRegistrationToken: "uniform",
ACLDNSToken: "victor",
} }
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600)) require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
@ -136,43 +147,48 @@ func TestStore_Load(t *testing.T) {
require.Equal(t, "oscar", store.UserToken()) require.Equal(t, "oscar", store.UserToken())
require.Equal(t, "papa", store.ReplicationToken()) require.Equal(t, "papa", store.ReplicationToken())
require.Equal(t, "lima", store.ConfigFileRegistrationToken()) require.Equal(t, "lima", store.ConfigFileRegistrationToken())
require.Equal(t, "kilo", store.DNSToken())
}) })
t.Run("with some persisted tokens", func(t *testing.T) { t.Run("with some persisted tokens", func(t *testing.T) {
tokens := `{ tokens := `{
"agent" : "uniform", "agent" : "xray",
"agent_recovery" : "victor" "agent_recovery" : "zulu"
}` }`
cfg := Config{ cfg := Config{
EnablePersistence: true, EnablePersistence: true,
DataDir: dataDir, DataDir: dataDir,
ACLDefaultToken: "whiskey", ACLDefaultToken: "alpha",
ACLAgentToken: "xray", ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "yankee", ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "zulu", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "victor", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600)) require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger)) require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "uniform", store.AgentToken()) require.Equal(t, "xray", store.AgentToken())
require.Equal(t, "victor", store.AgentRecoveryToken()) require.Equal(t, "zulu", store.AgentRecoveryToken())
require.Equal(t, "whiskey", store.UserToken())
require.Equal(t, "zulu", store.ReplicationToken()) require.Equal(t, "alpha", store.UserToken())
require.Equal(t, "victor", store.ConfigFileRegistrationToken()) require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
}) })
t.Run("persisted file contains invalid data", func(t *testing.T) { t.Run("persisted file contains invalid data", func(t *testing.T) {
cfg := Config{ cfg := Config{
EnablePersistence: true, EnablePersistence: true,
DataDir: dataDir, DataDir: dataDir,
ACLDefaultToken: "one", ACLDefaultToken: "alpha",
ACLAgentToken: "two", ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "three", ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "four", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "five", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
require.NoError(t, os.WriteFile(tokenFile, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 0600)) require.NoError(t, os.WriteFile(tokenFile, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 0600))
@ -180,11 +196,12 @@ func TestStore_Load(t *testing.T) {
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "failed to decode tokens file") require.Contains(t, err.Error(), "failed to decode tokens file")
require.Equal(t, "one", store.UserToken()) require.Equal(t, "alpha", store.UserToken())
require.Equal(t, "two", store.AgentToken()) require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "three", store.AgentRecoveryToken()) require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "four", store.ReplicationToken()) require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "five", store.ConfigFileRegistrationToken()) require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
}) })
t.Run("persisted file contains invalid json", func(t *testing.T) { t.Run("persisted file contains invalid json", func(t *testing.T) {
@ -194,8 +211,9 @@ func TestStore_Load(t *testing.T) {
ACLDefaultToken: "alfa", ACLDefaultToken: "alfa",
ACLAgentToken: "bravo", ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie", ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "foxtrot", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "golf", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
require.NoError(t, os.WriteFile(tokenFile, []byte("[1,2,3]"), 0600)) require.NoError(t, os.WriteFile(tokenFile, []byte("[1,2,3]"), 0600))
@ -206,23 +224,31 @@ func TestStore_Load(t *testing.T) {
require.Equal(t, "alfa", store.UserToken()) require.Equal(t, "alfa", store.UserToken())
require.Equal(t, "bravo", store.AgentToken()) require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "charlie", store.AgentRecoveryToken()) require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "foxtrot", store.ReplicationToken()) require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "golf", store.ConfigFileRegistrationToken()) require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
}) })
} }
func TestStore_WithPersistenceLock(t *testing.T) { func TestStore_WithPersistenceLock(t *testing.T) {
// ACLDefaultToken: alpha --> sierra
// ACLAgentToken: bravo --> tango
// ACLAgentRecoveryToken: charlie --> uniform
// ACLReplicationToken: delta --> victor
// ACLConfigFileRegistrationToken: echo --> xray
// ACLDNSToken: foxtrot --> zulu
setupStore := func() (string, *Store) { setupStore := func() (string, *Store) {
dataDir := testutil.TempDir(t, "datadir") dataDir := testutil.TempDir(t, "datadir")
store := new(Store) store := new(Store)
cfg := Config{ cfg := Config{
EnablePersistence: true, EnablePersistence: true,
DataDir: dataDir, DataDir: dataDir,
ACLDefaultToken: "default-token", ACLDefaultToken: "alpha",
ACLAgentToken: "agent-token", ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "recovery-token", ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "replication-token", ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "registration-token", ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
} }
err := store.Load(cfg, hclog.New(nil)) err := store.Load(cfg, hclog.New(nil))
require.NoError(t, err) require.NoError(t, err)
@ -240,37 +266,39 @@ func TestStore_WithPersistenceLock(t *testing.T) {
t.Run("persist some tokens", func(t *testing.T) { t.Run("persist some tokens", func(t *testing.T) {
dataDir, store := setupStore() dataDir, store := setupStore()
err := store.WithPersistenceLock(func() error { err := store.WithPersistenceLock(func() error {
require.True(t, store.UpdateUserToken("the-new-default-token", TokenSourceAPI)) require.True(t, store.UpdateUserToken("sierra", TokenSourceAPI))
require.True(t, store.UpdateAgentRecoveryToken("the-new-recovery-token", TokenSourceAPI)) require.True(t, store.UpdateAgentRecoveryToken("tango", TokenSourceAPI))
return nil return nil
}) })
require.NoError(t, err) require.NoError(t, err)
// Only API-sourced tokens are persisted. // Only API-sourced tokens are persisted.
requirePersistedTokens(t, dataDir, persistedTokens{ requirePersistedTokens(t, dataDir, persistedTokens{
Default: "the-new-default-token", Default: "sierra",
AgentRecovery: "the-new-recovery-token", AgentRecovery: "tango",
}) })
}) })
t.Run("persist all tokens", func(t *testing.T) { t.Run("persist all tokens", func(t *testing.T) {
dataDir, store := setupStore() dataDir, store := setupStore()
err := store.WithPersistenceLock(func() error { err := store.WithPersistenceLock(func() error {
require.True(t, store.UpdateUserToken("the-new-default-token", TokenSourceAPI)) require.True(t, store.UpdateUserToken("sierra", TokenSourceAPI))
require.True(t, store.UpdateAgentToken("the-new-agent-token", TokenSourceAPI)) require.True(t, store.UpdateAgentToken("tango", TokenSourceAPI))
require.True(t, store.UpdateAgentRecoveryToken("the-new-recovery-token", TokenSourceAPI)) require.True(t, store.UpdateAgentRecoveryToken("uniform", TokenSourceAPI))
require.True(t, store.UpdateReplicationToken("the-new-replication-token", TokenSourceAPI)) require.True(t, store.UpdateReplicationToken("victor", TokenSourceAPI))
require.True(t, store.UpdateConfigFileRegistrationToken("the-new-registration-token", TokenSourceAPI)) require.True(t, store.UpdateConfigFileRegistrationToken("xray", TokenSourceAPI))
require.True(t, store.UpdateDNSToken("zulu", TokenSourceAPI))
return nil return nil
}) })
require.NoError(t, err) require.NoError(t, err)
requirePersistedTokens(t, dataDir, persistedTokens{ requirePersistedTokens(t, dataDir, persistedTokens{
Default: "the-new-default-token", Default: "sierra",
Agent: "the-new-agent-token", Agent: "tango",
AgentRecovery: "the-new-recovery-token", AgentRecovery: "uniform",
Replication: "the-new-replication-token", Replication: "victor",
ConfigFileRegistration: "the-new-registration-token", ConfigFileRegistration: "xray",
DNS: "zulu",
}) })
}) })

View File

@ -24,6 +24,7 @@ const (
TokenKindUser TokenKindUser
TokenKindReplication TokenKindReplication
TokenKindConfigFileRegistration TokenKindConfigFileRegistration
TokenKindDNS
) )
type watcher struct { type watcher struct {
@ -52,7 +53,7 @@ type Store struct {
// also be used for agent operations if the agent token isn't set. // also be used for agent operations if the agent token isn't set.
userToken string userToken string
// userTokenSource indicates where this token originated from // userTokenSource indicates where this token originated from.
userTokenSource TokenSource userTokenSource TokenSource
// agentToken is used for internal agent operations like self-registering // agentToken is used for internal agent operations like self-registering
@ -60,7 +61,7 @@ type Store struct {
// user-initiated operations. // user-initiated operations.
agentToken string agentToken string
// agentTokenSource indicates where this token originated from // agentTokenSource indicates where this token originated from.
agentTokenSource TokenSource agentTokenSource TokenSource
// agentRecoveryToken is a special token that's only used locally for // agentRecoveryToken is a special token that's only used locally for
@ -68,23 +69,30 @@ type Store struct {
// available. // available.
agentRecoveryToken string agentRecoveryToken string
// agentRecoveryTokenSource indicates where this token originated from // agentRecoveryTokenSource indicates where this token originated from.
agentRecoveryTokenSource TokenSource agentRecoveryTokenSource TokenSource
// replicationToken is a special token that's used by servers to // replicationToken is a special token that's used by servers to
// replicate data from the primary datacenter. // replicate data from the primary datacenter.
replicationToken string replicationToken string
// replicationTokenSource indicates where this token originated from // replicationTokenSource indicates where this token originated from.
replicationTokenSource TokenSource replicationTokenSource TokenSource
// configFileRegistrationToken is used to register services and checks // configFileRegistrationToken is used to register services and checks
// that are defined in configuration files. // that are defined in configuration files.
configFileRegistrationToken string configFileRegistrationToken string
// configFileRegistrationTokenSource indicates where this token originated from // configFileRegistrationTokenSource indicates where this token originated from.
configFileRegistrationTokenSource TokenSource configFileRegistrationTokenSource TokenSource
// dnsToken is a special token that is used as the implicit token for DNS requests
// as well as for DNS-specific RPC requests.
dnsToken string
// dnsTokenSource indicates where the dnsToken originated from.
dnsTokenSource TokenSource
watchers map[int]watcher watchers map[int]watcher
watcherIndex int watcherIndex int
@ -204,6 +212,12 @@ func (t *Store) UpdateConfigFileRegistrationToken(token string, source TokenSour
&t.configFileRegistrationTokenSource, TokenKindConfigFileRegistration) &t.configFileRegistrationTokenSource, TokenKindConfigFileRegistration)
} }
// UpdateDNSToken replaces the current DNS token in the store.
// Returns true if it was changed.
func (t *Store) UpdateDNSToken(token string, source TokenSource) bool {
return t.updateToken(token, source, &t.dnsToken, &t.dnsTokenSource, TokenKindDNS)
}
func (t *Store) updateToken(token string, source TokenSource, dstToken *string, dstSource *TokenSource, kind TokenKind) bool { func (t *Store) updateToken(token string, source TokenSource, dstToken *string, dstSource *TokenSource, kind TokenKind) bool {
t.l.Lock() t.l.Lock()
changed := *dstToken != token || *dstSource != source changed := *dstToken != token || *dstSource != source
@ -261,6 +275,13 @@ func (t *Store) ConfigFileRegistrationToken() string {
return t.configFileRegistrationToken return t.configFileRegistrationToken
} }
func (t *Store) DNSToken() string {
t.l.RLock()
defer t.l.RUnlock()
return t.dnsToken
}
// UserToken returns the best token to use for user operations. // UserToken returns the best token to use for user operations.
func (t *Store) UserTokenAndSource() (string, TokenSource) { func (t *Store) UserTokenAndSource() (string, TokenSource) {
t.l.RLock() t.l.RLock()
@ -299,6 +320,14 @@ func (t *Store) ConfigFileRegistrationTokenAndSource() (string, TokenSource) {
return t.configFileRegistrationToken, t.configFileRegistrationTokenSource return t.configFileRegistrationToken, t.configFileRegistrationTokenSource
} }
// DNSTokenAndSource returns the best token to use for DNS-specific RPC requests and DNS requests
func (t *Store) DNSTokenAndSource() (string, TokenSource) {
t.l.RLock()
defer t.l.RUnlock()
return t.dnsToken, t.dnsTokenSource
}
// IsAgentRecoveryToken checks to see if a given token is the agent recovery token. // IsAgentRecoveryToken checks to see if a given token is the agent recovery token.
// This will never match an empty token for safety. // This will never match an empty token for safety.
func (t *Store) IsAgentRecoveryToken(token string) bool { func (t *Store) IsAgentRecoveryToken(token string) bool {

View File

@ -21,6 +21,8 @@ func TestStore_RegularTokens(t *testing.T) {
replSource TokenSource replSource TokenSource
registration string registration string
registrationSource TokenSource registrationSource TokenSource
dns string
dnsSource TokenSource
} }
tests := []struct { tests := []struct {
@ -95,11 +97,23 @@ func TestStore_RegularTokens(t *testing.T) {
raw: tokens{registration: "G", registrationSource: TokenSourceAPI}, raw: tokens{registration: "G", registrationSource: TokenSourceAPI},
effective: tokens{registration: "G"}, effective: tokens{registration: "G"},
}, },
{
name: "set dns - config",
set: tokens{dns: "D", dnsSource: TokenSourceConfig},
raw: tokens{dns: "D", dnsSource: TokenSourceConfig},
effective: tokens{dns: "D"},
},
{
name: "set dns - api",
set: tokens{dns: "D", dnsSource: TokenSourceAPI},
raw: tokens{dns: "D", dnsSource: TokenSourceAPI},
effective: tokens{dns: "D"},
},
{ {
name: "set all", name: "set all",
set: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G"}, set: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G", dns: "D"},
raw: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G"}, raw: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G", dns: "D"},
effective: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G"}, effective: tokens{user: "U", agent: "A", repl: "R", recovery: "M", registration: "G", dns: "D"},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -125,18 +139,24 @@ func TestStore_RegularTokens(t *testing.T) {
require.True(t, s.UpdateConfigFileRegistrationToken(tt.set.registration, tt.set.registrationSource)) require.True(t, s.UpdateConfigFileRegistrationToken(tt.set.registration, tt.set.registrationSource))
} }
if tt.set.dns != "" {
require.True(t, s.UpdateDNSToken(tt.set.dns, tt.set.dnsSource))
}
// If they don't change then they return false. // If they don't change then they return false.
require.False(t, s.UpdateUserToken(tt.set.user, tt.set.userSource)) require.False(t, s.UpdateUserToken(tt.set.user, tt.set.userSource))
require.False(t, s.UpdateAgentToken(tt.set.agent, tt.set.agentSource)) require.False(t, s.UpdateAgentToken(tt.set.agent, tt.set.agentSource))
require.False(t, s.UpdateReplicationToken(tt.set.repl, tt.set.replSource)) require.False(t, s.UpdateReplicationToken(tt.set.repl, tt.set.replSource))
require.False(t, s.UpdateAgentRecoveryToken(tt.set.recovery, tt.set.recoverySource)) require.False(t, s.UpdateAgentRecoveryToken(tt.set.recovery, tt.set.recoverySource))
require.False(t, s.UpdateConfigFileRegistrationToken(tt.set.registration, tt.set.registrationSource)) require.False(t, s.UpdateConfigFileRegistrationToken(tt.set.registration, tt.set.registrationSource))
require.False(t, s.UpdateDNSToken(tt.set.dns, tt.set.dnsSource))
require.Equal(t, tt.effective.user, s.UserToken()) require.Equal(t, tt.effective.user, s.UserToken())
require.Equal(t, tt.effective.agent, s.AgentToken()) require.Equal(t, tt.effective.agent, s.AgentToken())
require.Equal(t, tt.effective.recovery, s.AgentRecoveryToken()) require.Equal(t, tt.effective.recovery, s.AgentRecoveryToken())
require.Equal(t, tt.effective.repl, s.ReplicationToken()) require.Equal(t, tt.effective.repl, s.ReplicationToken())
require.Equal(t, tt.effective.registration, s.ConfigFileRegistrationToken()) require.Equal(t, tt.effective.registration, s.ConfigFileRegistrationToken())
require.Equal(t, tt.effective.dns, s.DNSToken())
tok, src := s.UserTokenAndSource() tok, src := s.UserTokenAndSource()
require.Equal(t, tt.raw.user, tok) require.Equal(t, tt.raw.user, tok)
@ -157,6 +177,10 @@ func TestStore_RegularTokens(t *testing.T) {
tok, src = s.ConfigFileRegistrationTokenAndSource() tok, src = s.ConfigFileRegistrationTokenAndSource()
require.Equal(t, tt.raw.registration, tok) require.Equal(t, tt.raw.registration, tok)
require.Equal(t, tt.raw.registrationSource, src) require.Equal(t, tt.raw.registrationSource, src)
tok, src = s.DNSTokenAndSource()
require.Equal(t, tt.raw.dns, tok)
require.Equal(t, tt.raw.dnsSource, src)
}) })
} }
} }
@ -211,6 +235,7 @@ func TestStore_Notify(t *testing.T) {
replicationNotifier := newNotification(t, s, TokenKindReplication) replicationNotifier := newNotification(t, s, TokenKindReplication)
replicationNotifier2 := newNotification(t, s, TokenKindReplication) replicationNotifier2 := newNotification(t, s, TokenKindReplication)
registrationNotifier := newNotification(t, s, TokenKindConfigFileRegistration) registrationNotifier := newNotification(t, s, TokenKindConfigFileRegistration)
dnsNotifier := newNotification(t, s, TokenKindDNS)
// perform an update of the user token // perform an update of the user token
require.True(t, s.UpdateUserToken("edcae2a2-3b51-4864-b412-c7a568f49cb1", TokenSourceConfig)) require.True(t, s.UpdateUserToken("edcae2a2-3b51-4864-b412-c7a568f49cb1", TokenSourceConfig))
@ -224,6 +249,7 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch) requireNotNotified(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
// update the agent token which should send a notification to the agent notifier. // update the agent token which should send a notification to the agent notifier.
require.True(t, s.UpdateAgentToken("5d748ec2-d536-461f-8e2a-1f7eae98d559", TokenSourceAPI)) require.True(t, s.UpdateAgentToken("5d748ec2-d536-461f-8e2a-1f7eae98d559", TokenSourceAPI))
@ -234,6 +260,7 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch) requireNotNotified(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
// update the agent recovery token which should send a notification to the agent recovery notifier. // update the agent recovery token which should send a notification to the agent recovery notifier.
require.True(t, s.UpdateAgentRecoveryToken("789badc8-f850-43e1-8742-9b9f484957cc", TokenSourceAPI)) require.True(t, s.UpdateAgentRecoveryToken("789badc8-f850-43e1-8742-9b9f484957cc", TokenSourceAPI))
@ -244,6 +271,7 @@ func TestStore_Notify(t *testing.T) {
requireNotifiedOnce(t, agentRecoveryNotifier.Ch) requireNotifiedOnce(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch) requireNotNotified(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
// update the replication token which should send a notification to the replication notifier. // update the replication token which should send a notification to the replication notifier.
require.True(t, s.UpdateReplicationToken("789badc8-f850-43e1-8742-9b9f484957cc", TokenSourceAPI)) require.True(t, s.UpdateReplicationToken("789badc8-f850-43e1-8742-9b9f484957cc", TokenSourceAPI))
@ -254,6 +282,7 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotifiedOnce(t, replicationNotifier2.Ch) requireNotifiedOnce(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
s.StopNotify(replicationNotifier2) s.StopNotify(replicationNotifier2)
@ -266,6 +295,7 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch) requireNotNotified(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
// update the config file registration token which should send a notification to the replication notifier. // update the config file registration token which should send a notification to the replication notifier.
require.True(t, s.UpdateConfigFileRegistrationToken("82fe7362-7d83-4f43-bb27-c35f1f15083c", TokenSourceAPI)) require.True(t, s.UpdateConfigFileRegistrationToken("82fe7362-7d83-4f43-bb27-c35f1f15083c", TokenSourceAPI))
@ -276,6 +306,18 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch) requireNotNotified(t, replicationNotifier2.Ch)
requireNotifiedOnce(t, registrationNotifier.Ch) requireNotifiedOnce(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
// update the dns token which should send a notification to the replication notifier.
require.True(t, s.UpdateDNSToken("ce8e829f-dc45-4ba7-9dd3-1dbbe070f573", TokenSourceAPI))
requireNotNotified(t, agentNotifier.Ch)
requireNotNotified(t, userNotifier.Ch)
requireNotNotified(t, replicationNotifier.Ch)
requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, replicationNotifier2.Ch)
requireNotNotified(t, registrationNotifier.Ch)
requireNotifiedOnce(t, dnsNotifier.Ch)
// request updates that are not changes // request updates that are not changes
require.False(t, s.UpdateAgentToken("5d748ec2-d536-461f-8e2a-1f7eae98d559", TokenSourceAPI)) require.False(t, s.UpdateAgentToken("5d748ec2-d536-461f-8e2a-1f7eae98d559", TokenSourceAPI))
@ -283,6 +325,7 @@ func TestStore_Notify(t *testing.T) {
require.False(t, s.UpdateUserToken("47788919-f944-476a-bda5-446d64be1df8", TokenSourceAPI)) require.False(t, s.UpdateUserToken("47788919-f944-476a-bda5-446d64be1df8", TokenSourceAPI))
require.False(t, s.UpdateReplicationToken("eb0b56b9-fa65-4ae1-902a-c64457c62ac6", TokenSourceAPI)) require.False(t, s.UpdateReplicationToken("eb0b56b9-fa65-4ae1-902a-c64457c62ac6", TokenSourceAPI))
require.False(t, s.UpdateConfigFileRegistrationToken("82fe7362-7d83-4f43-bb27-c35f1f15083c", TokenSourceAPI)) require.False(t, s.UpdateConfigFileRegistrationToken("82fe7362-7d83-4f43-bb27-c35f1f15083c", TokenSourceAPI))
require.False(t, s.UpdateDNSToken("ce8e829f-dc45-4ba7-9dd3-1dbbe070f573", TokenSourceAPI))
// ensure that notifications were not sent // ensure that notifications were not sent
requireNotNotified(t, agentNotifier.Ch) requireNotNotified(t, agentNotifier.Ch)
@ -290,4 +333,5 @@ func TestStore_Notify(t *testing.T) {
requireNotNotified(t, replicationNotifier.Ch) requireNotNotified(t, replicationNotifier.Ch)
requireNotNotified(t, agentRecoveryNotifier.Ch) requireNotNotified(t, agentRecoveryNotifier.Ch)
requireNotNotified(t, registrationNotifier.Ch) requireNotNotified(t, registrationNotifier.Ch)
requireNotNotified(t, dnsNotifier.Ch)
} }

View File

@ -1379,6 +1379,10 @@ func (a *Agent) UpdateConfigFileRegistrationToken(token string, q *WriteOptions)
return a.updateToken("config_file_service_registration", token, q) return a.updateToken("config_file_service_registration", token, q)
} }
func (a *Agent) UpdateDNSToken(token string, q *WriteOptions) (*WriteMeta, error) {
return a.updateToken("dns", token, q)
}
// updateToken can be used to update one of an agent's ACL tokens after the agent has // updateToken can be used to update one of an agent's ACL tokens after the agent has
// started. The tokens are may not be persisted, so will need to be updated again if // started. The tokens are may not be persisted, so will need to be updated again if
// the agent is restarted unless the agent is configured to persist them. // the agent is restarted unless the agent is configured to persist them.

View File

@ -1635,6 +1635,10 @@ func TestAPI_AgentUpdateToken(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if _, err := agent.UpdateDNSToken("root", nil); err != nil {
t.Fatalf("err: %v", err)
}
}) })
t.Run("new with fallback", func(t *testing.T) { t.Run("new with fallback", func(t *testing.T) {
@ -1723,6 +1727,9 @@ func TestAPI_AgentUpdateToken(t *testing.T) {
_, err = agent.UpdateConfigFileRegistrationToken("root", nil) _, err = agent.UpdateConfigFileRegistrationToken("root", nil)
require.Error(t, err) require.Error(t, err)
_, err = agent.UpdateDNSToken("root", nil)
require.Error(t, err)
}) })
} }

View File

@ -8,9 +8,10 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/flags" "github.com/hashicorp/consul/command/flags"
"github.com/hashicorp/consul/command/helpers" "github.com/hashicorp/consul/command/helpers"
"github.com/mitchellh/cli"
) )
func New(ui cli.Ui) *cmd { func New(ui cli.Ui) *cmd {
@ -63,6 +64,8 @@ func (c *cmd) Run(args []string) int {
_, err = client.Agent().UpdateReplicationACLToken(token, nil) _, err = client.Agent().UpdateReplicationACLToken(token, nil)
case "config_file_service_registration": case "config_file_service_registration":
_, err = client.Agent().UpdateConfigFileRegistrationToken(token, nil) _, err = client.Agent().UpdateConfigFileRegistrationToken(token, nil)
case "dns":
_, err = client.Agent().UpdateDNSToken(token, nil)
default: default:
c.UI.Error(fmt.Sprintf("Unknown token type")) c.UI.Error(fmt.Sprintf("Unknown token type"))
return 1 return 1
@ -140,6 +143,11 @@ Usage: consul acl set-agent-token [options] TYPE TOKEN
If a service or check definition contains a 'token' If a service or check definition contains a 'token'
field, then that token is used instead. field, then that token is used instead.
dns This is the token that the will be used in place of the default
token when specified for DNS requests and for DNS-specific RPCs.
If not provided the agent will attempt to use the default token
if one is present, then fallback to the anonymous token.
Example: Example:
$ consul acl set-agent-token default c4d0f8df-3aba-4ab6-a7a0-35b760dc29a1 $ consul acl set-agent-token default c4d0f8df-3aba-4ab6-a7a0-35b760dc29a1

View File

@ -6,7 +6,7 @@ require (
github.com/hashicorp/consul/api v1.24.0 github.com/hashicorp/consul/api v1.24.0
github.com/hashicorp/consul/sdk v0.14.1 github.com/hashicorp/consul/sdk v0.14.1
github.com/hashicorp/consul/test/integration/consul-container v0.0.0-20230628201853-bdf4fad7c5a5 github.com/hashicorp/consul/test/integration/consul-container v0.0.0-20230628201853-bdf4fad7c5a5
github.com/hashicorp/consul/testing/deployer v0.0.0-00010101000000-000000000000 github.com/hashicorp/consul/testing/deployer v0.0.0-20230811171106-4a0afb5d1373
github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-cleanhttp v0.5.2
github.com/itchyny/gojq v0.12.13 github.com/itchyny/gojq v0.12.13
github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/copystructure v1.2.0

View File

@ -14,7 +14,7 @@ require (
github.com/hashicorp/consul/envoyextensions v0.4.1 github.com/hashicorp/consul/envoyextensions v0.4.1
github.com/hashicorp/consul/proto-public v0.4.1 github.com/hashicorp/consul/proto-public v0.4.1
github.com/hashicorp/consul/sdk v0.14.1 github.com/hashicorp/consul/sdk v0.14.1
github.com/hashicorp/consul/testing/deployer v0.0.0-00010101000000-000000000000 github.com/hashicorp/consul/testing/deployer v0.0.0-20230811171106-4a0afb5d1373
github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-uuid v1.0.3

View File

@ -114,7 +114,7 @@ func AssertUpstreamEndpointStatusWithClient(
| length`, | length`,
clusterName, healthStatus) clusterName, healthStatus)
results, err := utils.JQFilter(clusters, filter) results, err := utils.JQFilter(clusters, filter)
require.NoErrorf(r, err, "could not found cluster name %q: %v \n%s", clusterName, err, clusters) require.NoErrorf(r, err, "could not find cluster name %q: %v \n%s", clusterName, err, clusters)
require.Len(r, results, 1) // the final part of the pipeline is "length" which only ever returns 1 result require.Len(r, results, 1) // the final part of the pipeline is "length" which only ever returns 1 result
result, err := strconv.Atoi(results[0]) result, err := strconv.Atoi(results[0])

View File

@ -614,6 +614,7 @@ func newContainerRequest(config Config, opts containerOpts, ports ...int) (podRe
"8500/tcp", // Consul HTTP API "8500/tcp", // Consul HTTP API
"8501/tcp", // Consul HTTPs API "8501/tcp", // Consul HTTPs API
"8502/tcp", // Consul gRPC API "8502/tcp", // Consul gRPC API
"8600/udp", // Consul DNS API
"8443/tcp", // Envoy Gateway Listener "8443/tcp", // Envoy Gateway Listener

View File

@ -43,7 +43,7 @@ type PeeringClusterSize struct {
// //
// - an accepting cluster with 3 servers and 1 client agent. The client should be used to // - an accepting cluster with 3 servers and 1 client agent. The client should be used to
// host a service for export: staticServerSvc. // host a service for export: staticServerSvc.
// - an dialing cluster with 1 server and 1 client. The client should be used to host a // - a dialing cluster with 1 server and 1 client. The client should be used to host a
// service connecting to staticServerSvc. // service connecting to staticServerSvc.
// - Create the peering, export the service from accepting cluster, and verify service // - Create the peering, export the service from accepting cluster, and verify service
// connectivity. // connectivity.
@ -120,7 +120,7 @@ func BasicPeeringTwoClustersSetup(
libassert.PeeringStatus(t, acceptingClient, AcceptingPeerName, api.PeeringStateActive) libassert.PeeringStatus(t, acceptingClient, AcceptingPeerName, api.PeeringStateActive)
// libassert.PeeringExports(t, acceptingClient, acceptingPeerName, 1) // libassert.PeeringExports(t, acceptingClient, acceptingPeerName, 1)
// Register an static-server service in acceptingCluster and export to dialing cluster // Register a static-server service in acceptingCluster and export to dialing cluster
var serverService, serverSidecarService libservice.Service var serverService, serverSidecarService libservice.Service
{ {
clientNode := acceptingCluster.Clients()[0] clientNode := acceptingCluster.Clients()[0]
@ -144,7 +144,7 @@ func BasicPeeringTwoClustersSetup(
require.NoError(t, serverService.Export("default", AcceptingPeerName, acceptingClient)) require.NoError(t, serverService.Export("default", AcceptingPeerName, acceptingClient))
} }
// Register an static-client service in dialing cluster and set upstream to static-server service // Register a static-client service in dialing cluster and set upstream to static-server service
var clientSidecarService *libservice.ConnectContainer var clientSidecarService *libservice.ConnectContainer
{ {
clientNode := dialingCluster.Clients()[0] clientNode := dialingCluster.Clients()[0]
@ -268,11 +268,11 @@ func NewClusterWithConfig(
} }
// Add numClients static clients to register the service // Add numClients static clients to register the service
configbuiilder := libcluster.NewConfigBuilder(ctx). configBuilder := libcluster.NewConfigBuilder(ctx).
Client(). Client().
Peering(true). Peering(true).
RetryJoin(retryJoin...) RetryJoin(retryJoin...)
clientConf := configbuiilder.ToAgentConfig(t) clientConf := configBuilder.ToAgentConfig(t)
t.Logf("%s client config: \n%s", opts.Datacenter, clientConf.JSON) t.Logf("%s client config: \n%s", opts.Datacenter, clientConf.JSON)
if clientHclConfig != "" { if clientHclConfig != "" {
clientConf.MutatebyAgentConfig(clientHclConfig) clientConf.MutatebyAgentConfig(clientHclConfig)

View File

@ -3,4 +3,4 @@
package topology package topology
const DefaultDataplaneImage = "hashicorp/consul-dataplane:1.1.0" const DefaultDataplaneImage = "hashicorp/consul-dataplane:1.2.1"