From dec1067addc5bd8f43fafeaca7cd4f6264738abf Mon Sep 17 00:00:00 2001 From: Erik De Lamarter Date: Mon, 25 Apr 2022 22:45:22 +0200 Subject: [PATCH 1/5] vault kubernetes auth --- cas/vaultcas/vaultcas.go | 72 +++++++++++++++++++++++++--------------- go.mod | 1 + go.sum | 2 ++ 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/cas/vaultcas/vaultcas.go b/cas/vaultcas/vaultcas.go index c29ef691..8a09a850 100644 --- a/cas/vaultcas/vaultcas.go +++ b/cas/vaultcas/vaultcas.go @@ -18,6 +18,7 @@ import ( vault "github.com/hashicorp/vault/api" auth "github.com/hashicorp/vault/api/auth/approle" + kubeauth "github.com/hashicorp/vault/api/auth/kubernetes" ) func init() { @@ -34,6 +35,7 @@ type VaultOptions struct { PKIRoleRSA string `json:"pkiRoleRSA,omitempty"` PKIRoleEC string `json:"pkiRoleEC,omitempty"` PKIRoleEd25519 string `json:"pkiRoleEd25519,omitempty"` + KubernetesRole string `json:"kubernetesRole,omitempty"` RoleID string `json:"roleID,omitempty"` SecretID auth.SecretID `json:"secretID,omitempty"` AppRole string `json:"appRole,omitempty"` @@ -77,31 +79,49 @@ func New(ctx context.Context, opts apiv1.Options) (*VaultCAS, error) { return nil, fmt.Errorf("unable to initialize vault client: %w", err) } - var appRoleAuth *auth.AppRoleAuth - if vc.IsWrappingToken { - appRoleAuth, err = auth.NewAppRoleAuth( - vc.RoleID, - &vc.SecretID, - auth.WithWrappingToken(), - auth.WithMountPath(vc.AppRole), + if vc.KubernetesRole != "" { + var kubernetesAuth *kubeauth.KubernetesAuth + kubernetesAuth, err = kubeauth.NewKubernetesAuth( + vc.KubernetesRole, ) - } else { - appRoleAuth, err = auth.NewAppRoleAuth( - vc.RoleID, - &vc.SecretID, - auth.WithMountPath(vc.AppRole), - ) - } - if err != nil { - return nil, fmt.Errorf("unable to initialize AppRole auth method: %w", err) - } + if err != nil { + return nil, fmt.Errorf("unable to initialize Kubernetes auth method: %w", err) + } - authInfo, err := client.Auth().Login(ctx, appRoleAuth) - if err != nil { - return nil, fmt.Errorf("unable to login to AppRole auth method: %w", err) - } - if authInfo == nil { - return nil, errors.New("no auth info was returned after login") + authInfo, err := client.Auth().Login(ctx, kubernetesAuth) + if err != nil { + return nil, fmt.Errorf("unable to login to Kubernetes auth method: %w", err) + } + if authInfo == nil { + return nil, errors.New("no auth info was returned after login") + } + } else { + var appRoleAuth *auth.AppRoleAuth + if vc.IsWrappingToken { + appRoleAuth, err = auth.NewAppRoleAuth( + vc.RoleID, + &vc.SecretID, + auth.WithWrappingToken(), + auth.WithMountPath(vc.AppRole), + ) + } else { + appRoleAuth, err = auth.NewAppRoleAuth( + vc.RoleID, + &vc.SecretID, + auth.WithMountPath(vc.AppRole), + ) + } + if err != nil { + return nil, fmt.Errorf("unable to initialize AppRole auth method: %w", err) + } + + authInfo, err := client.Auth().Login(ctx, appRoleAuth) + if err != nil { + return nil, fmt.Errorf("unable to login to AppRole auth method: %w", err) + } + if authInfo == nil { + return nil, errors.New("no auth info was returned after login") + } } return &VaultCAS{ @@ -272,11 +292,11 @@ func loadOptions(config json.RawMessage) (*VaultOptions, error) { vc.PKIRoleEd25519 = vc.PKIRoleDefault } - if vc.RoleID == "" { - return nil, errors.New("vaultCAS config options must define `roleID`") + if vc.RoleID == "" && vc.KubernetesRole == "" { + return nil, errors.New("vaultCAS config options must define `roleID` or `kubernetesRole`") } - if vc.SecretID.FromEnv == "" && vc.SecretID.FromFile == "" && vc.SecretID.FromString == "" { + if vc.SecretID.FromEnv == "" && vc.SecretID.FromFile == "" && vc.SecretID.FromString == "" && vc.RoleID != "" { return nil, errors.New("vaultCAS config options must define `secretID` object with one of `FromEnv`, `FromFile` or `FromString`") } diff --git a/go.mod b/go.mod index 8b66f470..0b772018 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/googleapis/gax-go/v2 v2.1.1 github.com/hashicorp/vault/api v1.3.1 github.com/hashicorp/vault/api/auth/approle v0.1.1 + github.com/hashicorp/vault/api/auth/kubernetes v0.1.0 github.com/jhump/protoreflect v1.9.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect diff --git a/go.sum b/go.sum index 4780111e..d76648c2 100644 --- a/go.sum +++ b/go.sum @@ -449,6 +449,8 @@ github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/api/auth/approle v0.1.1 h1:R5yA+xcNvw1ix6bDuWOaLOq2L4L77zDCVsethNw97xQ= github.com/hashicorp/vault/api/auth/approle v0.1.1/go.mod h1:mHOLgh//xDx4dpqXoq6tS8Ob0FoCFWLU2ibJ26Lfmag= +github.com/hashicorp/vault/api/auth/kubernetes v0.1.0 h1:6BtyahbF4aQp8gg3ww0A/oIoqzbhpNP1spXU3nHE0n0= +github.com/hashicorp/vault/api/auth/kubernetes v0.1.0/go.mod h1:Pdgk78uIs0mgDOLvc3a+h/vYIT9rznw2sz+ucuH9024= github.com/hashicorp/vault/sdk v0.3.0 h1:kR3dpxNkhh/wr6ycaJYqp6AFT/i2xaftbfnwZduTKEY= github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= From 6c44291d8df63e16e662a9cc03ffa8783fa364ce Mon Sep 17 00:00:00 2001 From: Erik De Lamarter Date: Mon, 9 May 2022 13:27:37 +0200 Subject: [PATCH 2/5] refactor vault auth --- cas/vaultcas/auth/approle/approle.go | 46 ++++ cas/vaultcas/auth/approle/approle_test.go | 16 ++ cas/vaultcas/auth/kubernetes/kubernetes.go | 43 +++ .../auth/kubernetes/kubernetes_test.go | 21 ++ cas/vaultcas/auth/kubernetes/token | 1 + cas/vaultcas/vaultcas.go | 120 +++----- cas/vaultcas/vaultcas_test.go | 256 ++++-------------- 7 files changed, 220 insertions(+), 283 deletions(-) create mode 100644 cas/vaultcas/auth/approle/approle.go create mode 100644 cas/vaultcas/auth/approle/approle_test.go create mode 100644 cas/vaultcas/auth/kubernetes/kubernetes.go create mode 100644 cas/vaultcas/auth/kubernetes/kubernetes_test.go create mode 100644 cas/vaultcas/auth/kubernetes/token diff --git a/cas/vaultcas/auth/approle/approle.go b/cas/vaultcas/auth/approle/approle.go new file mode 100644 index 00000000..38d3c51c --- /dev/null +++ b/cas/vaultcas/auth/approle/approle.go @@ -0,0 +1,46 @@ +package approle + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/vault/api/auth/approle" +) + +// AuthOptions defines the configuration options added using the +// VaultOptions.AuthOptions field when AuthType is approle +type AuthOptions struct { + RoleID string `json:"roleID,omitempty"` + SecretID string `json:"secretID,omitempty"` + IsWrappingToken bool `json:"isWrappingToken,omitempty"` +} + +func NewApproleAuthMethod(mountPath string, options json.RawMessage) (*approle.AppRoleAuth, error) { + var opts *AuthOptions + + err := json.Unmarshal(options, &opts) + if err != nil { + return nil, fmt.Errorf("error decoding AppRole auth options: %w", err) + } + + var approleAuth *approle.AppRoleAuth + + var loginOptions []approle.LoginOption + if mountPath != "" { + loginOptions = append(loginOptions, approle.WithMountPath(mountPath)) + } + if opts.IsWrappingToken { + loginOptions = append(loginOptions, approle.WithWrappingToken()) + } + + sid := approle.SecretID{ + FromString: opts.SecretID, + } + + approleAuth, err = approle.NewAppRoleAuth(opts.RoleID, &sid, loginOptions...) + if err != nil { + return nil, fmt.Errorf("unable to initialize Kubernetes auth method: %w", err) + } + + return approleAuth, nil +} diff --git a/cas/vaultcas/auth/approle/approle_test.go b/cas/vaultcas/auth/approle/approle_test.go new file mode 100644 index 00000000..ab7e6a97 --- /dev/null +++ b/cas/vaultcas/auth/approle/approle_test.go @@ -0,0 +1,16 @@ +package approle + +import ( + "encoding/json" + "testing" +) + +func TestKubernetes_NewKubernetesAuthMethod(t *testing.T) { + mountPath := "approle" + raw := `{"roleID": "roleID", "secretID": "secretIDwrapped", "isWrappedToken": true}` + + _, err := NewApproleAuthMethod(mountPath, json.RawMessage(raw)) + if err != nil { + t.Fatal(err) + } +} diff --git a/cas/vaultcas/auth/kubernetes/kubernetes.go b/cas/vaultcas/auth/kubernetes/kubernetes.go new file mode 100644 index 00000000..0c4db62f --- /dev/null +++ b/cas/vaultcas/auth/kubernetes/kubernetes.go @@ -0,0 +1,43 @@ +package kubernetes + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/vault/api/auth/kubernetes" +) + +// AuthOptions defines the configuration options added using the +// VaultOptions.AuthOptions field when AuthType is kubernetes +type AuthOptions struct { + Role string `json:"role,omitempty"` + TokenPath string `json:"tokenPath,omitempty"` +} + +func NewKubernetesAuthMethod(mountPath string, options json.RawMessage) (*kubernetes.KubernetesAuth, error) { + var opts *AuthOptions + + err := json.Unmarshal(options, &opts) + if err != nil { + return nil, fmt.Errorf("error decoding Kubernetes auth options: %w", err) + } + + var kubernetesAuth *kubernetes.KubernetesAuth + + var loginOptions []kubernetes.LoginOption + if mountPath != "" { + loginOptions = append(loginOptions, kubernetes.WithMountPath(mountPath)) + } + if opts.TokenPath != "" { + loginOptions = append(loginOptions, kubernetes.WithServiceAccountTokenPath(opts.TokenPath)) + } + kubernetesAuth, err = kubernetes.NewKubernetesAuth( + opts.Role, + loginOptions..., + ) + if err != nil { + return nil, fmt.Errorf("unable to initialize Kubernetes auth method: %w", err) + } + + return kubernetesAuth, nil +} diff --git a/cas/vaultcas/auth/kubernetes/kubernetes_test.go b/cas/vaultcas/auth/kubernetes/kubernetes_test.go new file mode 100644 index 00000000..604f1898 --- /dev/null +++ b/cas/vaultcas/auth/kubernetes/kubernetes_test.go @@ -0,0 +1,21 @@ +package kubernetes + +import ( + "encoding/json" + "path" + "path/filepath" + "runtime" + "testing" +) + +func TestKubernetes_NewKubernetesAuthMethod(t *testing.T) { + _, filename, _, _ := runtime.Caller(0) + tokenPath := filepath.Join(path.Dir(filename), "token") + mountPath := "kubernetes" + raw := `{"role": "SomeRoleName", "tokenPath": "` + tokenPath + `"}` + + _, err := NewKubernetesAuthMethod(mountPath, json.RawMessage(raw)) + if err != nil { + t.Fatal(err) + } +} diff --git a/cas/vaultcas/auth/kubernetes/token b/cas/vaultcas/auth/kubernetes/token new file mode 100644 index 00000000..6745be67 --- /dev/null +++ b/cas/vaultcas/auth/kubernetes/token @@ -0,0 +1 @@ +token \ No newline at end of file diff --git a/cas/vaultcas/vaultcas.go b/cas/vaultcas/vaultcas.go index 8a09a850..02c814b7 100644 --- a/cas/vaultcas/vaultcas.go +++ b/cas/vaultcas/vaultcas.go @@ -15,10 +15,10 @@ import ( "time" "github.com/smallstep/certificates/cas/apiv1" + "github.com/smallstep/certificates/cas/vaultcas/auth/approle" + "github.com/smallstep/certificates/cas/vaultcas/auth/kubernetes" vault "github.com/hashicorp/vault/api" - auth "github.com/hashicorp/vault/api/auth/approle" - kubeauth "github.com/hashicorp/vault/api/auth/kubernetes" ) func init() { @@ -30,16 +30,14 @@ func init() { // VaultOptions defines the configuration options added using the // apiv1.Options.Config field. type VaultOptions struct { - PKI string `json:"pki,omitempty"` - PKIRoleDefault string `json:"pkiRoleDefault,omitempty"` - PKIRoleRSA string `json:"pkiRoleRSA,omitempty"` - PKIRoleEC string `json:"pkiRoleEC,omitempty"` - PKIRoleEd25519 string `json:"pkiRoleEd25519,omitempty"` - KubernetesRole string `json:"kubernetesRole,omitempty"` - RoleID string `json:"roleID,omitempty"` - SecretID auth.SecretID `json:"secretID,omitempty"` - AppRole string `json:"appRole,omitempty"` - IsWrappingToken bool `json:"isWrappingToken,omitempty"` + PKIMountPath string `json:"pkiMountPath,omitempty"` + PKIRoleDefault string `json:"pkiRoleDefault,omitempty"` + PKIRoleRSA string `json:"pkiRoleRSA,omitempty"` + PKIRoleEC string `json:"pkiRoleEC,omitempty"` + PKIRoleEd25519 string `json:"pkiRoleEd25519,omitempty"` + AuthType string `json:"authType,omitempty"` + AuthMountPath string `json:"authMountPath,omitempty"` + AuthOptions json.RawMessage `json:"authOptions,omitempty"` } // VaultCAS implements a Certificate Authority Service using Hashicorp Vault. @@ -79,49 +77,25 @@ func New(ctx context.Context, opts apiv1.Options) (*VaultCAS, error) { return nil, fmt.Errorf("unable to initialize vault client: %w", err) } - if vc.KubernetesRole != "" { - var kubernetesAuth *kubeauth.KubernetesAuth - kubernetesAuth, err = kubeauth.NewKubernetesAuth( - vc.KubernetesRole, - ) - if err != nil { - return nil, fmt.Errorf("unable to initialize Kubernetes auth method: %w", err) - } + var method vault.AuthMethod + switch vc.AuthType { + case "kubernetes": + method, err = kubernetes.NewKubernetesAuthMethod(vc.AuthMountPath, vc.AuthOptions) + case "approle": + method, err = approle.NewApproleAuthMethod(vc.AuthMountPath, vc.AuthOptions) + default: + return nil, fmt.Errorf("unknown auth type: %v", vc.AuthType) + } + if err != nil { + return nil, fmt.Errorf("unable to configure auth method: %w", err) + } - authInfo, err := client.Auth().Login(ctx, kubernetesAuth) - if err != nil { - return nil, fmt.Errorf("unable to login to Kubernetes auth method: %w", err) - } - if authInfo == nil { - return nil, errors.New("no auth info was returned after login") - } - } else { - var appRoleAuth *auth.AppRoleAuth - if vc.IsWrappingToken { - appRoleAuth, err = auth.NewAppRoleAuth( - vc.RoleID, - &vc.SecretID, - auth.WithWrappingToken(), - auth.WithMountPath(vc.AppRole), - ) - } else { - appRoleAuth, err = auth.NewAppRoleAuth( - vc.RoleID, - &vc.SecretID, - auth.WithMountPath(vc.AppRole), - ) - } - if err != nil { - return nil, fmt.Errorf("unable to initialize AppRole auth method: %w", err) - } - - authInfo, err := client.Auth().Login(ctx, appRoleAuth) - if err != nil { - return nil, fmt.Errorf("unable to login to AppRole auth method: %w", err) - } - if authInfo == nil { - return nil, errors.New("no auth info was returned after login") - } + authInfo, err := client.Auth().Login(ctx, method) + if err != nil { + return nil, fmt.Errorf("unable to login to Kubernetes auth method: %w", err) + } + if authInfo == nil { + return nil, errors.New("no auth info was returned after login") } return &VaultCAS{ @@ -154,7 +128,7 @@ func (v *VaultCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv // GetCertificateAuthority returns the root certificate of the certificate // authority using the configured fingerprint. func (v *VaultCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequest) (*apiv1.GetCertificateAuthorityResponse, error) { - secret, err := v.client.Logical().Read(v.config.PKI + "/cert/ca_chain") + secret, err := v.client.Logical().Read(v.config.PKIMountPath + "/cert/ca_chain") if err != nil { return nil, fmt.Errorf("error reading ca chain: %w", err) } @@ -210,7 +184,7 @@ func (v *VaultCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv vaultReq := map[string]interface{}{ "serial_number": formatSerialNumber(sn), } - _, err := v.client.Logical().Write(v.config.PKI+"/revoke/", vaultReq) + _, err := v.client.Logical().Write(v.config.PKIMountPath+"/revoke/", vaultReq) if err != nil { return nil, fmt.Errorf("error revoking certificate: %w", err) } @@ -244,7 +218,7 @@ func (v *VaultCAS) createCertificate(cr *x509.CertificateRequest, lifetime time. "ttl": lifetime.Seconds(), } - secret, err := v.client.Logical().Write(v.config.PKI+"/sign/"+vaultPKIRole, vaultReq) + secret, err := v.client.Logical().Write(v.config.PKIMountPath+"/sign/"+vaultPKIRole, vaultReq) if err != nil { return nil, nil, fmt.Errorf("error signing certificate: %w", err) } @@ -267,21 +241,17 @@ func (v *VaultCAS) createCertificate(cr *x509.CertificateRequest, lifetime time. } func loadOptions(config json.RawMessage) (*VaultOptions, error) { - var vc *VaultOptions + // setup default values + vc := VaultOptions{ + PKIMountPath: "pki", + PKIRoleDefault: "default", + } err := json.Unmarshal(config, &vc) if err != nil { return nil, fmt.Errorf("error decoding vaultCAS config: %w", err) } - if vc.PKI == "" { - vc.PKI = "pki" // use default pki vault name - } - - if vc.PKIRoleDefault == "" { - vc.PKIRoleDefault = "default" // use default pki role name - } - if vc.PKIRoleRSA == "" { vc.PKIRoleRSA = vc.PKIRoleDefault } @@ -292,23 +262,7 @@ func loadOptions(config json.RawMessage) (*VaultOptions, error) { vc.PKIRoleEd25519 = vc.PKIRoleDefault } - if vc.RoleID == "" && vc.KubernetesRole == "" { - return nil, errors.New("vaultCAS config options must define `roleID` or `kubernetesRole`") - } - - if vc.SecretID.FromEnv == "" && vc.SecretID.FromFile == "" && vc.SecretID.FromString == "" && vc.RoleID != "" { - return nil, errors.New("vaultCAS config options must define `secretID` object with one of `FromEnv`, `FromFile` or `FromString`") - } - - if vc.PKI == "" { - vc.PKI = "pki" // use default pki vault name - } - - if vc.AppRole == "" { - vc.AppRole = "auth/approle" - } - - return vc, nil + return &vc, nil } func parseCertificates(pemCert string) []*x509.Certificate { diff --git a/cas/vaultcas/vaultcas_test.go b/cas/vaultcas/vaultcas_test.go index 9f73a1ee..3c1f09a3 100644 --- a/cas/vaultcas/vaultcas_test.go +++ b/cas/vaultcas/vaultcas_test.go @@ -14,7 +14,6 @@ import ( "time" vault "github.com/hashicorp/vault/api" - auth "github.com/hashicorp/vault/api/auth/approle" "github.com/smallstep/certificates/cas/apiv1" "go.step.sm/crypto/pemutil" ) @@ -99,7 +98,7 @@ func testCAHelper(t *testing.T) (*url.URL, *vault.Client) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { - case r.RequestURI == "/v1/auth/auth/approle/login": + case r.RequestURI == "/v1/auth/approle/login": w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "auth": { @@ -183,11 +182,10 @@ func TestNew_register(t *testing.T) { CertificateAuthority: caURL.String(), CertificateAuthorityFingerprint: testRootFingerprint, Config: json.RawMessage(`{ - "PKI": "pki", + "PKIMountPath": "pki", "PKIRoleDefault": "pki-role", - "RoleID": "roleID", - "SecretID": {"FromString": "secretID"}, - "IsWrappingToken": false + "AuthType": "approle", + "AuthOptions": {"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false} }`), }) @@ -201,15 +199,13 @@ func TestVaultCAS_CreateCertificate(t *testing.T) { _, client := testCAHelper(t) options := VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "rsa", - PKIRoleEC: "ec", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "rsa", + PKIRoleEC: "ec", + PKIRoleEd25519: "ed25519", + AuthType: "approle", + AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { @@ -291,7 +287,7 @@ func TestVaultCAS_GetCertificateAuthority(t *testing.T) { } options := VaultOptions{ - PKI: "pki", + PKIMountPath: "pki", } rootCert := parseCertificates(testRootCertificate)[0] @@ -335,15 +331,13 @@ func TestVaultCAS_RevokeCertificate(t *testing.T) { _, client := testCAHelper(t) options := VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "rsa", - PKIRoleEC: "ec", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "rsa", + PKIRoleEC: "ec", + PKIRoleEd25519: "ed25519", + AuthType: "approle", + AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { @@ -407,15 +401,13 @@ func TestVaultCAS_RenewCertificate(t *testing.T) { _, client := testCAHelper(t) options := VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "rsa", - PKIRoleEC: "ec", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "rsa", + PKIRoleEC: "ec", + PKIRoleEd25519: "ed25519", + AuthType: "approle", + AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { @@ -464,202 +456,66 @@ func TestVaultCAS_loadOptions(t *testing.T) { want *VaultOptions wantErr bool }{ - { - "ok mandatory with SecretID FromString", - `{"RoleID": "roleID", "SecretID": {"FromString": "secretID"}}`, - &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "default", - PKIRoleEC: "default", - PKIRoleEd25519: "default", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, - }, - false, - }, - { - "ok mandatory with SecretID FromFile", - `{"RoleID": "roleID", "SecretID": {"FromFile": "secretID"}}`, - &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "default", - PKIRoleEC: "default", - PKIRoleEd25519: "default", - RoleID: "roleID", - SecretID: auth.SecretID{FromFile: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, - }, - false, - }, - { - "ok mandatory with SecretID FromEnv", - `{"RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, - &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "default", - PKIRoleEC: "default", - PKIRoleEd25519: "default", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, - }, - false, - }, { "ok mandatory PKIRole PKIRoleEd25519", - `{"PKIRoleDefault": "role", "PKIRoleEd25519": "ed25519" , "RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, + `{"PKIRoleDefault": "role", "PKIRoleEd25519": "ed25519"}`, &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "role", - PKIRoleEC: "role", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "role", + PKIRoleEC: "role", + PKIRoleEd25519: "ed25519", }, false, }, { "ok mandatory PKIRole PKIRoleEC", - `{"PKIRoleDefault": "role", "PKIRoleEC": "ec" , "RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, + `{"PKIRoleDefault": "role", "PKIRoleEC": "ec"}`, &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "role", - PKIRoleEC: "ec", - PKIRoleEd25519: "role", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "role", + PKIRoleEC: "ec", + PKIRoleEd25519: "role", }, false, }, { "ok mandatory PKIRole PKIRoleRSA", - `{"PKIRoleDefault": "role", "PKIRoleRSA": "rsa" , "RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, + `{"PKIRoleDefault": "role", "PKIRoleRSA": "rsa"}`, &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "rsa", - PKIRoleEC: "role", - PKIRoleEd25519: "role", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "rsa", + PKIRoleEC: "role", + PKIRoleEd25519: "role", }, false, }, { "ok mandatory PKIRoleRSA PKIRoleEC PKIRoleEd25519", - `{"PKIRoleRSA": "rsa", "PKIRoleEC": "ec", "PKIRoleEd25519": "ed25519", "RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, + `{"PKIRoleRSA": "rsa", "PKIRoleEC": "ec", "PKIRoleEd25519": "ed25519"}`, &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "rsa", - PKIRoleEC: "ec", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "default", + PKIRoleRSA: "rsa", + PKIRoleEC: "ec", + PKIRoleEd25519: "ed25519", }, false, }, { "ok mandatory PKIRoleRSA PKIRoleEC PKIRoleEd25519 with useless PKIRoleDefault", - `{"PKIRoleDefault": "role", "PKIRoleRSA": "rsa", "PKIRoleEC": "ec", "PKIRoleEd25519": "ed25519", "RoleID": "roleID", "SecretID": {"FromEnv": "secretID"}}`, + `{"PKIRoleDefault": "role", "PKIRoleRSA": "rsa", "PKIRoleEC": "ec", "PKIRoleEd25519": "ed25519"}`, &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "role", - PKIRoleRSA: "rsa", - PKIRoleEC: "ec", - PKIRoleEd25519: "ed25519", - RoleID: "roleID", - SecretID: auth.SecretID{FromEnv: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: false, + PKIMountPath: "pki", + PKIRoleDefault: "role", + PKIRoleRSA: "rsa", + PKIRoleEC: "ec", + PKIRoleEd25519: "ed25519", }, false, }, - { - "ok mandatory with AppRole", - `{"AppRole": "test", "RoleID": "roleID", "SecretID": {"FromString": "secretID"}}`, - &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "default", - PKIRoleEC: "default", - PKIRoleEd25519: "default", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "test", - IsWrappingToken: false, - }, - false, - }, - { - "ok mandatory with IsWrappingToken", - `{"IsWrappingToken": true, "RoleID": "roleID", "SecretID": {"FromString": "secretID"}}`, - &VaultOptions{ - PKI: "pki", - PKIRoleDefault: "default", - PKIRoleRSA: "default", - PKIRoleEC: "default", - PKIRoleEd25519: "default", - RoleID: "roleID", - SecretID: auth.SecretID{FromString: "secretID"}, - AppRole: "auth/approle", - IsWrappingToken: true, - }, - false, - }, - { - "fail with SecretID FromFail", - `{"RoleID": "roleID", "SecretID": {"FromFail": "secretID"}}`, - nil, - true, - }, - { - "fail with SecretID empty FromEnv", - `{"RoleID": "roleID", "SecretID": {"FromEnv": ""}}`, - nil, - true, - }, - { - "fail with SecretID empty FromFile", - `{"RoleID": "roleID", "SecretID": {"FromFile": ""}}`, - nil, - true, - }, - { - "fail with SecretID empty FromString", - `{"RoleID": "roleID", "SecretID": {"FromString": ""}}`, - nil, - true, - }, - { - "fail mandatory with SecretID FromFail", - `{"RoleID": "roleID", "SecretID": {"FromFail": "secretID"}}`, - nil, - true, - }, - { - "fail missing RoleID", - `{"SecretID": {"FromString": "secretID"}}`, - nil, - true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 6989c7f146c534df3ebb9fbac5c92e9735fde882 Mon Sep 17 00:00:00 2001 From: Erik De Lamarter Date: Sun, 15 May 2022 17:42:08 +0200 Subject: [PATCH 3/5] vault auth unit tests --- cas/vaultcas/auth/approle/approle.go | 24 ++- cas/vaultcas/auth/approle/approle_test.go | 163 +++++++++++++++++- cas/vaultcas/auth/kubernetes/kubernetes.go | 6 + .../auth/kubernetes/kubernetes_test.go | 140 ++++++++++++++- cas/vaultcas/vaultcas_test.go | 8 - 5 files changed, 321 insertions(+), 20 deletions(-) diff --git a/cas/vaultcas/auth/approle/approle.go b/cas/vaultcas/auth/approle/approle.go index 38d3c51c..d842bae0 100644 --- a/cas/vaultcas/auth/approle/approle.go +++ b/cas/vaultcas/auth/approle/approle.go @@ -2,6 +2,7 @@ package approle import ( "encoding/json" + "errors" "fmt" "github.com/hashicorp/vault/api/auth/approle" @@ -12,6 +13,8 @@ import ( type AuthOptions struct { RoleID string `json:"roleID,omitempty"` SecretID string `json:"secretID,omitempty"` + SecretIDFile string `json:"secretIDFile,omitempty"` + SecretIDEnv string `json:"secretIDEnv,omitempty"` IsWrappingToken bool `json:"isWrappingToken,omitempty"` } @@ -33,8 +36,25 @@ func NewApproleAuthMethod(mountPath string, options json.RawMessage) (*approle.A loginOptions = append(loginOptions, approle.WithWrappingToken()) } - sid := approle.SecretID{ - FromString: opts.SecretID, + if opts.RoleID == "" { + return nil, errors.New("you must set roleID") + } + + var sid approle.SecretID + if opts.SecretID != "" { + sid = approle.SecretID{ + FromString: opts.SecretID, + } + } else if opts.SecretIDFile != "" { + sid = approle.SecretID{ + FromFile: opts.SecretIDFile, + } + } else if opts.SecretIDEnv != "" { + sid = approle.SecretID{ + FromEnv: opts.SecretIDEnv, + } + } else { + return nil, errors.New("you must set one of secretID, secretIDFile or secretIDEnv") } approleAuth, err = approle.NewAppRoleAuth(opts.RoleID, &sid, loginOptions...) diff --git a/cas/vaultcas/auth/approle/approle_test.go b/cas/vaultcas/auth/approle/approle_test.go index ab7e6a97..ec4d523f 100644 --- a/cas/vaultcas/auth/approle/approle_test.go +++ b/cas/vaultcas/auth/approle/approle_test.go @@ -1,16 +1,171 @@ package approle import ( + "context" "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" "testing" + + vault "github.com/hashicorp/vault/api" ) -func TestKubernetes_NewKubernetesAuthMethod(t *testing.T) { - mountPath := "approle" - raw := `{"roleID": "roleID", "secretID": "secretIDwrapped", "isWrappedToken": true}` +func testCAHelper(t *testing.T) (*url.URL, *vault.Client) { + t.Helper() - _, err := NewApproleAuthMethod(mountPath, json.RawMessage(raw)) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.RequestURI == "/v1/auth/approle/login": + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "auth": { + "client_token": "hvs.0000" + } + }`) + case r.RequestURI == "/v1/auth/custom-approle/login": + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "auth": { + "client_token": "hvs.9999" + } + }`) + default: + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, `{"error":"not found"}`) + } + })) + t.Cleanup(func() { + srv.Close() + }) + u, err := url.Parse(srv.URL) if err != nil { + srv.Close() t.Fatal(err) } + + config := vault.DefaultConfig() + config.Address = srv.URL + + client, err := vault.NewClient(config) + if err != nil { + srv.Close() + t.Fatal(err) + } + + return u, client +} + +func TestApprole_LoginMountPaths(t *testing.T) { + caURL, _ := testCAHelper(t) + + config := vault.DefaultConfig() + config.Address = caURL.String() + client, _ := vault.NewClient(config) + + tests := []struct { + name string + mountPath string + token string + }{ + { + name: "ok default mount path", + mountPath: "", + token: "hvs.0000", + }, + { + name: "ok explicit mount path", + mountPath: "approle", + token: "hvs.0000", + }, + { + name: "ok custom mount path", + mountPath: "custom-approle", + token: "hvs.9999", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + method, err := NewApproleAuthMethod(tt.mountPath, json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`)) + if err != nil { + t.Errorf("NewApproleAuthMethod() error = %v", err) + return + } + + secret, err := client.Auth().Login(context.Background(), method) + if err != nil { + t.Errorf("Login() error = %v", err) + return + } + + token, _ := secret.TokenID() + if token != tt.token { + t.Errorf("Token error got %v, expected %v", token, tt.token) + return + } + }) + } +} + +func TestApprole_NewApproleAuthMethod(t *testing.T) { + tests := []struct { + name string + mountPath string + raw string + wantErr bool + }{ + { + "ok secret-id string", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000"}`, + false, + }, + { + "ok secret-id string and wrapped", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000", "isWrappedToken": true}`, + false, + }, + { + "ok secret-id string and wrapped with custom mountPath", + "approle2", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000", "isWrappedToken": true}`, + false, + }, + { + "ok secret-id file", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretIDFile": "./secret-id"}`, + false, + }, + { + "ok secret-id env", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretIDEnv": "VAULT_APPROLE_SECRETID"}`, + false, + }, + { + "fail mandatory role-id", + "", + `{}`, + true, + }, + { + "fail mandatory secret-id any", + "", + `{"RoleID": "0000-0000-0000-0000"}`, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewApproleAuthMethod(tt.mountPath, json.RawMessage(tt.raw)) + if (err != nil) != tt.wantErr { + t.Errorf("Approle.NewApproleAuthMethod() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } } diff --git a/cas/vaultcas/auth/kubernetes/kubernetes.go b/cas/vaultcas/auth/kubernetes/kubernetes.go index 0c4db62f..267bcdca 100644 --- a/cas/vaultcas/auth/kubernetes/kubernetes.go +++ b/cas/vaultcas/auth/kubernetes/kubernetes.go @@ -2,6 +2,7 @@ package kubernetes import ( "encoding/json" + "errors" "fmt" "github.com/hashicorp/vault/api/auth/kubernetes" @@ -31,6 +32,11 @@ func NewKubernetesAuthMethod(mountPath string, options json.RawMessage) (*kubern if opts.TokenPath != "" { loginOptions = append(loginOptions, kubernetes.WithServiceAccountTokenPath(opts.TokenPath)) } + + if opts.Role == "" { + return nil, errors.New("you must set role") + } + kubernetesAuth, err = kubernetes.NewKubernetesAuth( opts.Role, loginOptions..., diff --git a/cas/vaultcas/auth/kubernetes/kubernetes_test.go b/cas/vaultcas/auth/kubernetes/kubernetes_test.go index 604f1898..55be904d 100644 --- a/cas/vaultcas/auth/kubernetes/kubernetes_test.go +++ b/cas/vaultcas/auth/kubernetes/kubernetes_test.go @@ -1,21 +1,149 @@ package kubernetes import ( + "context" "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" "path" "path/filepath" "runtime" "testing" + + vault "github.com/hashicorp/vault/api" ) -func TestKubernetes_NewKubernetesAuthMethod(t *testing.T) { - _, filename, _, _ := runtime.Caller(0) - tokenPath := filepath.Join(path.Dir(filename), "token") - mountPath := "kubernetes" - raw := `{"role": "SomeRoleName", "tokenPath": "` + tokenPath + `"}` +func testCAHelper(t *testing.T) (*url.URL, *vault.Client) { + t.Helper() - _, err := NewKubernetesAuthMethod(mountPath, json.RawMessage(raw)) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.RequestURI == "/v1/auth/kubernetes/login": + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "auth": { + "client_token": "hvs.0000" + } + }`) + case r.RequestURI == "/v1/auth/custom-kubernetes/login": + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "auth": { + "client_token": "hvs.9999" + } + }`) + default: + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, `{"error":"not found"}`) + } + })) + t.Cleanup(func() { + srv.Close() + }) + u, err := url.Parse(srv.URL) if err != nil { + srv.Close() t.Fatal(err) } + + config := vault.DefaultConfig() + config.Address = srv.URL + + client, err := vault.NewClient(config) + if err != nil { + srv.Close() + t.Fatal(err) + } + + return u, client +} + +func TestApprole_LoginMountPaths(t *testing.T) { + caURL, _ := testCAHelper(t) + _, filename, _, _ := runtime.Caller(0) + tokenPath := filepath.Join(path.Dir(filename), "token") + + config := vault.DefaultConfig() + config.Address = caURL.String() + client, _ := vault.NewClient(config) + + tests := []struct { + name string + mountPath string + token string + }{ + { + name: "ok default mount path", + mountPath: "", + token: "hvs.0000", + }, + { + name: "ok explicit mount path", + mountPath: "kubernetes", + token: "hvs.0000", + }, + { + name: "ok custom mount path", + mountPath: "custom-kubernetes", + token: "hvs.9999", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + method, err := NewKubernetesAuthMethod(tt.mountPath, json.RawMessage(`{"role": "SomeRoleName", "tokenPath": "`+tokenPath+`"}`)) + if err != nil { + t.Errorf("NewApproleAuthMethod() error = %v", err) + return + } + + secret, err := client.Auth().Login(context.Background(), method) + if err != nil { + t.Errorf("Login() error = %v", err) + return + } + + token, _ := secret.TokenID() + if token != tt.token { + t.Errorf("Token error got %v, expected %v", token, tt.token) + return + } + }) + } +} + +func TestApprole_NewApproleAuthMethod(t *testing.T) { + _, filename, _, _ := runtime.Caller(0) + tokenPath := filepath.Join(path.Dir(filename), "token") + + tests := []struct { + name string + mountPath string + raw string + wantErr bool + }{ + { + "ok secret-id string", + "", + `{"role": "SomeRoleName", "tokenPath": "` + tokenPath + `"}`, + false, + }, + { + "fail mandatory role", + "", + `{}`, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewKubernetesAuthMethod(tt.mountPath, json.RawMessage(tt.raw)) + if (err != nil) != tt.wantErr { + t.Errorf("Kubernetes.NewKubernetesAuthMethod() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } } diff --git a/cas/vaultcas/vaultcas_test.go b/cas/vaultcas/vaultcas_test.go index 3c1f09a3..0ea0c4b1 100644 --- a/cas/vaultcas/vaultcas_test.go +++ b/cas/vaultcas/vaultcas_test.go @@ -182,8 +182,6 @@ func TestNew_register(t *testing.T) { CertificateAuthority: caURL.String(), CertificateAuthorityFingerprint: testRootFingerprint, Config: json.RawMessage(`{ - "PKIMountPath": "pki", - "PKIRoleDefault": "pki-role", "AuthType": "approle", "AuthOptions": {"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false} }`), @@ -204,8 +202,6 @@ func TestVaultCAS_CreateCertificate(t *testing.T) { PKIRoleRSA: "rsa", PKIRoleEC: "ec", PKIRoleEd25519: "ed25519", - AuthType: "approle", - AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { @@ -336,8 +332,6 @@ func TestVaultCAS_RevokeCertificate(t *testing.T) { PKIRoleRSA: "rsa", PKIRoleEC: "ec", PKIRoleEd25519: "ed25519", - AuthType: "approle", - AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { @@ -406,8 +400,6 @@ func TestVaultCAS_RenewCertificate(t *testing.T) { PKIRoleRSA: "rsa", PKIRoleEC: "ec", PKIRoleEd25519: "ed25519", - AuthType: "approle", - AuthOptions: json.RawMessage(`{"RoleID":"roleID","SecretID":"secretID","IsWrappingToken":false}`), } type fields struct { From 9ec154aab02f25fad6ee47a545a8250ed76e3345 Mon Sep 17 00:00:00 2001 From: Erik De Lamarter Date: Tue, 17 May 2022 22:13:11 +0200 Subject: [PATCH 4/5] rewrite and improve secret-id config --- cas/vaultcas/auth/approle/approle.go | 9 +++++---- cas/vaultcas/auth/approle/approle_test.go | 24 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cas/vaultcas/auth/approle/approle.go b/cas/vaultcas/auth/approle/approle.go index d842bae0..118afb10 100644 --- a/cas/vaultcas/auth/approle/approle.go +++ b/cas/vaultcas/auth/approle/approle.go @@ -41,19 +41,20 @@ func NewApproleAuthMethod(mountPath string, options json.RawMessage) (*approle.A } var sid approle.SecretID - if opts.SecretID != "" { + switch { + case opts.SecretID != "" && opts.SecretIDFile == "" && opts.SecretIDEnv == "": sid = approle.SecretID{ FromString: opts.SecretID, } - } else if opts.SecretIDFile != "" { + case opts.SecretIDFile != "" && opts.SecretID == "" && opts.SecretIDEnv == "": sid = approle.SecretID{ FromFile: opts.SecretIDFile, } - } else if opts.SecretIDEnv != "" { + case opts.SecretIDEnv != "" && opts.SecretIDFile == "" && opts.SecretID == "": sid = approle.SecretID{ FromEnv: opts.SecretIDEnv, } - } else { + default: return nil, errors.New("you must set one of secretID, secretIDFile or secretIDEnv") } diff --git a/cas/vaultcas/auth/approle/approle_test.go b/cas/vaultcas/auth/approle/approle_test.go index ec4d523f..28b7b7f7 100644 --- a/cas/vaultcas/auth/approle/approle_test.go +++ b/cas/vaultcas/auth/approle/approle_test.go @@ -158,6 +158,30 @@ func TestApprole_NewApproleAuthMethod(t *testing.T) { `{"RoleID": "0000-0000-0000-0000"}`, true, }, + { + "fail multiple secret-id types id and env", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000", "SecretIDEnv": "VAULT_APPROLE_SECRETID"}`, + true, + }, + { + "fail multiple secret-id types id and file", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000", "SecretIDFile": "./secret-id"}`, + true, + }, + { + "fail multiple secret-id types env and file", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretIDFile": "./secret-id", "SecretIDEnv": "VAULT_APPROLE_SECRETID"}`, + true, + }, + { + "fail multiple secret-id types all", + "", + `{"RoleID": "0000-0000-0000-0000", "SecretID": "0000-0000-0000-0000", "SecretIDFile": "./secret-id", "SecretIDEnv": "VAULT_APPROLE_SECRETID"}`, + true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 07984a968fba1eedfa514d80e088a41e1f59651f Mon Sep 17 00:00:00 2001 From: Erik DeLamarter Date: Sat, 21 May 2022 21:00:50 +0200 Subject: [PATCH 5/5] better error messages Co-authored-by: Mariano Cano --- cas/vaultcas/vaultcas.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cas/vaultcas/vaultcas.go b/cas/vaultcas/vaultcas.go index 02c814b7..a5658620 100644 --- a/cas/vaultcas/vaultcas.go +++ b/cas/vaultcas/vaultcas.go @@ -84,15 +84,15 @@ func New(ctx context.Context, opts apiv1.Options) (*VaultCAS, error) { case "approle": method, err = approle.NewApproleAuthMethod(vc.AuthMountPath, vc.AuthOptions) default: - return nil, fmt.Errorf("unknown auth type: %v", vc.AuthType) + return nil, fmt.Errorf("unknown auth type: %s, only 'kubernetes' and 'approle' currently supported", vc.AuthType) } if err != nil { - return nil, fmt.Errorf("unable to configure auth method: %w", err) + return nil, fmt.Errorf("unable to configure %s auth method: %w", vc.AuthType, err) } authInfo, err := client.Auth().Login(ctx, method) if err != nil { - return nil, fmt.Errorf("unable to login to Kubernetes auth method: %w", err) + return nil, fmt.Errorf("unable to login to %s auth method: %w", vc.AuthType, err) } if authInfo == nil { return nil, errors.New("no auth info was returned after login")