agent: setup grpc server with auto_encrypt certs and add -https-port (#7086)

* setup grpc server with TLS config used across consul.
* add -https-port flag
This commit is contained in:
Hans Hasselberg 2020-01-22 11:32:17 +01:00 committed by GitHub
parent f3a01e6a4a
commit e00effa325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 14 deletions

View File

@ -723,9 +723,9 @@ func (a *Agent) listenAndServeGRPC() error {
if a.config.HTTPSPort > 0 { if a.config.HTTPSPort > 0 {
// gRPC uses the same TLS settings as the HTTPS API. If HTTPS is // gRPC uses the same TLS settings as the HTTPS API. If HTTPS is
// enabled then gRPC will require HTTPS as well. // enabled then gRPC will require HTTPS as well.
a.grpcServer, err = a.xdsServer.GRPCServer(a.config.CertFile, a.config.KeyFile) a.grpcServer, err = a.xdsServer.GRPCServer(a.tlsConfigurator)
} else { } else {
a.grpcServer, err = a.xdsServer.GRPCServer("", "") a.grpcServer, err = a.xdsServer.GRPCServer(nil)
} }
if err != nil { if err != nil {
return err return err

View File

@ -79,6 +79,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
add(&f.Config.EncryptKey, "encrypt", "Provides the gossip encryption key.") add(&f.Config.EncryptKey, "encrypt", "Provides the gossip encryption key.")
add(&f.Config.Ports.GRPC, "grpc-port", "Sets the gRPC API port to listen on (currently needed for Envoy xDS only).") add(&f.Config.Ports.GRPC, "grpc-port", "Sets the gRPC API port to listen on (currently needed for Envoy xDS only).")
add(&f.Config.Ports.HTTP, "http-port", "Sets the HTTP API port to listen on.") add(&f.Config.Ports.HTTP, "http-port", "Sets the HTTP API port to listen on.")
add(&f.Config.Ports.HTTPS, "https-port", "Sets the HTTPS API port to listen on.")
add(&f.Config.StartJoinAddrsLAN, "join", "Address of an agent to join at start time. Can be specified multiple times.") add(&f.Config.StartJoinAddrsLAN, "join", "Address of an agent to join at start time. Can be specified multiple times.")
add(&f.Config.StartJoinAddrsWAN, "join-wan", "Address of an agent to join -wan at start time. Can be specified multiple times.") add(&f.Config.StartJoinAddrsWAN, "join-wan", "Address of an agent to join -wan at start time. Can be specified multiple times.")
add(&f.Config.LogLevel, "log-level", "Log level of the agent.") add(&f.Config.LogLevel, "log-level", "Log level of the agent.")

View File

@ -52,6 +52,14 @@ func TestParseFlags(t *testing.T) {
args: []string{`-grpc-port`, `1`}, args: []string{`-grpc-port`, `1`},
flags: Flags{Config: Config{Ports: Ports{GRPC: pInt(1)}}}, flags: Flags{Config: Config{Ports: Ports{GRPC: pInt(1)}}},
}, },
{
args: []string{`-http-port`, `1`},
flags: Flags{Config: Config{Ports: Ports{HTTP: pInt(1)}}},
},
{
args: []string{`-https-port`, `1`},
flags: Flags{Config: Config{Ports: Ports{HTTPS: pInt(1)}}},
},
{ {
args: []string{`-serf-lan-port`, `1`}, args: []string{`-serf-lan-port`, `1`},
flags: Flags{Config: Config{Ports: Ports{SerfLAN: pInt(1)}}}, flags: Flags{Config: Config{Ports: Ports{SerfLAN: pInt(1)}}},

View File

@ -807,6 +807,7 @@ type RuntimeConfig struct {
// Setting this to a value <= 0 disables the endpoint. // Setting this to a value <= 0 disables the endpoint.
// //
// hcl: ports { https = int } // hcl: ports { https = int }
// flags: -https-port int
HTTPSPort int HTTPSPort int
// KeyFile is used to provide a TLS key that is used for serving TLS // KeyFile is used to provide a TLS key that is used for serving TLS

View File

@ -25,6 +25,7 @@ import (
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/tlsutil"
) )
// ADSStream is a shorter way of referring to this thing... // ADSStream is a shorter way of referring to this thing...
@ -536,16 +537,15 @@ func (s *Server) Check(ctx context.Context, r *envoyauthz.CheckRequest) (*envoya
// GRPCServer returns a server instance that can handle XDS and ext_authz // GRPCServer returns a server instance that can handle XDS and ext_authz
// requests. // requests.
func (s *Server) GRPCServer(certFile, keyFile string) (*grpc.Server, error) { func (s *Server) GRPCServer(tlsConfigurator *tlsutil.Configurator) (*grpc.Server, error) {
opts := []grpc.ServerOption{ opts := []grpc.ServerOption{
grpc.MaxConcurrentStreams(2048), grpc.MaxConcurrentStreams(2048),
} }
if certFile != "" && keyFile != "" { if tlsConfigurator != nil {
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile) if tlsConfigurator.Cert() != nil {
if err != nil { creds := credentials.NewTLS(tlsConfigurator.IncomingGRPCConfig())
return nil, err opts = append(opts, grpc.Creds(creds))
} }
opts = append(opts, grpc.Creds(creds))
} }
srv := grpc.NewServer(opts...) srv := grpc.NewServer(opts...)
envoydisco.RegisterAggregatedDiscoveryServiceServer(srv, s) envoydisco.RegisterAggregatedDiscoveryServiceServer(srv, s)

View File

@ -441,12 +441,7 @@ func (c *Configurator) commonTLSConfig(verifyIncoming bool) *tls.Config {
// autoEncrypt cert too so that a client can encrypt incoming // autoEncrypt cert too so that a client can encrypt incoming
// connections without having a manual cert configured. // connections without having a manual cert configured.
tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
cert := c.manual.cert return c.Cert(), nil
if cert == nil {
cert = c.autoEncrypt.cert
}
return cert, nil
} }
// GetClientCertificate is used when acting as a client and responding // GetClientCertificate is used when acting as a client and responding
@ -477,6 +472,17 @@ func (c *Configurator) commonTLSConfig(verifyIncoming bool) *tls.Config {
return tlsConfig return tlsConfig
} }
// This function acquires a read lock because it reads from the config.
func (c *Configurator) Cert() *tls.Certificate {
c.RLock()
defer c.RUnlock()
cert := c.manual.cert
if cert == nil {
cert = c.autoEncrypt.cert
}
return cert
}
// This function acquires a read lock because it reads from the config. // This function acquires a read lock because it reads from the config.
func (c *Configurator) VerifyIncomingRPC() bool { func (c *Configurator) VerifyIncomingRPC() bool {
c.RLock() c.RLock()
@ -561,6 +567,22 @@ func (c *Configurator) VerifyServerHostname() bool {
return c.base.VerifyServerHostname || c.autoEncrypt.verifyServerHostname return c.base.VerifyServerHostname || c.autoEncrypt.verifyServerHostname
} }
// IncomingGRPCConfig generates a *tls.Config for incoming GRPC connections.
func (c *Configurator) IncomingGRPCConfig() *tls.Config {
c.log("IncomingGRPCConfig")
// false has the effect that this config doesn't require a client cert
// verification. This is because there is no verify_incoming_grpc
// configuration option. And using verify_incoming would be backwards
// incompatible, because even if it was set before, it didn't have an
// effect on the grpc server.
config := c.commonTLSConfig(false)
config.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
return c.IncomingGRPCConfig(), nil
}
return config
}
// IncomingRPCConfig generates a *tls.Config for incoming RPC connections. // IncomingRPCConfig generates a *tls.Config for incoming RPC connections.
func (c *Configurator) IncomingRPCConfig() *tls.Config { func (c *Configurator) IncomingRPCConfig() *tls.Config {
c.log("IncomingRPCConfig") c.log("IncomingRPCConfig")

View File

@ -277,6 +277,10 @@ The options below are all specified on the command-line.
to an environment which communicates the HTTP port through the environment e.g. PaaS like CloudFoundry, allowing to an environment which communicates the HTTP port through the environment e.g. PaaS like CloudFoundry, allowing
you to set the port directly via a Procfile. you to set the port directly via a Procfile.
* <a name="_https_port"></a><a href="#_https_port">`-https-port`</a> - the HTTPS API
port to listen on. Default -1 (https disabled). See [ports](#ports)
documentation for more detail.
* <a name="_log_file"></a><a href="#_log_file">`-log-file`</a> - to redirect all the Consul agent log messages to a file. This can be specified with the complete path along with the name of the log. In case the path doesn't have the filename, the filename defaults to `consul-{timestamp}.log`. Can be combined with <a href="#_log_rotate_bytes"> -log-rotate-bytes</a> and <a href="#_log_rotate_duration"> -log-rotate-duration </a> for a fine-grained log rotation experience. * <a name="_log_file"></a><a href="#_log_file">`-log-file`</a> - to redirect all the Consul agent log messages to a file. This can be specified with the complete path along with the name of the log. In case the path doesn't have the filename, the filename defaults to `consul-{timestamp}.log`. Can be combined with <a href="#_log_rotate_bytes"> -log-rotate-bytes</a> and <a href="#_log_rotate_duration"> -log-rotate-duration </a> for a fine-grained log rotation experience.
* <a name="_log_rotate_bytes"></a><a href="#_log_rotate_bytes">`-log-rotate-bytes`</a> - to specify the number of bytes that should be written to a log before it needs to be rotated. Unless specified, there is no limit to the number of bytes that can be written to a log file. * <a name="_log_rotate_bytes"></a><a href="#_log_rotate_bytes">`-log-rotate-bytes`</a> - to specify the number of bytes that should be written to a log before it needs to be rotated. Unless specified, there is no limit to the number of bytes that can be written to a log file.