Allow passing ALPN next protocols down to connect services. Fixes #4466. (#9920)

* Allow passing ALPN next protocols down to connect services. Fixes #4466.

* Update connect/proxy/proxy_test.go

Co-authored-by: Paul Banks <banks@banksco.de>

Co-authored-by: Paul Banks <banks@banksco.de>
This commit is contained in:
Florian Apolloner 2021-03-26 12:34:47 +01:00 committed by GitHub
parent 7e03670b1c
commit 0398833f54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 24 deletions

3
.changelog/9920.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
connect: The builtin connect proxy no longer advertises support for h2 via ALPN. [[GH-4466](https://github.com/hashicorp/consul/issues/4466)].
```

View File

@ -46,7 +46,7 @@ type Config struct {
// Service returns the *connect.Service structure represented by this config. // Service returns the *connect.Service structure represented by this config.
func (c *Config) Service(client *api.Client, logger hclog.Logger) (*connect.Service, error) { func (c *Config) Service(client *api.Client, logger hclog.Logger) (*connect.Service, error) {
return connect.NewServiceWithLogger(c.ProxiedServiceName, client, logger) return connect.NewServiceWithConfig(c.ProxiedServiceName, connect.Config{Client: client, Logger: logger, ServerNextProtos: []string{}})
} }
// PublicListenerConfig contains the parameters needed for the incoming mTLS // PublicListenerConfig contains the parameters needed for the incoming mTLS

View File

@ -2,6 +2,7 @@ package proxy
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"testing" "testing"
@ -60,12 +61,16 @@ func TestProxy_public(t *testing.T) {
defer p.Close() defer p.Close()
go p.Serve() go p.Serve()
// We create this client with an explicit ServerNextProtos here which will use `h2`
// if the proxy supports it. This is so we can verify below that the proxy _doesn't_
// advertise `h2` support as it's only a L4 proxy.
svc, err := connect.NewServiceWithConfig("echo", connect.Config{Client: client, ServerNextProtos: []string{"h2"}})
require.NoError(err)
// Create a test connection to the proxy. We retry here a few times // Create a test connection to the proxy. We retry here a few times
// since this is dependent on the agent actually starting up and setting // since this is dependent on the agent actually starting up and setting
// up the CA. // up the CA.
var conn net.Conn var conn net.Conn
svc, err := connect.NewService("echo", client)
require.NoError(err)
retry.Run(t, func(r *retry.R) { retry.Run(t, func(r *retry.R) {
conn, err = svc.Dial(context.Background(), &connect.StaticResolver{ conn, err = svc.Dial(context.Background(), &connect.StaticResolver{
Addr: TestLocalAddr(ports[0]), Addr: TestLocalAddr(ports[0]),
@ -76,6 +81,10 @@ func TestProxy_public(t *testing.T) {
} }
}) })
// Verify that we did not select h2 via ALPN since the proxy is layer 4 only
tlsConn := conn.(*tls.Conn)
require.Equal("", tlsConn.ConnectionState().NegotiatedProtocol)
// Connection works, test it is the right one // Connection works, test it is the right one
TestEchoConn(t, conn, "") TestEchoConn(t, conn, "")
} }

View File

@ -53,29 +53,34 @@ type Service struct {
logger hclog.Logger logger hclog.Logger
} }
// NewService creates and starts a Service. The caller must close the returned // Config represents the configuration options for a service.
// service to free resources and allow the program to exit normally. This is type Config struct {
// typically called in a signal handler. // client is the mandatory Consul API client. Will panic if not set.
// Client *api.Client
// Caller must provide client which is already configured to speak to the local // Logger is the logger to use. If nil a default logger will be used.
// Consul agent, and with an ACL token that has `service:write` privileges for Logger hclog.Logger
// the service specified. // ServerNextProtos are the protocols advertised via ALPN. If nil, defaults to
func NewService(serviceName string, client *api.Client) (*Service, error) { // ["h2"] for backwards compatibility. Usually there is no need to change this,
logger := hclog.New(&hclog.LoggerOptions{}) // see https://github.com/hashicorp/consul/issues/4466 for some discussion on why
// this can be useful.
return NewServiceWithLogger(serviceName, client, ServerNextProtos []string
logger)
} }
// NewServiceWithLogger starts the service with a specified log.Logger. // NewServiceWithConfig starts a service with the specified Config.
func NewServiceWithLogger(serviceName string, client *api.Client, func NewServiceWithConfig(serviceName string, config Config) (*Service, error) {
logger hclog.Logger) (*Service, error) { if config.Logger == nil {
config.Logger = hclog.New(&hclog.LoggerOptions{})
}
tlsCfg := defaultTLSConfig()
if config.ServerNextProtos != nil {
tlsCfg.NextProtos = config.ServerNextProtos
}
s := &Service{ s := &Service{
service: serviceName, service: serviceName,
client: client, client: config.Client,
logger: logger.Named(logging.Connect).With("service", serviceName), logger: config.Logger.Named(logging.Connect).With("service", serviceName),
tlsCfg: newDynamicTLSConfig(defaultTLSConfig(), logger), tlsCfg: newDynamicTLSConfig(tlsCfg, config.Logger),
httpResolverFromAddr: ConsulResolverFromAddrFunc(client), httpResolverFromAddr: ConsulResolverFromAddrFunc(config.Client),
} }
// Set up root and leaf watches // Set up root and leaf watches
@ -98,12 +103,29 @@ func NewServiceWithLogger(serviceName string, client *api.Client,
s.leafWatch = p s.leafWatch = p
s.leafWatch.HybridHandler = s.leafWatchHandler s.leafWatch.HybridHandler = s.leafWatchHandler
go s.rootsWatch.RunWithClientAndHclog(client, s.logger) go s.rootsWatch.RunWithClientAndHclog(config.Client, s.logger)
go s.leafWatch.RunWithClientAndHclog(client, s.logger) go s.leafWatch.RunWithClientAndHclog(config.Client, s.logger)
return s, nil return s, nil
} }
// NewService creates and starts a Service. The caller must close the returned
// service to free resources and allow the program to exit normally. This is
// typically called in a signal handler.
//
// Caller must provide client which is already configured to speak to the local
// Consul agent, and with an ACL token that has `service:write` privileges for
// the service specified.
func NewService(serviceName string, client *api.Client) (*Service, error) {
return NewServiceWithConfig(serviceName, Config{Client: client})
}
// NewServiceWithLogger starts the service with a specified log.Logger.
func NewServiceWithLogger(serviceName string, client *api.Client,
logger hclog.Logger) (*Service, error) {
return NewServiceWithConfig(serviceName, Config{Client: client, Logger: logger})
}
// NewDevServiceFromCertFiles creates a Service using certificate and key files // NewDevServiceFromCertFiles creates a Service using certificate and key files
// passed instead of fetching them from the client. // passed instead of fetching them from the client.
func NewDevServiceFromCertFiles(serviceID string, logger hclog.Logger, func NewDevServiceFromCertFiles(serviceID string, logger hclog.Logger,