You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The vault KMS type supports server certificate verification via vaultCAFromSecret
but has no way to present a client certificate. We are requesting support for vaultClientCertFromSecret and vaultClientCertKeyFromSecret config options,
using the same K8s Secret lookup mechanism already implemented in the vaulttokens
type.
A reference implementation is available in PR #6375.
Background and motivation
We run Rook-Ceph for persistent storage with LUKS encryption. We are upgrading
from Rook v1.16.6 to v1.19.6. Starting in versions after 1.16, the CSI node
plugin DaemonSet enforces hostNetwork: true (required to avoid disrupting existing
mounts on pod restart). This is a hard constraint we cannot change.
Our cluster runs Istio with PeerAuthentication: STRICT enforced globally as a corporate security requirement — all in-cluster service-to-service communication
must use mTLS. No exceptions are permitted.
With hostNetwork: true, Istio cannot inject a sidecar into the CSI node plugin pod.
Without a sidecar, the node plugin's outbound connections bypass Istio's transparent
mTLS entirely. This means the node plugin can no longer reach our encryption key
service in a compliant way.
Our setup
We use a custom vault-compatible API to store LUKS volume encryption keys in our
corporate credential store. It exposes a Vault-compatible KV API
(/v1/auth/kubernetes/login, GET/PUT/DELETE /v1/<mount>/<path>/<handle>) backed
by our corporate secret management infrastructure rather than HashiCorp Vault itself.
The key-manager sits behind Istio's service mesh with PeerAuthentication: STRICT
and an AuthorizationPolicy that permits access only from the CSI plugin service
accounts. Prior to the upgrade, the CSI node plugin ran with hostNetwork: false
(possible in Rook v1.16.6) and received an Istio sidecar automatically, so
transparent mTLS was handled without any application-level configuration.
KMS configuration before the upgrade (working, Rook v1.16.6)
With hostNetwork: false and Istio sidecar injection, this worked without any TLS
configuration in the KMS config — Istio handled mTLS transparently at the network layer.
The problem after the upgrade (Rook v1.19.6)
With hostNetwork: true enforced:
Istio sidecar injection is not possible on the node plugin pods
The key-manager's PeerAuthentication: STRICT rejects plain HTTP connections
The corporate security policy does not allow relaxing mTLS to PERMISSIVE
The only viable path is application-level mTLS: the key-manager listens on a
dedicated TLS port (bypassed from Istio's iptables interception), and the CSI plugin
connects using a client certificate issued by the cluster's Istio CA.
Why the vault type specifically
We need to keep using the vault KMS type (Kubernetes SA token authentication via /v1/auth/kubernetes/login) rather than switching to vaulttokens. The vaulttokens
type requires a static Vault token in a Kubernetes Secret in each tenant namespace
(the PVC owner's namespace). Our cluster creates thousands of workspace namespaces
dynamically; managing a token Secret in every namespace is operationally impractical.
The vault type uses the CSI plugin's Kubernetes service account JWT for
authentication — no per-namespace Secrets required.
What already works in vaulttokens
The vaulttokens and vaulttenantsa types already support mTLS client certificates
via vaultClientCertFromSecret and vaultClientCertKeyFromSecret. These config
values are Kubernetes Secret names; the implementation calls k8s.GetSecret() to
read the PEM-encoded certificate and key from named fields ("cert" and "key")
within those Secrets, writes them to temp files, and passes them to the Vault SDK via api.EnvVaultClientCert / api.EnvVaultClientKey.
The same mechanism already handles vaultCAFromSecret in the vaulttokens type.
Requested change
Extend vaultConnection.initCertificates() in internal/kms/vault.go to support
the same three config options as vaulttokens, using k8s.GetSecret() for all three:
Config key
Type
Description
vaultCAFromSecret
K8s Secret name
Already present in vault; reads field "cert"
vaultClientCertFromSecret
K8s Secret name
New: reads field "cert" from named Secret
vaultClientCertKeyFromSecret
K8s Secret name
New: reads field "key" from named Secret
Secret lookup namespace: the CSI pod namespace (POD_NAMESPACE env var), consistent
with the fallback behaviour in vaulttokens.
Note on vaultCAFromSecret in vault today: the current vault implementation
reads vaultCAFromSecret as a key name inside args.Secrets (the node-stage secret
map from the StorageClass) rather than as a K8s Secret name. For consistency with vaulttokens and to allow all three cert options to be configured uniformly, the
implementation should also migrate vaultCAFromSecret to use k8s.GetSecret(). This
is a behaviour change for existing users of vaultCAFromSecret in the vault type
and should be clearly documented in the changelog.
istio-ca-root-cert-secret is a K8s Secret in rook-ceph containing the Istio
cluster CA root certificate under field "cert"
csi-rbdplugin-vault-client-tls is a K8s Secret in rook-ceph containing the
client certificate under field "cert" and private key under field "key",
issued by the Istio CA with SPIFFE URI SAN spiffe://cluster.local/ns/rook-ceph/sa/rook-csi-rbd-plugin-sa
Alternatives considered
vaulttokens with per-namespace token Secrets — rejected: thousands of
dynamic workspace namespaces make per-namespace Secret management impractical
Relaxing PeerAuthentication to PERMISSIVE — rejected: corporate
security policy requires STRICT mTLS for all in-cluster communication
Server-only TLS (no client cert) — rejected: corporate policy requires
mutual TLS, not one-way TLS
Implementation notes
The change is contained entirely within internal/kms/vault.go:
vaultConnection.initCertificates(): add vaultClientCertFromSecret and vaultClientCertKeyFromSecret handling using k8s.GetSecret()
vaultConnection.Destroy(): extend to also remove client cert and key temp files
No changes needed to ProviderInitArgs, GetKMS, callers, or vendor files. api.EnvVaultClientCert and api.EnvVaultClientKey are already defined in the
vendored Vault SDK (vendor/github.com/hashicorp/vault/api/client.go) and already
consumed by ConfigureTLS().
Feature Request
Summary
The
vaultKMS type supports server certificate verification viavaultCAFromSecretbut has no way to present a client certificate. We are requesting support for
vaultClientCertFromSecretandvaultClientCertKeyFromSecretconfig options,using the same K8s Secret lookup mechanism already implemented in the
vaulttokenstype.
A reference implementation is available in PR #6375.
Background and motivation
We run Rook-Ceph for persistent storage with LUKS encryption. We are upgrading
from Rook v1.16.6 to v1.19.6. Starting in versions after 1.16, the CSI node
plugin DaemonSet enforces
hostNetwork: true(required to avoid disrupting existingmounts on pod restart). This is a hard constraint we cannot change.
Our cluster runs Istio with
PeerAuthentication: STRICTenforced globally as acorporate security requirement — all in-cluster service-to-service communication
must use mTLS. No exceptions are permitted.
With
hostNetwork: true, Istio cannot inject a sidecar into the CSI node plugin pod.Without a sidecar, the node plugin's outbound connections bypass Istio's transparent
mTLS entirely. This means the node plugin can no longer reach our encryption key
service in a compliant way.
Our setup
We use a custom vault-compatible API to store LUKS volume encryption keys in our
corporate credential store. It exposes a Vault-compatible KV API
(
/v1/auth/kubernetes/login,GET/PUT/DELETE /v1/<mount>/<path>/<handle>) backedby our corporate secret management infrastructure rather than HashiCorp Vault itself.
The key-manager sits behind Istio's service mesh with
PeerAuthentication: STRICTand an
AuthorizationPolicythat permits access only from the CSI plugin serviceaccounts. Prior to the upgrade, the CSI node plugin ran with
hostNetwork: false(possible in Rook v1.16.6) and received an Istio sidecar automatically, so
transparent mTLS was handled without any application-level configuration.
KMS configuration before the upgrade (working, Rook v1.16.6)
{ "appstudio-cmk": { "encryptionKMSType": "vault", "vaultAddress": "http://key-manager.webide-system.svc.cluster.local:80", "vaultAuthPath": "/v1/auth/kubernetes/login", "vaultBackend": "kv", "vaultPassphraseRoot": "/v1/ceph-csi-overlay", "vaultPassphrasePath": "ceph-csi/", "vaultRole": "ceph-csi" } }With
hostNetwork: falseand Istio sidecar injection, this worked without any TLSconfiguration in the KMS config — Istio handled mTLS transparently at the network layer.
The problem after the upgrade (Rook v1.19.6)
With
hostNetwork: trueenforced:PeerAuthentication: STRICTrejects plain HTTP connectionsPERMISSIVEThe only viable path is application-level mTLS: the key-manager listens on a
dedicated TLS port (bypassed from Istio's iptables interception), and the CSI plugin
connects using a client certificate issued by the cluster's Istio CA.
Why the
vaulttype specificallyWe need to keep using the
vaultKMS type (Kubernetes SA token authentication via/v1/auth/kubernetes/login) rather than switching tovaulttokens. Thevaulttokenstype requires a static Vault token in a Kubernetes Secret in each tenant namespace
(the PVC owner's namespace). Our cluster creates thousands of workspace namespaces
dynamically; managing a token Secret in every namespace is operationally impractical.
The
vaulttype uses the CSI plugin's Kubernetes service account JWT forauthentication — no per-namespace Secrets required.
What already works in
vaulttokensThe
vaulttokensandvaulttenantsatypes already support mTLS client certificatesvia
vaultClientCertFromSecretandvaultClientCertKeyFromSecret. These configvalues are Kubernetes Secret names; the implementation calls
k8s.GetSecret()toread the PEM-encoded certificate and key from named fields (
"cert"and"key")within those Secrets, writes them to temp files, and passes them to the Vault SDK via
api.EnvVaultClientCert/api.EnvVaultClientKey.The same mechanism already handles
vaultCAFromSecretin thevaulttokenstype.Requested change
Extend
vaultConnection.initCertificates()ininternal/kms/vault.goto supportthe same three config options as
vaulttokens, usingk8s.GetSecret()for all three:vaultCAFromSecretvault; reads field"cert"vaultClientCertFromSecret"cert"from named SecretvaultClientCertKeyFromSecret"key"from named SecretSecret lookup namespace: the CSI pod namespace (
POD_NAMESPACEenv var), consistentwith the fallback behaviour in
vaulttokens.Note on
vaultCAFromSecretinvaulttoday: the currentvaultimplementationreads
vaultCAFromSecretas a key name insideargs.Secrets(the node-stage secretmap from the StorageClass) rather than as a K8s Secret name. For consistency with
vaulttokensand to allow all three cert options to be configured uniformly, theimplementation should also migrate
vaultCAFromSecretto usek8s.GetSecret(). Thisis a behaviour change for existing users of
vaultCAFromSecretin thevaulttypeand should be clearly documented in the changelog.
Desired KMS configuration after the change
{ "appstudio-cmk": { "encryptionKMSType": "vault", "vaultAddress": "https://key-manager.webide-system.svc.cluster.local:8443", "vaultAuthPath": "/v1/auth/kubernetes/login", "vaultBackend": "kv", "vaultPassphraseRoot": "/v1/ceph-csi-overlay", "vaultPassphrasePath": "ceph-csi/", "vaultRole": "ceph-csi", "vaultCAFromSecret": "istio-ca-root-cert-secret", "vaultClientCertFromSecret": "csi-rbdplugin-vault-client-tls", "vaultClientCertKeyFromSecret": "csi-rbdplugin-vault-client-tls" } }Where:
istio-ca-root-cert-secretis a K8s Secret inrook-cephcontaining the Istiocluster CA root certificate under field
"cert"csi-rbdplugin-vault-client-tlsis a K8s Secret inrook-cephcontaining theclient certificate under field
"cert"and private key under field"key",issued by the Istio CA with SPIFFE URI SAN
spiffe://cluster.local/ns/rook-ceph/sa/rook-csi-rbd-plugin-saAlternatives considered
vaulttokenswith per-namespace token Secrets — rejected: thousands ofdynamic workspace namespaces make per-namespace Secret management impractical
hostNetwork: false— rejected: upstream enforceshostNetwork: truein Rook v1.17+ to prevent disrupting existing mounts on node plugin restart
(see feat: make nodePlugin hostNetwork configurable ceph-csi-operator#520)
PeerAuthenticationtoPERMISSIVE— rejected: corporatesecurity policy requires STRICT mTLS for all in-cluster communication
mutual TLS, not one-way TLS
Implementation notes
The change is contained entirely within
internal/kms/vault.go:vaultConnection.initCertificates(): addvaultClientCertFromSecretandvaultClientCertKeyFromSecrethandling usingk8s.GetSecret()vaultConnection.Destroy(): extend to also remove client cert and key temp filesNo changes needed to
ProviderInitArgs,GetKMS, callers, or vendor files.api.EnvVaultClientCertandapi.EnvVaultClientKeyare already defined in thevendored Vault SDK (
vendor/github.com/hashicorp/vault/api/client.go) and alreadyconsumed by
ConfigureTLS().Reference implementation: PR #6375