Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

228 lines
4.7 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package pool
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/tlsutil"
)
func TestPeekForTLS_not_TLS(t *testing.T) {
type testcase struct {
name string
connData []byte
}
var cases []testcase
for _, rpcType := range []RPCType{
RPCConsul,
RPCRaft,
RPCMultiplex,
RPCTLS,
RPCMultiplexV2,
RPCSnapshot,
RPCGossip,
RPCTLSInsecure,
RPCGRPC,
} {
cases = append(cases, testcase{
name: fmt.Sprintf("tcp rpc type byte %d", rpcType),
connData: []byte{byte(rpcType), 'h', 'e', 'l', 'l', 'o'},
})
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
dead := time.Now().Add(1 * time.Second)
serverConn, clientConn, err := deadlineNetPipe(dead)
require.NoError(t, err)
go func() {
_, _ = clientConn.Write(tc.connData)
_ = clientConn.Close()
}()
defer serverConn.Close()
wrapped, isTLS, err := PeekForTLS(serverConn)
require.NoError(t, err)
require.False(t, isTLS)
all, err := io.ReadAll(wrapped)
require.NoError(t, err)
require.Equal(t, tc.connData, all)
})
}
}
func TestPeekForTLS_actual_TLS(t *testing.T) {
type testcase struct {
name string
connData []byte
}
var cases []testcase
for _, rpcType := range []RPCType{
RPCConsul,
RPCRaft,
RPCMultiplex,
RPCTLS,
RPCMultiplexV2,
RPCSnapshot,
RPCGossip,
RPCTLSInsecure,
RPCGRPC,
} {
cases = append(cases, testcase{
name: fmt.Sprintf("tcp rpc type byte %d", rpcType),
connData: []byte{byte(rpcType), 'h', 'e', 'l', 'l', 'o'},
})
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
testPeekForTLS_withTLS(t, tc.connData)
})
}
}
func testPeekForTLS_withTLS(t *testing.T, connData []byte) {
t.Helper()
cert, caPEM, err := generateTestCert("server.dc1.consul")
require.NoError(t, err)
roots := x509.NewCertPool()
require.True(t, roots.AppendCertsFromPEM(caPEM))
dead := time.Now().Add(1 * time.Second)
serverConn, clientConn, err := deadlineNetPipe(dead)
require.NoError(t, err)
var (
clientErrCh = make(chan error, 1)
serverErrCh = make(chan error, 1)
serverGotPayload []byte
)
go func(conn net.Conn) { // Client
config := &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: roots,
ServerName: "server.dc1.consul",
NextProtos: []string{"foo/bar"},
}
tlsConn := tls.Client(conn, config)
defer tlsConn.Close()
if err := tlsConn.Handshake(); err != nil {
clientErrCh <- err
return
}
_, err = tlsConn.Write(connData)
clientErrCh <- err
}(clientConn)
go func(conn net.Conn) { // Server
defer conn.Close()
wrapped, isTLS, err := PeekForTLS(conn)
if err != nil {
serverErrCh <- err
return
} else if !isTLS {
serverErrCh <- errors.New("expected to have peeked TLS but did not")
return
}
config := &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: roots,
Certificates: []tls.Certificate{cert},
ServerName: "server.dc1.consul",
NextProtos: []string{"foo/bar"},
}
tlsConn := tls.Server(wrapped, config)
defer tlsConn.Close()
if err := tlsConn.Handshake(); err != nil {
serverErrCh <- err
return
}
all, err := io.ReadAll(tlsConn)
if err != nil {
serverErrCh <- err
return
}
serverGotPayload = all
serverErrCh <- nil
}(serverConn)
require.NoError(t, <-clientErrCh)
require.NoError(t, <-serverErrCh)
require.Equal(t, connData, serverGotPayload)
}
func deadlineNetPipe(deadline time.Time) (net.Conn, net.Conn, error) {
server, client := net.Pipe()
if err := server.SetDeadline(deadline); err != nil {
server.Close()
client.Close()
return nil, nil, err
}
if err := client.SetDeadline(deadline); err != nil {
server.Close()
client.Close()
return nil, nil, err
}
return server, client, nil
}
func generateTestCert(serverName string) (cert tls.Certificate, caPEM []byte, err error) {
signer, _, err := tlsutil.GeneratePrivateKey()
if err != nil {
return tls.Certificate{}, nil, err
}
ca, _, err := tlsutil.GenerateCA(tlsutil.CAOpts{Signer: signer})
if err != nil {
return tls.Certificate{}, nil, err
}
certificate, privateKey, err := tlsutil.GenerateCert(tlsutil.CertOpts{
Signer: signer,
CA: ca,
Name: "Test Cert Name",
Days: 365,
DNSNames: []string{serverName},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
})
if err != nil {
return tls.Certificate{}, nil, err
}
cert, err = tls.X509KeyPair([]byte(certificate), []byte(privateKey))
if err != nil {
return tls.Certificate{}, nil, err
}
return cert, []byte(ca), nil
}