From 6b1ec05470f1930382380e0494d5af85e32cc32d Mon Sep 17 00:00:00 2001 From: Luke Kysow <1034429+lkysow@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:13:41 -0700 Subject: [PATCH] autoencrypt: helpful error for clients with wrong dc (#14832) * autoencrypt: helpful error for clients with wrong dc If clients have set a different datacenter than the servers they're connecting with for autoencrypt, give a helpful error message. --- .changelog/14832.txt | 3 ++ agent/consul/auto_encrypt_endpoint.go | 8 ++++ agent/consul/auto_encrypt_endpoint_test.go | 55 ++++++++++++++++++++++ agent/pool/pool.go | 4 +- 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 .changelog/14832.txt diff --git a/.changelog/14832.txt b/.changelog/14832.txt new file mode 100644 index 000000000..6fef1c257 --- /dev/null +++ b/.changelog/14832.txt @@ -0,0 +1,3 @@ +```release-note:improvement +agent: Give better error when client specifies wrong datacenter when auto-encrypt is enabled. +``` diff --git a/agent/consul/auto_encrypt_endpoint.go b/agent/consul/auto_encrypt_endpoint.go index 2f96efacf..3adfad8ec 100644 --- a/agent/consul/auto_encrypt_endpoint.go +++ b/agent/consul/auto_encrypt_endpoint.go @@ -2,6 +2,7 @@ package consul import ( "errors" + "fmt" "github.com/hashicorp/consul/agent/structs" ) @@ -24,6 +25,13 @@ func (a *AutoEncrypt) Sign( if !a.srv.config.AutoEncryptAllowTLS { return ErrAutoEncryptAllowTLSNotEnabled } + // There's no reason to forward the AutoEncrypt.Sign RPC to a remote datacenter because its certificates + // won't be valid in this datacenter. If the client is requesting a different datacenter, then this is a + // misconfiguration, and we can give them a useful error. + if args.Datacenter != a.srv.config.Datacenter { + return fmt.Errorf("mismatched datacenter (client_dc='%s' server_dc='%s');"+ + " check client has same datacenter set as servers", args.Datacenter, a.srv.config.Datacenter) + } if done, err := a.srv.ForwardRPC("AutoEncrypt.Sign", args, reply); done { return err } diff --git a/agent/consul/auto_encrypt_endpoint_test.go b/agent/consul/auto_encrypt_endpoint_test.go index a27cef26b..50d356d9f 100644 --- a/agent/consul/auto_encrypt_endpoint_test.go +++ b/agent/consul/auto_encrypt_endpoint_test.go @@ -139,3 +139,58 @@ func TestAutoEncryptSign(t *testing.T) { }) } } + +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 +} diff --git a/agent/pool/pool.go b/agent/pool/pool.go index 7c4b3e2d7..068bc56c2 100644 --- a/agent/pool/pool.go +++ b/agent/pool/pool.go @@ -592,14 +592,14 @@ func (p *ConnPool) rpcInsecure(dc string, addr net.Addr, method string, args int var codec rpc.ClientCodec conn, _, err := p.dial(dc, addr, 0, RPCTLSInsecure) if err != nil { - return fmt.Errorf("rpcinsecure error establishing connection: %w", err) + return fmt.Errorf("rpcinsecure: error establishing connection: %w", err) } codec = msgpackrpc.NewCodecFromHandle(true, true, conn, structs.MsgpackHandle) // Make the RPC call err = msgpackrpc.CallWithCodec(codec, method, args, reply) if err != nil { - return fmt.Errorf("rpcinsecure error making call: %w", err) + return fmt.Errorf("rpcinsecure: error making call: %w", err) } return nil