Vault: configurable appRole authentication path
This commit is contained in:
parent
bd7f15d5f4
commit
2995cc90a3
@ -55,6 +55,7 @@ We can now create a cluster issuer referencing this secret:
|
||||
path: pki_int/sign/example-dot-com
|
||||
server: https://vault
|
||||
auth:
|
||||
authPath: approle
|
||||
appRole:
|
||||
roleId: "291b9d21-8ff5-..."
|
||||
secretRef:
|
||||
@ -67,7 +68,9 @@ The Vault appRole credentials are supplied as the
|
||||
Vault authentication method using the appRole created in Vault. The secretRef
|
||||
references the Kubernetes secret created previously. More specifically, the field
|
||||
*name* is the Kubernetes secret name and *key* is the name given as the
|
||||
key value that store the *secretId*.
|
||||
key value that store the *secretId*. The optional attribute *authPath* specifies
|
||||
where the AppRole authentication is mounted in Vault. The *authPath* default value
|
||||
is *approle*.
|
||||
|
||||
Once we have created the above Issuer we can use it to obtain a certificate.
|
||||
|
||||
|
||||
@ -109,6 +109,8 @@ type VaultAuth struct {
|
||||
TokenSecretRef SecretKeySelector `json:"tokenSecretRef,omitempty"`
|
||||
// This Secret contains a AppRole and Secret
|
||||
AppRole VaultAppRole `json:"appRole,omitempty"`
|
||||
// Where the authentication path is mounted in Vault.
|
||||
AuthPath string `json:"authPath,omitempty"`
|
||||
}
|
||||
|
||||
type VaultAppRole struct {
|
||||
|
||||
@ -131,7 +131,12 @@ func (v *Vault) requestTokenWithAppRoleRef(client *vault.Client, appRole *v1alph
|
||||
"secret_id": secretId,
|
||||
}
|
||||
|
||||
url := "/v1/auth/approle/login"
|
||||
authPath := v.issuer.GetSpec().Vault.Auth.AuthPath
|
||||
if authPath == "" {
|
||||
authPath = "approle"
|
||||
}
|
||||
|
||||
url := path.Join("/v1/auth", authPath, "login")
|
||||
|
||||
request := client.NewRequest("POST", url)
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ const (
|
||||
messageServerAndPathRequired = "Vault server and path are required fields"
|
||||
messsageAuthFieldsRequired = "Vault tokenSecretRef or appRole is required"
|
||||
messageAuthFieldRequired = "Vault tokenSecretRef and appRole cannot be set on the same issuer"
|
||||
messageAuthPathInvalid = "Vault authPath cannot be used with a tokenSecretRef"
|
||||
)
|
||||
|
||||
func (v *Vault) Setup(ctx context.Context) error {
|
||||
@ -53,6 +54,13 @@ func (v *Vault) Setup(ctx context.Context) error {
|
||||
return fmt.Errorf(messageAuthFieldRequired)
|
||||
}
|
||||
|
||||
if v.issuer.GetSpec().Vault.Auth.TokenSecretRef.Name != "" &&
|
||||
v.issuer.GetSpec().Vault.Auth.AuthPath != "" {
|
||||
glog.Infof("%s: %s", v.issuer.GetObjectMeta().Name, messageAuthPathInvalid)
|
||||
v.issuer.UpdateStatusCondition(v1alpha1.IssuerConditionReady, v1alpha1.ConditionFalse, errorVault, messageAuthPathInvalid)
|
||||
return fmt.Errorf(messageAuthPathInvalid)
|
||||
}
|
||||
|
||||
client, err := v.initVaultClient()
|
||||
if err != nil {
|
||||
s := messageVaultClientInitFailed + err.Error()
|
||||
|
||||
@ -33,7 +33,7 @@ var _ = framework.CertManagerDescribe("Vault Certificate (AppRole)", func() {
|
||||
podList, err := f.KubeClientSet.CoreV1().Pods("vault").List(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
vaultPodName := podList.Items[0].Name
|
||||
vaultInit, err = vault.NewVaultInitializer(vaultPodName, rootMount, intermediateMount, role)
|
||||
vaultInit, err = vault.NewVaultInitializer(vaultPodName, rootMount, intermediateMount, role, "")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = vaultInit.Setup()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -54,7 +54,69 @@ var _ = framework.CertManagerDescribe("Vault Certificate (AppRole)", func() {
|
||||
vaultURL := "http://vault.vault:8200"
|
||||
It("should generate a new valid certificate", func() {
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName))
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName, ""))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
v1alpha1.IssuerCondition{
|
||||
Type: v1alpha1.IssuerConditionReady,
|
||||
Status: v1alpha1.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
f.WaitCertificateIssuedValid(cert)
|
||||
})
|
||||
})
|
||||
|
||||
var _ = framework.CertManagerDescribe("Vault Certificate (AppRole with a custom mount path)", func() {
|
||||
f := framework.NewDefaultFramework("create-vault-certificate")
|
||||
|
||||
rootMount := "root-ca"
|
||||
intermediateMount := "intermediate-ca"
|
||||
roleMount := "custom/path"
|
||||
role := "kubernetes-vault"
|
||||
issuerName := "test-vault-issuer"
|
||||
certificateName := "test-vault-certificate"
|
||||
certificateSecretName := "test-vault-certificate"
|
||||
vaultSecretAppRoleName := "vault-role"
|
||||
vaultPath := fmt.Sprintf("%s/sign/%s", intermediateMount, role)
|
||||
var vaultInit *vault.VaultInitializer
|
||||
var roleId string
|
||||
var secretId string
|
||||
|
||||
BeforeEach(func() {
|
||||
By("Configuring the Vault server")
|
||||
podList, err := f.KubeClientSet.CoreV1().Pods("vault").List(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
vaultPodName := podList.Items[0].Name
|
||||
vaultInit, err = vault.NewVaultInitializer(vaultPodName, rootMount, intermediateMount, role, roleMount)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = vaultInit.Setup()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
roleId, secretId, err = vaultInit.CreateAppRole()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(vault.NewVaultAppRoleSecret(vaultSecretAppRoleName, secretId))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Cleaning up")
|
||||
f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Delete(issuerName, nil)
|
||||
f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(vaultSecretAppRoleName, nil)
|
||||
vaultInit.CleanAppRole()
|
||||
vaultInit.Clean()
|
||||
})
|
||||
|
||||
vaultURL := "http://vault.vault:8200"
|
||||
It("should generate a new valid certificate", func() {
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName, roleMount))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
|
||||
@ -32,7 +32,7 @@ var _ = framework.CertManagerDescribe("Vault Issuer", func() {
|
||||
podList, err := f.KubeClientSet.CoreV1().Pods("vault").List(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
vaultPodName := podList.Items[0].Name
|
||||
vaultInit, err = vault.NewVaultInitializer(vaultPodName, rootMount, intermediateMount, role)
|
||||
vaultInit, err = vault.NewVaultInitializer(vaultPodName, rootMount, intermediateMount, role, "")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = vaultInit.Setup()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -56,7 +56,7 @@ var _ = framework.CertManagerDescribe("Vault Issuer", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating an Issuer")
|
||||
_, err = f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName))
|
||||
_, err = f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName, ""))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
@ -71,7 +71,7 @@ var _ = framework.CertManagerDescribe("Vault Issuer", func() {
|
||||
|
||||
It("should fail to init with missing Vault AppRole", func() {
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName))
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName, ""))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
|
||||
@ -413,7 +413,7 @@ func NewCertManagerVaultIssuerToken(name, vaultURL, vaultPath, vaultSecretToken
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertManagerVaultIssuerAppRole(name, vaultURL, vaultPath, roleId, vaultSecretAppRole string) *v1alpha1.Issuer {
|
||||
func NewCertManagerVaultIssuerAppRole(name, vaultURL, vaultPath, roleId, vaultSecretAppRole, authPath string) *v1alpha1.Issuer {
|
||||
return &v1alpha1.Issuer{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
@ -424,6 +424,7 @@ func NewCertManagerVaultIssuerAppRole(name, vaultURL, vaultPath, roleId, vaultSe
|
||||
Server: vaultURL,
|
||||
Path: vaultPath,
|
||||
Auth: v1alpha1.VaultAuth{
|
||||
AuthPath: authPath,
|
||||
AppRole: v1alpha1.VaultAppRole{
|
||||
RoleId: roleId,
|
||||
SecretRef: v1alpha1.SecretKeySelector{
|
||||
|
||||
@ -42,9 +42,10 @@ type VaultInitializer struct {
|
||||
rootMount string
|
||||
intermediateMount string
|
||||
role string
|
||||
authPath string
|
||||
}
|
||||
|
||||
func NewVaultInitializer(container, rootMount, intermediateMount, role string) (*VaultInitializer, error) {
|
||||
func NewVaultInitializer(container, rootMount, intermediateMount, role, authPath string) (*VaultInitializer, error) {
|
||||
args := []string{"port-forward", "-n", "vault", container, "8200:8200"}
|
||||
cmd := exec.Command("kubectl", args...)
|
||||
err := cmd.Start()
|
||||
@ -63,12 +64,17 @@ func NewVaultInitializer(container, rootMount, intermediateMount, role string) (
|
||||
|
||||
client.SetToken(vaultToken)
|
||||
|
||||
if authPath == "" {
|
||||
authPath = "approle"
|
||||
}
|
||||
|
||||
return &VaultInitializer{
|
||||
proxyCmd: cmd,
|
||||
client: client,
|
||||
rootMount: rootMount,
|
||||
intermediateMount: intermediateMount,
|
||||
role: role,
|
||||
authPath: authPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -144,21 +150,22 @@ func (v *VaultInitializer) CreateAppRole() (string, string, error) {
|
||||
"period": "24h",
|
||||
"policies": v.role,
|
||||
}
|
||||
url := path.Join("/v1/auth/approle/role", v.role)
|
||||
_, err = v.callVault("POST", url, "", params)
|
||||
|
||||
baseUrl := path.Join("/v1/auth", v.authPath, "role", v.role)
|
||||
_, err = v.callVault("POST", baseUrl, "", params)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Error creating approle: %s", err.Error())
|
||||
}
|
||||
|
||||
// # read the role-id
|
||||
url = path.Join("/v1/auth/approle/role", v.role, "role-id")
|
||||
url := path.Join(baseUrl, "role-id")
|
||||
roleId, err := v.callVault("GET", url, "role_id", map[string]string{})
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Error reading role_id: %s", err.Error())
|
||||
}
|
||||
|
||||
// # read the secret-id
|
||||
url = path.Join("/v1/auth/approle/role", v.role, "secret-id")
|
||||
url = path.Join(baseUrl, "secret-id")
|
||||
secretId, err := v.callVault("POST", url, "secret_id", map[string]string{})
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Error reading secret_id: %s", err.Error())
|
||||
@ -168,7 +175,7 @@ func (v *VaultInitializer) CreateAppRole() (string, string, error) {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) CleanAppRole() error {
|
||||
url := path.Join("/v1/auth/approle/role", v.role)
|
||||
url := path.Join("/v1/auth", v.authPath, "role", v.role)
|
||||
_, err := v.callVault("DELETE", url, "", map[string]string{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting AppRole: %s", err.Error())
|
||||
@ -280,14 +287,14 @@ func (v *VaultInitializer) setupRole() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error fetching auth mounts: %s", err.Error())
|
||||
}
|
||||
if _, ok := auths["approle/"]; !ok {
|
||||
|
||||
if _, ok := auths[v.authPath+"/"]; !ok {
|
||||
options := &vault.EnableAuthOptions{Type: "approle"}
|
||||
if err := v.client.Sys().EnableAuthWithOptions("approle", options); err != nil {
|
||||
if err := v.client.Sys().EnableAuthWithOptions(v.authPath, options); err != nil {
|
||||
return fmt.Errorf("Error enabling approle: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// vault write intermediate-ca/roles/kubernetes-vault allow_any_name=true max_ttl="2160h"
|
||||
params := map[string]string{
|
||||
"allow_any_name": "true",
|
||||
"max_ttl": "2160h",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user