open-nomad/command/tls_cert_create_test.go
Lance Haig 568da5918b
cli: tls certs not created with correct SANs (#16959)
The `nomad tls cert` command did not create certificates with the correct SANs for
them to work with non default domain and region names. This changset updates the
code to support non default domains and regions in the certificates.
2023-05-22 09:31:56 -04:00

329 lines
9.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"crypto/x509"
"net"
"os"
"strings"
"testing"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/require"
)
func TestTlsCertCreateCommand_InvalidArgs(t *testing.T) {
t.Parallel()
type testcase struct {
args []string
expectErr string
}
cases := map[string]testcase{
"no args (ca/key inferred)": {[]string{},
"Please provide either -server, -client, or -cli"},
"no ca": {[]string{"-ca", "", "-key", ""},
"Please provide the ca"},
"no key": {[]string{"-ca", "foo.pem", "-key", ""},
"Please provide the key"},
"server+client+cli": {[]string{"-server", "-client", "-cli"},
"Please provide either -server, -client, or -cli"},
"server+client": {[]string{"-server", "-client"},
"Please provide either -server, -client, or -cli"},
"server+cli": {[]string{"-server", "-cli"},
"Please provide either -server, -client, or -cli"},
"client+cli": {[]string{"-client", "-cli"},
"Please provide either -server, -client, or -cli"},
}
for name, tc := range cases {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
ui := cli.NewMockUi()
cmd := &TLSCertCreateCommand{Meta: Meta{Ui: ui}}
require.NotEqual(t, 0, cmd.Run(tc.args))
got := ui.ErrorWriter.String()
if tc.expectErr == "" {
require.NotEmpty(t, got) // don't care
} else {
require.Contains(t, got, tc.expectErr)
}
})
}
}
func TestTlsCertCreateCommandDefaults_fileCreate(t *testing.T) {
testDir := t.TempDir()
previousDirectory, err := os.Getwd()
require.NoError(t, err)
require.NoError(t, os.Chdir(testDir))
defer os.Chdir(previousDirectory)
ui := cli.NewMockUi()
caCmd := &TLSCACreateCommand{Meta: Meta{Ui: ui}}
// Setup CA keys
caCmd.Run([]string{"nomad"})
type testcase struct {
name string
typ string
args []string
certPath string
keyPath string
expectCN string
expectDNS []string
expectIP []net.IP
errOut string
}
// The following subtests must run serially.
cases := []testcase{
{"server0",
"server",
[]string{"-server"},
"global-server-nomad.pem",
"global-server-nomad-key.pem",
"server.global.nomad",
[]string{
"server.global.nomad",
"localhost",
},
[]net.IP{{127, 0, 0, 1}},
"==> WARNING: Server Certificates grants authority to become a\n server and access all state in the cluster including root keys\n and all ACL tokens. Do not distribute them to production hosts\n that are not server nodes. Store them as securely as CA keys.\n",
},
{"server0-region1",
"server",
[]string{"-server", "-region", "region1"},
"region1-server-nomad.pem",
"region1-server-nomad-key.pem",
"server.region1.nomad",
[]string{
"server.region1.nomad",
"server.global.nomad",
"localhost",
},
[]net.IP{{127, 0, 0, 1}},
"==> WARNING: Server Certificates grants authority to become a\n server and access all state in the cluster including root keys\n and all ACL tokens. Do not distribute them to production hosts\n that are not server nodes. Store them as securely as CA keys.\n",
},
{"client0",
"client",
[]string{"-client"},
"global-client-nomad.pem",
"global-client-nomad-key.pem",
"client.global.nomad",
[]string{
"client.global.nomad",
"localhost",
},
[]net.IP{{127, 0, 0, 1}},
"",
},
{"cli0",
"cli",
[]string{"-cli"},
"global-cli-nomad.pem",
"global-cli-nomad-key.pem",
"cli.global.nomad",
[]string{
"cli.global.nomad",
"localhost",
},
[]net.IP(nil),
"",
},
}
for _, tc := range cases {
tc := tc
require.True(t, t.Run(tc.name, func(t *testing.T) {
ui := cli.NewMockUi()
cmd := &TLSCertCreateCommand{Meta: Meta{Ui: ui}}
require.Equal(t, 0, cmd.Run(tc.args))
require.Equal(t, tc.errOut, ui.ErrorWriter.String())
// is a valid cert expects the cert
cert := testutil.IsValidCertificate(t, tc.certPath)
require.Equal(t, tc.expectCN, cert.Subject.CommonName)
require.True(t, cert.BasicConstraintsValid)
require.Equal(t, x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment, cert.KeyUsage)
switch tc.typ {
case "server":
require.Equal(t,
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
cert.ExtKeyUsage)
case "client":
require.Equal(t,
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
cert.ExtKeyUsage)
case "cli":
require.Equal(t,
[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
cert.ExtKeyUsage)
}
require.False(t, cert.IsCA)
require.Equal(t, tc.expectDNS, cert.DNSNames)
require.Equal(t, tc.expectIP, cert.IPAddresses)
}))
}
}
func TestTlsRecordPreparation(t *testing.T) {
type testcase struct {
name string
certType string
regionName string
domain string
dnsNames []string
ipAddresses []string
expectedipAddresses []net.IP
expectedDNSNames []string
expectedName string
expectedextKeyUsage []x509.ExtKeyUsage
expectedPrefix string
}
// The default values are region = global and domain = nomad.
cases := []testcase{
{
name: "server0",
certType: "server",
regionName: "global",
domain: "nomad",
dnsNames: []string{},
ipAddresses: []string{},
expectedipAddresses: []net.IP{net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"server.global.nomad",
"localhost",
},
expectedName: "server.global.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "global-server-nomad",
},
{
name: "server0-region1",
certType: "server",
regionName: "region1",
domain: "nomad",
dnsNames: []string{},
ipAddresses: []string{},
expectedipAddresses: []net.IP{net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"server.region1.nomad",
"server.global.nomad",
"localhost",
},
expectedName: "server.region1.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "region1-server-nomad",
},
{
name: "server0-domain1",
certType: "server",
regionName: "global",
domain: "domain1",
dnsNames: []string{},
ipAddresses: []string{},
expectedipAddresses: []net.IP{net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"server.global.nomad",
"server.global.domain1",
"localhost",
},
expectedName: "server.global.domain1",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "global-server-domain1",
},
{
name: "server0-dns",
certType: "server",
regionName: "global",
domain: "nomad",
dnsNames: []string{"server.global.foo"},
ipAddresses: []string{},
expectedipAddresses: []net.IP{net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"server.global.foo",
"server.global.nomad",
"localhost",
},
expectedName: "server.global.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "global-server-nomad",
},
{
name: "server0-ips",
certType: "server",
regionName: "global",
domain: "nomad",
dnsNames: []string{},
ipAddresses: []string{"10.0.0.1"},
expectedipAddresses: []net.IP{net.ParseIP("10.0.0.1"), net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"server.global.nomad",
"localhost",
},
expectedName: "server.global.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "global-server-nomad",
},
{
name: "client0",
certType: "client",
regionName: "global",
domain: "nomad",
dnsNames: []string{},
ipAddresses: []string{},
expectedipAddresses: []net.IP{net.ParseIP("127.0.0.1")},
expectedDNSNames: []string{
"client.global.nomad",
"localhost",
},
expectedName: "client.global.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
expectedPrefix: "global-client-nomad",
},
{
name: "cli0",
certType: "cli",
regionName: "global",
domain: "nomad",
dnsNames: []string{},
ipAddresses: []string{},
expectedipAddresses: []net.IP(nil),
expectedDNSNames: []string{
"cli.global.nomad",
"localhost",
},
expectedName: "cli.global.nomad",
expectedextKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
expectedPrefix: "global-cli-nomad",
},
}
for _, tc := range cases {
tc := tc
require.True(t, t.Run(tc.name, func(t *testing.T) {
var ipAddresses []net.IP
for _, i := range tc.ipAddresses {
if len(i) > 0 {
ipAddresses = append(ipAddresses, net.ParseIP(strings.TrimSpace(i)))
}
}
ipAddresses, dnsNames, name, extKeyUsage, prefix := recordPreparation(tc.certType, tc.regionName, tc.domain, tc.dnsNames, ipAddresses)
require.Equal(t, tc.expectedipAddresses, ipAddresses)
require.Equal(t, tc.expectedDNSNames, dnsNames)
require.Equal(t, tc.expectedName, name)
require.Equal(t, tc.expectedextKeyUsage, extKeyUsage)
require.Equal(t, tc.expectedPrefix, prefix)
}))
}
}