Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport of Do not use static certificates for diagnose tests into release/1.18.x #29126

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions command/operator_diagnose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"github.com/hashicorp/cli"
"github.com/hashicorp/vault/helper/constants"
pkihelper "github.com/hashicorp/vault/helper/testhelpers/pki"
"github.com/hashicorp/vault/vault/diagnose"
)

Expand All @@ -31,8 +33,55 @@ func testOperatorDiagnoseCommand(tb testing.TB) *OperatorDiagnoseCommand {
}
}

func generateTLSConfigOk(t *testing.T, ca pkihelper.LeafWithIntermediary) string {
t.Helper()
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "tls_config_ok.hcl")

templateFile := "./server/test-fixtures/tls_config_ok.hcl"
contents, err := os.ReadFile(templateFile)
if err != nil {
t.Fatalf("failed to read file %s: %v", templateFile, err)
}
contents = []byte(strings.ReplaceAll(string(contents), "{REPLACE_LEAF_CERT_FILE}", ca.Leaf.CertFile))
contents = []byte(strings.ReplaceAll(string(contents), "{REPLACE_LEAF_KEY_FILE}", ca.Leaf.KeyFile))

err = os.WriteFile(configPath, contents, 0o644)
if err != nil {
t.Fatalf("failed to write file %s: %v", configPath, err)
}

return configPath
}

func generateTransitTLSCheck(t *testing.T, ca pkihelper.LeafWithIntermediary) string {
t.Helper()
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "diagnose_seal_transit_tls_check.hcl")

templateFile := "./server/test-fixtures/diagnose_seal_transit_tls_check.hcl"
contents, err := os.ReadFile(templateFile)
if err != nil {
t.Fatalf("failed to read file %s: %v", templateFile, err)
}
contents = []byte(strings.ReplaceAll(string(contents), "{REPLACE_LEAF_CERT_FILE}", ca.Leaf.CertFile))
contents = []byte(strings.ReplaceAll(string(contents), "{REPLACE_LEAF_KEY_FILE}", ca.Leaf.KeyFile))
contents = []byte(strings.ReplaceAll(string(contents), "{REPLACE_COMBINED_CA_CHAIN_FILE}", ca.CombinedCaFile))

err = os.WriteFile(configPath, contents, 0o644)
if err != nil {
t.Fatalf("failed to write file %s: %v", configPath, err)
}

return configPath
}

func TestOperatorDiagnoseCommand_Run(t *testing.T) {
t.Parallel()
testca := pkihelper.GenerateCertWithIntermediaryRoot(t)
tlsConfigOkConfigFile := generateTLSConfigOk(t, testca)
transitTLSCheckConfigFile := generateTransitTLSCheck(t, testca)

cases := []struct {
name string
args []string
Expand Down Expand Up @@ -349,7 +398,7 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) {
{
"diagnose_listener_config_ok",
[]string{
"-config", "./server/test-fixtures/tls_config_ok.hcl",
"-config", tlsConfigOkConfigFile,
},
[]*diagnose.Result{
{
Expand Down Expand Up @@ -461,7 +510,7 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) {
{
"diagnose_seal_transit_tls_check_fail",
[]string{
"-config", "./server/test-fixtures/diagnose_seal_transit_tls_check.hcl",
"-config", transitTLSCheckConfigFile,
},
[]*diagnose.Result{
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ backend "consul" {
seal "transit" {

// TLS Configuration
tls_ca_cert = "./../vault/diagnose/test-fixtures/chain.crt.pem"
tls_client_cert = "./../vault/diagnose/test-fixtures/goodcertwithroot.pem"
tls_client_key = "./../vault/diagnose//test-fixtures/goodkey.pem"
tls_ca_cert = "{REPLACE_COMBINED_CA_CHAIN_FILE}"
tls_client_cert = "{REPLACE_LEAF_CERT_FILE}"
tls_client_key = "{REPLACE_LEAF_KEY_FILE}"
tls_server_name = "vault"
tls_skip_verify = "false"
}
Expand Down
4 changes: 2 additions & 2 deletions command/server/test-fixtures/tls_config_ok.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ ui = true

listener "tcp" {
address = "127.0.0.1:1025"
tls_cert_file = "./../api/test-fixtures/keys/cert.pem"
tls_key_file = "./../api/test-fixtures/keys/key.pem"
tls_cert_file = "{REPLACE_LEAF_CERT_FILE}"
tls_key_file = "{REPLACE_LEAF_KEY_FILE}"
}

backend "consul" {
Expand Down
224 changes: 224 additions & 0 deletions helper/testhelpers/pki/pkihelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package pki

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
mathrand2 "math/rand/v2"
"net"
"os"
"path/filepath"
"testing"
"time"
)

// This file contains helper functions for generating CA hierarchies for testing

type LeafWithRoot struct {
RootCa GeneratedCert
Leaf GeneratedCert
CombinedLeafCaFile string
}

type LeafWithIntermediary struct {
RootCa GeneratedCert
IntCa GeneratedCert
Leaf GeneratedCert
CombinedCaFile string
}

type GeneratedCert struct {
KeyFile string
CertFile string
CertPem *pem.Block
Cert *x509.Certificate
Key *ecdsa.PrivateKey
}

// GenerateCertWithIntermediaryRoot generates a leaf certificate signed by an intermediary root CA
func GenerateCertWithIntermediaryRoot(t testing.TB) LeafWithIntermediary {
t.Helper()
tempDir := t.TempDir()
template := &x509.Certificate{
Subject: pkix.Name{
CommonName: "localhost",
},
SerialNumber: big.NewInt(mathrand2.Int64()),
DNSNames: []string{"localhost"},
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(60 * 24 * time.Hour),
}

ca := GenerateRootCa(t)
caIntTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "Intermediary CA",
},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
SerialNumber: big.NewInt(mathrand2.Int64()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
}
caInt := generateCertAndSign(t, caIntTemplate, ca, tempDir, "int_")
leafCert := generateCertAndSign(t, template, caInt, tempDir, "leaf_")

combinedCasFile := filepath.Join(tempDir, "cas.pem")
err := os.WriteFile(combinedCasFile, append(pem.EncodeToMemory(caInt.CertPem), pem.EncodeToMemory(ca.CertPem)...), 0o644)
if err != nil {
t.Fatal(err)
}

return LeafWithIntermediary{
RootCa: ca,
IntCa: caInt,
Leaf: leafCert,
CombinedCaFile: combinedCasFile,
}
}

// generateCertAndSign generates a certificate and associated key signed by a CA
func generateCertAndSign(t testing.TB, template *x509.Certificate, ca GeneratedCert, tempDir string, filePrefix string) GeneratedCert {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
certBytes, err := x509.CreateCertificate(rand.Reader, template, ca.Cert, key.Public(), ca.Key)
if err != nil {
t.Fatal(err)
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
t.Fatal(err)
}
certPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certFile := filepath.Join(tempDir, filePrefix+"cert.pem")
err = os.WriteFile(certFile, pem.EncodeToMemory(certPEMBlock), 0o644)
if err != nil {
t.Fatal(err)
}
marshaledKey, err := x509.MarshalECPrivateKey(key)
if err != nil {
t.Fatal(err)
}
keyPEMBlock := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: marshaledKey,
}
keyFile := filepath.Join(tempDir, filePrefix+"key.pem")
err = os.WriteFile(keyFile, pem.EncodeToMemory(keyPEMBlock), 0o644)
if err != nil {
t.Fatal(err)
}
return GeneratedCert{
KeyFile: keyFile,
CertFile: certFile,
CertPem: certPEMBlock,
Cert: cert,
Key: key,
}
}

// GenerateCertWithRoot generates a leaf certificate signed by a root CA
func GenerateCertWithRoot(t testing.TB) LeafWithRoot {
t.Helper()
tempDir := t.TempDir()
leafTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "localhost",
},
SerialNumber: big.NewInt(mathrand2.Int64()),
DNSNames: []string{"localhost"},
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(60 * 24 * time.Hour),
}

ca := GenerateRootCa(t)
leafCert := generateCertAndSign(t, leafTemplate, ca, tempDir, "leaf_")

combinedCaLeafFile := filepath.Join(tempDir, "leaf-ca.pem")
err := os.WriteFile(combinedCaLeafFile, append(pem.EncodeToMemory(leafCert.CertPem), pem.EncodeToMemory(ca.CertPem)...), 0o644)
if err != nil {
t.Fatal(err)
}

return LeafWithRoot{
RootCa: ca,
Leaf: leafCert,
CombinedLeafCaFile: combinedCaLeafFile,
}
}

// GenerateRootCa generates a self-signed root CA certificate and key
func GenerateRootCa(t testing.TB) GeneratedCert {
t.Helper()
tempDir := t.TempDir()

caCertTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "Root CA",
},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
SerialNumber: big.NewInt(mathrand2.Int64()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
}
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caKey.Public(), caKey)
if err != nil {
t.Fatal(err)
}
caCert, err := x509.ParseCertificate(caBytes)
if err != nil {
t.Fatal(err)
}
caCertPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
caFile := filepath.Join(tempDir, "ca_root_cert.pem")
err = os.WriteFile(caFile, pem.EncodeToMemory(caCertPEMBlock), 0o644)
if err != nil {
t.Fatal(err)
}
marshaledCAKey, err := x509.MarshalECPrivateKey(caKey)
if err != nil {
t.Fatal(err)
}
caKeyPEMBlock := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: marshaledCAKey,
}
caKeyFile := filepath.Join(tempDir, "ca_root_key.pem")
err = os.WriteFile(caKeyFile, pem.EncodeToMemory(caKeyPEMBlock), 0o644)
if err != nil {
t.Fatal(err)
}
return GeneratedCert{
CertPem: caCertPEMBlock,
CertFile: caFile,
KeyFile: caKeyFile,
Cert: caCert,
Key: caKey,
}
}
42 changes: 0 additions & 42 deletions vault/diagnose/test-fixtures/goodcertwithroot.pem

This file was deleted.

Loading
Loading