Browse Source

Merge pull request #152 from xxxbobrxxx/vault-ldap-auth-method

Vault ldap and jwt/oidc auth methods
paul-the-alien[bot] 4 years ago
parent
commit
4a1d43f1ed

+ 39 - 2
apis/externalsecrets/v1alpha1/secretstore_vault_types.go

@@ -61,8 +61,9 @@ type VaultProvider struct {
 	CABundle []byte `json:"caBundle,omitempty"`
 }
 
-// Configuration used to authenticate with a Vault server.
-// Only one of `tokenSecretRef`, `appRole` or `kubernetes` may be specified.
+// VaultAuth is the configuration used to authenticate with a Vault server.
+// Only one of `tokenSecretRef`, `appRole`,  `kubernetes`, `ldap` or `jwt`
+// can be specified.
 type VaultAuth struct {
 	// TokenSecretRef authenticates with Vault by presenting a token.
 	// +optional
@@ -77,6 +78,16 @@ type VaultAuth struct {
 	// token stored in the named Secret resource to the Vault server.
 	// +optional
 	Kubernetes *VaultKubernetesAuth `json:"kubernetes,omitempty"`
+
+	// Ldap authenticates with Vault by passing username/password pair using
+	// the LDAP authentication method
+	// +optional
+	Ldap *VaultLdapAuth `json:"ldap,omitempty"`
+
+	// Jwt authenticates with Vault by passing role and JWT token using the
+	// JWT/OIDC authentication method
+	// +optional
+	Jwt *VaultJwtAuth `json:"jwt,omitempty"`
 }
 
 // VaultAppRole authenticates with Vault using the App Role auth mechanism,
@@ -124,3 +135,29 @@ type VaultKubernetesAuth struct {
 	// Kubernetes ServiceAccount with a set of Vault policies.
 	Role string `json:"role"`
 }
+
+// VaultLdapAuth authenticates with Vault using the LDAP authentication method,
+// with the username and password stored in a Kubernetes Secret resource.
+type VaultLdapAuth struct {
+	// Username is a LDAP user name used to authenticate using the LDAP Vault
+	// authentication method
+	Username string `json:"username"`
+
+	// SecretRef to a key in a Secret resource containing password for the LDAP
+	// user used to authenticate with Vault using the LDAP authentication
+	// method
+	SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}
+
+// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
+// method, with the role name and token stored in a Kubernetes Secret resource.
+type VaultJwtAuth struct {
+	// Role is a JWT role to authenticate using the JWT/OIDC Vault
+	// authentication method
+	// +optional
+	Role string `json:"role"`
+
+	// SecretRef to a key in a Secret resource containing JWT token to
+	// authenticate with Vault using the JWT/OIDC authentication method
+	SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}

+ 42 - 0
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -567,6 +567,16 @@ func (in *VaultAuth) DeepCopyInto(out *VaultAuth) {
 		*out = new(VaultKubernetesAuth)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Ldap != nil {
+		in, out := &in.Ldap, &out.Ldap
+		*out = new(VaultLdapAuth)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Jwt != nil {
+		in, out := &in.Jwt, &out.Jwt
+		*out = new(VaultJwtAuth)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAuth.
@@ -580,6 +590,22 @@ func (in *VaultAuth) DeepCopy() *VaultAuth {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
+	*out = *in
+	in.SecretRef.DeepCopyInto(&out.SecretRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultJwtAuth.
+func (in *VaultJwtAuth) DeepCopy() *VaultJwtAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(VaultJwtAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
 	*out = *in
 	if in.ServiceAccountRef != nil {
@@ -605,6 +631,22 @@ func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultLdapAuth) DeepCopyInto(out *VaultLdapAuth) {
+	*out = *in
+	in.SecretRef.DeepCopyInto(&out.SecretRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultLdapAuth.
+func (in *VaultLdapAuth) DeepCopy() *VaultLdapAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(VaultLdapAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *VaultProvider) DeepCopyInto(out *VaultProvider) {
 	*out = *in
 	in.Auth.DeepCopyInto(&out.Auth)

+ 70 - 0
deploy/crds/external-secrets.io_clustersecretstores.yaml

@@ -189,6 +189,39 @@ spec:
                             - roleId
                             - secretRef
                             type: object
+                          jwt:
+                            description: Jwt authenticates with Vault by passing role
+                              and JWT token using the JWT/OIDC authentication method
+                            properties:
+                              role:
+                                description: Role is a JWT role to authenticate using
+                                  the JWT/OIDC Vault authentication method
+                                type: string
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing JWT token to authenticate with Vault
+                                  using the JWT/OIDC authentication method
+                                properties:
+                                  key:
+                                    description: The key of the entry in the Secret
+                                      resource's `data` field to be used. Some instances
+                                      of this field may be defaulted, in others it
+                                      may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                            type: object
                           kubernetes:
                             description: Kubernetes authenticates with Vault by passing
                               the ServiceAccount token stored in the named Secret
@@ -255,6 +288,43 @@ spec:
                             - mountPath
                             - role
                             type: object
+                          ldap:
+                            description: Ldap authenticates with Vault by passing
+                              username/password pair using the LDAP authentication
+                              method
+                            properties:
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing password for the LDAP user used to authenticate
+                                  with Vault using the LDAP authentication method
+                                properties:
+                                  key:
+                                    description: The key of the entry in the Secret
+                                      resource's `data` field to be used. Some instances
+                                      of this field may be defaulted, in others it
+                                      may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                              username:
+                                description: Username is a LDAP user name used to
+                                  authenticate using the LDAP Vault authentication
+                                  method
+                                type: string
+                            required:
+                            - username
+                            type: object
                           tokenSecretRef:
                             description: TokenSecretRef authenticates with Vault by
                               presenting a token.

+ 70 - 0
deploy/crds/external-secrets.io_secretstores.yaml

@@ -189,6 +189,39 @@ spec:
                             - roleId
                             - secretRef
                             type: object
+                          jwt:
+                            description: Jwt authenticates with Vault by passing role
+                              and JWT token using the JWT/OIDC authentication method
+                            properties:
+                              role:
+                                description: Role is a JWT role to authenticate using
+                                  the JWT/OIDC Vault authentication method
+                                type: string
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing JWT token to authenticate with Vault
+                                  using the JWT/OIDC authentication method
+                                properties:
+                                  key:
+                                    description: The key of the entry in the Secret
+                                      resource's `data` field to be used. Some instances
+                                      of this field may be defaulted, in others it
+                                      may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                            type: object
                           kubernetes:
                             description: Kubernetes authenticates with Vault by passing
                               the ServiceAccount token stored in the named Secret
@@ -255,6 +288,43 @@ spec:
                             - mountPath
                             - role
                             type: object
+                          ldap:
+                            description: Ldap authenticates with Vault by passing
+                              username/password pair using the LDAP authentication
+                              method
+                            properties:
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing password for the LDAP user used to authenticate
+                                  with Vault using the LDAP authentication method
+                                properties:
+                                  key:
+                                    description: The key of the entry in the Secret
+                                      resource's `data` field to be used. Some instances
+                                      of this field may be defaulted, in others it
+                                      may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: Namespace of the resource being referred
+                                      to. Ignored if referent is not cluster-scoped.
+                                      cluster-scoped defaults to the namespace of
+                                      the referent.
+                                    type: string
+                                required:
+                                - name
+                                type: object
+                              username:
+                                description: Username is a LDAP user name used to
+                                  authenticate using the LDAP Vault authentication
+                                  method
+                                type: string
+                            required:
+                            - username
+                            type: object
                           tokenSecretRef:
                             description: TokenSecretRef authenticates with Vault by
                               presenting a token.

+ 22 - 0
docs/provider-hashicorp-vault.md

@@ -44,3 +44,25 @@ options of optaining credentials for vault:
 ```yaml
 {% include 'vault-kubernetes-store.yaml' %}
 ```
+
+#### LDAP authentication
+
+[LDAP authentication](https://www.vaultproject.io/docs/auth/ldap) uses
+username/password pair to get an access token. Username is stored directly in
+a `Kind=SecretStore` or `Kind=ClusterSecretStore` resource, password is stored
+in a `Kind=Secret` referenced by the `secretRef`.
+
+```yaml
+{% include 'vault-ldap-store.yaml' %}
+```
+
+#### JWT/OIDC authentication
+
+[JWT/OIDC](https://www.vaultproject.io/docs/auth/jwt) uses a
+[JWT](https://jwt.io/) token stored in a `Kind=Secret` and referenced by the
+`secretRef`. Optionally a `role` field can be defined in a `Kind=SecretStore`
+or `Kind=ClusterSecretStore` resource.
+
+```yaml
+{% include 'vault-jwt-store.yaml' %}
+```

+ 21 - 0
docs/snippets/vault-jwt-store.yaml

@@ -0,0 +1,21 @@
+apiVerson: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: vault-backend
+  namespace: example
+spec:
+  provider:
+    vault:
+      server: "https://vault.acme.org"
+      path: "secret"
+      version: "v2"
+      auth:
+        # VaultJwt authenticates with Vault using the JWT/OIDC auth mechanism
+        # https://www.vaultproject.io/docs/auth/jwt
+        jwt:
+          # JWT role configured in a Vault server, optional.
+          role: "vault-jwt-role"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "jwt-token"

+ 21 - 0
docs/snippets/vault-ldap-store.yaml

@@ -0,0 +1,21 @@
+apiVerson: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: vault-backend
+  namespace: example
+spec:
+  provider:
+    vault:
+      server: "https://vault.acme.org"
+      path: "secret"
+      version: "v2"
+      auth:
+        # VaultLdap authenticates with Vault using the LDAP auth mechanism
+        # https://www.vaultproject.io/docs/auth/ldap
+        ldap:
+          # LDAP username
+          username: "username"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "ldap-password"

+ 123 - 2
docs/spec.md

@@ -1306,8 +1306,9 @@ resource is used as the app role secret.</p>
 <a href="#external-secrets.io/v1alpha1.VaultProvider">VaultProvider</a>)
 </p>
 <p>
-<p>Configuration used to authenticate with a Vault server.
-Only one of <code>tokenSecretRef</code>, <code>appRole</code> or <code>kubernetes</code> may be specified.</p>
+<p>VaultAuth is the configuration used to authenticate with a Vault server.
+Only one of <code>tokenSecretRef</code>, <code>appRole</code>,  <code>kubernetes</code>, <code>ldap</code> or <code>jwt</code>
+can be specified.</p>
 </p>
 <table>
 <thead>
@@ -1359,6 +1360,81 @@ VaultKubernetesAuth
 token stored in the named Secret resource to the Vault server.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>ldap</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.VaultLdapAuth">
+VaultLdapAuth
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Ldap authenticates with Vault by passing username/password pair using
+the LDAP authentication method</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>jwt</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.VaultJwtAuth">
+VaultJwtAuth
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Jwt authenticates with Vault by passing role and JWT token using the
+JWT/OIDC authentication method</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1alpha1.VaultJwtAuth">VaultJwtAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.VaultAuth">VaultAuth</a>)
+</p>
+<p>
+<p>VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
+method, with the role name and token stored in a Kubernetes Secret resource.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>role</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Role is a JWT role to authenticate using the JWT/OIDC Vault
+authentication method</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>secretRef</code></br>
+<em>
+github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
+</em>
+</td>
+<td>
+<p>SecretRef to a key in a Secret resource containing JWT token to
+authenticate with Vault using the JWT/OIDC authentication method</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="external-secrets.io/v1alpha1.VaultKVStoreVersion">VaultKVStoreVersion
@@ -1456,6 +1532,51 @@ Kubernetes ServiceAccount with a set of Vault policies.</p>
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1alpha1.VaultLdapAuth">VaultLdapAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.VaultAuth">VaultAuth</a>)
+</p>
+<p>
+<p>VaultLdapAuth authenticates with Vault using the LDAP authentication method,
+with the username and password stored in a Kubernetes Secret resource.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>username</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>Username is a LDAP user name used to authenticate using the LDAP Vault
+authentication method</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>secretRef</code></br>
+<em>
+github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
+</em>
+</td>
+<td>
+<p>SecretRef to a key in a Secret resource containing password for the LDAP
+user used to authenticate with Vault using the LDAP authentication
+method</p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1alpha1.VaultProvider">VaultProvider
 </h3>
 <p>

+ 99 - 0
pkg/provider/vault/vault.go

@@ -257,6 +257,26 @@ func (v *client) setAuth(ctx context.Context, client Client) error {
 		return nil
 	}
 
+	ldapAuth := v.store.Auth.Ldap
+	if ldapAuth != nil {
+		token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
+		if err != nil {
+			return err
+		}
+		client.SetToken(token)
+		return nil
+	}
+
+	jwtAuth := v.store.Auth.Jwt
+	if jwtAuth != nil {
+		token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
+		if err != nil {
+			return err
+		}
+		client.SetToken(token)
+		return nil
+	}
+
 	return errors.New(errAuthFormat)
 }
 
@@ -427,3 +447,82 @@ func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Clie
 
 	return token, nil
 }
+
+func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
+	username := strings.TrimSpace(ldapAuth.Username)
+
+	password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
+	if err != nil {
+		return "", err
+	}
+
+	parameters := map[string]string{
+		"password": password,
+	}
+	url := strings.Join([]string{"/v1", "auth", "ldap", "login", username}, "/")
+	request := client.NewRequest("POST", url)
+
+	err = request.SetJSONBody(parameters)
+	if err != nil {
+		return "", fmt.Errorf(errVaultReqParams, err)
+	}
+
+	resp, err := client.RawRequestWithContext(ctx, request)
+	if err != nil {
+		return "", fmt.Errorf(errVaultRequest, err)
+	}
+
+	defer resp.Body.Close()
+
+	vaultResult := vault.Secret{}
+	if err = resp.DecodeJSON(&vaultResult); err != nil {
+		return "", fmt.Errorf(errVaultResponse, err)
+	}
+
+	token, err := vaultResult.TokenID()
+	if err != nil {
+		return "", fmt.Errorf(errVaultToken, err)
+	}
+
+	return token, nil
+}
+
+func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1alpha1.VaultJwtAuth) (string, error) {
+	role := strings.TrimSpace(jwtAuth.Role)
+
+	jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
+	if err != nil {
+		return "", err
+	}
+
+	parameters := map[string]string{
+		"role": role,
+		"jwt":  jwt,
+	}
+	url := strings.Join([]string{"/v1", "auth", "jwt", "login"}, "/")
+	request := client.NewRequest("POST", url)
+
+	err = request.SetJSONBody(parameters)
+	if err != nil {
+		return "", fmt.Errorf(errVaultReqParams, err)
+	}
+
+	resp, err := client.RawRequestWithContext(ctx, request)
+	if err != nil {
+		return "", fmt.Errorf(errVaultRequest, err)
+	}
+
+	defer resp.Body.Close()
+
+	vaultResult := vault.Secret{}
+	if err = resp.DecodeJSON(&vaultResult); err != nil {
+		return "", fmt.Errorf(errVaultResponse, err)
+	}
+
+	token, err := vaultResult.TokenID()
+	if err != nil {
+		return "", fmt.Errorf(errVaultToken, err)
+	}
+
+	return token, nil
+}