diff --git a/agent/consul/auto_config_endpoint.go b/agent/consul/auto_config_endpoint.go index 336f6a138c..f55adbcf41 100644 --- a/agent/consul/auto_config_endpoint.go +++ b/agent/consul/auto_config_endpoint.go @@ -80,27 +80,34 @@ func (a *jwtAuthorizer) Authorize(req *agentpb.AutoConfigRequest) (AutoConfigOpt } type AutoConfigBackend interface { - GetConfig() *Config CreateACLToken(template *structs.ACLToken) (*structs.ACLToken, error) DatacenterJoinAddresses(segment string) ([]string, error) - TLSConfigurator() *tlsutil.Configurator ForwardRPC(method string, info structs.RPCInfo, args, reply interface{}) (bool, error) } // AutoConfig endpoint is used for cluster auto configuration operations type AutoConfig struct { + // currently AutoConfig does not support pushing down any configuration that would be reloadable on the servers + // (outside of some TLS settings such as the configured CA certs which are retrieved via the TLS configurator) + // If that changes then we will need to change this to use an atomic.Value and provide means of reloading it. + config *Config + tlsConfigurator *tlsutil.Configurator + backend AutoConfigBackend authorizer AutoConfigAuthorizer } -// getBackendConfig is a small wrapper around the backend's GetConfig to ensure -// that all helper functions have a non-nil configuration to operate on. -func (ac *AutoConfig) getBackendConfig() *Config { - if conf := ac.backend.GetConfig(); conf != nil { - return conf +func NewAutoConfig(conf *Config, tlsConfigurator *tlsutil.Configurator, backend AutoConfigBackend, authz AutoConfigAuthorizer) *AutoConfig { + if conf == nil { + conf = DefaultConfig() } - return DefaultConfig() + return &AutoConfig{ + config: conf, + tlsConfigurator: tlsConfigurator, + backend: backend, + authorizer: authz, + } } // updateTLSCertificatesInConfig will ensure that the TLS settings regarding how an agent is @@ -108,28 +115,26 @@ func (ac *AutoConfig) getBackendConfig() *Config { // in some cases only if auto_encrypt is enabled on the servers. This endpoint has the option // to configure auto_encrypt or potentially in the future to generate the certificates inline. func (ac *AutoConfig) updateTLSCertificatesInConfig(opts AutoConfigOptions, conf *config.Config) error { - conf.AutoEncrypt = &config.AutoEncrypt{TLS: ac.getBackendConfig().AutoEncryptAllowTLS} + conf.AutoEncrypt = &config.AutoEncrypt{TLS: ac.config.AutoEncryptAllowTLS} return nil } // updateACLtokensInConfig will configure all of the agents ACL settings and will populate // the configuration with an agent token usable for all default agent operations. func (ac *AutoConfig) updateACLsInConfig(opts AutoConfigOptions, conf *config.Config) error { - backendConf := ac.getBackendConfig() - acl := &config.ACL{ - Enabled: backendConf.ACLsEnabled, - PolicyTTL: backendConf.ACLPolicyTTL.String(), - RoleTTL: backendConf.ACLRoleTTL.String(), - TokenTTL: backendConf.ACLTokenTTL.String(), - DisabledTTL: backendConf.ACLDisabledTTL.String(), - DownPolicy: backendConf.ACLDownPolicy, - DefaultPolicy: backendConf.ACLDefaultPolicy, - EnableKeyListPolicy: backendConf.ACLEnableKeyListPolicy, + Enabled: ac.config.ACLsEnabled, + PolicyTTL: ac.config.ACLPolicyTTL.String(), + RoleTTL: ac.config.ACLRoleTTL.String(), + TokenTTL: ac.config.ACLTokenTTL.String(), + DisabledTTL: ac.config.ACLDisabledTTL.String(), + DownPolicy: ac.config.ACLDownPolicy, + DefaultPolicy: ac.config.ACLDefaultPolicy, + EnableKeyListPolicy: ac.config.ACLEnableKeyListPolicy, } // when ACLs are enabled we want to create a local token with a node identity - if backendConf.ACLsEnabled { + if ac.config.ACLsEnabled { // set up the token template - the ids and create template := structs.ACLToken{ Description: fmt.Sprintf("Auto Config Token for Node %q", opts.NodeName), @@ -137,7 +142,7 @@ func (ac *AutoConfig) updateACLsInConfig(opts AutoConfigOptions, conf *config.Co NodeIdentities: []*structs.ACLNodeIdentity{ { NodeName: opts.NodeName, - Datacenter: backendConf.Datacenter, + Datacenter: ac.config.Datacenter, }, }, EnterpriseMeta: *structs.DefaultEnterpriseMeta(), @@ -148,16 +153,6 @@ func (ac *AutoConfig) updateACLsInConfig(opts AutoConfigOptions, conf *config.Co return fmt.Errorf("Failed to generate an ACL token for node %q - %w", opts.NodeName, err) } - // req := structs.ACLTokenBatchSetRequest{ - // Tokens: structs.ACLTokens{&token}, - // CAS: false, - // } - - // // perform the request to mint the new token - // if _, err := c.srv.raftApplyMsgpack(structs.ACLTokenSetRequestType, &req); err != nil { - // return err - // } - acl.Tokens = &config.ACLTokens{Agent: token.SecretID} } @@ -173,14 +168,6 @@ func (ac *AutoConfig) updateJoinAddressesInConfig(opts AutoConfigOptions, conf * return err } - // var joinAddrs []string - // for _, m := range members { - // if ok, _ := metadata.IsConsulServer(m); ok { - // serfAddr := net.TCPAddr{IP: m.Addr, Port: int(m.Port)} - // joinAddrs = append(joinAddrs, serfAddr.String()) - // } - // } - if conf.Gossip == nil { conf.Gossip = &config.Gossip{} } @@ -192,7 +179,7 @@ func (ac *AutoConfig) updateJoinAddressesInConfig(opts AutoConfigOptions, conf * // updateGossipEncryptionInConfig will populate the gossip encryption configuration settings func (ac *AutoConfig) updateGossipEncryptionInConfig(_ AutoConfigOptions, conf *config.Config) error { // Add gossip encryption settings if there is any key loaded - memberlistConfig := ac.getBackendConfig().SerfLANConfig.MemberlistConfig + memberlistConfig := ac.config.SerfLANConfig.MemberlistConfig if lanKeyring := memberlistConfig.Keyring; lanKeyring != nil { if conf.Gossip == nil { conf.Gossip = &config.Gossip{} @@ -216,8 +203,7 @@ func (ac *AutoConfig) updateGossipEncryptionInConfig(_ AutoConfigOptions, conf * // updateTLSSettingsInConfig will populate the TLS configuration settings but will not // populate leaf or ca certficiates. func (ac *AutoConfig) updateTLSSettingsInConfig(_ AutoConfigOptions, conf *config.Config) error { - tlsConfigurator := ac.backend.TLSConfigurator() - if tlsConfigurator == nil { + if ac.tlsConfigurator == nil { // TLS is not enabled? return nil } @@ -227,8 +213,8 @@ func (ac *AutoConfig) updateTLSSettingsInConfig(_ AutoConfigOptions, conf *confi conf.TLS = &config.TLS{} } - conf.TLS.VerifyServerHostname = tlsConfigurator.VerifyServerHostname() - base := tlsConfigurator.Base() + conf.TLS.VerifyServerHostname = ac.tlsConfigurator.VerifyServerHostname() + base := ac.tlsConfigurator.Base() conf.TLS.VerifyOutgoing = base.VerifyOutgoing conf.TLS.MinVersion = base.TLSMinVersion conf.TLS.PreferServerCipherSuites = base.PreferServerCipherSuites @@ -241,14 +227,12 @@ func (ac *AutoConfig) updateTLSSettingsInConfig(_ AutoConfigOptions, conf *confi // baseConfig will populate the configuration with some base settings such as the // datacenter names, node name etc. func (ac *AutoConfig) baseConfig(opts AutoConfigOptions, conf *config.Config) error { - backendConf := ac.getBackendConfig() - if opts.NodeName == "" { return fmt.Errorf("Cannot generate auto config response without a node name") } - conf.Datacenter = backendConf.Datacenter - conf.PrimaryDatacenter = backendConf.PrimaryDatacenter + conf.Datacenter = ac.config.Datacenter + conf.PrimaryDatacenter = ac.config.PrimaryDatacenter conf.NodeName = opts.NodeName conf.SegmentName = opts.SegmentName @@ -274,19 +258,22 @@ var ( // AgentAutoConfig will authorize the incoming request and then generate the configuration // to push down to the client func (ac *AutoConfig) InitialConfiguration(req *agentpb.AutoConfigRequest, resp *agentpb.AutoConfigResponse) error { - backendConf := ac.getBackendConfig() - // default the datacenter to our datacenter - agents do not have to specify this as they may not // yet know the datacenter name they are going to be in. if req.Datacenter == "" { - req.Datacenter = backendConf.Datacenter + req.Datacenter = ac.config.Datacenter } // TODO (autoconf) Is performing auto configuration over the WAN really a bad idea? - if req.Datacenter != backendConf.Datacenter { + if req.Datacenter != ac.config.Datacenter { return fmt.Errorf("invalid datacenter %q - agent auto configuration cannot target a remote datacenter", req.Datacenter) } + // TODO (autoconf) maybe panic instead? + if ac.backend == nil { + return fmt.Errorf("No Auto Config backend is configured") + } + // forward to the leader if done, err := ac.backend.ForwardRPC("AutoConfig.InitialConfiguration", req, req, resp); done { return err diff --git a/agent/consul/auto_config_endpoint_test.go b/agent/consul/auto_config_endpoint_test.go index 9d8ed7099e..2bdd4b5bdd 100644 --- a/agent/consul/auto_config_endpoint_test.go +++ b/agent/consul/auto_config_endpoint_test.go @@ -29,13 +29,6 @@ type mockAutoConfigBackend struct { mock.Mock } -func (m *mockAutoConfigBackend) GetConfig() *Config { - ret := m.Called() - // this handles converting an untyped nil to a typed nil - cfg, _ := ret.Get(0).(*Config) - return cfg -} - func (m *mockAutoConfigBackend) CreateACLToken(template *structs.ACLToken) (*structs.ACLToken, error) { ret := m.Called(template) // this handles converting an untyped nil to a typed nil @@ -50,13 +43,6 @@ func (m *mockAutoConfigBackend) DatacenterJoinAddresses(segment string) ([]strin return addrs, ret.Error(1) } -func (m *mockAutoConfigBackend) TLSConfigurator() *tlsutil.Configurator { - ret := m.Called() - // this handles converting an untyped nil to a typed nil - cfg, _ := ret.Get(0).(*tlsutil.Configurator) - return cfg -} - func (m *mockAutoConfigBackend) ForwardRPC(method string, info structs.RPCInfo, args, reply interface{}) (bool, error) { ret := m.Called(method, info, args, reply) return ret.Bool(0), ret.Error(1) @@ -315,11 +301,8 @@ func TestAutoConfig_baseConfig(t *testing.T) { for name, tcase := range cases { t.Run(name, func(t *testing.T) { - backend := &mockAutoConfigBackend{} - backend.On("GetConfig").Return(&tcase.serverConfig).Once() - ac := AutoConfig{ - backend: backend, + config: &tcase.serverConfig, } var actual config.Config @@ -330,8 +313,6 @@ func TestAutoConfig_baseConfig(t *testing.T) { } else { testutil.RequireErrorContains(t, err, tcase.err) } - - backend.AssertExpectations(t) }) } } @@ -406,19 +387,14 @@ func TestAutoConfig_updateTLSSettingsInConfig(t *testing.T) { configurator, err := tlsutil.NewConfigurator(tcase.tlsConfig, logger) require.NoError(t, err) - backend := &mockAutoConfigBackend{} - backend.On("TLSConfigurator").Return(configurator).Once() - ac := &AutoConfig{ - backend: backend, + tlsConfigurator: configurator, } var actual config.Config err = ac.updateTLSSettingsInConfig(AutoConfigOptions{}, &actual) require.NoError(t, err) require.Equal(t, tcase.expected, actual) - - backend.AssertExpectations(t) }) } } @@ -483,19 +459,14 @@ func TestAutoConfig_updateGossipEncryptionInConfig(t *testing.T) { cfg := DefaultConfig() cfg.SerfLANConfig.MemberlistConfig = &tcase.conf - backend := &mockAutoConfigBackend{} - backend.On("GetConfig").Return(cfg).Once() - ac := AutoConfig{ - backend: backend, + config: cfg, } var actual config.Config err := ac.updateGossipEncryptionInConfig(AutoConfigOptions{}, &actual) require.NoError(t, err) require.Equal(t, tcase.expected, actual) - - backend.AssertExpectations(t) }) } } @@ -529,19 +500,14 @@ func TestAutoConfig_updateTLSCertificatesInConfig(t *testing.T) { for name, tcase := range cases { t.Run(name, func(t *testing.T) { - backend := &mockAutoConfigBackend{} - backend.On("GetConfig").Return(&tcase.serverConfig).Once() - ac := AutoConfig{ - backend: backend, + config: &tcase.serverConfig, } var actual config.Config err := ac.updateTLSCertificatesInConfig(AutoConfigOptions{}, &actual) require.NoError(t, err) require.Equal(t, tcase.expected, actual) - - backend.AssertExpectations(t) }) } } @@ -632,8 +598,6 @@ func TestAutoConfig_updateACLsInConfig(t *testing.T) { for name, tcase := range cases { t.Run(name, func(t *testing.T) { backend := &mockAutoConfigBackend{} - backend.On("GetConfig").Return(&tcase.config).Once() - expectedTemplate := &structs.ACLToken{ Description: `Auto Config Token for Node "something"`, Local: true, @@ -664,7 +628,7 @@ func TestAutoConfig_updateACLsInConfig(t *testing.T) { backend.On("CreateACLToken", expectedTemplate).Return(testToken, tcase.err).Once() } - ac := AutoConfig{backend: backend} + ac := AutoConfig{config: &tcase.config, backend: backend} var actual config.Config err := ac.updateACLsInConfig(AutoConfigOptions{NodeName: "something"}, &actual) diff --git a/agent/consul/server.go b/agent/consul/server.go index a08b9dc805..67f46c0bd7 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -876,7 +876,7 @@ func (s *Server) setupRPC() error { authz = &disabledAuthorizer{} } // now register with the insecure RPC server - s.insecureRPCServer.Register(&AutoConfig{backend: s, authorizer: authz}) + s.insecureRPCServer.Register(NewAutoConfig(s.config, s.tlsConfigurator, s, authz)) ln, err := net.ListenTCP("tcp", s.config.RPCAddr) if err != nil { @@ -1502,18 +1502,6 @@ func (s *Server) DatacenterJoinAddresses(segment string) ([]string, error) { return joinAddrs, nil } -// GetConfig will return the servers configuration - this is needed to satisfy -// interfaces: AutoConfigDelegate -func (s *Server) GetConfig() *Config { - return s.config -} - -// TLSConfigurator just returns the servers TLS Configurator - this is needed to satisfy -// interfaces: AutoConfigDelegate -func (s *Server) TLSConfigurator() *tlsutil.Configurator { - return s.tlsConfigurator -} - // peersInfoContent is used to help operators understand what happened to the // peers.json file. This is written to a file called peers.info in the same // location.