1
0
Fork 0
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:
Gustavo Fernandes de Carvalho 2023-01-06 13:17:18 -03:00 committed by GitHub
parent a2da89e348
commit a051da82cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 15 deletions

View file

@ -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

View 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.

View file

@ -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

View file

@ -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)) {