You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
consul/agent/token/persistence_test.go

306 lines
10 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package token
import (
"os"
"path/filepath"
"testing"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/sdk/testutil"
)
func TestStore_Load(t *testing.T) {
dataDir := testutil.TempDir(t, "datadir")
tokenFile := filepath.Join(dataDir, tokensPath)
logger := hclog.New(nil)
store := new(Store)
t.Run("with empty store", func(t *testing.T) {
cfg := Config{
DataDir: dataDir,
ACLAgentToken: "alfa",
ACLAgentRecoveryToken: "bravo",
ACLDefaultToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "alfa", store.AgentToken())
require.Equal(t, "bravo", store.AgentRecoveryToken())
require.Equal(t, "charlie", store.UserToken())
require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
})
t.Run("updated from Config", func(t *testing.T) {
cfg := Config{
DataDir: dataDir,
ACLDefaultToken: "sierra",
ACLAgentToken: "tango",
ACLAgentRecoveryToken: "uniform",
ACLReplicationToken: "victor",
ACLConfigFileRegistrationToken: "xray",
ACLDNSToken: "zulu",
}
// ensures no error for missing persisted tokens file
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "sierra", store.UserToken())
require.Equal(t, "tango", store.AgentToken())
require.Equal(t, "uniform", store.AgentRecoveryToken())
require.Equal(t, "victor", store.ReplicationToken())
require.Equal(t, "xray", store.ConfigFileRegistrationToken())
require.Equal(t, "zulu", store.DNSToken())
})
t.Run("with persisted tokens", func(t *testing.T) {
cfg := Config{
DataDir: dataDir,
ACLDefaultToken: "alpha",
ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
tokens := `{
"agent" : "golf",
"agent_recovery" : "hotel",
"default": "india",
"replication": "juliet",
"config_file_service_registration": "kilo",
"dns": "lima"
}`
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger))
// no updates since token persistence is not enabled
require.Equal(t, "alpha", store.UserToken())
require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
cfg.EnablePersistence = true
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "golf", store.AgentToken())
require.Equal(t, "hotel", store.AgentRecoveryToken())
require.Equal(t, "india", store.UserToken())
require.Equal(t, "juliet", store.ReplicationToken())
require.Equal(t, "kilo", store.ConfigFileRegistrationToken())
require.Equal(t, "lima", store.DNSToken())
// check store persistence was enabled
require.NotNil(t, store.persistence)
})
t.Run("persisted tokens include pre-1.11 agent_master naming", func(t *testing.T) {
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLAgentRecoveryToken: "golf",
}
tokens := `{"agent_master": "juliett"}`
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "juliett", store.AgentRecoveryToken())
})
t.Run("with persisted tokens, persisted tokens override config", func(t *testing.T) {
tokens := `{
"agent" : "mike",
"agent_recovery" : "november",
"default": "oscar",
"replication" : "papa",
"config_file_service_registration" : "lima",
"dns": "kilo"
}`
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "quebec",
ACLAgentToken: "romeo",
ACLAgentRecoveryToken: "sierra",
ACLReplicationToken: "tango",
ACLConfigFileRegistrationToken: "uniform",
ACLDNSToken: "victor",
}
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "mike", store.AgentToken())
require.Equal(t, "november", store.AgentRecoveryToken())
require.Equal(t, "oscar", store.UserToken())
require.Equal(t, "papa", store.ReplicationToken())
require.Equal(t, "lima", store.ConfigFileRegistrationToken())
require.Equal(t, "kilo", store.DNSToken())
})
t.Run("with some persisted tokens", func(t *testing.T) {
tokens := `{
"agent" : "xray",
"agent_recovery" : "zulu"
}`
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "alpha",
ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
require.NoError(t, os.WriteFile(tokenFile, []byte(tokens), 0600))
require.NoError(t, store.Load(cfg, logger))
require.Equal(t, "xray", store.AgentToken())
require.Equal(t, "zulu", store.AgentRecoveryToken())
require.Equal(t, "alpha", store.UserToken())
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) {
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "alpha",
ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
require.NoError(t, os.WriteFile(tokenFile, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 0600))
err := store.Load(cfg, logger)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to decode tokens file")
require.Equal(t, "alpha", store.UserToken())
require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
})
t.Run("persisted file contains invalid json", func(t *testing.T) {
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "alfa",
ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
require.NoError(t, os.WriteFile(tokenFile, []byte("[1,2,3]"), 0600))
err := store.Load(cfg, logger)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to decode tokens file")
require.Equal(t, "alfa", store.UserToken())
require.Equal(t, "bravo", store.AgentToken())
require.Equal(t, "charlie", store.AgentRecoveryToken())
require.Equal(t, "delta", store.ReplicationToken())
require.Equal(t, "echo", store.ConfigFileRegistrationToken())
require.Equal(t, "foxtrot", store.DNSToken())
})
}
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) {
dataDir := testutil.TempDir(t, "datadir")
store := new(Store)
cfg := Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "alpha",
ACLAgentToken: "bravo",
ACLAgentRecoveryToken: "charlie",
ACLReplicationToken: "delta",
ACLConfigFileRegistrationToken: "echo",
ACLDNSToken: "foxtrot",
}
err := store.Load(cfg, hclog.New(nil))
require.NoError(t, err)
return dataDir, store
}
requirePersistedTokens := func(t *testing.T, dataDir string, expected persistedTokens) {
t.Helper()
tokens, err := readPersistedFromFile(filepath.Join(dataDir, tokensPath))
require.NoError(t, err)
require.Equal(t, expected, tokens)
}
t.Run("persist some tokens", func(t *testing.T) {
dataDir, store := setupStore()
err := store.WithPersistenceLock(func() error {
require.True(t, store.UpdateUserToken("sierra", TokenSourceAPI))
require.True(t, store.UpdateAgentRecoveryToken("tango", TokenSourceAPI))
return nil
})
require.NoError(t, err)
// Only API-sourced tokens are persisted.
requirePersistedTokens(t, dataDir, persistedTokens{
Default: "sierra",
AgentRecovery: "tango",
})
})
t.Run("persist all tokens", func(t *testing.T) {
dataDir, store := setupStore()
err := store.WithPersistenceLock(func() error {
require.True(t, store.UpdateUserToken("sierra", TokenSourceAPI))
require.True(t, store.UpdateAgentToken("tango", TokenSourceAPI))
require.True(t, store.UpdateAgentRecoveryToken("uniform", TokenSourceAPI))
require.True(t, store.UpdateReplicationToken("victor", TokenSourceAPI))
require.True(t, store.UpdateConfigFileRegistrationToken("xray", TokenSourceAPI))
require.True(t, store.UpdateDNSToken("zulu", TokenSourceAPI))
return nil
})
require.NoError(t, err)
requirePersistedTokens(t, dataDir, persistedTokens{
Default: "sierra",
Agent: "tango",
AgentRecovery: "uniform",
Replication: "victor",
ConfigFileRegistration: "xray",
DNS: "zulu",
})
})
}