From 0eed4da25d10472fc103e205a500b8b44df1839e Mon Sep 17 00:00:00 2001 From: Thibault VINCENT Date: Fri, 15 May 2026 15:30:47 +0200 Subject: [PATCH] feat(chart, docs): expose jks.passphraseSecretRef in schema + examples --- chart/README.md | 21 +++++++++++-------- chart/README.md.gotmpl | 19 ++++++++++------- chart/values.schema.json | 19 +++++++++++++++++ chart/values.yaml | 16 ++++++++++---- docs/examples/secrets-tuned.values.yaml | 18 ++++++++++++++++ ...ypes-jks-secretref-missing-name.expect.txt | 2 ++ ...ecrettypes-jks-secretref-missing-name.yaml | 8 +++++++ ...s-passphrasesecretref-cross-namespace.yaml | 13 ++++++++++++ 8 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 test/schema/invalid/secrettypes-jks-secretref-missing-name.expect.txt create mode 100644 test/schema/invalid/secrettypes-jks-secretref-missing-name.yaml create mode 100644 test/schema/valid/secrettypes-jks-passphrasesecretref-cross-namespace.yaml diff --git a/chart/README.md b/chart/README.md index c213054..08bf57f 100644 --- a/chart/README.md +++ b/chart/README.md @@ -66,8 +66,9 @@ cluster it observes, but equally happy as a standalone binary. sibling key in the same Secret, an external file, a cross-namespace Secret reference, or none (`tryEmptyPassphrase`). - **JKS / JCEKS** keystores and truststores — magic-byte auto-detection - between JKS and JCEKS; passphrase from a sibling key, external file, - or `tryEmptyPassphrase`. + between JKS and JCEKS; passphrase from a sibling key in the same Secret, + a separate Secret in any namespace, an external file, or none + (`tryEmptyPassphrase`). - **Certificate Revocation Lists** — `X509 CRL` PEM blocks (intermixed freely with `CERTIFICATE` blocks) and raw DER `*.crl` files (`format: der`) are parsed into the dedicated `x509_crl_*` family so @@ -169,15 +170,17 @@ The default install runs a single Deployment watching `kubernetes.io/tls` Secrets across all namespaces — disable with `secretsExporter.enabled: false`. -### Multiple Secret types and PKCS#12 +### Multiple Secret types, PKCS#12 and JKS `secretsExporter.secretTypes` accepts any mix of types and keys: a literal `key` (regex `^$` is built for you) or a `keyPatterns` -list for full regex control, with optional `format: pkcs12` plus a -`pkcs12:` block. Passphrases are pulled from a sibling Secret key -(`pkcs12.passphraseKey`), an external file, a cross-namespace Secret -ref, or skipped entirely with `tryEmptyPassphrase: true` for -passwordless keystores. +list for full regex control, with optional `format: pkcs12` or +`format: jks` plus a matching `pkcs12:` / `jks:` block. Both blocks +expose the same passphrase sources: a sibling Secret key +(`passphraseKey`), a separate Secret in any namespace +(`passphraseSecretRef`), an external file (`passphraseFile`), or +skipped entirely with `tryEmptyPassphrase: true` for passwordless +keystores. ### Watching ConfigMaps @@ -313,7 +316,7 @@ exporter-toolkit is the recommended path on new installs. | secretsExporter.securityContext | object | see `values.yaml` | SecurityContext for containers of the TLS Secrets exporter | | secretsExporter.extraVolumes | list | `[]` | Additional volumes added to Pods of the TLS Secrets exporter (combined with global `extraVolumes`) | | secretsExporter.extraVolumeMounts | list | `[]` | Additional volume mounts added to Pod containers of the TLS Secrets exporter (combined with global `extraVolumeMounts`) | -| secretsExporter.secretTypes | list | see `values.yaml` | Which type of Secrets should be watched. Each entry takes either `key` (a single Secret data key — the matching regex `^$` is built for you) or `keyPatterns` (a list of regexes, full control). Optional `format` is `pem` (default), `pkcs12`, `der`, or `jks` (Java KeyStore); `pkcs12` block accepts `passphrase`, `passphraseKey` (read passphrase from a sibling key in the same Secret), `passphraseFile`, `passphraseSecretRef`, `tryEmptyPassphrase`; `jks` block accepts the same fields except `passphraseSecretRef`. | +| secretsExporter.secretTypes | list | see `values.yaml` | Which type of Secrets should be watched. Each entry takes either `key` (a single Secret data key — the matching regex `^$` is built for you) or `keyPatterns` (a list of regexes, full control). Optional `format` is `pem` (default), `pkcs12`, `der`, or `jks` (Java KeyStore); `pkcs12` and `jks` blocks accept the same passphrase options: `passphrase`, `passphraseKey` (read passphrase from a sibling key in the same Secret), `passphraseFile`, `passphraseSecretRef` (read from a separate Secret, optionally in another namespace), `tryEmptyPassphrase`. | | secretsExporter.configMapKeys | list | see `values.yaml` | If the exporter should watch for certificates in ConfigMaps, just specify the keys it needs to watch. E.g.: `configMapKeys: ["tls.crt"]` | | secretsExporter.includeNamespaces | list | `[]` | Restrict the list of namespaces the TLS Secrets exporter should scan for certificates to watch (all namespaces if empty). Each entry is a shell-glob pattern (`*`, `?`, `[abc]`) or a literal name — e.g. `team-*` matches `team-alpha` and `team-beta`. | | secretsExporter.excludeNamespaces | list | `[]` | Exclude namespaces from being scanned by the TLS Secrets exporter (evaluated after `includeNamespaces`). Same shell-glob syntax as `includeNamespaces`. | diff --git a/chart/README.md.gotmpl b/chart/README.md.gotmpl index f98e5df..10d8bba 100644 --- a/chart/README.md.gotmpl +++ b/chart/README.md.gotmpl @@ -66,8 +66,9 @@ cluster it observes, but equally happy as a standalone binary. sibling key in the same Secret, an external file, a cross-namespace Secret reference, or none (`tryEmptyPassphrase`). - **JKS / JCEKS** keystores and truststores — magic-byte auto-detection - between JKS and JCEKS; passphrase from a sibling key, external file, - or `tryEmptyPassphrase`. + between JKS and JCEKS; passphrase from a sibling key in the same Secret, + a separate Secret in any namespace, an external file, or none + (`tryEmptyPassphrase`). - **Certificate Revocation Lists** — `X509 CRL` PEM blocks (intermixed freely with `CERTIFICATE` blocks) and raw DER `*.crl` files (`format: der`) are parsed into the dedicated `x509_crl_*` family so @@ -169,15 +170,17 @@ The default install runs a single Deployment watching `kubernetes.io/tls` Secrets across all namespaces — disable with `secretsExporter.enabled: false`. -### Multiple Secret types and PKCS#12 +### Multiple Secret types, PKCS#12 and JKS `secretsExporter.secretTypes` accepts any mix of types and keys: a literal `key` (regex `^$` is built for you) or a `keyPatterns` -list for full regex control, with optional `format: pkcs12` plus a -`pkcs12:` block. Passphrases are pulled from a sibling Secret key -(`pkcs12.passphraseKey`), an external file, a cross-namespace Secret -ref, or skipped entirely with `tryEmptyPassphrase: true` for -passwordless keystores. +list for full regex control, with optional `format: pkcs12` or +`format: jks` plus a matching `pkcs12:` / `jks:` block. Both blocks +expose the same passphrase sources: a sibling Secret key +(`passphraseKey`), a separate Secret in any namespace +(`passphraseSecretRef`), an external file (`passphraseFile`), or +skipped entirely with `tryEmptyPassphrase: true` for passwordless +keystores. ### Watching ConfigMaps diff --git a/chart/values.schema.json b/chart/values.schema.json index aacf79c..58b151f 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -1200,6 +1200,25 @@ "passphraseKey": { "type": "string" }, + "passphraseSecretRef": { + "additionalProperties": false, + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + } + }, + "required": [ + "name", + "key" + ], + "type": "object" + }, "tryEmptyPassphrase": { "type": "boolean" } diff --git a/chart/values.yaml b/chart/values.yaml index e495e65..9d9060a 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -289,6 +289,14 @@ secretsExporter: # passphrase: {type: string} # passphraseKey: {type: string} # passphraseFile: {type: string} + # passphraseSecretRef: + # type: object + # additionalProperties: false + # required: [name, key] + # properties: + # namespace: {type: string} + # name: {type: string} + # key: {type: string} # tryEmptyPassphrase: {type: boolean} # oneOf: # - required: [key] @@ -298,10 +306,10 @@ secretsExporter: # (a single Secret data key — the matching regex `^$` is built for # you) or `keyPatterns` (a list of regexes, full control). Optional # `format` is `pem` (default), `pkcs12`, `der`, or `jks` (Java KeyStore); - # `pkcs12` block accepts `passphrase`, `passphraseKey` (read passphrase from - # a sibling key in the same Secret), `passphraseFile`, `passphraseSecretRef`, - # `tryEmptyPassphrase`; `jks` block accepts the same fields except - # `passphraseSecretRef`. + # `pkcs12` and `jks` blocks accept the same passphrase options: + # `passphrase`, `passphraseKey` (read passphrase from a sibling key in + # the same Secret), `passphraseFile`, `passphraseSecretRef` (read from a + # separate Secret, optionally in another namespace), `tryEmptyPassphrase`. # @default -- see `values.yaml` secretTypes: - type: kubernetes.io/tls diff --git a/docs/examples/secrets-tuned.values.yaml b/docs/examples/secrets-tuned.values.yaml index bdf453e..1efdde7 100644 --- a/docs/examples/secrets-tuned.values.yaml +++ b/docs/examples/secrets-tuned.values.yaml @@ -103,6 +103,24 @@ secretsExporter: pkcs12: tryEmptyPassphrase: true + # JKS truststore — sibling passphrase key, same semantics as PKCS#12. + - type: Opaque + key: truststore.jks + format: jks + jks: + passphraseKey: jks-passphrase + + # JKS with passphrase centrally managed (mirrors the PKCS#12 vault + # pattern above — `jks.passphraseSecretRef` accepts the same shape). + - type: Opaque + key: vault.jks + format: jks + jks: + passphraseSecretRef: + namespace: secrets-jks-vault + name: shared-jks-passphrase + key: passphrase + # ConfigMaps holding PEM material (e.g. cluster-issuer CA bundles) configMapKeys: - tls.crt diff --git a/test/schema/invalid/secrettypes-jks-secretref-missing-name.expect.txt b/test/schema/invalid/secrettypes-jks-secretref-missing-name.expect.txt new file mode 100644 index 0000000..4314faf --- /dev/null +++ b/test/schema/invalid/secrettypes-jks-secretref-missing-name.expect.txt @@ -0,0 +1,2 @@ +at '/secretsExporter/secretTypes/0 +passphraseSecretRef diff --git a/test/schema/invalid/secrettypes-jks-secretref-missing-name.yaml b/test/schema/invalid/secrettypes-jks-secretref-missing-name.yaml new file mode 100644 index 0000000..45b16e2 --- /dev/null +++ b/test/schema/invalid/secrettypes-jks-secretref-missing-name.yaml @@ -0,0 +1,8 @@ +secretsExporter: + secretTypes: + - type: Opaque + key: truststore.jks + format: jks + jks: + passphraseSecretRef: + key: passphrase diff --git a/test/schema/valid/secrettypes-jks-passphrasesecretref-cross-namespace.yaml b/test/schema/valid/secrettypes-jks-passphrasesecretref-cross-namespace.yaml new file mode 100644 index 0000000..41ec672 --- /dev/null +++ b/test/schema/valid/secrettypes-jks-passphrasesecretref-cross-namespace.yaml @@ -0,0 +1,13 @@ +# Mirrors the pkcs12 fixture: `jks.passphraseSecretRef` shape with optional +# `namespace` so a JKS truststore in any workload namespace can reach a +# centralized passphrase vault. +secretsExporter: + secretTypes: + - type: Opaque + key: truststore.jks + format: jks + jks: + passphraseSecretRef: + namespace: vault + name: jks-passphrase + key: passphrase