mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
9eaa8eb026
* 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
237 lines
7.0 KiB
Go
237 lines
7.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package token
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/hashicorp/consul/lib/file"
|
|
)
|
|
|
|
// Logger used by Store.Load to report warnings.
|
|
type Logger interface {
|
|
Warn(msg string, args ...interface{})
|
|
}
|
|
|
|
// Config used by Store.Load, which includes tokens and settings for persistence.
|
|
type Config struct {
|
|
EnablePersistence bool
|
|
DataDir string
|
|
ACLDefaultToken string
|
|
ACLAgentToken string
|
|
ACLAgentRecoveryToken string
|
|
ACLReplicationToken string
|
|
ACLConfigFileRegistrationToken string
|
|
ACLDNSToken string
|
|
|
|
EnterpriseConfig
|
|
}
|
|
|
|
const tokensPath = "acl-tokens.json"
|
|
|
|
// Load tokens from Config and optionally from a persisted file in the cfg.DataDir.
|
|
// If a token exists in both the persisted file and in the Config a warning will
|
|
// be logged and the persisted token will be used.
|
|
//
|
|
// Failures to load the persisted file will result in loading tokens from the
|
|
// config before returning the error.
|
|
func (t *Store) Load(cfg Config, logger Logger) error {
|
|
t.persistenceLock.RLock()
|
|
if !cfg.EnablePersistence {
|
|
t.persistence = nil
|
|
t.persistenceLock.RUnlock()
|
|
loadTokens(t, cfg, persistedTokens{}, logger)
|
|
return nil
|
|
}
|
|
|
|
defer t.persistenceLock.RUnlock()
|
|
t.persistence = &fileStore{
|
|
filename: filepath.Join(cfg.DataDir, tokensPath),
|
|
logger: logger,
|
|
}
|
|
return t.persistence.load(t, cfg)
|
|
}
|
|
|
|
// WithPersistenceLock executes f while hold a lock. If f returns a nil error,
|
|
// the tokens in Store will be persisted to the tokens file. Otherwise no
|
|
// tokens will be persisted, and the error from f will be returned.
|
|
//
|
|
// The lock is held so that the writes are persisted before some other thread
|
|
// can change the value.
|
|
func (t *Store) WithPersistenceLock(f func() error) error {
|
|
t.persistenceLock.Lock()
|
|
if t.persistence == nil {
|
|
t.persistenceLock.Unlock()
|
|
return f()
|
|
}
|
|
defer t.persistenceLock.Unlock()
|
|
return t.persistence.withPersistenceLock(t, f)
|
|
}
|
|
|
|
type persistedTokens struct {
|
|
Replication string `json:"replication,omitempty"`
|
|
AgentRecovery string `json:"agent_recovery,omitempty"`
|
|
Default string `json:"default,omitempty"`
|
|
Agent string `json:"agent,omitempty"`
|
|
ConfigFileRegistration string `json:"config_file_service_registration,omitempty"`
|
|
DNS string `json:"dns,omitempty"`
|
|
}
|
|
|
|
type fileStore struct {
|
|
filename string
|
|
logger Logger
|
|
}
|
|
|
|
func (p *fileStore) load(s *Store, cfg Config) error {
|
|
tokens, err := readPersistedFromFile(p.filename)
|
|
if err != nil {
|
|
p.logger.Warn("unable to load persisted tokens", "error", err)
|
|
}
|
|
loadTokens(s, cfg, tokens, p.logger)
|
|
return err
|
|
}
|
|
|
|
func loadTokens(s *Store, cfg Config, tokens persistedTokens, logger Logger) {
|
|
if tokens.Default != "" {
|
|
s.UpdateUserToken(tokens.Default, TokenSourceAPI)
|
|
|
|
if cfg.ACLDefaultToken != "" {
|
|
logger.Warn("\"default\" token present in both the configuration and persisted token store, using the persisted token")
|
|
}
|
|
} else {
|
|
s.UpdateUserToken(cfg.ACLDefaultToken, TokenSourceConfig)
|
|
}
|
|
|
|
if tokens.Agent != "" {
|
|
s.UpdateAgentToken(tokens.Agent, TokenSourceAPI)
|
|
|
|
if cfg.ACLAgentToken != "" {
|
|
logger.Warn("\"agent\" token present in both the configuration and persisted token store, using the persisted token")
|
|
}
|
|
} else {
|
|
s.UpdateAgentToken(cfg.ACLAgentToken, TokenSourceConfig)
|
|
}
|
|
|
|
if tokens.AgentRecovery != "" {
|
|
s.UpdateAgentRecoveryToken(tokens.AgentRecovery, TokenSourceAPI)
|
|
|
|
if cfg.ACLAgentRecoveryToken != "" {
|
|
logger.Warn("\"agent_recovery\" token present in both the configuration and persisted token store, using the persisted token")
|
|
}
|
|
} else {
|
|
s.UpdateAgentRecoveryToken(cfg.ACLAgentRecoveryToken, TokenSourceConfig)
|
|
}
|
|
|
|
if tokens.Replication != "" {
|
|
s.UpdateReplicationToken(tokens.Replication, TokenSourceAPI)
|
|
|
|
if cfg.ACLReplicationToken != "" {
|
|
logger.Warn("\"replication\" token present in both the configuration and persisted token store, using the persisted token")
|
|
}
|
|
} else {
|
|
s.UpdateReplicationToken(cfg.ACLReplicationToken, TokenSourceConfig)
|
|
}
|
|
|
|
if tokens.ConfigFileRegistration != "" {
|
|
s.UpdateConfigFileRegistrationToken(tokens.ConfigFileRegistration, TokenSourceAPI)
|
|
|
|
if cfg.ACLConfigFileRegistrationToken != "" {
|
|
logger.Warn("\"config_file_service_registration\" token present in both the configuration and persisted token store, using the persisted token")
|
|
}
|
|
} else {
|
|
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)
|
|
}
|
|
|
|
func readPersistedFromFile(filename string) (persistedTokens, error) {
|
|
var tokens struct {
|
|
persistedTokens
|
|
|
|
// Support reading tokens persisted by versions <1.11, where agent_master was
|
|
// renamed to agent_recovery.
|
|
LegacyAgentMaster string `json:"agent_master"`
|
|
}
|
|
|
|
buf, err := os.ReadFile(filename)
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
// non-existence is not an error we care about
|
|
return tokens.persistedTokens, nil
|
|
case err != nil:
|
|
return tokens.persistedTokens, fmt.Errorf("failed reading tokens file %q: %w", filename, err)
|
|
}
|
|
|
|
if err := json.Unmarshal(buf, &tokens); err != nil {
|
|
return tokens.persistedTokens, fmt.Errorf("failed to decode tokens file %q: %w", filename, err)
|
|
}
|
|
|
|
if tokens.AgentRecovery == "" {
|
|
tokens.AgentRecovery = tokens.LegacyAgentMaster
|
|
}
|
|
|
|
return tokens.persistedTokens, nil
|
|
}
|
|
|
|
func (p *fileStore) withPersistenceLock(s *Store, f func() error) error {
|
|
if err := f(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return p.saveToFile(s)
|
|
}
|
|
|
|
func (p *fileStore) saveToFile(s *Store) error {
|
|
tokens := persistedTokens{}
|
|
if tok, source := s.UserTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.Default = tok
|
|
}
|
|
|
|
if tok, source := s.AgentTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.Agent = tok
|
|
}
|
|
|
|
if tok, source := s.AgentRecoveryTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.AgentRecovery = tok
|
|
}
|
|
|
|
if tok, source := s.ReplicationTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.Replication = tok
|
|
}
|
|
|
|
if tok, source := s.ConfigFileRegistrationTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.ConfigFileRegistration = tok
|
|
}
|
|
|
|
if tok, source := s.DNSTokenAndSource(); tok != "" && source == TokenSourceAPI {
|
|
tokens.DNS = tok
|
|
}
|
|
|
|
data, err := json.Marshal(tokens)
|
|
if err != nil {
|
|
p.logger.Warn("failed to persist tokens", "error", err)
|
|
return fmt.Errorf("Failed to marshal tokens for persistence: %v", err)
|
|
}
|
|
|
|
if err := file.WriteAtomicWithPerms(p.filename, data, 0700, 0600); err != nil {
|
|
p.logger.Warn("failed to persist tokens", "error", err)
|
|
return fmt.Errorf("Failed to persist tokens - %v", err)
|
|
}
|
|
return nil
|
|
}
|