Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/v1beta1/vaultdynamicsecret_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ type VaultDynamicSecretSpec struct {
RolloutRestartTargets []RolloutRestartTarget `json:"rolloutRestartTargets,omitempty"`
// Destination provides configuration necessary for syncing the Vault secret to Kubernetes.
Destination Destination `json:"destination"`
// SyncConfig configures sync behavior from Vault to VSO
SyncConfig *VaultDynamicSecretSyncConfig `json:"syncConfig,omitempty"`
// RefreshAfter a period of time for VSO to sync the source secret data, in
// duration notation e.g. 30s, 1m, 24h. This value only needs to be set when
// syncing from a secret's engine that does not provide a lease TTL in its
Expand Down Expand Up @@ -153,3 +155,10 @@ type VaultDynamicSecretList struct {
func init() {
SchemeBuilder.Register(&VaultDynamicSecret{}, &VaultDynamicSecretList{})
}

// VaultDynamicSecretSyncConfig configures sync behavior from Vault to VSO
type VaultDynamicSecretSyncConfig struct {
// InstantUpdates is a flag to indicate that event-driven updates are
// enabled for this VaultDynamicSecret
InstantUpdates bool `json:"instantUpdates,omitempty"`
}
20 changes: 20 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions chart/crds/secrets.hashicorp.com_vaultdynamicsecrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,15 @@ spec:
- name
type: object
type: array
syncConfig:
description: SyncConfig configures sync behavior from Vault to VSO
properties:
instantUpdates:
description: |-
InstantUpdates is a flag to indicate that event-driven updates are
enabled for this VaultDynamicSecret
type: boolean
type: object
vaultAuthRef:
description: |-
VaultAuthRef to the VaultAuth resource, can be prefixed with a namespace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,15 @@ spec:
- name
type: object
type: array
syncConfig:
description: SyncConfig configures sync behavior from Vault to VSO
properties:
instantUpdates:
description: |-
InstantUpdates is a flag to indicate that event-driven updates are
enabled for this VaultDynamicSecret
type: boolean
type: object
vaultAuthRef:
description: |-
VaultAuthRef to the VaultAuth resource, can be prefixed with a namespace,
Expand Down
3 changes: 3 additions & 0 deletions controllers/instant_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ func (cfg *InstantUpdateConfig) streamSecretEvents(ctx context.Context, obj clie
} else {
specPath = strings.Join([]string{o.Spec.Mount, "data", o.Spec.Path}, "/")
}
case *secretsv1beta1.VaultDynamicSecret:
specNamespace = strings.Trim(o.Spec.Namespace, "/")
specPath = vault.JoinPath(o.Spec.Mount, o.Spec.Path)
default:
return false, fmt.Errorf("unexpected object type %T", obj)
}
Expand Down
20 changes: 20 additions & 0 deletions controllers/instant_updates_test.go
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you have these unit tests which is also helpful, but can you also add an integration test for the event-watching functionality like you have in your other PR?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, I think it'd be good to have an integration test that sets up the database and LDAP secrets engine and validates that events from those specific paths trigger the instant updates that we're expecting.

Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ func TestStreamSecretEvents(t *testing.T) {
)),
wantMatch: false,
},
{
name: "vds-match",
obj: vds,
eventJSON: []byte(fmt.Sprintf(
`{"data":{"event":{"metadata":{"path":"%s","modified":"true"}},"namespace":"/%s"}}`,
"db/creds/app",
vds.Spec.Namespace,
)),
wantMatch: true,
},
{
name: "vds-path-mismatch",
obj: vds,
eventJSON: []byte(fmt.Sprintf(
`{"data":{"event":{"metadata":{"path":"%s","modified":"true"}},"namespace":"/%s"}}`,
"db/creds/other",
vds.Spec.Namespace,
)),
wantMatch: false,
},
{
name: "modified-missing",
obj: vssKVV2,
Expand Down
30 changes: 30 additions & 0 deletions controllers/vaultdynamicsecret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type VaultDynamicSecretReconciler struct {
HMACValidator helpers.HMACValidator
SyncRegistry *SyncRegistry
BackOffRegistry *BackOffRegistry
eventWatcherRegistry *eventWatcherRegistry
referenceCache ResourceReferenceCache
GlobalTransformationOptions *helpers.GlobalTransformationOptions
// sourceCh is used to trigger a requeue of resource instances from an
Expand Down Expand Up @@ -141,6 +142,25 @@ func (r *VaultDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.R
return ctrl.Result{RequeueAfter: computeHorizonWithJitter(requeueDurationOnError)}, nil
}

if o.Spec.SyncConfig != nil && o.Spec.SyncConfig.InstantUpdates {
logger.V(consts.LogLevelDebug).Info("Event watcher enabled")

err := EnsureEventWatcher(ctx, &InstantUpdateConfig{
Secret: o,
Client: vClient,
Registry: r.eventWatcherRegistry,
BackOffRegistry: r.BackOffRegistry,
SourceCh: r.SourceCh,
Recorder: r.Recorder,
NewClientFunc: r.newVDSClient,
})
if err != nil {
r.Recorder.Eventf(o, corev1.EventTypeWarning, consts.ReasonEventWatcherError, "Failed to watch events: %s", err)
}
} else {
UnwatchEvents(r.eventWatcherRegistry, o)
}

// we can ignore the error here, since it was handled above in the Get() call.
clientCacheKey, _ := vClient.GetCacheKey()
lastClientCacheKey := o.Status.VaultClientMeta.CacheKey
Expand Down Expand Up @@ -684,12 +704,21 @@ func (r *VaultDynamicSecretReconciler) renewLease(
return r.getVaultSecretLease(resp.Secret()), nil
}

func (r *VaultDynamicSecretReconciler) newVDSClient(ctx context.Context, obj client.Object) (vault.Client, error) {
vds, ok := obj.(*secretsv1beta1.VaultDynamicSecret)
if !ok {
return nil, fmt.Errorf("unexpected object type %T", obj)
}
return r.ClientFactory.Get(ctx, r.Client, vds)
}

// SetupWithManager sets up the controller with the Manager.
func (r *VaultDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
r.referenceCache = NewResourceReferenceCache()
if r.BackOffRegistry == nil {
r.BackOffRegistry = NewBackOffRegistry()
}
r.eventWatcherRegistry = newEventWatcherRegistry()

r.ClientFactory.RegisterClientCallbackHandler(
vault.ClientCallbackHandler{
Expand Down Expand Up @@ -748,6 +777,7 @@ func (r *VaultDynamicSecretReconciler) handleDeletion(ctx context.Context, o *se
r.SyncRegistry.Delete(objKey)
r.BackOffRegistry.Delete(objKey)
r.referenceCache.Remove(SecretTransformation, objKey)
UnwatchEvents(r.eventWatcherRegistry, o)
if controllerutil.ContainsFinalizer(o, vaultDynamicSecretFinalizer) {
logger.Info("Removing finalizer")
if controllerutil.RemoveFinalizer(o, vaultDynamicSecretFinalizer) {
Expand Down
17 changes: 17 additions & 0 deletions docs/api/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1166,11 +1166,28 @@ _Appears in:_
| `allowStaticCreds` _boolean_ | AllowStaticCreds should be set when syncing credentials that are periodically<br />rotated by the Vault server, rather than created upon request. These secrets<br />are sometimes referred to as "static roles", or "static credentials", with a<br />request path that contains "static-creds". | | |
| `rolloutRestartTargets` _[RolloutRestartTarget](#rolloutrestarttarget) array_ | RolloutRestartTargets should be configured whenever the application(s) consuming the Vault secret does<br />not support dynamically reloading a rotated secret.<br />In that case one, or more RolloutRestartTarget(s) can be configured here. The Operator will<br />trigger a "rollout-restart" for each target whenever the Vault secret changes between reconciliation events.<br />See RolloutRestartTarget for more details. | | |
| `destination` _[Destination](#destination)_ | Destination provides configuration necessary for syncing the Vault secret to Kubernetes. | | |
| `syncConfig` _[VaultDynamicSecretSyncConfig](#vaultdynamicsecretsyncconfig)_ | SyncConfig configures sync behavior from Vault to VSO | | |
| `refreshAfter` _string_ | RefreshAfter a period of time for VSO to sync the source secret data, in<br />duration notation e.g. 30s, 1m, 24h. This value only needs to be set when<br />syncing from a secret's engine that does not provide a lease TTL in its<br />response. The value should be within the secret engine's configured ttl or<br />max_ttl. The source secret's lease duration takes precedence over this<br />configuration when it is greater than 0. | | Pattern: `^([0-9]+(\.[0-9]+)?(s\|m\|h))$` <br />Type: string <br /> |




#### VaultDynamicSecretSyncConfig



VaultDynamicSecretSyncConfig configures sync behavior from Vault to VSO



_Appears in:_
- [VaultDynamicSecretSpec](#vaultdynamicsecretspec)

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `instantUpdates` _boolean_ | InstantUpdates is a flag to indicate that event-driven updates are<br />enabled for this VaultDynamicSecret | | |


#### VaultPKISecret


Expand Down