diff --git a/agent/agent.go b/agent/agent.go
index 195c9b2c9d..69787b2c34 100644
--- a/agent/agent.go
+++ b/agent/agent.go
@@ -723,9 +723,9 @@ func (a *Agent) listenAndServeGRPC() error {
if a.config.HTTPSPort > 0 {
// gRPC uses the same TLS settings as the HTTPS API. If HTTPS is
// 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 {
- a.grpcServer, err = a.xdsServer.GRPCServer("", "")
+ a.grpcServer, err = a.xdsServer.GRPCServer(nil)
}
if err != nil {
return err
diff --git a/agent/config/flags.go b/agent/config/flags.go
index 4fd8611fa1..c61a2fc505 100644
--- a/agent/config/flags.go
+++ b/agent/config/flags.go
@@ -79,6 +79,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
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.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.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.")
diff --git a/agent/config/flags_test.go b/agent/config/flags_test.go
index 94876f3a03..b4bc5888c6 100644
--- a/agent/config/flags_test.go
+++ b/agent/config/flags_test.go
@@ -52,6 +52,14 @@ func TestParseFlags(t *testing.T) {
args: []string{`-grpc-port`, `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`},
flags: Flags{Config: Config{Ports: Ports{SerfLAN: pInt(1)}}},
diff --git a/agent/config/runtime.go b/agent/config/runtime.go
index ec00f6d3bd..8b243fc25f 100644
--- a/agent/config/runtime.go
+++ b/agent/config/runtime.go
@@ -807,6 +807,7 @@ type RuntimeConfig struct {
// Setting this to a value <= 0 disables the endpoint.
//
// hcl: ports { https = int }
+ // flags: -https-port int
HTTPSPort int
// KeyFile is used to provide a TLS key that is used for serving TLS
diff --git a/agent/xds/server.go b/agent/xds/server.go
index 7f11994ea0..f5d48ac0e7 100644
--- a/agent/xds/server.go
+++ b/agent/xds/server.go
@@ -25,6 +25,7 @@ import (
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
+ "github.com/hashicorp/consul/tlsutil"
)
// 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
// requests.
-func (s *Server) GRPCServer(certFile, keyFile string) (*grpc.Server, error) {
+func (s *Server) GRPCServer(tlsConfigurator *tlsutil.Configurator) (*grpc.Server, error) {
opts := []grpc.ServerOption{
grpc.MaxConcurrentStreams(2048),
}
- if certFile != "" && keyFile != "" {
- creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
- if err != nil {
- return nil, err
+ if tlsConfigurator != nil {
+ if tlsConfigurator.Cert() != nil {
+ creds := credentials.NewTLS(tlsConfigurator.IncomingGRPCConfig())
+ opts = append(opts, grpc.Creds(creds))
}
- opts = append(opts, grpc.Creds(creds))
}
srv := grpc.NewServer(opts...)
envoydisco.RegisterAggregatedDiscoveryServiceServer(srv, s)
diff --git a/tlsutil/config.go b/tlsutil/config.go
index 58352af1b0..42a4e022ef 100644
--- a/tlsutil/config.go
+++ b/tlsutil/config.go
@@ -441,12 +441,7 @@ func (c *Configurator) commonTLSConfig(verifyIncoming bool) *tls.Config {
// autoEncrypt cert too so that a client can encrypt incoming
// connections without having a manual cert configured.
tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
- cert := c.manual.cert
- if cert == nil {
- cert = c.autoEncrypt.cert
- }
-
- return cert, nil
+ return c.Cert(), nil
}
// GetClientCertificate is used when acting as a client and responding
@@ -477,6 +472,17 @@ func (c *Configurator) commonTLSConfig(verifyIncoming bool) *tls.Config {
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.
func (c *Configurator) VerifyIncomingRPC() bool {
c.RLock()
@@ -561,6 +567,22 @@ func (c *Configurator) VerifyServerHostname() bool {
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.
func (c *Configurator) IncomingRPCConfig() *tls.Config {
c.log("IncomingRPCConfig")
diff --git a/website/source/docs/agent/options.html.md b/website/source/docs/agent/options.html.md
index 2f00562a1a..0b4b4b1be2 100644
--- a/website/source/docs/agent/options.html.md
+++ b/website/source/docs/agent/options.html.md
@@ -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
you to set the port directly via a Procfile.
+* `-https-port` - the HTTPS API
+ port to listen on. Default -1 (https disabled). See [ports](#ports)
+ documentation for more detail.
+
* `-log-file` - 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 -log-rotate-bytes and -log-rotate-duration for a fine-grained log rotation experience.
* `-log-rotate-bytes` - 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.