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

acl: bootstrap master token from config file #7935

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions command/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ func convertServerConfig(agentConfig *Config) (*nomad.Config, error) {
if agentConfig.ACL.ReplicationToken != "" {
conf.ReplicationToken = agentConfig.ACL.ReplicationToken
}
if agentConfig.ACL.MasterToken != "" {
conf.ACLMasterToken = agentConfig.ACL.MasterToken
}
if agentConfig.Sentinel != nil {
conf.SentinelConfig = agentConfig.Sentinel
}
Expand Down
5 changes: 5 additions & 0 deletions command/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ type ACLConfig struct {
PolicyTTL time.Duration
PolicyTTLHCL string `hcl:"policy_ttl" json:"-"`

MasterToken string `hcl:"master_token"`

// ReplicationToken is used by servers to replicate tokens and policies
// from the authoritative region. This must be a valid management token
// within the authoritative region.
Expand Down Expand Up @@ -1287,6 +1289,9 @@ func (a *ACLConfig) Merge(b *ACLConfig) *ACLConfig {
if b.PolicyTTLHCL != "" {
result.PolicyTTLHCL = b.PolicyTTLHCL
}
if b.MasterToken != "" {
result.MasterToken = b.MasterToken
}
if b.ReplicationToken != "" {
result.ReplicationToken = b.ReplicationToken
}
Expand Down
5 changes: 5 additions & 0 deletions nomad/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,11 @@ type Config struct {
// ACLEnabled controls if ACL enforcement and management is enabled.
ACLEnabled bool

// ACLMasterToken is used to bootstrap the ACL system. It should be specified
// on the servers in the AuthoritativeRegion. When the leader comes online, it ensures
// that the Master token is available. This provides the initial token.
ACLMasterToken string

// ReplicationBackoff is how much we backoff when replication errors.
// This is a tunable knob for testing primarily.
ReplicationBackoff time.Duration
Expand Down
59 changes: 59 additions & 0 deletions nomad/leader.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ func (s *Server) establishLeadership(stopCh chan struct{}) error {
return err
}

if s.config.ACLEnabled {
if err := s.initializeMasterToken(); err != nil {
return err
}
}
// Start replication of ACLs and Policies if they are enabled,
// and we are not the authoritative region.
if s.config.ACLEnabled && s.config.Region != s.config.AuthoritativeRegion {
Expand All @@ -318,6 +323,60 @@ func (s *Server) establishLeadership(stopCh chan struct{}) error {
return nil
}

func (s *Server) initializeMasterToken() error {
if !s.config.ACLEnabled {
return nil
}

if s.config.Region != s.config.AuthoritativeRegion {
return nil
}
if master := s.config.ACLMasterToken; len(master) > 0 {
state, err := s.State().Snapshot()
if err != nil {
return err
}
token, err := state.ACLTokenBySecretID(nil, master)
if err != nil {
return fmt.Errorf("failed to get master token: %v", err)
}
if token == nil {
token = &structs.ACLToken{
AccessorID: uuid.Generate(),
Copy link
Member

Choose a reason for hiding this comment

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

Would it be useful to allow the AccessorID to be specified in the HCL as well? Consul doesn't for their master_token, but they do allow it for subsequent tokens created via their API in order to ease automation.

SecretID: master,
Name: "Bootstrap Token",
Type: structs.ACLManagementToken,
Global: true,
CreateTime: time.Now().UTC(),
}
token.SetHash()

if canBootstrap, _, err := state.CanBootstrapACLToken(); err == nil && canBootstrap {
req := structs.ACLTokenBootstrapRequest{
Token: token,
ResetIndex: 0,
}
if _, _, err := s.raftApply(structs.ACLTokenBootstrapRequestType, &req); err == nil {
s.logger.Info("Bootstrapped ACL master token from configuration")
} else {
return fmt.Errorf("Failed to bootstap master token: %v", err)
}
} else {
req := structs.ACLTokenUpsertRequest{
Tokens: []*structs.ACLToken{token},
}
if _, _, err := s.raftApply(structs.ACLTokenUpsertRequestType, &req); err != nil {
return fmt.Errorf("Failed to create master token: %v", err)
}

s.logger.Info("Created ACL master token from configuration")
}

}
}
return nil
}

// restoreEvals is used to restore pending evaluations into the eval broker and
// blocked evaluations into the blocked eval tracker. The broker and blocked
// eval tracker is maintained only by the leader, so it must be restored anytime
Expand Down
4 changes: 2 additions & 2 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9991,8 +9991,8 @@ type ACLTokenDeleteRequest struct {

// ACLTokenBootstrapRequest is used to bootstrap ACLs
type ACLTokenBootstrapRequest struct {
Token *ACLToken // Not client specifiable
ResetIndex uint64 // Reset index is used to clear the bootstrap token
Token *ACLToken
ResetIndex uint64 // Reset index is used to clear the bootstrap token
WriteRequest
}

Expand Down
10 changes: 10 additions & 0 deletions website/pages/docs/configuration/acl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ acl {
the request load against servers. If a client cannot reach a server, for example
because of an outage, the TTL will be ignored and the cached value used.

- `master_token` `(string: "")` - Only used for servers in the
[authoritative_region](/docs/configuration/server/#authoritative_region).
This token will be created with management-level permissions if it does not exist.
It allows operators to bootstrap the ACL system with a token Secret ID that is well-known.
The master token is only installed when a server acquires cluster leadership.
If you would like to install or change the master_token, set the new value for
master in the configuration for all servers. Once this is done, restart the current
leader to force a leader election. If the master token is not supplied, then
the servers do not create a master token. When you provide a value, it should be a UUID.

- `replication_token` `(string: "")` - Specifies the Secret ID of the ACL token
to use for replicating policies and tokens. This is used by servers in non-authoritative
region to mirror the policies and tokens into the local region.
Expand Down