mirror of https://github.com/hashicorp/consul
200 lines
5.6 KiB
Go
200 lines
5.6 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package consul
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
msgpackrpc "github.com/hashicorp/consul-net-rpc/net-rpc-msgpackrpc"
|
|
|
|
"github.com/hashicorp/consul/agent/connect"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
"github.com/hashicorp/consul/testrpc"
|
|
"github.com/hashicorp/consul/tlsutil"
|
|
)
|
|
|
|
func TestAutoEncryptSign(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
type test struct {
|
|
Name string
|
|
Config tlsutil.Config
|
|
ConnError bool
|
|
RPCError bool
|
|
Cert string
|
|
Key string
|
|
}
|
|
root := "../../test/ca/root.cer"
|
|
badRoot := "../../test/ca_path/cert1.crt"
|
|
|
|
tests := []test{
|
|
{Name: "Works with defaults", Config: tlsutil.Config{}, ConnError: false},
|
|
{Name: "Works with good root", Config: tlsutil.Config{InternalRPC: tlsutil.ProtocolConfig{CAFile: root}}, ConnError: false},
|
|
{Name: "VerifyOutgoing fails because of bad root", Config: tlsutil.Config{InternalRPC: tlsutil.ProtocolConfig{CAFile: badRoot}}, ConnError: true},
|
|
{Name: "VerifyServerHostname fails", Config: tlsutil.Config{InternalRPC: tlsutil.ProtocolConfig{CAFile: root, VerifyServerHostname: true}}, ConnError: false, RPCError: true},
|
|
{Name: "VerifyServerHostname succeeds", Cert: "../../test/key/ourdomain_server.cer", Key: "../../test/key/ourdomain_server.key",
|
|
Config: tlsutil.Config{InternalRPC: tlsutil.ProtocolConfig{VerifyServerHostname: true, CAFile: root}}, ConnError: false, RPCError: false},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
cert := test.Cert
|
|
key := test.Key
|
|
if cert == "" {
|
|
cert = "../../test/key/ourdomain.cer"
|
|
}
|
|
if key == "" {
|
|
key = "../../test/key/ourdomain.key"
|
|
}
|
|
dir, s := testServerWithConfig(t, func(c *Config) {
|
|
c.AutoEncryptAllowTLS = true
|
|
c.PrimaryDatacenter = "dc1"
|
|
c.Bootstrap = true
|
|
c.TLSConfig.InternalRPC.CAFile = root
|
|
c.TLSConfig.InternalRPC.VerifyOutgoing = true
|
|
c.TLSConfig.InternalRPC.CertFile = cert
|
|
c.TLSConfig.InternalRPC.KeyFile = key
|
|
})
|
|
defer os.RemoveAll(dir)
|
|
defer s.Shutdown()
|
|
testrpc.WaitForLeader(t, s.RPC, "dc1")
|
|
|
|
info := fmt.Sprintf("case %d", i)
|
|
|
|
// Generate a CSR and request signing
|
|
id := &connect.SpiffeIDAgent{
|
|
Host: strings.TrimSuffix("domain", "."),
|
|
Datacenter: "dc1",
|
|
Agent: "uuid",
|
|
}
|
|
|
|
// Create a new private key
|
|
pk, _, err := connect.GeneratePrivateKey()
|
|
require.NoError(t, err, info)
|
|
|
|
// Create a CSR.
|
|
dnsNames := []string{"localhost"}
|
|
ipAddresses := []net.IP{net.ParseIP("127.0.0.1")}
|
|
csr, err := connect.CreateCSR(id, pk, dnsNames, ipAddresses)
|
|
require.NoError(t, err, info)
|
|
require.NotEmpty(t, csr, info)
|
|
args := &structs.CASignRequest{
|
|
Datacenter: "dc1",
|
|
CSR: csr,
|
|
}
|
|
|
|
cfg := test.Config
|
|
cfg.AutoTLS = true
|
|
cfg.Domain = "consul"
|
|
codec, err := insecureRPCClient(s, cfg)
|
|
if test.ConnError {
|
|
require.Error(t, err, info)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err, info)
|
|
var reply structs.SignedResponse
|
|
err = msgpackrpc.CallWithCodec(codec, "AutoEncrypt.Sign", args, &reply)
|
|
codec.Close()
|
|
if test.RPCError {
|
|
require.Error(t, err, info)
|
|
return
|
|
}
|
|
require.NoError(t, err, info)
|
|
|
|
// Get the current CA
|
|
state := s.fsm.State()
|
|
_, ca, err := state.CARootActive(nil)
|
|
require.NoError(t, err, info)
|
|
|
|
// Verify that the cert is signed by the CA
|
|
roots := x509.NewCertPool()
|
|
assert.True(t, roots.AppendCertsFromPEM([]byte(ca.RootCert)))
|
|
leaf, err := connect.ParseCert(reply.IssuedCert.CertPEM)
|
|
require.NoError(t, err, info)
|
|
_, err = leaf.Verify(x509.VerifyOptions{
|
|
Roots: roots,
|
|
})
|
|
require.NoError(t, err, info)
|
|
|
|
// Verify SANs
|
|
require.Equal(t, dnsNames, leaf.DNSNames)
|
|
require.Len(t, leaf.IPAddresses, 1)
|
|
require.True(t, ipAddresses[0].Equal(leaf.IPAddresses[0]))
|
|
|
|
// Verify other fields
|
|
require.Equal(t, "uuid", reply.IssuedCert.Agent, info)
|
|
require.Len(t, reply.ManualCARoots, 1, info)
|
|
require.Len(t, reply.ConnectCARoots.Roots, 1, info)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAutoEncryptSign_MismatchedDC(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cert := "../../test/key/ourdomain.cer"
|
|
key := "../../test/key/ourdomain.key"
|
|
root := "../../test/ca/root.cer"
|
|
dir, s := testServerWithConfig(t, func(c *Config) {
|
|
c.AutoEncryptAllowTLS = true
|
|
c.PrimaryDatacenter = "dc1"
|
|
c.Bootstrap = true
|
|
c.TLSConfig.InternalRPC.CAFile = root
|
|
c.TLSConfig.InternalRPC.VerifyOutgoing = true
|
|
c.TLSConfig.InternalRPC.CertFile = cert
|
|
c.TLSConfig.InternalRPC.KeyFile = key
|
|
})
|
|
defer os.RemoveAll(dir)
|
|
defer s.Shutdown()
|
|
testrpc.WaitForLeader(t, s.RPC, "dc1")
|
|
|
|
// Generate a CSR and request signing
|
|
id := &connect.SpiffeIDAgent{
|
|
Host: strings.TrimSuffix("domain", "."),
|
|
Datacenter: "different",
|
|
Agent: "uuid",
|
|
}
|
|
|
|
// Create a new private key
|
|
pk, _, err := connect.GeneratePrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
// Create a CSR.
|
|
dnsNames := []string{"localhost"}
|
|
ipAddresses := []net.IP{net.ParseIP("127.0.0.1")}
|
|
csr, err := connect.CreateCSR(id, pk, dnsNames, ipAddresses)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, csr)
|
|
args := &structs.CASignRequest{
|
|
Datacenter: "different",
|
|
CSR: csr,
|
|
}
|
|
|
|
cfg := tlsutil.Config{
|
|
AutoTLS: true,
|
|
Domain: "consul",
|
|
}
|
|
codec, err := insecureRPCClient(s, cfg)
|
|
require.NoError(t, err)
|
|
|
|
var reply structs.SignedResponse
|
|
err = msgpackrpc.CallWithCodec(codec, "AutoEncrypt.Sign", args, &reply)
|
|
codec.Close()
|
|
require.EqualError(t, err, "mismatched datacenter (client_dc='different' server_dc='dc1'); check client has same datacenter set as servers")
|
|
return
|
|
}
|