Skip to content

Commit

Permalink
acl: bootstrap master token from config file
Browse files Browse the repository at this point in the history
  • Loading branch information
skarrok committed May 12, 2020
1 parent a28f18e commit 4ffb8f2
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 2 deletions.
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(),
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

0 comments on commit 4ffb8f2

Please sign in to comment.