tests: require Vault mTLS during e2e

Signed-off-by: Rodrigo Fior Kuntzer <rodrigo@miro.com>
This commit is contained in:
Rodrigo Fior Kuntzer 2024-01-05 07:56:35 -03:00
parent 199c98689f
commit 0e51dc709a
No known key found for this signature in database
GPG Key ID: 285926BF553709F7
5 changed files with 596 additions and 6 deletions

View File

@ -38,8 +38,9 @@ type AddonTransferableData = internal.AddonTransferableData
var (
// Base is a base addon containing Kubernetes clients
Base = &base.Base{}
Vault = &vault.Vault{}
Base = &base.Base{}
Vault = &vault.Vault{}
VaultEnforceMtls = &vault.Vault{}
// allAddons is populated by InitGlobals and defines the order in which
// addons will be provisioned
@ -65,9 +66,16 @@ func InitGlobals(cfg *config.Config) {
Namespace: "e2e-vault",
Name: "vault",
}
*VaultEnforceMtls = vault.Vault{
Base: Base,
Namespace: "e2e-vault-mtls",
Name: "vault-mtls",
EnforceMtls: true,
}
allAddons = []Addon{
Base,
Vault,
VaultEnforceMtls,
}
}

View File

@ -18,6 +18,7 @@ package vault
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
@ -183,6 +184,19 @@ func NewVaultKubernetesSecret(secretName, serviceAccountName string) *corev1.Sec
}
}
func NewVaultClientCertificateSecret(secretName string, certificate, key []byte) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
Data: map[string][]byte{
corev1.TLSCertKey: certificate,
corev1.TLSPrivateKeyKey: key,
},
Type: corev1.SecretTypeTLS,
}
}
// Set up a new Vault client, port-forward to the Vault instance.
func (v *VaultInitializer) Init() error {
cfg := vault.DefaultConfiguration()
@ -193,6 +207,13 @@ func (v *VaultInitializer) Init() error {
return fmt.Errorf("error loading Vault CA bundle: %s", v.details.VaultCA)
}
cfg.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs = caCertPool
if v.details.EnforceMtls {
clientCertificate, err := tls.X509KeyPair(v.details.VaultClientCertificate, v.details.VaultClientPrivateKey)
if err != nil {
return fmt.Errorf("unable to read vault client certificate: %s", err)
}
cfg.HTTPClient.Transport.(*http.Transport).TLSClientConfig.Certificates = []tls.Certificate{clientCertificate}
}
client, err := vault.New(vault.WithConfiguration(cfg))
if err != nil {
@ -211,7 +232,10 @@ func (v *VaultInitializer) Init() error {
return fmt.Errorf("error parsing proxy URL: %s", err.Error())
}
var lastError error
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 20*time.Second, true, func(ctx context.Context) (bool, error) {
// The timeout below must be aligned with the time taken by the Vault addons to start,
// each addon safely takes about 20 seconds to start and two addons are started one after another,
// one for without mTLS enforced and another with mTLS enforced
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 45*time.Second, true, func(ctx context.Context) (bool, error) {
conn, err := net.DialTimeout("tcp", proxyUrl.Host, time.Second)
if err != nil {
lastError = err

View File

@ -30,6 +30,7 @@ import (
"math/big"
"net"
"os"
"strconv"
"strings"
"time"
@ -63,6 +64,10 @@ type Vault struct {
// Namespace is the namespace to deploy Vault into
Namespace string
// EnforceMtls defines if mTLS is enforced in the vault server
// and clients must provide client certificates
EnforceMtls bool
// Proxy is the proxy that can be used to connect to Vault
proxy *proxy
@ -84,6 +89,16 @@ type Details struct {
// VaultCA is the CA used to sign the vault serving certificate
VaultCA []byte
// VaultClientCertificate is the certificate used by clients when connecting to vault
VaultClientCertificate []byte
// VaultClientPrivateKey is the private key used by clients when connecting to vault
VaultClientPrivateKey []byte
// EnforceMtls defines if mTLS is enforced in the vault server
// and clients must provide client certificates
EnforceMtls bool
}
func convertInterfaceToDetails(unmarshalled interface{}) (Details, error) {
@ -149,6 +164,15 @@ func (v *Vault) Setup(cfg *config.Config, leaderData ...internal.AddonTransferab
Key: "server.extraEnvironmentVars.VAULT_DEV_ROOT_TOKEN_ID",
Value: "vault-root-token",
},
// configure client certificates used in the readiness/liveness probes exec commands
{
Key: "server.extraEnvironmentVars.VAULT_CLIENT_CERT",
Value: "/vault/tls/client.crt",
},
{
Key: "server.extraEnvironmentVars.VAULT_CLIENT_KEY",
Value: "/vault/tls/client.key",
},
// configure tls certificate
{
Key: "global.tlsDisable",
@ -156,15 +180,16 @@ func (v *Vault) Setup(cfg *config.Config, leaderData ...internal.AddonTransferab
},
{
Key: "server.standalone.config",
Value: `
Value: fmt.Sprintf(`
listener "tcp" {
tls_disable = false
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = false
tls_client_ca_file = "/vault/tls/ca.crt"
tls_cert_file = "/vault/tls/server.crt"
tls_key_file = "/vault/tls/server.key"
}`,
tls_require_and_verify_client_cert = %s
}`, strconv.FormatBool(v.EnforceMtls)),
},
{
Key: "server.volumes[0].name",
@ -267,6 +292,14 @@ func (v *Vault) Setup(cfg *config.Config, leaderData ...internal.AddonTransferab
return nil, err
}
vaultClientCertificate, vaultClientPrivateKey, err := generateVaultClientCert(vaultCA, vaultCAPrivateKey)
if err != nil {
return nil, err
}
v.details.VaultClientCertificate = vaultClientCertificate
v.details.VaultClientPrivateKey = vaultClientPrivateKey
v.details.EnforceMtls = v.EnforceMtls
if cfg.Kubectl == "" {
return nil, fmt.Errorf("path to kubectl must be specified")
}
@ -310,8 +343,11 @@ func (v *Vault) Provision() error {
Namespace: v.Namespace,
},
StringData: map[string]string{
"ca.crt": string(v.details.VaultCA),
"server.crt": string(v.vaultCert),
"server.key": string(v.vaultCertPrivateKey),
"client.crt": string(v.details.VaultClientCertificate),
"client.key": string(v.details.VaultClientPrivateKey),
},
}
_, err = kubeClient.CoreV1().Secrets(v.Namespace).Create(context.TODO(), tlsSecret, metav1.CreateOptions{})
@ -438,6 +474,30 @@ func generateVaultServingCert(vaultCA []byte, vaultCAPrivateKey []byte, dnsName
return encodePublicKey(certBytes), encodePrivateKey(privateKey), nil
}
func generateVaultClientCert(vaultCA []byte, vaultCAPrivateKey []byte) ([]byte, []byte, error) {
catls, _ := tls.X509KeyPair(vaultCA, vaultCAPrivateKey)
ca, _ := x509.ParseCertificate(catls.Certificate[0])
cert := &x509.Certificate{
Version: 3,
SerialNumber: big.NewInt(1658),
Subject: pkix.Name{
CommonName: "cert-manager vault client",
Organization: []string{"cert-manager"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
}
privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
certBytes, _ := x509.CreateCertificate(rand.Reader, cert, ca, &privateKey.PublicKey, catls.PrivateKey)
return encodePublicKey(certBytes), encodePrivateKey(privateKey), nil
}
func GenerateCA() ([]byte, []byte, error) {
ca := &x509.Certificate{
Version: 3,

View File

@ -0,0 +1,468 @@
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package vault
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/rand"
"github.com/cert-manager/cert-manager/e2e-tests/framework"
"github.com/cert-manager/cert-manager/e2e-tests/framework/addon"
vaultaddon "github.com/cert-manager/cert-manager/e2e-tests/framework/addon/vault"
e2eutil "github.com/cert-manager/cert-manager/e2e-tests/util"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
"github.com/cert-manager/cert-manager/test/unit/gen"
)
var _ = framework.CertManagerDescribe("Vault Issuer [mtls]", func() {
f := framework.NewDefaultFramework("create-vault-issuer")
issuerName := "test-vault-issuer"
vaultSecretServiceAccount := "vault-serviceaccount"
vaultClientCertificateSecretName := "vault-client-cert-secret-" + rand.String(5)
var roleId, secretId, vaultSecretName string
appRoleSecretGeneratorName := "vault-approle-secret-"
var setup *vaultaddon.VaultInitializer
details := addon.VaultEnforceMtls.Details()
BeforeEach(func() {
By("Configuring the Vault server")
setup = vaultaddon.NewVaultInitializerAllAuth(
addon.Base.Details().KubeClient,
*details,
false,
"https://kubernetes.default.svc.cluster.local",
)
Expect(setup.Init()).NotTo(HaveOccurred(), "failed to init vault")
Expect(setup.Setup()).NotTo(HaveOccurred(), "failed to setup vault")
var err error
roleId, secretId, err = setup.CreateAppRole()
Expect(err).NotTo(HaveOccurred())
By("creating a service account for Vault authentication")
err = setup.CreateKubernetesRole(f.KubeClientSet, f.Namespace.Name, vaultSecretServiceAccount)
Expect(err).NotTo(HaveOccurred())
By("creating a client certificate for Vault mTLS")
secret := vaultaddon.NewVaultClientCertificateSecret(vaultClientCertificateSecretName, details.VaultClientCertificate, details.VaultClientPrivateKey)
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
})
JustAfterEach(func() {
By("Cleaning up AppRole")
f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), vaultSecretName, metav1.DeleteOptions{})
setup.CleanAppRole()
By("Cleaning up Kubernetes")
setup.CleanKubernetesRole(f.KubeClientSet, f.Namespace.Name, vaultSecretServiceAccount)
By("Cleaning up Vault")
Expect(setup.Clean()).NotTo(HaveOccurred())
})
It("should be ready with a valid AppRole", func() {
sec, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultAppRoleSecret(appRoleSecretGeneratorName, secretId), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultSecretName = sec.Name
vaultIssuer := gen.IssuerWithRandomName(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultAppRoleAuth("secretkey", vaultSecretName, roleId, setup.AppRoleAuthPath()))
iss, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
iss.Name,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
It("should fail to init with missing client certificates", func() {
sec, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultAppRoleSecret(appRoleSecretGeneratorName, secretId), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultSecretName = sec.Name
By("Creating an Issuer")
vaultIssuer := gen.IssuerWithRandomName(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultAppRoleAuth("secretkey", vaultSecretName, roleId, setup.AppRoleAuthPath()))
iss, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
iss.Name,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
})
It("should fail to init with missing Vault AppRole", func() {
By("Creating an Issuer")
vaultIssuer := gen.IssuerWithRandomName(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultAppRoleAuth("secretkey", roleId, setup.Role(), setup.AppRoleAuthPath()))
iss, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
iss.Name,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
})
It("should fail to init with missing Vault Token", func() {
By("Creating an Issuer")
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultTokenAuth("secretkey", "vault-token"))
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
})
It("should be ready with a valid Kubernetes Role and ServiceAccount Secret", func() {
saTokenSecretName := "vault-sa-secret-" + rand.String(5)
_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultKubernetesSecret(saTokenSecretName, vaultSecretServiceAccount), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthSecret("token", saTokenSecretName, setup.Role(), setup.KubernetesAuthPath()))
_, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
It("should fail to init with missing Kubernetes Role", func() {
saTokenSecretName := "vault-sa-secret-" + rand.String(5)
// we test without creating the secret
By("Creating an Issuer")
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthSecret("token", saTokenSecretName, setup.Role(), setup.KubernetesAuthPath()))
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
})
It("should fail to init when both caBundle and caBundleSecretRef are set", func() {
By("Creating an Issuer")
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultCABundleSecretRef("ca-bundle", f.Namespace.Name, "ca.crt"))
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(
"spec.vault.caBundle: Invalid value: \"<snip>\": specified caBundle and caBundleSecretRef cannot be used together",
))
Expect(err.Error()).To(ContainSubstring("spec.vault.caBundleSecretRef: Invalid value: \"ca-bundle\": specified caBundleSecretRef and caBundle cannot be used together"))
})
It("should be ready with a caBundle from a Kubernetes Secret", func() {
saTokenSecretName := "vault-sa-secret-" + rand.String(5)
_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultKubernetesSecret(saTokenSecretName, vaultSecretServiceAccount), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "ca-bundle",
},
Type: "Opaque",
Data: map[string][]byte{
"ca.crt": details.VaultCA,
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundleSecretRef("ca-bundle", f.Namespace.Name, "ca.crt"),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthSecret("token", saTokenSecretName, setup.Role(), setup.KubernetesAuthPath()))
_, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
It("should be eventually ready when the CA bundle secret gets created after the Issuer", func() {
saTokenSecretName := "vault-sa-secret-" + rand.String(5)
_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultKubernetesSecret(saTokenSecretName, vaultSecretServiceAccount), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundleSecretRef("ca-bundle", f.Namespace.Name, "ca.crt"),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthSecret("token", saTokenSecretName, setup.Role(), setup.KubernetesAuthPath()))
_, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Validate that the Issuer is not ready yet")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "ca-bundle",
},
Type: "Opaque",
Data: map[string][]byte{
"ca.crt": details.VaultCA,
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
It("should be eventually ready when the Vault client certificate secret gets created after the Issuer", func() {
sec, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultAppRoleSecret(appRoleSecretGeneratorName, secretId), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultSecretName = sec.Name
customVaultClientCertificateSecretName := "vault-client-cert-secret-custom-" + rand.String(5)
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(customVaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(customVaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultAppRoleAuth("secretkey", vaultSecretName, roleId, setup.AppRoleAuthPath()))
iss, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Validate that the Issuer is not ready yet")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
By("creating a client certificate for Vault mTLS")
secret := vaultaddon.NewVaultClientCertificateSecret(customVaultClientCertificateSecretName, details.VaultClientCertificate, details.VaultClientPrivateKey)
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
iss.Name,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
It("it should become not ready when the CA certificate in the secret changes and doesn't match Vault's CA anymore", func() {
saTokenSecretName := "vault-sa-secret-" + rand.String(5)
_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), vaultaddon.NewVaultKubernetesSecret(saTokenSecretName, vaultSecretServiceAccount), metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "ca-bundle",
},
Type: "Opaque",
Data: map[string][]byte{
"ca.crt": details.VaultCA,
},
}, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundleSecretRef("ca-bundle", f.Namespace.Name, "ca.crt"),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthSecret("token", saTokenSecretName, setup.Role(), setup.KubernetesAuthPath()))
_, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
By("Updating CA bundle")
public, _, err := vaultaddon.GenerateCA()
Expect(err).NotTo(HaveOccurred())
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "ca-bundle",
},
Data: map[string][]byte{
"ca.crt": public,
},
}, metav1.UpdateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Validate that the issuer isn't ready anymore due to Vault still using the old certificate")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionFalse,
})
Expect(err).NotTo(HaveOccurred())
})
It("should be ready with a valid serviceAccountRef", func() {
// Note that we reuse the same service account as for the Kubernetes
// auth based on secretRef. There should be no problem doing so.
By("Creating the Role and RoleBinding to let cert-manager use TokenRequest for the ServiceAccount")
vaultaddon.CreateKubernetesRoleForServiceAccountRefAuth(f.KubeClientSet, setup.Role(), f.Namespace.Name, vaultSecretServiceAccount)
defer vaultaddon.CleanKubernetesRoleForServiceAccountRefAuth(f.KubeClientSet, setup.Role(), f.Namespace.Name, vaultSecretServiceAccount)
By("Creating an Issuer")
vaultIssuer := gen.Issuer(issuerName,
gen.SetIssuerNamespace(f.Namespace.Name),
gen.SetIssuerVaultURL(details.URL),
gen.SetIssuerVaultPath(setup.IntermediateSignPath()),
gen.SetIssuerVaultCABundle(details.VaultCA),
gen.SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, corev1.TLSCertKey),
gen.SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, corev1.TLSPrivateKeyKey),
gen.SetIssuerVaultKubernetesAuthServiceAccount(vaultSecretServiceAccount, setup.Role(), setup.KubernetesAuthPath()))
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), vaultIssuer, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
By("Waiting for Issuer to become Ready")
err = e2eutil.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
issuerName,
v1.IssuerCondition{
Type: v1.IssuerConditionReady,
Status: cmmeta.ConditionTrue,
})
Expect(err).NotTo(HaveOccurred())
})
})

View File

@ -302,6 +302,36 @@ func SetIssuerVaultCABundleSecretRef(name, namespace, key string) IssuerModifier
}
}
func SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, key string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.ClientCertSecretRef = &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: vaultClientCertificateSecretName,
},
Key: key,
}
}
}
func SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, key string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.ClientKeySecretRef = &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: vaultClientCertificateSecretName,
},
Key: key,
}
}
}
func SetIssuerVaultTokenAuth(keyName, tokenName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()