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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 1.4.0 (May 5th, 2026)
## Unreleased

Enhancements:
* VDS: instant updates for `database` and `ldap` static-roles via Vault event notifications. Set `spec.syncConfig.instantUpdates: true` and `spec.syncConfig.engineType: database|ldap` on a `VaultDynamicSecret` to subscribe to rotation events on the configured Vault mount. Requires Vault Enterprise (>= 1.16 for database, >= 1.21 for ldap) and a VaultAuth role with `read` on `sys/events/subscribe/<engine>/*` plus `list`+`subscribe`+`subscribe_event_types = ["*"]` on the secret path.

## 1.4.0 (May 5th, 2026)
Fix:
* Detect TTL reset for uneven rotation schedules with ttl rollover bug: ([#1259](https://github.com/hashicorp/vault-secrets-operator/pull/1259))
* Update kube-rbac-proxy version for openshift: ([#1254](https://github.com/hashicorp/vault-secrets-operator/pull/1254))
Expand Down
25 changes: 25 additions & 0 deletions api/v1beta1/vaultdynamicsecret_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,31 @@ type VaultDynamicSecretSpec struct {
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(s|m|h))$`
RefreshAfter string `json:"refreshAfter,omitempty"`
// SyncConfig configures sync behavior from Vault to VSO. When
// SyncConfig.InstantUpdates is true, EngineType MUST be set to one of
// "database" or "ldap"; the operator subscribes to the corresponding Vault
// event stream and triggers a reconcile on rotation events that match this
// resource's mount and role name. Requires Vault Enterprise >= 1.16
// (database) or >= 1.21 (ldap).
SyncConfig *VaultDynamicSecretSyncConfig `json:"syncConfig,omitempty"`
}

// VaultDynamicSecretSyncConfig configures sync behavior from Vault to VSO for
// a VaultDynamicSecret. It is the dynamic-secret counterpart of
// VaultStaticSecret.Spec.SyncConfig.
type VaultDynamicSecretSyncConfig struct {
// InstantUpdates enables event-driven updates for this VaultDynamicSecret.
// Requires Vault Enterprise >= 1.16 (database) or >= 1.21 (ldap), and a
// VaultAuth role with read on sys/events/subscribe/<engineType>/* and
// list+subscribe on the secret path with subscribe_event_types = ["*"].
// +kubebuilder:default=false
InstantUpdates bool `json:"instantUpdates,omitempty"`
// EngineType declares which Vault secrets-engine plugin produces the events
// VSO should subscribe to. Required when InstantUpdates is true. The
// selection is intentionally restricted to engines that publish rotation
// events suitable for instant updates.
// +kubebuilder:validation:Enum={database,ldap}
EngineType string `json:"engineType,omitempty"`
}

// VaultDynamicSecretStatus defines the observed state of VaultDynamicSecret
Expand Down
51 changes: 51 additions & 0 deletions api/v1beta1/vaultdynamicsecret_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package v1beta1

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestVaultDynamicSecret_DeepCopy_SyncConfig(t *testing.T) {
t.Run("nil-syncconfig", func(t *testing.T) {
o := &VaultDynamicSecret{
Spec: VaultDynamicSecretSpec{
Mount: "database",
Path: "static-creds/myrole",
},
}
c := o.DeepCopy()
require.NotNil(t, c)
assert.Nil(t, c.Spec.SyncConfig)
})

t.Run("populated-syncconfig", func(t *testing.T) {
o := &VaultDynamicSecret{
Spec: VaultDynamicSecretSpec{
Mount: "database",
Path: "static-creds/myrole",
SyncConfig: &VaultDynamicSecretSyncConfig{
InstantUpdates: true,
EngineType: "database",
},
},
}
c := o.DeepCopy()
require.NotNil(t, c)
require.NotNil(t, c.Spec.SyncConfig)
assert.NotSame(t, o.Spec.SyncConfig, c.Spec.SyncConfig)
assert.Equal(t, o.Spec.SyncConfig.InstantUpdates, c.Spec.SyncConfig.InstantUpdates)
assert.Equal(t, o.Spec.SyncConfig.EngineType, c.Spec.SyncConfig.EngineType)

c.Spec.SyncConfig.InstantUpdates = false
c.Spec.SyncConfig.EngineType = "ldap"
assert.True(t, o.Spec.SyncConfig.InstantUpdates,
"original mutated when copy was modified")
assert.Equal(t, "database", o.Spec.SyncConfig.EngineType,
"original engineType mutated when copy was modified")
})
}
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.

28 changes: 28 additions & 0 deletions chart/crds/secrets.hashicorp.com_vaultdynamicsecrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,34 @@ spec:
- name
type: object
type: array
syncConfig:
description: |-
SyncConfig configures sync behavior from Vault to VSO. When
SyncConfig.InstantUpdates is true, EngineType MUST be set to one of
"database" or "ldap"; the operator subscribes to the corresponding Vault
event stream and triggers a reconcile on rotation events that match this
resource's mount and role name. Requires Vault Enterprise >= 1.16
(database) or >= 1.21 (ldap).
properties:
engineType:
description: |-
EngineType declares which Vault secrets-engine plugin produces the events
VSO should subscribe to. Required when InstantUpdates is true. The
selection is intentionally restricted to engines that publish rotation
events suitable for instant updates.
enum:
- database
- ldap
type: string
instantUpdates:
default: false
description: |-
InstantUpdates enables event-driven updates for this VaultDynamicSecret.
Requires Vault Enterprise >= 1.16 (database) or >= 1.21 (ldap), and a
VaultAuth role with read on sys/events/subscribe/<engineType>/* and
list+subscribe on the secret path with subscribe_event_types = ["*"].
type: boolean
type: object
vaultAuthRef:
description: |-
VaultAuthRef to the VaultAuth resource, can be prefixed with a namespace,
Expand Down
28 changes: 28 additions & 0 deletions config/crd/bases/secrets.hashicorp.com_vaultdynamicsecrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,34 @@ spec:
- name
type: object
type: array
syncConfig:
description: |-
SyncConfig configures sync behavior from Vault to VSO. When
SyncConfig.InstantUpdates is true, EngineType MUST be set to one of
"database" or "ldap"; the operator subscribes to the corresponding Vault
event stream and triggers a reconcile on rotation events that match this
resource's mount and role name. Requires Vault Enterprise >= 1.16
(database) or >= 1.21 (ldap).
properties:
engineType:
description: |-
EngineType declares which Vault secrets-engine plugin produces the events
VSO should subscribe to. Required when InstantUpdates is true. The
selection is intentionally restricted to engines that publish rotation
events suitable for instant updates.
enum:
- database
- ldap
type: string
instantUpdates:
default: false
description: |-
InstantUpdates enables event-driven updates for this VaultDynamicSecret.
Requires Vault Enterprise >= 1.16 (database) or >= 1.21 (ldap), and a
VaultAuth role with read on sys/events/subscribe/<engineType>/* and
list+subscribe on the secret path with subscribe_event_types = ["*"].
type: boolean
type: object
vaultAuthRef:
description: |-
VaultAuthRef to the VaultAuth resource, can be prefixed with a namespace,
Expand Down
1 change: 1 addition & 0 deletions config/samples/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ resources:
- secrets_v1beta1_vaultauth.yaml
- secrets_v1beta1_vaultconnection.yaml
- secrets_v1beta1_vaultdynamicsecret.yaml
- secrets_v1beta1_vaultdynamicsecret_instant_updates.yaml
- secrets_v1beta1_hcpvaultsecretsapp.yaml
- secrets_v1beta1_hcpauth.yaml
- secrets_v1beta1_secrettransformation.yaml
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
labels:
app.kubernetes.io/name: vaultdynamicsecret
app.kubernetes.io/instance: vaultdynamicsecret-instant-updates
app.kubernetes.io/part-of: vault-secrets-operator
name: vaultdynamicsecret-instant-updates
spec:
vaultAuthRef: example
mount: database
path: static-creds/myrole
allowStaticCreds: true
destination:
name: app-db-credentials
create: true
syncConfig:
instantUpdates: true
engineType: database
3 changes: 3 additions & 0 deletions consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ const (

AnnotationResync = "vso.hashicorp.com/resync"
HeaderUserAgent = "User-Agent"

VaultEngineTypeDatabase = "database"
VaultEngineTypeLDAP = "ldap"
)
1 change: 1 addition & 0 deletions consts/reasons.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
ReasonHVSClientConfigError = "HVSClientConfigError"
ReasonVaultClientError = "VaultClientError"
ReasonVaultStaticSecret = "VaultStaticSecretError"
ReasonVaultDynamicSecret = "VaultDynamicSecretError"
ReasonHVSSecret = "HVSSecretError"
ReasonSecretDataDrift = "SecretDataDrift"
ReasonInexistentDestination = "InexistentDestination"
Expand Down
Loading
Loading