From 4ffb8f21d55b38b11a5e2d0251570d4912b7e8da Mon Sep 17 00:00:00 2001 From: denis Date: Tue, 12 May 2020 15:51:16 +0300 Subject: [PATCH] acl: bootstrap master token from config file --- command/agent/agent.go | 3 ++ command/agent/config.go | 5 ++ nomad/config.go | 5 ++ nomad/leader.go | 59 ++++++++++++++++++++++++ nomad/structs/structs.go | 4 +- website/pages/docs/configuration/acl.mdx | 10 ++++ 6 files changed, 84 insertions(+), 2 deletions(-) diff --git a/command/agent/agent.go b/command/agent/agent.go index 825a8eb6e142..b4dff2353465 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -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 } diff --git a/command/agent/config.go b/command/agent/config.go index 8e7c658ba734..d234b84c203a 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -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. @@ -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 } diff --git a/nomad/config.go b/nomad/config.go index c325df5db18f..60b17c0b8801 100644 --- a/nomad/config.go +++ b/nomad/config.go @@ -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 diff --git a/nomad/leader.go b/nomad/leader.go index 18c36ef386ef..8717a65c783e 100644 --- a/nomad/leader.go +++ b/nomad/leader.go @@ -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 { @@ -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 diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 9b6c72dad0b8..f3ec71130f66 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -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 } diff --git a/website/pages/docs/configuration/acl.mdx b/website/pages/docs/configuration/acl.mdx index bb4e19341661..def13d9b2d45 100644 --- a/website/pages/docs/configuration/acl.mdx +++ b/website/pages/docs/configuration/acl.mdx @@ -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.