diff --git a/agent/agent.go b/agent/agent.go index 3c866bc481..8f7dd90439 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -894,6 +894,24 @@ func (a *Agent) consulConfig() (*consul.Config, error) { base.TLSCipherSuites = a.config.TLSCipherSuites base.TLSPreferServerCipherSuites = a.config.TLSPreferServerCipherSuites + // Copy the Connect CA bootstrap config + if a.config.ConnectEnabled { + base.ConnectEnabled = true + + if a.config.ConnectCAProvider != "" { + base.CAConfig.Provider = a.config.ConnectCAProvider + + // Merge with the default config if it's the consul provider. + if a.config.ConnectCAProvider == "consul" { + for k, v := range a.config.ConnectCAConfig { + base.CAConfig.Config[k] = v + } + } else { + base.CAConfig.Config = a.config.ConnectCAConfig + } + } + } + // Setup the user event callback base.UserEventHandler = func(e serf.UserEvent) { select { diff --git a/agent/config/builder.go b/agent/config/builder.go index ec36e9ab06..6ad6c70b54 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -522,6 +522,14 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) { // Connect proxy defaults. proxyBindMinPort, proxyBindMaxPort := b.connectProxyPortRange(c.Connect) + var connectEnabled bool + var connectCAProvider string + var connectCAConfig map[string]interface{} + if c.Connect != nil { + connectEnabled = b.boolVal(c.Connect.Enabled) + connectCAProvider = b.stringVal(c.Connect.CAProvider) + connectCAConfig = c.Connect.CAConfig + } // ---------------------------------------------------------------- // build runtime config @@ -641,8 +649,11 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) { CheckUpdateInterval: b.durationVal("check_update_interval", c.CheckUpdateInterval), Checks: checks, ClientAddrs: clientAddrs, + ConnectEnabled: connectEnabled, ConnectProxyBindMinPort: proxyBindMinPort, ConnectProxyBindMaxPort: proxyBindMaxPort, + ConnectCAProvider: connectCAProvider, + ConnectCAConfig: connectCAConfig, DataDir: b.stringVal(c.DataDir), Datacenter: strings.ToLower(b.stringVal(c.Datacenter)), DevMode: b.boolVal(b.Flags.DevMode), diff --git a/agent/config/config.go b/agent/config/config.go index f652c90768..5eb2314726 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -368,8 +368,10 @@ type ServiceConnectProxy struct { type Connect struct { // Enabled opts the agent into connect. It should be set on all clients and // servers in a cluster for correct connect operation. TODO(banks) review that. - Enabled bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` - ProxyDefaults *ConnectProxyDefaults `json:"proxy_defaults,omitempty" hcl:"proxy_defaults" mapstructure:"proxy_defaults"` + Enabled *bool `json:"enabled,omitempty" hcl:"enabled" mapstructure:"enabled"` + ProxyDefaults *ConnectProxyDefaults `json:"proxy_defaults,omitempty" hcl:"proxy_defaults" mapstructure:"proxy_defaults"` + CAProvider *string `json:"ca_provider,omitempty" hcl:"ca_provider" mapstructure:"ca_provider"` + CAConfig map[string]interface{} `json:"ca_config,omitempty" hcl:"ca_config" mapstructure:"ca_config"` } // ConnectProxyDefaults is the agent-global connect proxy configuration. diff --git a/agent/config/runtime.go b/agent/config/runtime.go index b31630d277..15a7ac2bab 100644 --- a/agent/config/runtime.go +++ b/agent/config/runtime.go @@ -647,6 +647,12 @@ type RuntimeConfig struct { // registration time to allow global control of defaults. ConnectProxyDefaultConfig map[string]interface{} + // ConnectCAProvider is the type of CA provider to use with Connect. + ConnectCAProvider string + + // ConnectCAConfig is the config to use for the CA provider. + ConnectCAConfig map[string]interface{} + // DNSAddrs contains the list of TCP and UDP addresses the DNS server will // bind to. If the DNS endpoint is disabled (ports.dns <= 0) the list is // empty. diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go index 773b7a036d..1db5ab2074 100644 --- a/agent/config/runtime_test.go +++ b/agent/config/runtime_test.go @@ -2354,6 +2354,11 @@ func TestFullConfig(t *testing.T) { "check_update_interval": "16507s", "client_addr": "93.83.18.19", "connect": { + "ca_provider": "b8j4ynx9", + "ca_config": { + "g4cvJyys": "IRLXE9Ds", + "hyMy9Oxn": "XeBp4Sis" + }, "enabled": true, "proxy_defaults": { "bind_min_port": 2000, @@ -2811,6 +2816,11 @@ func TestFullConfig(t *testing.T) { check_update_interval = "16507s" client_addr = "93.83.18.19" connect { + ca_provider = "b8j4ynx9" + ca_config { + "g4cvJyys" = "IRLXE9Ds" + "hyMy9Oxn" = "XeBp4Sis" + } enabled = true proxy_defaults { bind_min_port = 2000 @@ -3403,10 +3413,15 @@ func TestFullConfig(t *testing.T) { DeregisterCriticalServiceAfter: 13209 * time.Second, }, }, - CheckUpdateInterval: 16507 * time.Second, - ClientAddrs: []*net.IPAddr{ipAddr("93.83.18.19")}, - ConnectProxyBindMinPort: 2000, - ConnectProxyBindMaxPort: 3000, + CheckUpdateInterval: 16507 * time.Second, + ClientAddrs: []*net.IPAddr{ipAddr("93.83.18.19")}, + ConnectProxyBindMinPort: 2000, + ConnectProxyBindMaxPort: 3000, + ConnectCAProvider: "b8j4ynx9", + ConnectCAConfig: map[string]interface{}{ + "g4cvJyys": "IRLXE9Ds", + "hyMy9Oxn": "XeBp4Sis", + }, DNSAddrs: []net.Addr{tcpAddr("93.95.95.81:7001"), udpAddr("93.95.95.81:7001")}, DNSARecordLimit: 29907, DNSAllowStale: true, diff --git a/agent/consul/config.go b/agent/consul/config.go index df4e55e42a..94c8bc06a7 100644 --- a/agent/consul/config.go +++ b/agent/consul/config.go @@ -348,6 +348,9 @@ type Config struct { // dead servers. AutopilotInterval time.Duration + // ConnectEnabled is whether to enable Connect features such as the CA. + ConnectEnabled bool + // CAConfig is used to apply the initial Connect CA configuration when // bootstrapping. CAConfig *structs.CAConfiguration diff --git a/agent/consul/connect_ca_endpoint.go b/agent/consul/connect_ca_endpoint.go index b952c5f877..f52c9218e4 100644 --- a/agent/consul/connect_ca_endpoint.go +++ b/agent/consul/connect_ca_endpoint.go @@ -1,6 +1,7 @@ package consul import ( + "errors" "fmt" "reflect" @@ -11,6 +12,8 @@ import ( "github.com/hashicorp/go-memdb" ) +var ErrConnectNotEnabled = errors.New("Connect must be enabled in order to use this endpoint") + // ConnectCA manages the Connect CA. type ConnectCA struct { // srv is a pointer back to the server. @@ -21,6 +24,11 @@ type ConnectCA struct { func (s *ConnectCA) ConfigurationGet( args *structs.DCSpecificRequest, reply *structs.CAConfiguration) error { + // Exit early if Connect hasn't been enabled. + if !s.srv.config.ConnectEnabled { + return ErrConnectNotEnabled + } + if done, err := s.srv.forward("ConnectCA.ConfigurationGet", args, args, reply); done { return err } @@ -48,6 +56,11 @@ func (s *ConnectCA) ConfigurationGet( func (s *ConnectCA) ConfigurationSet( args *structs.CARequest, reply *interface{}) error { + // Exit early if Connect hasn't been enabled. + if !s.srv.config.ConnectEnabled { + return ErrConnectNotEnabled + } + if done, err := s.srv.forward("ConnectCA.ConfigurationSet", args, args, reply); done { return err } @@ -244,6 +257,11 @@ func (s *ConnectCA) Roots( func (s *ConnectCA) Sign( args *structs.CASignRequest, reply *structs.IssuedCert) error { + // Exit early if Connect hasn't been enabled. + if !s.srv.config.ConnectEnabled { + return ErrConnectNotEnabled + } + if done, err := s.srv.forward("ConnectCA.Sign", args, args, reply); done { return err } diff --git a/agent/consul/connect_ca_provider.go b/agent/consul/connect_ca_provider.go index 1dbe884c79..f9321138b2 100644 --- a/agent/consul/connect_ca_provider.go +++ b/agent/consul/connect_ca_provider.go @@ -291,7 +291,7 @@ func (c *ConsulCAProvider) CrossSignCA(cert *x509.Certificate) (string, error) { privKey, err := connect.ParseSigner(providerState.PrivateKey) if err != nil { - return "", fmt.Errorf("error parsing private key %q: %v", providerState.PrivateKey, err) + return "", fmt.Errorf("error parsing private key %q: %s", providerState.PrivateKey, err) } rootCA, err := connect.ParseCert(providerState.RootCert) @@ -363,7 +363,7 @@ func (c *ConsulCAProvider) generateCA(privateKey string, sn uint64) (string, err privKey, err := connect.ParseSigner(privateKey) if err != nil { - return "", fmt.Errorf("error parsing private key %q: %v", privateKey, err) + return "", fmt.Errorf("error parsing private key %q: %s", privateKey, err) } name := fmt.Sprintf("Consul CA %d", sn) diff --git a/agent/consul/leader.go b/agent/consul/leader.go index 1670add29e..d9c3a83eaf 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -402,6 +402,11 @@ func (s *Server) initializeCAConfig() (*structs.CAConfiguration, error) { // initializeCA sets up the CA provider when gaining leadership, bootstrapping // the root in the state store if necessary. func (s *Server) initializeCA() error { + // Bail if connect isn't enabled. + if !s.config.ConnectEnabled { + return nil + } + conf, err := s.initializeCAConfig() if err != nil { return err