mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
🐛 Fixes vault PushSecret logic (#1866)
Signed-off-by: Gustavo Carvalho <gusfcarvalho@gmail.com>
This commit is contained in:
parent
a2da89e348
commit
a051da82cf
4 changed files with 56 additions and 15 deletions
|
@ -315,6 +315,16 @@ or `Kind=ClusterSecretStore` resource.
|
|||
```
|
||||
**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
|
||||
|
||||
### PushSecret
|
||||
Vault supports PushSecret features which allow you to sync a given kubernetes secret key into a hashicorp vault secret. In order to do so, it is expected that the secret key is a valid JSON object.
|
||||
|
||||
In order to use PushSecret, you need to give `create`, `read` and `update` permissions to the path where you want to push secrets to. Use it with care!
|
||||
|
||||
Here is an example on how to set it up:
|
||||
```yaml
|
||||
{% include 'vault-pushsecret.yaml' %}
|
||||
```
|
||||
|
||||
### Vault Enterprise
|
||||
|
||||
#### Eventual Consistency and Performance Standby Nodes
|
||||
|
|
26
docs/snippets/vault-pushsecret.yaml
Normal file
26
docs/snippets/vault-pushsecret.yaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: source-secret
|
||||
namespace: default
|
||||
stringData:
|
||||
source-key: "{\"foo\":\"bar\"}" # Needs to be a JSON
|
||||
---
|
||||
apiVersion: external-secrets.io/v1alpha1
|
||||
kind: PushSecret
|
||||
metadata:
|
||||
name: pushsecret-example
|
||||
namespace: default
|
||||
spec:
|
||||
refreshInterval: 10s # Refresh interval for which push secret will reconcile
|
||||
secretStoreRefs: # A list of secret stores to push secrets to
|
||||
- name: vault-secretstore
|
||||
kind: SecretStore
|
||||
selector:
|
||||
secret:
|
||||
name: source-secret # Source Kubernetes secret to be pushed
|
||||
data:
|
||||
- match:
|
||||
secretKey: source-key # Source Kubernetes secret key containing the vault secret (in JSON format)
|
||||
remoteRef:
|
||||
remoteKey: vault/secret # path to vault secret. This path is appended with the vault-store path.
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
|
@ -450,10 +451,13 @@ func (v *client) PushSecret(ctx context.Context, value []byte, remoteRef esv1bet
|
|||
"managed-by": "external-secrets",
|
||||
},
|
||||
}
|
||||
secretVal := make(map[string]interface{})
|
||||
err := json.Unmarshal(value, &secretVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert value to a valid JSON: %w", err)
|
||||
}
|
||||
secretToPush := map[string]interface{}{
|
||||
"data": map[string]string{
|
||||
remoteRef.GetRemoteKey(): string(value),
|
||||
},
|
||||
"data": secretVal,
|
||||
}
|
||||
path := v.buildPath(remoteRef.GetRemoteKey())
|
||||
metaPath, err := v.buildMetadataPath(remoteRef.GetRemoteKey())
|
||||
|
@ -462,20 +466,11 @@ func (v *client) PushSecret(ctx context.Context, value []byte, remoteRef esv1bet
|
|||
}
|
||||
|
||||
// Retrieve the secret map from vault and convert the secret value in string form.
|
||||
vaultSecret, err := v.GetSecretMap(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: path})
|
||||
vaultSecretValue := string(vaultSecret[remoteRef.GetRemoteKey()])
|
||||
vaultSecret, err := v.readSecret(ctx, path, "")
|
||||
// If error is not of type secret not found, we should error
|
||||
if err != nil && !strings.Contains(err.Error(), "secret not found") {
|
||||
return err
|
||||
}
|
||||
|
||||
// Retrieve the secret value to be pushed and convert it to string form.
|
||||
pushSecretValue := string(value)
|
||||
|
||||
if vaultSecretValue == pushSecretValue {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the secret exists (err == nil), we should check if it is managed by external-secrets
|
||||
if err == nil {
|
||||
metadata, err := v.readSecretMetadata(ctx, remoteRef.GetRemoteKey())
|
||||
|
@ -487,6 +482,13 @@ func (v *client) PushSecret(ctx context.Context, value []byte, remoteRef esv1bet
|
|||
return fmt.Errorf("secret not managed by external-secrets")
|
||||
}
|
||||
}
|
||||
vaultSecretValue, err := json.Marshal(vaultSecret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling vault secret: %w", err)
|
||||
}
|
||||
if bytes.Equal(vaultSecretValue, value) {
|
||||
return nil
|
||||
}
|
||||
_, err = v.logical.WriteWithContext(ctx, metaPath, label)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1438,6 +1438,9 @@ func TestSetSecret(t *testing.T) {
|
|||
"data": map[string]interface{}{
|
||||
"fake-key": "fake-value",
|
||||
},
|
||||
"custom_metadata": map[string]interface{}{
|
||||
"managed-by": "external-secrets",
|
||||
},
|
||||
}, nil),
|
||||
},
|
||||
},
|
||||
|
@ -1482,12 +1485,12 @@ func TestSetSecret(t *testing.T) {
|
|||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ref := fakeRef{key: "fake-key"}
|
||||
ref := fakeRef{key: "secret"}
|
||||
client := &client{
|
||||
logical: tc.args.vLogical,
|
||||
store: tc.args.store,
|
||||
}
|
||||
err := client.PushSecret(context.Background(), []byte("fake-value"), ref)
|
||||
err := client.PushSecret(context.Background(), []byte(`{"fake-key":"fake-value"}`), ref)
|
||||
|
||||
// Error nil XOR tc.want.err nil
|
||||
if ((err == nil) || (tc.want.err == nil)) && !((err == nil) && (tc.want.err == nil)) {
|
||||
|
|
Loading…
Reference in a new issue