From ecf6324d6757bb29e080d0729f8d970ce5048466 Mon Sep 17 00:00:00 2001 From: sabsari Date: Mon, 3 Aug 2026 21:21:14 +0900 Subject: [PATCH 01/22] Make the external server's downstream RBAC subject configurable (#899) Replace the hardcoded `User: spire-root` subject with an `externalServerSubject` block (`kind`/`name`/`namespace`) so the downstream RBAC can bind to a User, Group, or ServiceAccount. Defaults preserve the previous behavior. Signed-off-by: sabsari Co-authored-by: Claude Opus 4.8 --- charts/spire/charts/spire-server/README.md | 3 ++ .../spire-server/templates/_helpers.tpl | 19 ++++++++++-- charts/spire/charts/spire-server/values.yaml | 8 +++++ tests/unit/spire_test.go | 31 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index a66b952..290b3fa 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -88,6 +88,9 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | | `kind` | Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. | `statefulset` | | `externalServer` | Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. | `false` | +| `externalServerSubject.kind` | RBAC subject kind the external (nested) server's downstream bindings are granted to. One of "User" (client-certificate identity, the historical default), "Group", or "ServiceAccount" (e.g. for a static-token kubeconfig). Only used when externalServer is true. | `User` | +| `externalServerSubject.name` | Name of the subject. For kind "User" it must match the CN of the client certificate the external server presents; for kind "Group" it is the group name (e.g. a certificate O value); for kind "ServiceAccount" it is the name of the (operator-managed, out-of-band) ServiceAccount. | `spire-root` | +| `externalServerSubject.namespace` | Namespace of the ServiceAccount. Only used when kind is "ServiceAccount"; empty uses the server namespace. | `""` | | `imagePullSecrets` | Pull secrets for images | `[]` | | `nameOverride` | Name override | `""` | | `crNameOverride` | Name override for any custom resources | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index ac56a29..42370b6 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -412,12 +412,27 @@ The code below determines what connection type should be used. {{- default .Values.caSubject.commonName $g }} {{- end }} +{{- define "spire-server.external-server-subject-kind" -}} +{{- $kind := .Values.externalServerSubject.kind | default "User" }} +{{- if not (has $kind (list "User" "Group" "ServiceAccount")) }} +{{- fail (printf "Unknown externalServerSubject.kind: %s (must be \"User\", \"Group\", or \"ServiceAccount\")" $kind) }} +{{- end }} +{{- $kind }} +{{- end }} + {{- define "spire-server.subject" }} subjects: {{- if .Values.externalServer }} +{{- $kind := include "spire-server.external-server-subject-kind" . }} +{{- if eq $kind "ServiceAccount" }} +- kind: ServiceAccount + name: {{ .Values.externalServerSubject.name | quote }} + namespace: {{ .Values.externalServerSubject.namespace | default (include "spire-server.namespace" .) | quote }} +{{- else }} - apiGroup: rbac.authorization.k8s.io - kind: User - name: spire-root + kind: {{ $kind }} + name: {{ .Values.externalServerSubject.name | quote }} +{{- end }} {{- else }} - kind: ServiceAccount name: {{ include "spire-server.serviceAccountName" . }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index a7a24cc..6766dc8 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -26,6 +26,14 @@ kind: statefulset ## @param externalServer Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. externalServer: false +## @param externalServerSubject.kind RBAC subject kind the external (nested) server's downstream bindings are granted to. One of "User" (client-certificate identity, the historical default), "Group", or "ServiceAccount" (e.g. for a static-token kubeconfig). Only used when externalServer is true. +## @param externalServerSubject.name Name of the subject. For kind "User" it must match the CN of the client certificate the external server presents; for kind "Group" it is the group name (e.g. a certificate O value); for kind "ServiceAccount" it is the name of the (operator-managed, out-of-band) ServiceAccount. +## @param externalServerSubject.namespace Namespace of the ServiceAccount. Only used when kind is "ServiceAccount"; empty uses the server namespace. +externalServerSubject: + kind: User + name: spire-root + namespace: "" + ## @param imagePullSecrets [array] Pull secrets for images imagePullSecrets: [] diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 1d3e437..a7248e7 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -288,4 +288,35 @@ spire-server: Expect(objs[serverTmpl]).Should(ContainSubstring("path: clusterb")) }) }) + Describe("spire-server.externalServerSubject", func() { + It("binds the external server's downstream RBAC to a ServiceAccount subject", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + externalServer: true + externalServerSubject: + kind: ServiceAccount + name: spire-external + namespace: spire-ext +`) + Expect(err).Should(Succeed()) + roles := objs["spire/charts/spire-server/templates/roles.yaml"] + Expect(roles).Should(ContainSubstring("kind: ServiceAccount")) + Expect(roles).Should(ContainSubstring(`name: "spire-external"`)) + Expect(roles).Should(ContainSubstring(`namespace: "spire-ext"`)) + }) + It("binds the external server's downstream RBAC to a Group subject", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + externalServer: true + externalServerSubject: + kind: Group + name: spire-admins +`) + Expect(err).Should(Succeed()) + roles := objs["spire/charts/spire-server/templates/roles.yaml"] + Expect(roles).Should(ContainSubstring("apiGroup: rbac.authorization.k8s.io")) + Expect(roles).Should(ContainSubstring("kind: Group")) + Expect(roles).Should(ContainSubstring(`name: "spire-admins"`)) + }) + }) }) From 58dab12e55443661141779293f9266f2b7560633 Mon Sep 17 00:00:00 2001 From: JoelGoh92 <30492251+JoelGoh92@users.noreply.github.com> Date: Wed, 5 Aug 2026 02:00:33 +0800 Subject: [PATCH 02/22] Include filterByClassName setting for controller manager (#905) * Expose filterByClassName setting to restrict the controller manager's ClusterSPIFFEID cache by class name, defaulting to false for backward compatibility. Signed-off-by: Joel Goh * Shorten filterByClassName param descriptions and regenerate README. Signed-off-by: Joel Goh --------- Signed-off-by: Joel Goh Co-authored-by: Joel Goh --- charts/spire/charts/spire-server/README.md | 2 ++ .../spire-server/templates/controller-manager-configmap.yaml | 1 + charts/spire/charts/spire-server/values.yaml | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 290b3fa..c29a43e 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -310,6 +310,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `controllerManager.staticManifestMode` | Flag to configure static mode. Valid options off, internal, and external. If internal, the identities config options will be rendered to an included configmap | `off` | | `controllerManager.className` | specify to use an explicit class name. If empty, it will be automatically set to Release.Namespace-Release.Name to not conflict with other installs, enabling parallel installs. | `""` | | `controllerManager.watchClassless` | specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. | `false` | +| `controllerManager.filterByClassName` | Restrict the ClusterSPIFFEID cache to this controller's className. Only enable after confirming target ClusterSPIFFEIDs already carry the className label, or existing registrations will be deleted. | `false` | | `controllerManager.entryIDPrefixCleanup` | Sets which entry prefixes to remove for migrations. Consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. | `false` | | `controllerManager.addEntryIDPrefix` | If true, prepends the clusterName to the entryID of each entry the controller manager registers. | `true` | | `controllerManager.gcInterval` | How often the SPIRE state is reconciled when the controller is otherwise idle. This impacts how quickly SPIRE state will converge after CRDs are removed or SPIRE state is mutated underneath the controller. Values are in nanoseconds. | `10000000000` | @@ -399,6 +400,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `externalControllerManagers.defaults.reconcile.clusterFederatedTrustDomains` | Enable reconciliation of clusterFederatedTrustDomains from K8s to the SPIRE server | `false` | | `externalControllerManagers.defaults.className` | specify to use an explicit class name. If empty, it will be automatically set to Release.Namespace-Release.Name to not conflict with other installs, enabling parallel installs. | `""` | | `externalControllerManagers.defaults.watchClassless` | specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. | `false` | +| `externalControllerManagers.defaults.filterByClassName` | Restrict the ClusterSPIFFEID cache to this controller's className. Only enable after confirming target ClusterSPIFFEIDs already carry the className label, or existing registrations will be deleted. | `false` | | `externalControllerManagers.defaults.entryIDPrefixCleanup` | consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. | `false` | | `externalControllerManagers.defaults.parentIDTemplate` | The template that is used to register workloads. | `spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | | `externalControllerManagers.defaults.leaderElection.leaseDuration` | Duration that non-leader candidates will wait to force acquire leadership. Increase this in high-load clusters to reduce API server pressure. | `15s` | diff --git a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml index ea85007..f8c40d8 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml @@ -91,6 +91,7 @@ ignoreNamespaces: spireServerSocketPath: "/tmp/spire-server/private/api.sock" className: {{ include "spire-server.controller-manager-class-name" . | quote}} watchClassless: {{ if hasKey .settings "watchClassless" }}{{ .settings.watchClassless | toYaml }}{{ else }}{{ .defaults.watchClassless | toYaml }}{{ end }} +filterByClassName: {{ if hasKey .settings "filterByClassName" }}{{ .settings.filterByClassName | toYaml }}{{ else }}{{ .defaults.filterByClassName | toYaml }}{{ end }} parentIDTemplate: {{ if hasKey .settings "parentIDTemplate" }}{{ .settings.parentIDTemplate | quote }}{{ else }}{{ .defaults.parentIDTemplate | quote }}{{ end }} {{- $reconcile := dict }} {{- if hasKey .settings "reconcile" }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 6766dc8..a5a08ef 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -646,6 +646,8 @@ controllerManager: className: "" ## @param controllerManager.watchClassless specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. watchClassless: false + ## @param controllerManager.filterByClassName Restrict the ClusterSPIFFEID cache to this controller's className. Only enable after confirming target ClusterSPIFFEIDs already carry the className label, or existing registrations will be deleted. + filterByClassName: false ## @param controllerManager.entryIDPrefixCleanup Sets which entry prefixes to remove for migrations. Consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. entryIDPrefixCleanup: false @@ -958,6 +960,8 @@ externalControllerManagers: className: "" ## @param externalControllerManagers.defaults.watchClassless specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. watchClassless: false + ## @param externalControllerManagers.defaults.filterByClassName Restrict the ClusterSPIFFEID cache to this controller's className. Only enable after confirming target ClusterSPIFFEIDs already carry the className label, or existing registrations will be deleted. + filterByClassName: false ## @param externalControllerManagers.defaults.entryIDPrefixCleanup consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. entryIDPrefixCleanup: false ## @param externalControllerManagers.defaults.parentIDTemplate The template that is used to register workloads. From 890ada3e1590849385f9efdc58e7589b05eb2493 Mon Sep 17 00:00:00 2001 From: David Mosyan Date: Tue, 4 Aug 2026 16:36:48 -0400 Subject: [PATCH 03/22] Add externalTrafficPolicy support for spire-server LoadBalancer service (#906) Signed-off-by: David Mosyan Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 1 + charts/spire/charts/spire-server/templates/service.yaml | 3 +++ charts/spire/charts/spire-server/values.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index c29a43e..e7b71c4 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -108,6 +108,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `service.port` | Port for the created service | `443` | | `service.annotations` | Annotations to add to the service object | `{}` | | `service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `service.externalTrafficPolicy` | Traffic policy applied when service.type is LoadBalancer (e.g. "Local" to preserve client source IP). Defaults to "Cluster" when left empty. | `""` | | `configMap.annotations` | Annotations to add to the SPIRE Server ConfigMap | `{}` | | `resources` | Resource requests and limits | `{}` | | `autoscaling.enabled` | Flag to enable autoscaling | `false` | diff --git a/charts/spire/charts/spire-server/templates/service.yaml b/charts/spire/charts/spire-server/templates/service.yaml index 49a3430..8bfb7f7 100644 --- a/charts/spire/charts/spire-server/templates/service.yaml +++ b/charts/spire/charts/spire-server/templates/service.yaml @@ -15,6 +15,9 @@ spec: {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP }} loadBalancerIP: {{ .Values.service.loadBalancerIP }} {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} ports: - name: grpc port: {{ .Values.service.port }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index a5a08ef..e3fac03 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -90,6 +90,8 @@ service: annotations: {} ## @param service.loadBalancerIP IP address to assign to load balancer (if supported) loadBalancerIP: "" + ## @param service.externalTrafficPolicy Traffic policy applied when service.type is LoadBalancer (e.g. "Local" to preserve client source IP). Defaults to "Cluster" when left empty. + externalTrafficPolicy: "" configMap: ## @param configMap.annotations [object] Annotations to add to the SPIRE Server ConfigMap From 80705999dda3598f0b1ee82cf3e920df48905863 Mon Sep 17 00:00:00 2001 From: savitha-qs <126019623+savitha-qs@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:48:09 -0500 Subject: [PATCH 04/22] feat(spire-server): support x509pop externalPKI ca bundle (#908) * feat(spire-server): support x509pop externalPKI ca bundle Add externalPKI mode support to the x509pop node attestor configuration. Allows operators to configure CA bundles for external PKI-based node attestation via two approaches: - Inline PEM content (chart creates and manages ConfigMap) - Reference to existing ConfigMap with ca-bundle.pem key Includes volume/volumeMount definitions for CA bundle mounting at /run/spire/data/x509pop-ca-bundle.pem and unit tests for both modes. Signed-off-by: Savitha Ganapathi * refactor: simplify x509pop externalPKI template guard logic Remove nested conditional guard for ca_bundle_path rendering. When externalPKI mode is enabled, ca_bundle_path is always rendered; if no CA bundle is provided, SPIRE will fail at startup with a clear error. Drop unit tests pending fix to the unit test framework (which currently has issues loading values from chart, forcing overly-defensive template guards for test compatibility). Tests can be re-added once framework is fixed. Signed-off-by: Savitha Ganapathi * refactor: simplify x509pop volume/volumeMount guard logic Remove nested caBundle existence checks from volume and volumeMount guard conditions. When externalPKI mode is enabled, volume/volumeMount are created; if no CA bundle is provided, SPIRE fails at startup with clear error (missing mount). Signed-off-by: Savitha Ganapathi * refactor: reorder if/with clauses for clarity Move if condition checks to outer scope before entering with blocks. This is more idiomatic Helm pattern and avoids unnecessary context switching if condition fails. Signed-off-by: Savitha Ganapathi * refactor: simplify conditionals to match chart patterns Replace complex toString/eq comparisons with simpler boolean checks that match existing patterns in the chart (e.g., federation.tls.certManager.enabled). Changes: - .enabled checks: remove toString wrapping, use simple boolean test - .mode checks: remove toString, use simple eq comparison - .caBundle checks: simplify from 'ne (... | default "") ""' to simple boolean test This aligns with chart conventions and avoids tripping broken unit test framework that struggles with complex conditionals. Signed-off-by: Savitha Ganapathi * test: resurrect x509POP unit tests with simplified conditionals Re-add unit tests for externalPKI mode now that template conditionals have been simplified to match chart patterns. Simplified conditionals should be less fragile with unit test framework. Tests cover: - externalPKI with chart-managed CA bundle (inline) - externalPKI with existing ConfigMap reference Signed-off-by: Savitha Ganapathi * docs: regenerate spire-server README for x509pop caBundle params Updated parameter documentation for nodeAttestor.x509POP section to include new caBundle configuration options (inline bundle and existing ConfigMap reference). Auto-generated documentation based on @param comments in values.yaml. Signed-off-by: Savitha Ganapathi --------- Signed-off-by: Savitha Ganapathi Co-authored-by: Savitha Ganapathi --- charts/spire/charts/spire-server/README.md | 5 +- .../spire-server/templates/configmap.yaml | 7 +++ .../templates/server-resource.yaml | 19 ++++++++ .../templates/x509pop-configmap.yaml | 10 ++++ charts/spire/charts/spire-server/values.yaml | 8 +++- tests/unit/spire_test.go | 46 +++++++++++++++++++ 6 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 charts/spire/charts/spire-server/templates/x509pop-configmap.yaml diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index e7b71c4..55decff 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -501,7 +501,10 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `nodeAttestor.gcpIIT.metadataValueMaxSize` | Sets the maximum metadata value size considered by the plugin for selectors | `0` | | `nodeAttestor.gcpIIT.agentPathTemplate` | A URL path portion format of Agent's SPIFFE ID. Describe in text/template format. | `""` | | `nodeAttestor.x509POP.enabled` | Enable the x509_popg node attestor | `false` | -| `nodeAttestor.x509POP.mode` | What mode to set the plugin to. Currently only spiffe mode is supported | `spiffe` | +| `nodeAttestor.x509POP.mode` | Plugin mode: spiffe (exchange) or externalPKI (enrollment CA bundle) | `spiffe` | +| `nodeAttestor.x509POP.caBundle` | CA bundle for externalPKI mode. Provide inline PEM contents or reference an existing ConfigMap. | | +| `nodeAttestor.x509POP.caBundle.bundle` | PEM CA bundle contents. When set, the chart creates and mounts a ConfigMap. | `""` | +| `nodeAttestor.x509POP.caBundle.existingConfigMap` | Name of a ConfigMap containing a `ca-bundle.pem` key with the PEM CA bundle. | `""` | | `nodeAttestor.x509POP.spiffePrefix` | What prefix to use when mode is spiffe | `/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/` | | `nodeAttestor.x509POP.agentPathTemplate` | Override the default agent path template | `""` | | `nodeAttestor.x509POP.maxIntermediates` | Maximum number of intermediate certificates allowed in the certificate chain | `4` | diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index a029070..63d9f96 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -281,6 +281,12 @@ plugins: {{- if or (eq (.enabled | toString) "true") $root.Values.spireIdentityExchange.enabled }} x509pop: plugin_data: + {{- if eq .mode "externalPKI" }} + mode: external_pki + ca_bundle_path: "/run/spire/data/x509pop-ca-bundle.pem" + max_intermediates: {{ .maxIntermediates }} + max_rsa_key_size: {{ .maxRSAKeySize }} + {{- else }} mode: {{ .mode }} spiffe_prefix: {{ include "spire-server.identity-exchange-spiffe-prefix" $root | quote }} max_intermediates: {{ .maxIntermediates }} @@ -298,6 +304,7 @@ plugins: {{- $cn = printf "/%s" (include "spire-lib.cluster-name" $root) }} {{- end }} agent_path_template: {{ replace "${HELM_ADD_CLUSTER_NAME}" $cn $agentPathTemplate | quote }} + {{- end }} {{- end }} {{- end }} {{- with .Values.nodeAttestor.awsIID }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index fa918b9..1a5b969 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -408,6 +408,14 @@ spec: mountPath: /tmp-direct-hashes {{- end }} {{- end }} + {{- if and .Values.nodeAttestor.x509POP.enabled (eq .Values.nodeAttestor.x509POP.mode "externalPKI") }} + {{- with .Values.nodeAttestor.x509POP }} + - name: x509pop-ca-bundle + mountPath: /run/spire/data/x509pop-ca-bundle.pem + subPath: ca-bundle.pem + readOnly: true + {{- end }} + {{- end }} {{- if or .Values.federation.tls.certManager.enabled .Values.federation.tls.externalSecret.enabled }} - name: bundle-endpoint-tls mountPath: /bundle-endpoint-tls @@ -646,6 +654,17 @@ spec: name: {{ include "spire-server.fullname" . }}-tpm-direct-hash {{- end }} {{- end }} + {{- if and .Values.nodeAttestor.x509POP.enabled (eq .Values.nodeAttestor.x509POP.mode "externalPKI") }} + {{- with .Values.nodeAttestor.x509POP }} + - name: x509pop-ca-bundle + configMap: + {{- if .caBundle.bundle }} + name: {{ $fullname }}-x509pop-ca + {{- else if .caBundle.existingConfigMap }} + name: {{ .caBundle.existingConfigMap }} + {{- end }} + {{- end }} + {{- end }} {{- if .Values.federation.tls.certManager.enabled }} - name: bundle-endpoint-tls secret: diff --git a/charts/spire/charts/spire-server/templates/x509pop-configmap.yaml b/charts/spire/charts/spire-server/templates/x509pop-configmap.yaml new file mode 100644 index 0000000..f98d443 --- /dev/null +++ b/charts/spire/charts/spire-server/templates/x509pop-configmap.yaml @@ -0,0 +1,10 @@ +{{- if and .Values.nodeAttestor.x509POP.enabled .Values.nodeAttestor.x509POP.caBundle.bundle }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "spire-server.fullname" . }}-x509pop-ca + namespace: {{ include "spire-server.namespace" . }} +data: + ca-bundle.pem: | + {{ .Values.nodeAttestor.x509POP.caBundle.bundle | nindent 4 }} +{{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index e3fac03..151019e 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -1239,8 +1239,14 @@ nodeAttestor: x509POP: ## @param nodeAttestor.x509POP.enabled Enable the x509_popg node attestor enabled: false - ## @param nodeAttestor.x509POP.mode What mode to set the plugin to. Currently only spiffe mode is supported + ## @param nodeAttestor.x509POP.mode Plugin mode: spiffe (exchange) or externalPKI (enrollment CA bundle) mode: spiffe + ## @extra nodeAttestor.x509POP.caBundle CA bundle for externalPKI mode. Provide inline PEM contents or reference an existing ConfigMap. + caBundle: + ## @param nodeAttestor.x509POP.caBundle.bundle [nullable] PEM CA bundle contents. When set, the chart creates and mounts a ConfigMap. + bundle: "" + ## @param nodeAttestor.x509POP.caBundle.existingConfigMap [nullable] Name of a ConfigMap containing a `ca-bundle.pem` key with the PEM CA bundle. + existingConfigMap: "" ## @param nodeAttestor.x509POP.spiffePrefix What prefix to use when mode is spiffe spiffePrefix: "/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/" ## @param nodeAttestor.x509POP.agentPathTemplate Override the default agent path template diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index a7248e7..eb6cabf 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -187,6 +187,52 @@ spire-server: Expect(notes).Should(ContainSubstring("Installed")) }) }) + Describe("spire-server.nodeAttestor.x509POP", func() { + It("renders externalPKI mode with chart-managed ca bundle", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + nodeAttestor: + k8sPSAT: + enabled: false + x509POP: + enabled: true + mode: externalPKI + caBundle: + bundle: | + -----BEGIN CERTIFICATE----- + MIIB... + -----END CERTIFICATE----- +`) + Expect(err).Should(Succeed()) + serverCM := objs["spire/charts/spire-server/templates/configmap.yaml"] + Expect(serverCM).Should(ContainSubstring(`"mode": "external_pki"`)) + Expect(serverCM).Should(ContainSubstring(`"ca_bundle_path": "/run/spire/data/x509pop-ca-bundle.pem"`)) + Expect(objs).Should(HaveKey("spire/charts/spire-server/templates/x509pop-configmap.yaml")) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("x509pop-ca-bundle")) + Expect(serverResource).Should(ContainSubstring("/run/spire/data/x509pop-ca-bundle.pem")) + }) + It("renders externalPKI mode with existing ConfigMap reference", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + nodeAttestor: + k8sPSAT: + enabled: false + x509POP: + enabled: true + mode: externalPKI + caBundle: + existingConfigMap: my-enrollment-ca +`) + Expect(err).Should(Succeed()) + serverCM := objs["spire/charts/spire-server/templates/configmap.yaml"] + Expect(serverCM).Should(ContainSubstring(`"mode": "external_pki"`)) + Expect(serverCM).Should(ContainSubstring(`"ca_bundle_path": "/run/spire/data/x509pop-ca-bundle.pem"`)) + Expect(objs["spire/charts/spire-server/templates/x509pop-configmap.yaml"]).ShouldNot(ContainSubstring("kind: ConfigMap")) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("name: my-enrollment-ca")) + }) + }) Describe("spire-server.nodeAttestor.awsIID.verifyOrganization", func() { It("emits verify_organization in server config JSON", func() { objs, err := ValueStringRender(chart, ` From 0666f5668140a2fed6acf79f594029cb56e37eef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 08:40:16 -0700 Subject: [PATCH 05/22] Bump regclient/actions/regctl-installer (#911) Bumps [regclient/actions/regctl-installer](https://github.com/regclient/actions) from 5c882eb04fcca27ebb4f5904e0da01f0780063ea to 78eb729dbdb4ef6480e85ff697b4410e22112583. - [Release notes](https://github.com/regclient/actions/releases) - [Changelog](https://github.com/regclient/actions/blob/main/RELEASE.md) - [Commits](https://github.com/regclient/actions/compare/5c882eb04fcca27ebb4f5904e0da01f0780063ea...78eb729dbdb4ef6480e85ff697b4410e22112583) --- updated-dependencies: - dependency-name: regclient/actions/regctl-installer dependency-version: 78eb729dbdb4ef6480e85ff697b4410e22112583 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-devcontainer-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-devcontainer-image.yaml b/.github/workflows/update-devcontainer-image.yaml index b358406..e9dfca4 100644 --- a/.github/workflows/update-devcontainer-image.yaml +++ b/.github/workflows/update-devcontainer-image.yaml @@ -29,7 +29,7 @@ jobs: with: cosign-release: v2.2.3 - name: Install regctl - uses: regclient/actions/regctl-installer@5c882eb04fcca27ebb4f5904e0da01f0780063ea # main + uses: regclient/actions/regctl-installer@78eb729dbdb4ef6480e85ff697b4410e22112583 # main - name: Log in to GHCR uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: From a481bab3f00a6b73720399b8b6f6c8708bb1229a Mon Sep 17 00:00:00 2001 From: David Mosyan Date: Mon, 10 Aug 2026 15:23:55 -0400 Subject: [PATCH 06/22] Add PodDisruptionBudget support to spire-server (#909) * Add PodDisruptionBudget support to spire-server Signed-off-by: David Mosyan * Revert spire-ha-agent changes and set default pdb value for spire-server Signed-off-by: David Mosyan --------- Signed-off-by: David Mosyan Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 3 +++ .../templates/poddisruptionbudget.yaml | 23 +++++++++++++++++++ charts/spire/charts/spire-server/values.yaml | 9 ++++++++ 3 files changed, 35 insertions(+) create mode 100644 charts/spire/charts/spire-server/templates/poddisruptionbudget.yaml diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 55decff..d02aa30 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -116,6 +116,9 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `autoscaling.maxReplicas` | Maximum replicas for autoscaling | `100` | | `autoscaling.scaleOnSPIREServerOnly` | Flag to only consider the main SPIRE container for autoscaling purposes | `false` | | `autoscaling.targetCPUUtilizationPercentage` | Target CPU utilization that triggers autoscaling | `80` | +| `podDisruptionBudget.enabled` | Flag to enable a PodDisruptionBudget for the SPIRE server pods | `false` | +| `podDisruptionBudget.minAvailable` | Minimum number/percentage of pods that must remain available (mutually exclusive with maxUnavailable) | `""` | +| `podDisruptionBudget.maxUnavailable` | Maximum number/percentage of pods that can be unavailable (mutually exclusive with minAvailable) | `""` | | `nodeSelector` | Select specific nodes to run on (currently only amd64 is supported by Tornjak) | `{}` | | `tolerations` | List of tolerations | `[]` | | `affinity` | List of node affinities | `{}` | diff --git a/charts/spire/charts/spire-server/templates/poddisruptionbudget.yaml b/charts/spire/charts/spire-server/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..443ae38 --- /dev/null +++ b/charts/spire/charts/spire-server/templates/poddisruptionbudget.yaml @@ -0,0 +1,23 @@ +{{- if not .Values.externalServer }} +{{- if .Values.podDisruptionBudget.enabled }} +{{- if and .Values.podDisruptionBudget.minAvailable .Values.podDisruptionBudget.maxUnavailable }} +{{- fail "podDisruptionBudget.minAvailable and podDisruptionBudget.maxUnavailable are mutually exclusive" }} +{{- end }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "spire-server.fullname" . }} + namespace: {{ include "spire-server.namespace" . }} + labels: + {{- include "spire-server.labels" . | nindent 4 }} +spec: + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- else }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable | default (max 1 (sub .Values.replicaCount 1)) }} + {{- end }} + selector: + matchLabels: + {{- include "spire-server.selectorLabels" . | nindent 6 }} +{{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 151019e..175bb41 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -124,6 +124,15 @@ autoscaling: targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 +## @param podDisruptionBudget.enabled Flag to enable a PodDisruptionBudget for the SPIRE server pods +## @param podDisruptionBudget.minAvailable Minimum number/percentage of pods that must remain available (mutually exclusive with maxUnavailable) +## @param podDisruptionBudget.maxUnavailable Maximum number/percentage of pods that can be unavailable (mutually exclusive with minAvailable) +## +podDisruptionBudget: + enabled: false + minAvailable: "" + maxUnavailable: "" + ## @param nodeSelector [object] Select specific nodes to run on (currently only amd64 is supported by Tornjak) nodeSelector: {} From 648e0e45e55a59f744c6209ab4a805f0e82a77f8 Mon Sep 17 00:00:00 2001 From: sabsari Date: Tue, 11 Aug 2026 22:40:05 +0900 Subject: [PATCH 07/22] Add JWT-SVID exec-auth source for kubeConfigs entries (#907) Add jwtSVIDExec as a fourth exactly-one kubeConfigs source: the chart generates an exec-credential kubeconfig that authenticates to an external cluster with short-lived SPIFFE JWT-SVIDs instead of a static credential. Signed-off-by: sabsari Co-authored-by: Claude Opus 4.8 --- charts/spire/charts/spire-server/README.md | 6 ++ .../_controller-manager-container.tpl | 8 +++ .../spire-server/templates/_helpers.tpl | 57 +++++++++++++++++++ .../templates/kubeconfig-secret.yaml | 5 +- .../templates/server-resource.yaml | 23 +++++++- charts/spire/charts/spire-server/values.yaml | 31 +++++++++- tests/unit/spire_test.go | 15 +++++ 7 files changed, 140 insertions(+), 5 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index d02aa30..ba80fab 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -634,5 +634,11 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | | `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:90041f375e30f41aa7e0390075d8a69dc61900771d52fa98d63ee5d03d866a58` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | +| `jwtSVIDExecConfig.image.registry` | The OCI registry to pull the exec credential plugin image from | `ghcr.io` | +| `jwtSVIDExecConfig.image.repository` | The repository within the registry | `spiffe/k8s-spiffe-workload-jwt-exec-auth` | +| `jwtSVIDExecConfig.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `jwtSVIDExecConfig.image.tag` | Overrides the image tag | `0.2.0` | +| `jwtSVIDExecConfig.pluginPath` | The path of the plugin binary inside the plugin image, staged for exec by the kubeConfigs consumers | `/ko-app/cmd` | +| `jwtSVIDExecConfig.spiffeID` | The SPIFFE ID the plugin mints a JWT-SVID for; a full spiffe:// URI, or a "/"-prefixed path expanded with the chart trust domain (e.g. /spire-root); required when any kubeConfigs entry uses jwtSVIDExec | `/spire-root` | | `spireIdentityExchange.enabled` | Enable the server side of the SPIRE Identity Exchange system | `false` | | `spike.enabled` | Enable the server side of SPIKE | `false` | diff --git a/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl b/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl index 177c593..14cc911 100644 --- a/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl +++ b/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl @@ -173,6 +173,14 @@ Auto-generation preserves trailing numbers from cluster names or uses hash for u subPath: {{ . }} readOnly: true {{- end }} + {{- if hasKey . "kubeConfig" }} + {{- $entry := default dict (index .Values.kubeConfigs .kubeConfig) }} + {{- if hasKey $entry "jwtSVIDExec" }} + - name: plugins + mountPath: /plugins + readOnly: true + {{- end }} + {{- end }} - name: spire-controller-manager-tmp mountPath: /tmp subPath: {{ printf "spire-controller-manager%s" .suffix }} diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index 42370b6..e2c0019 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -186,6 +186,63 @@ Name of the chart-generated Secret holding the inline kubeConfigs entries. {{ include "spire-server.fullname" . }}-kubeconfigs {{- end }} +{{/* +Path of the staged jwt-svid exec plugin binary inside the shared plugins volume. Used both as the +init-container copy target and as the exec kubeconfig command, so the two must stay in sync. +*/}} +{{- define "spire-server.jwt-svid-exec-binary-path" -}} +/plugins/jwt-svid-exec +{{- end }} + +{{- define "spire-server.jwt-svid-exec-kubeconfig" -}} +{{- $jwtSVIDExec := .jwtSVIDExec -}} +{{- $root := .root -}} +{{- $spiffeID := $root.Values.jwtSVIDExecConfig.spiffeID -}} +{{- if not $spiffeID -}} +{{- fail "jwtSVIDExecConfig.spiffeID is required when a kubeConfigs entry uses jwtSVIDExec" -}} +{{- end -}} +{{- $chartTD := include "spire-lib.trust-domain" $root -}} +{{- if hasPrefix "/" $spiffeID -}} +{{- $spiffeID = printf "spiffe://%s%s" $chartTD $spiffeID -}} +{{- else if hasPrefix "spiffe://" $spiffeID -}} +{{- $idTD := $spiffeID | trimPrefix "spiffe://" | splitList "/" | first -}} +{{- if ne $idTD $chartTD -}} +{{- fail (printf "jwtSVIDExecConfig.spiffeID trust domain %q must match the chart trust domain %q" $idTD $chartTD) -}} +{{- end -}} +{{- else -}} +{{- fail (printf "jwtSVIDExecConfig.spiffeID %q must be a spiffe:// URI or a path starting with \"/\"" $spiffeID) -}} +{{- end -}} +apiVersion: v1 +kind: Config +clusters: +- name: cluster + cluster: + server: {{ $jwtSVIDExec.server | quote }} + certificate-authority-data: {{ $jwtSVIDExec.certificateAuthorityData | quote }} +users: +- name: spiffe + user: + exec: + apiVersion: client.authentication.k8s.io/v1 + command: {{ include "spire-server.jwt-svid-exec-binary-path" $root }} + interactiveMode: Never + env: + - name: SPIFFE_JWT_SOURCE + value: "server-admin-api" + - name: SPIRE_SERVER_SOCKET + value: "unix:///tmp/spire-server/private/api.sock" + - name: SPIFFE_ID + value: {{ $spiffeID | quote }} + - name: SPIFFE_JWT_AUDIENCE + value: {{ $jwtSVIDExec.audience | default "k8s" | quote }} +contexts: +- name: cluster + context: + cluster: cluster + user: spiffe +current-context: cluster +{{- end }} + {{- define "spire-server.serviceAccountAllowedList" }} {{- $releaseNamespace := include "spire-server.agent-namespace" . }} {{- if ne (len .Values.nodeAttestor.k8sPSAT.serviceAccountAllowList) 0 }} diff --git a/charts/spire/charts/spire-server/templates/kubeconfig-secret.yaml b/charts/spire/charts/spire-server/templates/kubeconfig-secret.yaml index eb87c6e..0a406e9 100644 --- a/charts/spire/charts/spire-server/templates/kubeconfig-secret.yaml +++ b/charts/spire/charts/spire-server/templates/kubeconfig-secret.yaml @@ -6,8 +6,9 @@ {{- if hasKey $value "kubeConfig" }}{{ $present = append $present "kubeConfig" }}{{- end }} {{- if hasKey $value "kubeConfigBase64" }}{{ $present = append $present "kubeConfigBase64" }}{{- end }} {{- if hasKey $value "externalSecret" }}{{ $present = append $present "externalSecret" }}{{- end }} +{{- if hasKey $value "jwtSVIDExec" }}{{ $present = append $present "jwtSVIDExec" }}{{- end }} {{- if ne (len $present) 1 }} -{{- fail (printf "kubeConfigs entry %q must set exactly one of kubeConfig, kubeConfigBase64, or externalSecret (got: %v)" $name $present) }} +{{- fail (printf "kubeConfigs entry %q must set exactly one of kubeConfig, kubeConfigBase64, externalSecret, or jwtSVIDExec (got: %v)" $name $present) }} {{- end }} {{- if hasKey $value "externalSecret" }} {{- if not $value.externalSecret.name }} @@ -30,6 +31,8 @@ data: {{- range $name, $value := $inline }} {{- if hasKey $value "kubeConfig" }} {{ $name }}: {{ $value.kubeConfig | b64enc }} + {{- else if hasKey $value "jwtSVIDExec" }} + {{ $name }}: {{ include "spire-server.jwt-svid-exec-kubeconfig" (dict "jwtSVIDExec" $value.jwtSVIDExec "root" $root) | b64enc }} {{- else }} {{ $name }}: {{ $value.kubeConfigBase64 | nospace }} {{- end }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index 1a5b969..be1a6c7 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -65,7 +65,11 @@ {{- end }} {{- end }} {{- $pluginsToLoad := include "spire-lib.extract_custom_plugin_images" . | fromYamlArray }} -{{- $pluginLoaderNeeded := or .Values.credentialComposer.cel.enabled .Values.spireIdentityExchange.enabled (gt (len $pluginsToLoad) 0) }} +{{- $jwtExecNeeded := false }} +{{- range $name, $value := .Values.kubeConfigs }} +{{- if hasKey $value "jwtSVIDExec" }}{{ $jwtExecNeeded = true }}{{- end }} +{{- end }} +{{- $pluginLoaderNeeded := or .Values.credentialComposer.cel.enabled .Values.spireIdentityExchange.enabled (gt (len $pluginsToLoad) 0) $jwtExecNeeded }} {{- if not .Values.externalServer }} apiVersion: apps/v1 {{- if eq .Values.kind "statefulset" }} @@ -176,6 +180,23 @@ spec: mountPath: /plugins imagePullPolicy: {{ .Values.credentialComposer.spireIdentityExchange.image.pullPolicy }} {{- end }} + {{- if $jwtExecNeeded }} + - name: init-jwt-svid-exec + securityContext: + {{- include "spire-lib.securitycontext" . | nindent 12 }} + image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.jwtSVIDExecConfig.image "global" .Values.global) }} + # Use the previously copied busybox to stage the exec credential plugin binary where the kubeConfigs consumers can fork it. + command: + - /plugins/busybox + - sh + - -ec + - | + /plugins/busybox cp -a {{ .Values.jwtSVIDExecConfig.pluginPath }} {{ include "spire-server.jwt-svid-exec-binary-path" . }} + volumeMounts: + - name: plugins + mountPath: /plugins + imagePullPolicy: {{ .Values.jwtSVIDExecConfig.image.pullPolicy }} + {{- end }} {{- range $idx, $plugin := $pluginsToLoad }} - name: {{ printf "init-plugin-%d" $idx }} securityContext: diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 175bb41..da39180 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -1622,9 +1622,11 @@ tests: tag: latest@sha256:90041f375e30f41aa7e0390075d8a69dc61900771d52fa98d63ee5d03d866a58 ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters -## Each entry sets exactly one of kubeConfig, kubeConfigBase64, or externalSecret. Use externalSecret to -## reference a kubeconfig from an externally-managed Secret instead of embedding it in values; -## entries may reference different Secrets and mix with inline ones. +## Each entry sets exactly one of kubeConfig, kubeConfigBase64, externalSecret, or jwtSVIDExec. Use externalSecret +## to reference a kubeconfig from an externally-managed Secret instead of embedding it in values; entries may +## reference different Secrets and mix with inline ones. Use jwtSVIDExec to have the chart generate an +## exec-credential kubeconfig that authenticates to the target cluster with short-lived SPIFFE JWT-SVIDs fetched +## at call time by the exec plugin (see jwtSVIDExecConfig). kubeConfigs: {} # clustera: # kubeConfig: | @@ -1636,6 +1638,29 @@ kubeConfigs: {} # externalSecret: # name: my-kubeconfigs-secret # name of the externally-managed Secret to read from # key: clusterc # optional, defaults to the entry name +# clusterd: +# jwtSVIDExec: +# server: https://clusterd-api.example.com:6443 # target apiserver URL +# certificateAuthorityData: LS0tLS1CRUdJ... # apiserver CA bundle, base64-encoded PEM (kubeconfig certificate-authority-data) +# audience: k8s # optional, JWT-SVID audience the target expects (default k8s) + +## @param jwtSVIDExecConfig.image.registry The OCI registry to pull the exec credential plugin image from +## @param jwtSVIDExecConfig.image.repository The repository within the registry +## @param jwtSVIDExecConfig.image.pullPolicy The image pull policy +## @param jwtSVIDExecConfig.image.tag Overrides the image tag +## @param jwtSVIDExecConfig.pluginPath The path of the plugin binary inside the plugin image, staged for exec by the kubeConfigs consumers +## @param jwtSVIDExecConfig.spiffeID The SPIFFE ID the plugin mints a JWT-SVID for; a full spiffe:// URI, or a "/"-prefixed path expanded with the chart trust domain (e.g. /spire-root); required when any kubeConfigs entry uses jwtSVIDExec +## Global wiring shared by every kubeConfigs entry that uses jwtSVIDExec. The plugin binary is staged from this +## image into the shared plugins volume, and every such entry mints a JWT-SVID for spiffeID from the SPIRE Server +## admin API socket, which is already mounted into the kubeConfigs consumers, so no agent Workload API socket is required. +jwtSVIDExecConfig: + image: + registry: ghcr.io + repository: spiffe/k8s-spiffe-workload-jwt-exec-auth + pullPolicy: IfNotPresent + tag: "0.2.0" + pluginPath: /ko-app/cmd + spiffeID: "/spire-root" spireIdentityExchange: ## @param spireIdentityExchange.enabled Enable the server side of the SPIRE Identity Exchange system diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index eb6cabf..24964e3 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -333,6 +333,21 @@ spire-server: Expect(objs[serverTmpl]).Should(ContainSubstring("name: my-ext-secret")) Expect(objs[serverTmpl]).Should(ContainSubstring("path: clusterb")) }) + It("jwtSVIDExec entry generates the Secret and stages the exec plugin", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + jwtSVIDExecConfig: + spiffeID: spiffe://example.org/external-spire-server + kubeConfigs: + clusterd: + jwtSVIDExec: + server: https://clusterd-api.example.com:6443 + certificateAuthorityData: TESTCADATAB64== +`) + Expect(err).Should(Succeed()) + Expect(objs[secretTmpl]).Should(ContainSubstring("kind: Secret")) + Expect(objs[serverTmpl]).Should(ContainSubstring("init-jwt-svid-exec")) + }) }) Describe("spire-server.externalServerSubject", func() { It("binds the external server's downstream RBAC to a ServiceAccount subject", func() { From d56bfd6804ab4a0462674034d4db6ef1208e323a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:55:49 -0700 Subject: [PATCH 08/22] Bump github.com/onsi/ginkgo/v2 from 2.32.0 to 2.32.1 in /tests (#915) Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.32.0 to 2.32.1. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.32.0...v2.32.1) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-version: 2.32.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 5c58732..40bd1f9 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -3,7 +3,7 @@ module github.com/spiffe/helm-charts/tests go 1.26.0 require ( - github.com/onsi/ginkgo/v2 v2.32.0 + github.com/onsi/ginkgo/v2 v2.32.1 github.com/onsi/gomega v1.42.1 helm.sh/helm/v3 v3.21.3 ) diff --git a/tests/go.sum b/tests/go.sum index 8ea0e9b..26a3841 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -89,8 +89,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= -github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.32.1 h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw= +github.com/onsi/ginkgo/v2 v2.32.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From b60c222c78d139a67d9f2bcd81c4a8d0e84c1b08 Mon Sep 17 00:00:00 2001 From: scubadam Date: Fri, 14 Aug 2026 17:20:56 +0100 Subject: [PATCH 09/22] fix(spire-agent): suffix spire-config ConfigMap name per agent profile (#913) The spire-config volume (agent.conf) hardcodes {{ include "spire-agent.fullname" . }} with no $nameSuffix, unlike every other per-profile resource this chart renders (the ConfigMap itself, the trust-bundle volume, the DaemonSet name). Any additional agents. profile's DaemonSet therefore silently mounts the default profile's agent.conf, regardless of what's configured under that profile -- workloadAttestors, customPlugins, anything. Confirmed via a real cluster: an agents.gvisor profile's disableContainerSelectors never took effect because its DaemonSet was mounting the default spire-agent ConfigMap the whole time, not spire-agent-gvisor (which rendered correctly, just was never read). Verified via `helm template`: additional profiles now get their own correctly-suffixed ConfigMap reference, matching the DaemonSet's own name and the trust-bundle volume's existing (correct) behavior. Signed-off-by: dmorris Co-authored-by: dmorris Co-authored-by: kfox1111 --- charts/spire/charts/spire-agent/templates/daemonset.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/spire/charts/spire-agent/templates/daemonset.yaml b/charts/spire/charts/spire-agent/templates/daemonset.yaml index 7b1624d..3af2559 100644 --- a/charts/spire/charts/spire-agent/templates/daemonset.yaml +++ b/charts/spire/charts/spire-agent/templates/daemonset.yaml @@ -473,7 +473,7 @@ spec: volumes: - name: spire-config configMap: - name: {{ include "spire-agent.fullname" . }} + name: {{ printf "%s%s" (include "spire-agent.fullname" .) $nameSuffix | quote }} {{- if .Values.keyManager.disk.enabled }} - name: spire-key-manager {{- if eq .Values.keyManager.disk.mode "hostPath" }} From de48d14312c654639540d4e3d8ed82563f04f055 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 03:53:03 +0000 Subject: [PATCH 10/22] Bump helm.sh/helm/v3 from 3.21.3 to 3.21.4 in /tests Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.21.3 to 3.21.4. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.21.3...v3.21.4) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-version: 3.21.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- tests/go.mod | 18 +++++++++--------- tests/go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 40bd1f9..c665a2e 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -5,7 +5,7 @@ go 1.26.0 require ( github.com/onsi/ginkgo/v2 v2.32.1 github.com/onsi/gomega v1.42.1 - helm.sh/helm/v3 v3.21.3 + helm.sh/helm/v3 v3.21.4 ) require ( @@ -44,16 +44,16 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.53.0 // indirect - golang.org/x/mod v0.36.0 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/mod v0.37.0 // indirect golang.org/x/net v0.56.0 // indirect - golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.21.0 // indirect - golang.org/x/sys v0.46.0 // indirect - golang.org/x/term v0.44.0 // indirect - golang.org/x/text v0.38.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/term v0.45.0 // indirect + golang.org/x/text v0.40.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.45.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 26a3841..51ec3d7 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -133,26 +133,26 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= -golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -165,8 +165,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.21.3 h1:wkamdwI3liEkW6wI1l9aGqQZGxcTKyt8kx0qJLPcmCg= -helm.sh/helm/v3 v3.21.3/go.mod h1:iaJ0iNsPoTZl++7h6vzQFyT0VEVtLYJiyRBDkPOOBTs= +helm.sh/helm/v3 v3.21.4 h1:T/GcIEXU/gNjJnkITlIZ3e9xqkZjhFTmISuStTZ6+Qg= +helm.sh/helm/v3 v3.21.4/go.mod h1:cS2FBb+xfLuaSqvEmbqIeKUVFgHdHVHtVeXb2epof3M= k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY= k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg= k8s.io/apiextensions-apiserver v0.36.2 h1:3O5gqOj/dt2XWWbpMe+TXWpE9yU6pjM/tXxtHHJT/K4= From 07ba722da057113c8de2c71ad6949b9660acf551 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Mon, 17 Aug 2026 22:17:43 -0700 Subject: [PATCH 11/22] Update spire-identity-exchange for 0.4.0 (#900) * Update spire-identity-exchange for 0.4.0 Signed-off-by: Kevin Fox * Understand the plugin config Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Update ip Signed-off-by: Kevin Fox * Update name Signed-off-by: Kevin Fox * Update name Signed-off-by: Kevin Fox * Update name Signed-off-by: Kevin Fox * Fix broken socket path Signed-off-by: Kevin Fox * Nope, it was right before Signed-off-by: Kevin Fox * Try disabling the spiffe plugin for now Signed-off-by: Kevin Fox * Try logging more Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Map non container behavior Signed-off-by: Kevin Fox * Add missing csi driver settings Signed-off-by: Kevin Fox * Test Signed-off-by: Kevin Fox * Test Signed-off-by: Kevin Fox * Fix Signed-off-by: Kevin Fox * Use local oidc discovery provider path by default Signed-off-by: Kevin Fox * Enable spire-identity-exchange in shared infrastructure Signed-off-by: Kevin Fox * Update timeout Signed-off-by: Kevin Fox * Update timeout Signed-off-by: Kevin Fox * Test config Signed-off-by: Kevin Fox * Test config Signed-off-by: Kevin Fox * Test config Signed-off-by: Kevin Fox * Test config Signed-off-by: Kevin Fox * Fix Signed-off-by: Kevin Fox * Fix Signed-off-by: Kevin Fox * Bump spire-ha-agent version to fix issue Signed-off-by: Kevin Fox * Fix Signed-off-by: Kevin Fox * Fix Signed-off-by: Kevin Fox * Bump version Signed-off-by: Kevin Fox * Update version bits to match what it should be, minus final bump Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Faisal Memon --- .github/tests/common.sh | 8 + charts/spire-ha-agent/Chart.yaml | 2 +- charts/spire-identity-exchange/Chart.yaml | 2 +- charts/spire-identity-exchange/README.md | 223 ++++++-- .../ci/required-values.yaml | 14 - .../templates/_helpers.tpl | 112 ++++ .../templates/certificate.yaml | 18 +- .../templates/configmap.yaml | 185 +++++- .../templates/deployment.yaml | 45 +- .../templates/grpc-gateway.yaml | 13 - .../templates/issuer.yaml | 2 +- .../templates/podmonitor.yaml | 28 + .../templates/rest-gateway.yaml | 13 - .../templates/spiffe-grpc-gateway.yaml | 15 + .../templates/spiffe-grpc-ingress.yaml | 39 ++ .../templates/spiffe-grpc-service.yaml | 23 + .../templates/spiffe-rest-gateway.yaml | 15 + .../templates/spiffe-rest-ingress.yaml | 39 ++ .../templates/spiffe-rest-service.yaml | 23 + .../templates/tls-grpc-gateway.yaml | 14 + ...rpc-ingress.yaml => tls-grpc-ingress.yaml} | 14 +- ...rpc-service.yaml => tls-grpc-service.yaml} | 12 +- .../templates/tls-rest-gateway.yaml | 14 + ...est-ingress.yaml => tls-rest-ingress.yaml} | 14 +- ...est-service.yaml => tls-rest-service.yaml} | 12 +- charts/spire-identity-exchange/values.yaml | 541 ++++++++++++------ charts/spire-nested/README.md | 421 ++++++++------ charts/spire-nested/templates/_helpers.tpl | 64 +++ charts/spire-nested/templates/gateway.yaml | 3 + ...identity-exchange-spiffe-grpc-gateway.yaml | 15 + ...identity-exchange-spiffe-grpc-ingress.yaml | 39 ++ ...identity-exchange-spiffe-grpc-service.yaml | 25 + ...identity-exchange-spiffe-rest-gateway.yaml | 15 + ...identity-exchange-spiffe-rest-ingress.yaml | 39 ++ ...identity-exchange-spiffe-rest-service.yaml | 25 + .../identity-exchange-tls-grpc-gateway.yaml | 14 + .../identity-exchange-tls-grpc-ingress.yaml | 39 ++ .../identity-exchange-tls-grpc-service.yaml | 25 + .../identity-exchange-tls-rest-gateway.yaml | 14 + .../identity-exchange-tls-rest-ingress.yaml | 39 ++ .../identity-exchange-tls-rest-service.yaml | 25 + charts/spire-nested/values.yaml | 329 ++++++++++- .../bottom-turtle-ha/federation-test-job.yaml | 6 +- examples/bottom-turtle-ha/run-tests.sh | 75 ++- .../spire-identity-exchange-values.yaml | 48 +- 45 files changed, 2120 insertions(+), 580 deletions(-) delete mode 100644 charts/spire-identity-exchange/ci/required-values.yaml delete mode 100644 charts/spire-identity-exchange/templates/grpc-gateway.yaml create mode 100644 charts/spire-identity-exchange/templates/podmonitor.yaml delete mode 100644 charts/spire-identity-exchange/templates/rest-gateway.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-grpc-gateway.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-grpc-ingress.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-grpc-service.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-rest-gateway.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-rest-ingress.yaml create mode 100644 charts/spire-identity-exchange/templates/spiffe-rest-service.yaml create mode 100644 charts/spire-identity-exchange/templates/tls-grpc-gateway.yaml rename charts/spire-identity-exchange/templates/{grpc-ingress.yaml => tls-grpc-ingress.yaml} (69%) rename charts/spire-identity-exchange/templates/{grpc-service.yaml => tls-grpc-service.yaml} (54%) create mode 100644 charts/spire-identity-exchange/templates/tls-rest-gateway.yaml rename charts/spire-identity-exchange/templates/{rest-ingress.yaml => tls-rest-ingress.yaml} (69%) rename charts/spire-identity-exchange/templates/{rest-service.yaml => tls-rest-service.yaml} (54%) create mode 100644 charts/spire-nested/templates/_helpers.tpl create mode 100644 charts/spire-nested/templates/gateway.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-grpc-gateway.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-grpc-ingress.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-grpc-service.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-rest-gateway.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-rest-ingress.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-spiffe-rest-service.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-grpc-gateway.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-grpc-ingress.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-grpc-service.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-rest-gateway.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-rest-ingress.yaml create mode 100644 charts/spire-nested/templates/identity-exchange-tls-rest-service.yaml diff --git a/.github/tests/common.sh b/.github/tests/common.sh index cead988..6216691 100755 --- a/.github/tests/common.sh +++ b/.github/tests/common.sh @@ -25,6 +25,14 @@ $(kubectl get pods -o name -n "$1" | while read -r line; do echo logs for "${lin $( ([[ -n "$2" ]] && kubectl get pods -o name -n "$2") | while read -r line; do echo logs for "${line}"; kubectl logs -n "$2" "${line}" --all-containers=true --ignore-errors=true; done) \`\`\` +MAX_BYTES=1048576 +if [ "$(wc -c < "${GITHUB_STEP_SUMMARY}")" -gt "${MAX_BYTES}" ]; then + # shellcheck disable=SC2094 + truncate -s $((MAX_BYTES - 14)) "${GITHUB_STEP_SUMMARY}" + # shellcheck disable=SC2094 + printf "\ntruncated...\n" >> "${GITHUB_STEP_SUMMARY}" +fi + EOF } diff --git a/charts/spire-ha-agent/Chart.yaml b/charts/spire-ha-agent/Chart.yaml index 60aa504..6d9b719 100644 --- a/charts/spire-ha-agent/Chart.yaml +++ b/charts/spire-ha-agent/Chart.yaml @@ -3,7 +3,7 @@ name: spire-ha-agent description: A Helm chart to install the SPIRE HA agent. type: application version: 0.3.0 -appVersion: "0.2.0" +appVersion: "0.3.0" keywords: ["spiffe", "spire-ha-agent"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire-ha-agent sources: diff --git a/charts/spire-identity-exchange/Chart.yaml b/charts/spire-identity-exchange/Chart.yaml index b09d0c3..84f79ca 100644 --- a/charts/spire-identity-exchange/Chart.yaml +++ b/charts/spire-identity-exchange/Chart.yaml @@ -3,7 +3,7 @@ name: spire-identity-exchange description: A Helm chart to install the SPIRE Identity Exchange. type: application version: 0.2.0 -appVersion: "v0.3.0" +appVersion: "v0.5.0" keywords: ["spiffe", "spire", "identity exchange"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire-identity-exchange sources: diff --git a/charts/spire-identity-exchange/README.md b/charts/spire-identity-exchange/README.md index 3a3940b..ff8db16 100644 --- a/charts/spire-identity-exchange/README.md +++ b/charts/spire-identity-exchange/README.md @@ -1,6 +1,6 @@ # spire-identity-exchange -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-informational?style=flat-square) A Helm chart to install the SPIRE Identity Exchange. @@ -18,6 +18,54 @@ A Helm chart to install the SPIRE Identity Exchange. * +## Listeners + +Serving is a two-axis matrix: protocol (gRPC or REST) crossed with the source of the +certificate the listener presents. All four listeners are independent and can run at once. + +| Values block | Serves with | Default port | Default | +| -------------- | ---------------------------------------------------------- | ------------ | ------- | +| `tls.rest` | the certificate from `tls.externalSecret` / `tls.certManager` | 8444 | off | +| `tls.grpc` | the certificate from `tls.externalSecret` / `tls.certManager` | 8443 | off | +| `spiffe.rest` | this deployment's own X509-SVID | 8544 | **on** | +| `spiffe.grpc` | this deployment's own X509-SVID | 8543 | off | + +The `spiffe.*` listeners need no certificate files: the SVID is fetched from the SPIRE Agent +Workload API and rotated automatically, so a SPIFFE-only install requires neither cert-manager +nor a TLS Secret. A cert source under `tls:` is required only when `tls.rest` or `tls.grpc` is +enabled. Client authentication is identical on all four — callers present a bearer token. + +With `tls.certManager`, the requested certificate's `dnsNames` are taken from whichever exposures +are enabled — the `ingress.host`, the `gatewayAPI.host`, or both — across every enabled `tls.*` +listener, deduplicated. Each enabled `tls.*` listener must therefore have an ingress or a gateway +enabled, unless you set `tls.certManager.certificate.dnsNames` explicitly. + +## The stack selector + +Every exchange addresses a **stack**: an entry in `auth.stacks`, or — with +`auth.passthroughPlugins` (the default) — a single plugin addressed under its own name. It is +the `{stack}` segment of the REST path (`/api/v1/svid/{stack}/x509`). + +As of app version v0.4.0 the exchange asserts one selector on its own behalf, naming the stack +that was addressed: + +| Selector type | Value | Example | +| ------------------------ | ---------------------- | ---------------------------------------------- | +| `spire_identity_exchange` | `stack:name:` | `spire_identity_exchange:stack:name:k8s_psat` | + +Add it to a registration entry to scope that entry to a single stack: + +```yaml +controllerManager: + identities: + clusterStaticEntries: + test: + selectors: + - k8s_psat:namespace:default + - k8s_psat:service_account_name:default + - spire_identity_exchange:stack:name:k8s_psat # only issuable via this stack +``` + ## Parameters @@ -57,19 +105,12 @@ A Helm chart to install the SPIRE Identity Exchange. | `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `5` | | `podAnnotations` | Pod annotations for SPIRE Identity Exchange | `{}` | | `podLabels` | Labels to add to pods | `{}` | -| `tls.externalSecret.enabled` | Provide your own certificate/key via tls style Kubernetes Secret | `false` | -| `tls.externalSecret.secretName` | Specify which Secret to use | `""` | -| `tls.certManager.enabled` | Use certificateManager to create the certificate | `false` | -| `tls.certManager.issuer.create` | Create an issuer to use to issue the certificate | `true` | -| `tls.certManager.issuer.acme.email` | Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service | `""` | -| `tls.certManager.issuer.acme.server` | Server to use to get certificate. Defaults to LetsEncrypt | `https://acme-v02.api.letsencrypt.org/directory` | -| `tls.certManager.issuer.acme.solvers` | Configure the issuer solvers. Defaults to http01 via ingress. | `{}` | -| `tls.certManager.certificate.dnsNames` | Override the dnsNames on the certificate request. Defaults to the same settings as Ingress | `[]` | -| `tls.certManager.certificate.issuerRef.group` | If you are using an external plugin, specify the group for it here | `""` | -| `tls.certManager.certificate.issuerRef.kind` | Kind of the issuer reference. Override if you want to use a ClusterIssuer | `Issuer` | -| `tls.certManager.certificate.issuerRef.name` | Name of the issuer to use. If unset, it will use the name of the built in issuer | `""` | | `config.logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | | `config.logFormat` | The log format, valid values are "text" and "json" | `text` | +| `telemetry.prometheus.port` | Port for prometheus metrics | `4950` | +| `telemetry.prometheus.podMonitor.enabled` | Enable podMonitor for prometheus | `false` | +| `telemetry.prometheus.podMonitor.namespace` | Override where to install the podMonitor, if not set will use the same namespace as the helm release | `""` | +| `telemetry.prometheus.podMonitor.labels` | Pod labels to filter for prometheus monitoring | `{}` | | `imagePullSecrets` | Image pull secret names | `[]` | | `nameOverride` | Name override | `""` | | `fullnameOverride` | Full name override | `""` | @@ -83,54 +124,122 @@ A Helm chart to install the SPIRE Identity Exchange. | `autoscaling.targetCPUUtilizationPercentage` | Target CPU utlization that triggers autoscaling | `80` | | `autoscaling.targetMemoryUtilizationPercentage` | Target Memory utlization that triggers autoscaling | `80` | | `nodeSelector` | Node selector | `{}` | -| `tolerations` | iist of tolerations | `[]` | +| `tolerations` | list of tolerations | `[]` | | `affinity` | Node affinity | `{}` | | `trustDomain` | Set the trust domain to be used for the SPIFFE identifiers | `example.org` | +| `clusterName` | The name of this Kubernetes cluster, as it appears in SPIFFE ID paths | `example-cluster` | +| `jwtIssuer` | The issuer URL for JWT-SVIDs. Defaults to https://oidc-discovery.$trustDomain | `""` | | `clusterDomain` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `cluster.local` | -| `auth.plugins` | Plugins to load | `{}` | -| `auth.stacks` | Stacks to load | `{}` | -| `rest.enabled` | Enable the rest service | `true` | -| `rest.service.type` | Service type | `ClusterIP` | -| `rest.service.port` | port for the service | `443` | -| `rest.service.annotations` | Annotations for service resource | `{}` | -| `rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `rest.ingress.enabled` | Flag to enable ingress | `false` | -| `rest.ingress.className` | Ingress class name | `""` | -| `rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `rest.ingress.annotations` | Annotations for ingress object | `{}` | -| `rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest` | -| `rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `rest.gatewayAPI.enabled` | Flag to expose the REST endpoint via Gateway API | `false` | -| `rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` | -| `rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | -| `rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | -| `grpc.enabled` | Enable the grpc service | `false` | -| `grpc.service.type` | Service type | `ClusterIP` | -| `grpc.service.port` | port for the service | `443` | -| `grpc.service.annotations` | Annotations for service resource | `{}` | -| `grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `grpc.ingress.enabled` | Flag to enable ingress | `false` | -| `grpc.ingress.className` | Ingress class name | `""` | -| `grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `grpc.ingress.annotations` | Annotations for ingress object | `{}` | -| `grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc` | -| `grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `grpc.gatewayAPI.enabled` | Flag to expose the gRPC endpoint via Gateway API | `false` | -| `grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` | -| `grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | -| `grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `auth.plugins.k8s_psat.enabled` | Enable the k8s psat plugin | `true` | +| `auth.plugins.k8s_psat.config.audiences` | The audiences to allow | `[]` | +| `auth.plugins.k8s_psat.config.allowedServiceAccounts` | The service accounts that are allowed | `[]` | +| `auth.plugins.spiffe.enabled` | Enable the spiffe plugin | `true` | +| `auth.plugins.spiffe.keySource` | What source to use to fetch the keys. Can be oidc or oidcLocal. oidcLocal forces discoveryURL to be the internal discovery address. | `oidcLocal` | +| `auth.plugins.spiffe.csiDriverName` | The CSI driver providing the SPIRE Agent workload socket this plugin attests against. Defaults to the chart level csiDriverName. Requires config.connectWithTrustBundle. | | +| `auth.plugins.spiffe.config.issuerURL` | The url to connect to for JWKS discovery | `${SPIFFE_JWT_ISSUER}` | +| `auth.plugins.spiffe.config.trustDomain` | The trust domain to use | `${SPIFFE_TRUST_DOMAIN}` | +| `auth.plugins.spiffe.config.pathPatterns` | The service accounts that are allowed | `[]` | +| `auth.plugins.spiffe.config.audiences` | The audiences to allow | `[]` | +| `auth.plugins.spiffe.config.connectWithTrustBundle` | Use the trust bundle to validate the issuerURL | `true` | +| `auth.stacks.image_pull.enabled` | Enable the image_pull stack | `true` | +| `auth.stacks.image_pull.plugins` | List of plugins that are required by this stack | `[]` | +| `auth.unsupportedBuiltInPlugins` | Unsupported mechanism to use plugins not yet supported by the chart. | `{}` | +| `auth.passthroughPlugins` | Address each plugin as a stack of its own, in addition to any stacks defined | `false` | +| `tls.externalSecret.enabled` | Provide your own certificate/key via tls style Kubernetes Secret | `false` | +| `tls.externalSecret.secretName` | Specify which Secret to use | `""` | +| `tls.certManager.enabled` | Use certificateManager to create the certificate | `false` | +| `tls.certManager.issuer.create` | Create an issuer to use to issue the certificate | `true` | +| `tls.certManager.issuer.acme.email` | Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service | `""` | +| `tls.certManager.issuer.acme.server` | Server to use to get certificate. Defaults to LetsEncrypt | `https://acme-v02.api.letsencrypt.org/directory` | +| `tls.certManager.issuer.acme.solvers` | Configure the issuer solvers. Defaults to http01 via ingress. | `{}` | +| `tls.certManager.certificate.dnsNames` | Override the dnsNames on the certificate request. Defaults to the same settings as Ingress | `[]` | +| `tls.certManager.certificate.issuerRef.group` | If you are using an external plugin, specify the group for it here | `""` | +| `tls.certManager.certificate.issuerRef.kind` | Kind of the issuer reference. Override if you want to use a ClusterIssuer | `Issuer` | +| `tls.certManager.certificate.issuerRef.name` | Name of the issuer to use. If unset, it will use the name of the built in issuer | `""` | +| `tls.rest.enabled` | Enable the REST listener served with the certificate from disk | `false` | +| `tls.rest.port` | Container port for the REST listener served with the certificate from disk | `8444` | +| `tls.rest.service.type` | Service type | `ClusterIP` | +| `tls.rest.service.port` | port for the service | `443` | +| `tls.rest.service.annotations` | Annotations for service resource | `{}` | +| `tls.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `tls.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `tls.rest.ingress.className` | Ingress class name | `""` | +| `tls.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `tls.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `tls.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest` | +| `tls.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `tls.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `tls.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `tls.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `tls.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` | +| `tls.rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `tls.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `tls.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `tls.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `tls.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `tls.rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `tls.grpc.enabled` | Enable the gRPC listener served with the certificate from disk | `false` | +| `tls.grpc.port` | Container port for the gRPC listener served with the certificate from disk | `8443` | +| `tls.grpc.service.type` | Service type | `ClusterIP` | +| `tls.grpc.service.port` | port for the service | `443` | +| `tls.grpc.service.annotations` | Annotations for service resource | `{}` | +| `tls.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `tls.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `tls.grpc.ingress.className` | Ingress class name | `""` | +| `tls.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `tls.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `tls.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc` | +| `tls.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `tls.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `tls.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `tls.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `tls.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` | +| `tls.grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `tls.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `tls.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `tls.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `tls.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `tls.grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `spiffe.rest.enabled` | Enable the REST listener served with this deployment's own X509-SVID | `true` | +| `spiffe.rest.port` | Container port for the REST listener served with this deployment's own X509-SVID | `8544` | +| `spiffe.rest.service.type` | Service type | `ClusterIP` | +| `spiffe.rest.service.port` | port for the service | `443` | +| `spiffe.rest.service.annotations` | Annotations for service resource | `{}` | +| `spiffe.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spiffe.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `spiffe.rest.ingress.className` | Ingress class name | `""` | +| `spiffe.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spiffe.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `spiffe.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest-spiffe` | +| `spiffe.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spiffe.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spiffe.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spiffe.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spiffe.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest-spiffe` | +| `spiffe.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spiffe.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spiffe.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spiffe.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `spiffe.grpc.enabled` | Enable the gRPC listener served with this deployment's own X509-SVID | `false` | +| `spiffe.grpc.port` | Container port for the gRPC listener served with this deployment's own X509-SVID | `8543` | +| `spiffe.grpc.service.type` | Service type | `ClusterIP` | +| `spiffe.grpc.service.port` | port for the service | `443` | +| `spiffe.grpc.service.annotations` | Annotations for service resource | `{}` | +| `spiffe.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spiffe.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `spiffe.grpc.ingress.className` | Ingress class name | `""` | +| `spiffe.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spiffe.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `spiffe.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc-spiffe` | +| `spiffe.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spiffe.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spiffe.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spiffe.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spiffe.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc-spiffe` | +| `spiffe.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spiffe.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spiffe.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spiffe.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | | `tools.kubectl.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` | | `tools.kubectl.image.repository` | The repository within the registry | `kubectl` | | `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire-identity-exchange/ci/required-values.yaml b/charts/spire-identity-exchange/ci/required-values.yaml deleted file mode 100644 index daa693b..0000000 --- a/charts/spire-identity-exchange/ci/required-values.yaml +++ /dev/null @@ -1,14 +0,0 @@ -spire-identity-exchange: - enabled: true - tls: - externalSecret: - enabled: true - secretName: spire-identity-exchange - auth: - plugins: - - plugin: k8s_psat - config: - audiences: - - spire-identity-exchange - allowedServiceAccounts: - - default/default diff --git a/charts/spire-identity-exchange/templates/_helpers.tpl b/charts/spire-identity-exchange/templates/_helpers.tpl index cbfe68f..9e350a2 100644 --- a/charts/spire-identity-exchange/templates/_helpers.tpl +++ b/charts/spire-identity-exchange/templates/_helpers.tpl @@ -92,6 +92,64 @@ Create the name of the service account to use {{- printf "/spiffe-workload-api/%s" .Values.agentSocketName }} {{- end }} +{{/* +Volume name for an extra SPIFFE CSI driver. Driver names are DNS subdomains and may +contain dots, which a volume name (a DNS-1123 label) may not, so squash every run of +non-alphanumeric characters down to a single dash. +Args: the driver name as a string +*/}} +{{- define "spire-identity-exchange.csi-volume-name" -}} +{{- printf "spiffe-workload-api-%s" (trimAll "-" (regexReplaceAll "[^a-z0-9]+" (lower .) "-")) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Path to the SPIRE Agent workload socket one auth plugin should talk to. A plugin that +names no driver of its own, or names the one the exchange itself uses, gets the socket +already mounted for the pod; anything else gets its own mount under /spiffe-workload-apis. +Args: dict "root" "driver" +*/}} +{{- define "spire-identity-exchange.plugin-workload-api-socket-path" -}} +{{- $root := .root }} +{{- $driver := .driver | default "" }} +{{- if or (eq $driver "") (eq $driver $root.Values.csiDriverName) }} +{{- include "spire-identity-exchange.workload-api-socket-path" $root }} +{{- else }} +{{- printf "/spiffe-workload-apis/%s/%s" $driver $root.Values.agentSocketName }} +{{- end }} +{{- end }} + +{{/* +The CSI drivers this release must mount in addition to the pod's own, collected from the +enabled spiffe auth plugins. Deduplicated, so two plugins naming the same driver share one +volume. Returns JSON of driver name -> volume name; callers pipe it through fromJson. +*/}} +{{- define "spire-identity-exchange.extra-csi-drivers" -}} +{{- $root := . }} +{{- $drivers := dict }} +{{- $volumeNames := dict }} +{{- range $name, $config := .Values.auth.plugins }} +{{- $config = $config | default dict }} +{{- if ne (dig "enabled" true $config) false }} +{{- $pluginType := include "spire-identity-exchange.plugin-type" (dict "root" $root "name" $name "config" $config) }} +{{- $driver := dig "csiDriverName" "" $config }} +{{- if and (eq $pluginType "spiffe") (not (empty $driver)) }} +{{- if not (kindIs "string" $driver) }} +{{- fail (printf "auth.plugins.%s.csiDriverName: expected string, got %s" $name (kindOf $driver)) }} +{{- end }} +{{- if ne $driver $root.Values.csiDriverName }} +{{- $volumeName := include "spire-identity-exchange.csi-volume-name" $driver }} +{{- if and (hasKey $volumeNames $volumeName) (ne (index $volumeNames $volumeName) $driver) }} +{{- fail (printf "auth.plugins.%s.csiDriverName: %q and %q both reduce to the volume name %q. Volume names allow only lowercase alphanumerics and dashes, so these two drivers cannot be told apart; rename one so they differ by more than punctuation." $name $driver (index $volumeNames $volumeName) $volumeName) }} +{{- end }} +{{- $_ := set $volumeNames $volumeName $driver }} +{{- $_ := set $drivers $driver $volumeName }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- $drivers | toJson }} +{{- end }} + {{- define "spire-identity-exchange.podSecurityContext" -}} {{- $podSecurityContext := include "spire-lib.podsecuritycontext" . | fromYaml }} {{- $openshift := ((.Values).global).openshift | default false }} @@ -131,3 +189,57 @@ Create the name of the service account to use {{ .Release.Name }}-server.{{ include "spire-identity-exchange.server.namespace" . }} {{- end }} {{- end }} + +{{- define "spire-identity-exchange.plugin-type" }} +{{- $type := .name }} +{{- with .config.plugin }} +{{- $type = . }} +{{- end }} +{{- if not (has $type (list "k8s_psat" "spiffe" "github" "gitlab" )) }} +{{- fail (printf "Unknown plugin type specified: %s" $type) }} +{{- end }} +{{- printf "%s" $type }} +{{- end }} + +{{/* +Validate one plugin's config block against the option table for its type. +Emits nothing; only fails. +Args: dict "name" "type" "config" + "options" "string" | "[]string" | "bool"> +*/}} +{{- define "spire-identity-exchange.check-plugin-options" }} +{{- $ctx := . }} +{{- $valid := keys $ctx.options | sortAlpha | join ", " }} +{{- range $key, $val := $ctx.config }} +{{- if not (hasKey $ctx.options $key) }} +{{- fail (printf "auth.plugins.%s.config: %q is not a valid option for plugin type %q (valid options: %s). Use auth.unsupportedBuiltInPlugins to pass through options this chart does not model." $ctx.name $key $ctx.type $valid) }} +{{- end }} +{{- $want := index $ctx.options $key }} +{{- if eq $want "[]string" }} +{{- if not (kindIs "slice" $val) }} +{{- fail (printf "auth.plugins.%s.config.%s: expected a list of strings, got %s" $ctx.name $key (kindOf $val)) }} +{{- end }} +{{- range $val }} +{{- if not (kindIs "string" .) }} +{{- fail (printf "auth.plugins.%s.config.%s: every entry must be a string, got %s" $ctx.name $key (kindOf .)) }} +{{- end }} +{{- end }} +{{- else if not (kindIs $want $val) }} +{{- fail (printf "auth.plugins.%s.config.%s: expected %s, got %s" $ctx.name $key $want (kindOf $val)) }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Fail if any of the named options is absent or empty. Emits nothing. +Args: dict "name" "type" "config" + "required" +*/}} +{{- define "spire-identity-exchange.check-plugin-required" }} +{{- $ctx := . }} +{{- range $ctx.required }} +{{- if empty (index $ctx.config .) }} +{{- fail (printf "auth.plugins.%s.config.%s is required for plugin type %q" $ctx.name . $ctx.type) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/certificate.yaml b/charts/spire-identity-exchange/templates/certificate.yaml index 81da348..f153c58 100644 --- a/charts/spire-identity-exchange/templates/certificate.yaml +++ b/charts/spire-identity-exchange/templates/certificate.yaml @@ -4,12 +4,18 @@ dnsNames: {{- if ne (len .Values.tls.certManager.certificate.dnsNames) 0 }} {{- toYaml .Values.tls.certManager.certificate.dnsNames | nindent 4 }} {{- else }} - {{- if .Values.rest.enabled }} - - {{ include "spire-lib.ingress-calculated-name" (dict "ingress" .Values.rest.ingress "Values" .Values) }} - {{- end }} - {{- if .Values.grpc.enabled }} - - {{ include "spire-lib.ingress-calculated-name" (dict "ingress" .Values.grpc.ingress "Values" .Values) }} + {{- $hosts := list }} + {{- range $l := list .Values.tls.rest .Values.tls.grpc }} + {{- if $l.enabled }} + {{- if $l.ingress.enabled }} + {{- $hosts = append $hosts (include "spire-lib.ingress-calculated-name" (dict "ingress" $l.ingress "Values" $.Values) | trim) }} + {{- end }} + {{- if $l.gatewayAPI.enabled }} + {{- $hosts = append $hosts (include "spire-lib.ingress-calculated-name" (dict "ingress" (dict "host" $l.gatewayAPI.host) "Values" $.Values) | trim) }} + {{- end }} + {{- end }} {{- end }} + {{- toYaml (uniq $hosts) | nindent 4 }} {{- end }} issuerRef: {{- with .Values.tls.certManager.certificate.issuerRef.group }} @@ -19,7 +25,7 @@ issuerRef: name: {{ default $fullName .Values.tls.certManager.certificate.issuerRef.name }} secretName: {{ $fullName }}-cert {{- end }} -{{- if .Values.tls.certManager.enabled }} +{{- if and .Values.tls.certManager.enabled (or .Values.tls.rest.enabled .Values.tls.grpc.enabled) }} --- apiVersion: cert-manager.io/v1 kind: Certificate diff --git a/charts/spire-identity-exchange/templates/configmap.yaml b/charts/spire-identity-exchange/templates/configmap.yaml index 74a6060..87a97e2 100644 --- a/charts/spire-identity-exchange/templates/configmap.yaml +++ b/charts/spire-identity-exchange/templates/configmap.yaml @@ -1,20 +1,47 @@ -{{- $tlsCount := 0 }} -{{- if .Values.tls.externalSecret.enabled }} -{{- $tlsCount = add $tlsCount 1 }} +{{- $fileTLS := or .Values.tls.rest.enabled .Values.tls.grpc.enabled }} +{{- if $fileTLS }} +{{- $tlsCount := 0 }} +{{- if .Values.tls.externalSecret.enabled }} +{{- $tlsCount = add $tlsCount 1 }} +{{- end }} +{{- if .Values.tls.certManager.enabled }} +{{- $tlsCount = add $tlsCount 1 }} +{{- end }} +{{- if ne $tlsCount 1 }} +{{- fail "You must have one and only one TLS configuration enabled (tls.externalSecret or tls.certManager) when a tls listener is enabled" }} +{{- end }} +{{- if and .Values.tls.certManager.enabled (eq (len .Values.tls.certManager.certificate.dnsNames) 0) }} +{{- if and .Values.tls.rest.enabled (not (or .Values.tls.rest.ingress.enabled .Values.tls.rest.gatewayAPI.enabled)) }} +{{- fail "tls.certManager takes the certificate hostname from the exposure: enable tls.rest.ingress or tls.rest.gatewayAPI, or set tls.certManager.certificate.dnsNames" }} +{{- end }} +{{- if and .Values.tls.grpc.enabled (not (or .Values.tls.grpc.ingress.enabled .Values.tls.grpc.gatewayAPI.enabled)) }} +{{- fail "tls.certManager takes the certificate hostname from the exposure: enable tls.grpc.ingress or tls.grpc.gatewayAPI, or set tls.certManager.certificate.dnsNames" }} +{{- end }} +{{- end }} {{- end }} -{{- if .Values.tls.certManager.enabled }} -{{- $tlsCount = add $tlsCount 1 }} +{{- if kindIs "slice" .Values.auth.plugins }} +{{- fail "auth.plugins is a mapping keyed by plugin name, not a list: replace each `- plugin: k8s_psat` entry with a `k8s_psat:` key holding its config" }} {{- end }} -{{- if ne $tlsCount 1 }} -{{- fail "You must have one and only one TLS configuration enabled" }} +{{- if kindIs "slice" .Values.auth.stacks }} +{{- fail "auth.stacks is a mapping keyed by stack name, not a list: replace each `- name: foo` / `plugins: [...]` entry with a `foo:` key holding `plugins: [...]`" }} {{- end }} {{- if lt (len .Values.auth.plugins) 1 }} {{- fail "You must have at least one auth plugin defined" }} {{- end }} -{{- if not (or .Values.rest.enabled .Values.grpc.enabled) }} -{{- fail "You must have rest and/or grpc enabled" }} +{{- if not (or $fileTLS .Values.spiffe.rest.enabled .Values.spiffe.grpc.enabled) }} +{{- fail "You must enable at least one listener: tls.rest, tls.grpc, spiffe.rest or spiffe.grpc" }} {{- end }} {{- $trustDomain := include "spire-lib.trust-domain" . }} +{{- $root := . }} +{{- $enabledPlugins := list }} +{{- range $name, $config := .Values.auth.plugins }} +{{- if ne (dig "enabled" true ($config | default dict)) false }} +{{- $enabledPlugins = append $enabledPlugins $name }} +{{- end }} +{{- end }} +{{- if lt (len $enabledPlugins) 1 }} +{{- fail "Every auth plugin is disabled: at least one entry in auth.plugins must have enabled: true" }} +{{- end }} {{- include "spire-lib.check-strict-mode" (list . "trustDomain must be set" (eq $trustDomain "example.org"))}} apiVersion: v1 kind: ConfigMap @@ -28,23 +55,147 @@ metadata: data: six.conf: | name: spire-identity-exchange - logLevel: info + logLevel: {{ .Values.config.logLevel }} server: - port: 8443 - restPort: 8444 - metricsPort: 4950 + metricsPort: {{ .Values.telemetry.prometheus.port }} tls: + {{- if $fileTLS }} certFile: /secret/tls.crt keyFile: /secret/tls.key + {{- end }} + grpc: + enable: {{ .Values.tls.grpc.enabled }} + port: {{ .Values.tls.grpc.port }} + rest: + enable: {{ .Values.tls.rest.enabled }} + port: {{ .Values.tls.rest.port }} + spiffe: + grpc: + enable: {{ .Values.spiffe.grpc.enabled }} + port: {{ .Values.spiffe.grpc.port }} + rest: + enable: {{ .Values.spiffe.rest.enabled }} + port: {{ .Values.spiffe.rest.port }} spire: - agentWorkloadSocketPath: /spiffe-workload-api/spire-agent.sock + agentWorkloadSocketPath: {{ include "spire-identity-exchange.workload-api-socket-path" . }} agentDelegatedSocketPath: /agent/admin.sock trustDomain: {{ $trustDomain }} svidTTL: 1h auth: + passthroughPlugins: {{ .Values.auth.passthroughPlugins }} plugins: - {{- toYaml .Values.auth.plugins | nindent 8 }} - {{ with .Values.auth.stacks }} + {{- range $name, $config := .Values.auth.plugins }} + {{- if has $name $enabledPlugins }} + {{- $pluginType := include "spire-identity-exchange.plugin-type" (dict "root" $root "name" $name "config" $config) }} + {{- $cfg := $config.config | default dict }} + {{- if hasKey ($config | default dict) "csiDriverName" }} + {{- if ne $pluginType "spiffe" }} + {{- fail (printf "auth.plugins.%s: csiDriverName is only supported on plugins of type \"spiffe\". Plugin type %q does not talk to a SPIRE Agent workload socket, so there is nothing to mount the driver for." $name $pluginType) }} + {{- end }} + {{- if not (kindIs "string" $config.csiDriverName) }} + {{- fail (printf "auth.plugins.%s.csiDriverName: expected string, got %s" $name (kindOf $config.csiDriverName)) }} + {{- end }} + {{- end }} + {{ if eq $name "k8sPSAT" }}k8s_psat{{ else }}{{ $name | quote }}{{ end }}: + {{- with $config.plugin }} + plugin: {{ . | quote }} + {{- end }} + config: + {{- if eq $pluginType "k8s_psat" }} + {{- if hasKey $cfg "kubeconfig" }} + {{- fail (printf "auth.plugins.%s.config: kubeconfig is not supported by this chart. In a pod, spire-identity-exchange always authenticates to the Kubernetes API with the in-cluster credentials of its own ServiceAccount and ignores a kubeconfig file, so pointing it at another cluster would silently validate tokens against the local one instead." $name) }} + {{- end }} + {{- $_ := include "spire-identity-exchange.check-plugin-options" (dict "name" $name "type" $pluginType "config" $cfg "options" (dict + "clusterName" "string" + "audiences" "[]string" + "allowedNamespaces" "[]string" + "allowedServiceAccounts" "[]string" + "jwksCheck" "bool" + "tokenReview" "bool")) }} + {{- $jwksCheck := ne (dig "jwksCheck" true $cfg) false }} + {{- $tokenReview := ne (dig "tokenReview" true $cfg) false }} + {{- if not (or $jwksCheck $tokenReview) }} + {{- fail (printf "auth.plugins.%s.config: jwksCheck and tokenReview cannot both be false; at least one validation stage must remain active" $name) }} + {{- end }} + {{- if $jwksCheck }} + {{- $_ := include "spire-identity-exchange.check-plugin-required" (dict "name" $name "type" $pluginType "config" $cfg "required" (list "audiences")) }} + {{- end }} + {{- if and (empty $cfg.allowedNamespaces) (empty $cfg.allowedServiceAccounts) }} + {{- fail (printf "auth.plugins.%s.config: at least one of allowedNamespaces or allowedServiceAccounts must be set" $name) }} + {{- end }} + {{- toYaml $config.config | nindent 12 }} + {{- else if eq $pluginType "spiffe" }} + {{- if hasKey $cfg "agentWorkloadSocketPath" }} + {{- fail (printf "auth.plugins.%s.config: agentWorkloadSocketPath is set by this chart, not in values. The SPIRE Agent workload socket is mounted from the SPIFFE CSI driver and the path is filled in automatically when connectWithTrustBundle is true." $name) }} + {{- end }} + {{- $_ := include "spire-identity-exchange.check-plugin-options" (dict "name" $name "type" $pluginType "config" $cfg "options" (dict + "issuerURL" "string" + "discoveryURL" "string" + "trustDomain" "string" + "audiences" "[]string" + "pathPatterns" "[]string" + "connectWithTrustBundle" "bool")) }} + {{- $_ := include "spire-identity-exchange.check-plugin-required" (dict "name" $name "type" $pluginType "config" $cfg "required" (list "issuerURL" "audiences" "trustDomain" "pathPatterns")) }} + {{- $driver := dig "csiDriverName" "" ($config | default dict) }} + {{- if and (not (empty $driver)) (not $cfg.connectWithTrustBundle) }} + {{- fail (printf "auth.plugins.%s: csiDriverName is only meaningful when config.connectWithTrustBundle is true; the SPIRE Agent workload socket is not used otherwise." $name) }} + {{- end }} + {{- $keySource := dig "keySource" "oidc" ($config | default dict) }} + {{- if not (has $keySource (list "oidc" "oidcLocal")) }} + {{- fail (printf "auth.plugins.%s.keySource: %q is not valid; must be oidc or oidcLocal" $name $keySource) }} + {{- end }} + {{- $effective := $cfg }} + {{- if $cfg.connectWithTrustBundle }} + {{- $effective = merge (dict "agentWorkloadSocketPath" (include "spire-identity-exchange.plugin-workload-api-socket-path" (dict "root" $root "driver" $driver))) $cfg }} + {{- end }} + {{- if and (eq $keySource "oidcLocal") (empty $cfg.discoveryURL) }} + {{- $effective = merge (dict "discoveryURL" (printf "https://%s-spiffe-oidc-discovery-provider" $root.Release.Name)) $effective }} + {{- end }} + {{- toYaml $effective | nindent 12 }} + {{- else if eq $pluginType "github" }} + {{- $_ := include "spire-identity-exchange.check-plugin-options" (dict "name" $name "type" $pluginType "config" $cfg "options" (dict + "issuerURL" "string" + "audiences" "[]string" + "allowedRepositoryOwners" "[]string" + "allowedRepositories" "[]string")) }} + {{- $_ := include "spire-identity-exchange.check-plugin-required" (dict "name" $name "type" $pluginType "config" $cfg "required" (list "audiences")) }} + {{- if and (empty $cfg.allowedRepositoryOwners) (empty $cfg.allowedRepositories) }} + {{- fail (printf "auth.plugins.%s.config: at least one of allowedRepositoryOwners or allowedRepositories must be set" $name) }} + {{- end }} + {{- toYaml $config.config | nindent 12 }} + {{- else if eq $pluginType "gitlab" }} + {{- $_ := include "spire-identity-exchange.check-plugin-options" (dict "name" $name "type" $pluginType "config" $cfg "options" (dict + "issuerURL" "string" + "audiences" "[]string" + "allowedNamespacePaths" "[]string" + "allowedProjectPaths" "[]string")) }} + {{- $_ := include "spire-identity-exchange.check-plugin-required" (dict "name" $name "type" $pluginType "config" $cfg "required" (list "audiences")) }} + {{- if and (empty $cfg.allowedNamespacePaths) (empty $cfg.allowedProjectPaths) }} + {{- fail (printf "auth.plugins.%s.config: at least one of allowedNamespacePaths or allowedProjectPaths must be set" $name) }} + {{- end }} + {{- toYaml $config.config | nindent 12 }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.auth.unsupportedBuiltInPlugins }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- $stacks := dict }} + {{- range $stackName, $stack := .Values.auth.stacks }} + {{- if ne (dig "enabled" true ($stack | default dict)) false }} + {{- range $plugin := $stack.plugins }} + {{- if not (has $plugin $enabledPlugins) }} + {{- if hasKey $root.Values.auth.plugins $plugin }} + {{- fail (printf "auth.stacks.%s lists plugin %q, which is disabled. Set auth.plugins.%s.enabled: true or drop it from the stack; the exchange refuses to start when a stack names a plugin it did not load." $stackName $plugin $plugin) }} + {{- else }} + {{- fail (printf "auth.stacks.%s lists plugin %q, which is not defined in auth.plugins" $stackName $plugin) }} + {{- end }} + {{- end }} + {{- end }} + {{- $_ := set $stacks $stackName (omit $stack "enabled") }} + {{- end }} + {{- end }} + {{- with $stacks }} stacks: {{- toYaml . | nindent 8 }} {{- end }} @@ -80,7 +231,7 @@ data: NodeAttestor "x509pop" { plugin_data { - spiffe_endpoint_socket = "unix:///spiffe-workload-api/spire-agent.sock" + spiffe_endpoint_socket = "unix://{{ include "spire-identity-exchange.workload-api-socket-path" . }}" } } diff --git a/charts/spire-identity-exchange/templates/deployment.yaml b/charts/spire-identity-exchange/templates/deployment.yaml index feae63d..0d90193 100644 --- a/charts/spire-identity-exchange/templates/deployment.yaml +++ b/charts/spire-identity-exchange/templates/deployment.yaml @@ -1,5 +1,7 @@ {{- $configSum := (include (print $.Template.BasePath "/configmap.yaml") . | sha256sum) }} {{- $trustDomain := include "spire-lib.trust-domain" . }} +{{- $fileTLS := or .Values.tls.rest.enabled .Values.tls.grpc.enabled }} +{{- $extraCSIDrivers := include "spire-identity-exchange.extra-csi-drivers" . | fromJson }} apiVersion: apps/v1 kind: Deployment metadata: @@ -55,7 +57,7 @@ spec: - /trustbundle/socket env: - name: SPIFFE_ENDPOINT_SOCKET - value: "unix:///spiffe-workload-api/spire-agent.sock" + value: "unix://{{ include "spire-identity-exchange.workload-api-socket-path" . }}" - name: SPIFFE_TRUST_DOMAIN value: {{ $trustDomain }} readinessProbe: @@ -124,26 +126,49 @@ spec: - -config - /etc/spire/identity-exchange/six.conf - -expand-env - {{- with .Values.extraEnv }} env: + - name: SPIFFE_TRUST_DOMAIN + value: {{ $trustDomain | quote }} + - name: K8S_CLUSTER_NAME + value: {{ include "spire-lib.cluster-name" . | trim | quote }} + - name: SPIFFE_JWT_ISSUER + value: {{ include "spire-lib.jwt-issuer" . | trim | quote }} + {{- with .Values.extraEnv }} {{- . | toYaml | nindent 12 }} {{- end }} ports: - {{- if .Values.rest.enabled }} - - containerPort: 8444 + {{- if .Values.tls.rest.enabled }} + - containerPort: {{ .Values.tls.rest.port }} name: rest {{- end }} - {{- if .Values.grpc.enabled }} - - containerPort: 8443 + {{- if .Values.tls.grpc.enabled }} + - containerPort: {{ .Values.tls.grpc.port }} name: grpc {{- end }} + {{- if .Values.spiffe.rest.enabled }} + - containerPort: {{ .Values.spiffe.rest.port }} + name: rest-spiffe + {{- end }} + {{- if .Values.spiffe.grpc.enabled }} + - containerPort: {{ .Values.spiffe.grpc.port }} + name: grpc-spiffe + {{- end }} + - containerPort: {{ .Values.telemetry.prometheus.port }} + name: prom volumeMounts: - name: spiffe-workload-api mountPath: {{ include "spire-identity-exchange.workload-api-socket-path" . | dir }} readOnly: true + {{- range $driver, $volumeName := $extraCSIDrivers }} + - name: {{ $volumeName }} + mountPath: /spiffe-workload-apis/{{ $driver }} + readOnly: true + {{- end }} + {{- if $fileTLS }} - name: certdir mountPath: /secret readOnly: true + {{- end }} - name: spire-identity-exchange-config mountPath: /etc/spire/identity-exchange/six.conf subPath: six.conf @@ -168,6 +193,13 @@ spec: csi: driver: "{{ .Values.csiDriverName }}" readOnly: true + {{- range $driver, $volumeName := $extraCSIDrivers }} + - name: {{ $volumeName }} + csi: + driver: "{{ $driver }}" + readOnly: true + {{- end }} + {{- if $fileTLS }} - name: certdir {{- if .Values.tls.externalSecret.enabled }} secret: @@ -176,6 +208,7 @@ spec: secret: secretName: {{ include "spire-identity-exchange.fullname" . }}-cert {{- end }} + {{- end }} - name: spire-agent-socket emptyDir: {} - name: spire-agent-data diff --git a/charts/spire-identity-exchange/templates/grpc-gateway.yaml b/charts/spire-identity-exchange/templates/grpc-gateway.yaml deleted file mode 100644 index fe5cced..0000000 --- a/charts/spire-identity-exchange/templates/grpc-gateway.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.grpc.gatewayAPI.enabled -}} -{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.grpc.gatewayAPI) -}} -{{- include "spire-lib.gateway-routes" (dict - "root" . - "gatewayAPI" .Values.grpc.gatewayAPI - "name" (printf "%s-grpc" (include "spire-identity-exchange.fullname" .)) - "namespace" (include "spire-identity-exchange.namespace" .) - "svcName" (printf "%s-grpc" (include "spire-identity-exchange.fullname" .)) - "port" .Values.grpc.service.port - "labels" (include "spire-identity-exchange.labels" .) - "routeKind" $routeKind - "backendTLS" (eq $routeKind "HTTPRoute")) }} -{{- end }} diff --git a/charts/spire-identity-exchange/templates/issuer.yaml b/charts/spire-identity-exchange/templates/issuer.yaml index 65979d9..b3e2441 100644 --- a/charts/spire-identity-exchange/templates/issuer.yaml +++ b/charts/spire-identity-exchange/templates/issuer.yaml @@ -10,7 +10,7 @@ solvers: - http01: ingress: {} {{- end }} -{{- if and .Values.tls.certManager.enabled .Values.tls.certManager.issuer.create }} +{{- if and .Values.tls.certManager.enabled .Values.tls.certManager.issuer.create (or .Values.tls.rest.enabled .Values.tls.grpc.enabled) }} apiVersion: cert-manager.io/v1 kind: Issuer metadata: diff --git a/charts/spire-identity-exchange/templates/podmonitor.yaml b/charts/spire-identity-exchange/templates/podmonitor.yaml new file mode 100644 index 0000000..7d315e5 --- /dev/null +++ b/charts/spire-identity-exchange/templates/podmonitor.yaml @@ -0,0 +1,28 @@ +{{- if (dig "telemetry" "prometheus" "podMonitor" "enabled" .Values.telemetry.prometheus.podMonitor.enabled .Values.global) }} +{{- $namespace := include "spire-identity-exchange.podMonitor.namespace" . }} +{{- $podNamespace := include "spire-identity-exchange.namespace" . }} +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: {{ include "spire-identity-exchange.fullname" . }} + namespace: {{ $namespace | quote }} + labels: + {{- include "spire-identity-exchange.labels" . | nindent 4 }} + {{- if ne (len (dig "telemetry" "prometheus" "podMonitor" "labels" (dict) .Values.global)) 0 }} + {{- .Values.global.telemetry.prometheus.podMonitor.labels | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.telemetry.prometheus.podMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "spire-identity-exchange.selectorLabels" . | nindent 6 }} + podMetricsEndpoints: + - port: prom + {{- if ne $namespace $podNamespace }} + namespaceSelector: + matchNames: + - {{ $podNamespace | quote }} + {{- end }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/rest-gateway.yaml b/charts/spire-identity-exchange/templates/rest-gateway.yaml deleted file mode 100644 index bf05f56..0000000 --- a/charts/spire-identity-exchange/templates/rest-gateway.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.rest.gatewayAPI.enabled -}} -{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.rest.gatewayAPI) -}} -{{- include "spire-lib.gateway-routes" (dict - "root" . - "gatewayAPI" .Values.rest.gatewayAPI - "name" (printf "%s-rest" (include "spire-identity-exchange.fullname" .)) - "namespace" (include "spire-identity-exchange.namespace" .) - "svcName" (printf "%s-rest" (include "spire-identity-exchange.fullname" .)) - "port" .Values.rest.service.port - "labels" (include "spire-identity-exchange.labels" .) - "routeKind" $routeKind - "backendTLS" (eq $routeKind "HTTPRoute")) }} -{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-grpc-gateway.yaml b/charts/spire-identity-exchange/templates/spiffe-grpc-gateway.yaml new file mode 100644 index 0000000..cdd8405 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-grpc-gateway.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.spiffe.grpc.enabled .Values.spiffe.grpc.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-grpc-spiffe" (include "spire-identity-exchange.fullname" .) -}} +{{/* Passthrough only. This backend serves an X509-SVID, whose only SAN is a + spiffe:// URI, so a BackendTLSPolicy hostname check could never match. */}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spiffe.grpc.gatewayAPI + "name" $fullName + "namespace" (include "spire-identity-exchange.namespace" .) + "svcName" $fullName + "port" .Values.spiffe.grpc.service.port + "labels" (include "spire-identity-exchange.labels" .) + "routeKind" "TLSRoute" + "backendTLS" false) }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-grpc-ingress.yaml b/charts/spire-identity-exchange/templates/spiffe-grpc-ingress.yaml new file mode 100644 index 0000000..043cb76 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-grpc-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.spiffe.grpc.enabled .Values.spiffe.grpc.ingress.enabled -}} +{{- $port := .Values.spiffe.grpc.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spiffe.grpc.ingress) }} +{{- $fullName := printf "%s-grpc-spiffe" (include "spire-identity-exchange.fullname" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spiffe.grpc.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spiffe.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spiffe.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-identity-exchange.namespace" . }} + labels: + {{ include "spire-identity-exchange.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spiffe.grpc.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-grpc-service.yaml b/charts/spire-identity-exchange/templates/spiffe-grpc-service.yaml new file mode 100644 index 0000000..0559029 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-grpc-service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.spiffe.grpc.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-identity-exchange.fullname" . }}-grpc-spiffe + namespace: {{ include "spire-identity-exchange.namespace" . }} + {{- with .Values.spiffe.grpc.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spiffe.grpc.service.type }} + {{- if and (eq .Values.spiffe.grpc.service.type "LoadBalancer") .Values.spiffe.grpc.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spiffe.grpc.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spiffe.grpc.service.port }} + targetPort: grpc-spiffe + protocol: TCP + selector: + {{- include "spire-identity-exchange.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-rest-gateway.yaml b/charts/spire-identity-exchange/templates/spiffe-rest-gateway.yaml new file mode 100644 index 0000000..f32d7d8 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-rest-gateway.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.spiffe.rest.enabled .Values.spiffe.rest.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-rest-spiffe" (include "spire-identity-exchange.fullname" .) -}} +{{/* Passthrough only. This backend serves an X509-SVID, whose only SAN is a + spiffe:// URI, so a BackendTLSPolicy hostname check could never match. */}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spiffe.rest.gatewayAPI + "name" $fullName + "namespace" (include "spire-identity-exchange.namespace" .) + "svcName" $fullName + "port" .Values.spiffe.rest.service.port + "labels" (include "spire-identity-exchange.labels" .) + "routeKind" "TLSRoute" + "backendTLS" false) }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-rest-ingress.yaml b/charts/spire-identity-exchange/templates/spiffe-rest-ingress.yaml new file mode 100644 index 0000000..9ec6dd6 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-rest-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.spiffe.rest.enabled .Values.spiffe.rest.ingress.enabled -}} +{{- $port := .Values.spiffe.rest.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spiffe.rest.ingress) }} +{{- $fullName := printf "%s-rest-spiffe" (include "spire-identity-exchange.fullname" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spiffe.rest.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spiffe.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spiffe.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-identity-exchange.namespace" . }} + labels: + {{ include "spire-identity-exchange.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spiffe.rest.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/spiffe-rest-service.yaml b/charts/spire-identity-exchange/templates/spiffe-rest-service.yaml new file mode 100644 index 0000000..84cbba3 --- /dev/null +++ b/charts/spire-identity-exchange/templates/spiffe-rest-service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.spiffe.rest.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-identity-exchange.fullname" . }}-rest-spiffe + namespace: {{ include "spire-identity-exchange.namespace" . }} + {{- with .Values.spiffe.rest.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spiffe.rest.service.type }} + {{- if and (eq .Values.spiffe.rest.service.type "LoadBalancer") .Values.spiffe.rest.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spiffe.rest.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spiffe.rest.service.port }} + targetPort: rest-spiffe + protocol: TCP + selector: + {{- include "spire-identity-exchange.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/tls-grpc-gateway.yaml b/charts/spire-identity-exchange/templates/tls-grpc-gateway.yaml new file mode 100644 index 0000000..3e792e1 --- /dev/null +++ b/charts/spire-identity-exchange/templates/tls-grpc-gateway.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.tls.grpc.enabled .Values.tls.grpc.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-grpc" (include "spire-identity-exchange.fullname" .) -}} +{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.tls.grpc.gatewayAPI) -}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.tls.grpc.gatewayAPI + "name" $fullName + "namespace" (include "spire-identity-exchange.namespace" .) + "svcName" $fullName + "port" .Values.tls.grpc.service.port + "labels" (include "spire-identity-exchange.labels" .) + "routeKind" $routeKind + "backendTLS" (eq $routeKind "HTTPRoute")) }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/grpc-ingress.yaml b/charts/spire-identity-exchange/templates/tls-grpc-ingress.yaml similarity index 69% rename from charts/spire-identity-exchange/templates/grpc-ingress.yaml rename to charts/spire-identity-exchange/templates/tls-grpc-ingress.yaml index 09b683d..9439cf6 100644 --- a/charts/spire-identity-exchange/templates/grpc-ingress.yaml +++ b/charts/spire-identity-exchange/templates/tls-grpc-ingress.yaml @@ -1,20 +1,20 @@ -{{- if .Values.grpc.ingress.enabled -}} -{{- $port := .Values.grpc.service.port }} -{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.grpc.ingress) }} +{{- if and .Values.tls.grpc.enabled .Values.tls.grpc.ingress.enabled -}} +{{- $port := .Values.tls.grpc.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.tls.grpc.ingress) }} {{- $fullName := printf "%s-grpc" (include "spire-identity-exchange.fullname" .) }} {{- $path := "/" }} {{- $pathType := "Prefix" }} {{- $tlsSection := true }} -{{- $annotations := deepCopy .Values.grpc.ingress.annotations }} +{{- $annotations := deepCopy .Values.tls.grpc.ingress.annotations }} {{- if eq $ingressControllerType "ingress-nginx" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} -{{- if not (and .Values.grpc.ingress.enabled .Values.grpc.ingress.tlsSecret) }} +{{- if not .Values.tls.grpc.ingress.tlsSecret }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} {{- end }} {{- else if eq $ingressControllerType "openshift" }} -{{- if and .Values.grpc.ingress.enabled .Values.grpc.ingress.tlsSecret }} +{{- if .Values.tls.grpc.ingress.tlsSecret }} {{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} {{- else }} {{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} @@ -35,5 +35,5 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.grpc.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.tls.grpc.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} {{- end }} diff --git a/charts/spire-identity-exchange/templates/grpc-service.yaml b/charts/spire-identity-exchange/templates/tls-grpc-service.yaml similarity index 54% rename from charts/spire-identity-exchange/templates/grpc-service.yaml rename to charts/spire-identity-exchange/templates/tls-grpc-service.yaml index 107d6b8..c919c17 100644 --- a/charts/spire-identity-exchange/templates/grpc-service.yaml +++ b/charts/spire-identity-exchange/templates/tls-grpc-service.yaml @@ -1,21 +1,21 @@ -{{- if .Values.grpc.enabled }} +{{- if .Values.tls.grpc.enabled }} apiVersion: v1 kind: Service metadata: name: {{ include "spire-identity-exchange.fullname" . }}-grpc namespace: {{ include "spire-identity-exchange.namespace" . }} - {{- with .Values.service.annotations }} + {{- with .Values.tls.grpc.service.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - type: {{ .Values.grpc.service.type }} - {{- if and (eq .Values.grpc.service.type "LoadBalancer") .Values.grpc.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.grpc.service.loadBalancerIP }} + type: {{ .Values.tls.grpc.service.type }} + {{- if and (eq .Values.tls.grpc.service.type "LoadBalancer") .Values.tls.grpc.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.tls.grpc.service.loadBalancerIP }} {{- end }} ports: - name: https - port: {{ .Values.grpc.service.port }} + port: {{ .Values.tls.grpc.service.port }} targetPort: grpc protocol: TCP selector: diff --git a/charts/spire-identity-exchange/templates/tls-rest-gateway.yaml b/charts/spire-identity-exchange/templates/tls-rest-gateway.yaml new file mode 100644 index 0000000..70b6b9c --- /dev/null +++ b/charts/spire-identity-exchange/templates/tls-rest-gateway.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.tls.rest.enabled .Values.tls.rest.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-rest" (include "spire-identity-exchange.fullname" .) -}} +{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.tls.rest.gatewayAPI) -}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.tls.rest.gatewayAPI + "name" $fullName + "namespace" (include "spire-identity-exchange.namespace" .) + "svcName" $fullName + "port" .Values.tls.rest.service.port + "labels" (include "spire-identity-exchange.labels" .) + "routeKind" $routeKind + "backendTLS" (eq $routeKind "HTTPRoute")) }} +{{- end }} diff --git a/charts/spire-identity-exchange/templates/rest-ingress.yaml b/charts/spire-identity-exchange/templates/tls-rest-ingress.yaml similarity index 69% rename from charts/spire-identity-exchange/templates/rest-ingress.yaml rename to charts/spire-identity-exchange/templates/tls-rest-ingress.yaml index fee650f..6e545a6 100644 --- a/charts/spire-identity-exchange/templates/rest-ingress.yaml +++ b/charts/spire-identity-exchange/templates/tls-rest-ingress.yaml @@ -1,20 +1,20 @@ -{{- if .Values.rest.ingress.enabled -}} -{{- $port := .Values.rest.service.port }} -{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.rest.ingress) }} +{{- if and .Values.tls.rest.enabled .Values.tls.rest.ingress.enabled -}} +{{- $port := .Values.tls.rest.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.tls.rest.ingress) }} {{- $fullName := printf "%s-rest" (include "spire-identity-exchange.fullname" .) }} {{- $path := "/" }} {{- $pathType := "Prefix" }} {{- $tlsSection := true }} -{{- $annotations := deepCopy .Values.rest.ingress.annotations }} +{{- $annotations := deepCopy .Values.tls.rest.ingress.annotations }} {{- if eq $ingressControllerType "ingress-nginx" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} -{{- if not (and .Values.rest.ingress.enabled .Values.rest.ingress.tlsSecret) }} +{{- if not .Values.tls.rest.ingress.tlsSecret }} {{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} {{- end }} {{- else if eq $ingressControllerType "openshift" }} -{{- if and .Values.rest.ingress.enabled .Values.rest.ingress.tlsSecret }} +{{- if .Values.tls.rest.ingress.tlsSecret }} {{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} {{- else }} {{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} @@ -35,5 +35,5 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.rest.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.tls.rest.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} {{- end }} diff --git a/charts/spire-identity-exchange/templates/rest-service.yaml b/charts/spire-identity-exchange/templates/tls-rest-service.yaml similarity index 54% rename from charts/spire-identity-exchange/templates/rest-service.yaml rename to charts/spire-identity-exchange/templates/tls-rest-service.yaml index 0a45d41..f563049 100644 --- a/charts/spire-identity-exchange/templates/rest-service.yaml +++ b/charts/spire-identity-exchange/templates/tls-rest-service.yaml @@ -1,21 +1,21 @@ -{{- if .Values.rest.enabled }} +{{- if .Values.tls.rest.enabled }} apiVersion: v1 kind: Service metadata: name: {{ include "spire-identity-exchange.fullname" . }}-rest namespace: {{ include "spire-identity-exchange.namespace" . }} - {{- with .Values.rest.service.annotations }} + {{- with .Values.tls.rest.service.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - type: {{ .Values.rest.service.type }} - {{- if and (eq .Values.rest.service.type "LoadBalancer") .Values.rest.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.rest.service.loadBalancerIP }} + type: {{ .Values.tls.rest.service.type }} + {{- if and (eq .Values.tls.rest.service.type "LoadBalancer") .Values.tls.rest.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.tls.rest.service.loadBalancerIP }} {{- end }} ports: - name: https - port: {{ .Values.rest.service.port }} + port: {{ .Values.tls.rest.service.port }} targetPort: rest protocol: TCP selector: diff --git a/charts/spire-identity-exchange/values.yaml b/charts/spire-identity-exchange/values.yaml index 2922fc5..16084bf 100644 --- a/charts/spire-identity-exchange/values.yaml +++ b/charts/spire-identity-exchange/values.yaml @@ -138,49 +138,25 @@ podAnnotations: {} ## @param podLabels [object] Labels to add to pods podLabels: {} -# Select one of the options below to be the source of certificates for SPIRE Identity Exchange -tls: - - externalSecret: - ## @param tls.externalSecret.enabled Provide your own certificate/key via tls style Kubernetes Secret - enabled: false - ## @param tls.externalSecret.secretName Specify which Secret to use - secretName: "" - - certManager: - ## @param tls.certManager.enabled Use certificateManager to create the certificate - enabled: false - issuer: - ## @param tls.certManager.issuer.create Create an issuer to use to issue the certificate - create: true - acme: - ## @param tls.certManager.issuer.acme.email Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service - email: "" - ## @param tls.certManager.issuer.acme.server Server to use to get certificate. Defaults to LetsEncrypt - server: https://acme-v02.api.letsencrypt.org/directory - # Testing server: https://acme-staging-v02.api.letsencrypt.org/directory - ## @param tls.certManager.issuer.acme.solvers [object] Configure the issuer solvers. Defaults to http01 via ingress. - solvers: {} - # - http01: - # ingress: - # ingressClassName: nginx - certificate: - ## @param tls.certManager.certificate.dnsNames Override the dnsNames on the certificate request. Defaults to the same settings as Ingress - dnsNames: [] - ## @param tls.certManager.certificate.issuerRef.group If you are using an external plugin, specify the group for it here - ## @param tls.certManager.certificate.issuerRef.kind Kind of the issuer reference. Override if you want to use a ClusterIssuer - ## @param tls.certManager.certificate.issuerRef.name Name of the issuer to use. If unset, it will use the name of the built in issuer - issuerRef: - group: "" - kind: Issuer - name: "" - config: ## @param config.logLevel The log level, valid values are "debug", "info", "warn", and "error" logLevel: info ## @param config.logFormat The log format, valid values are "text" and "json" logFormat: text +# The metrics endpoint is always served; spire-identity-exchange requires a nonzero port for it. +telemetry: + prometheus: + ## @param telemetry.prometheus.port Port for prometheus metrics + port: 4950 + podMonitor: + ## @param telemetry.prometheus.podMonitor.enabled Enable podMonitor for prometheus + enabled: false + ## @param telemetry.prometheus.podMonitor.namespace Override where to install the podMonitor, if not set will use the same namespace as the helm release + namespace: "" + ## @param telemetry.prometheus.podMonitor.labels [object] Pod labels to filter for prometheus monitoring + labels: {} + ## @param imagePullSecrets [array] Image pull secret names imagePullSecrets: [] @@ -219,7 +195,7 @@ autoscaling: ## @param nodeSelector [object] Node selector nodeSelector: {} -## @param tolerations [array] iist of tolerations +## @param tolerations [array] list of tolerations tolerations: [] ## @param affinity [object] Node affinity @@ -228,158 +204,375 @@ affinity: {} ## @param trustDomain Set the trust domain to be used for the SPIFFE identifiers trustDomain: example.org +## @param clusterName The name of this Kubernetes cluster, as it appears in SPIFFE ID paths +clusterName: example-cluster + +## @param jwtIssuer The issuer URL for JWT-SVIDs. Defaults to https://oidc-discovery.$trustDomain +jwtIssuer: "" + ## @param clusterDomain The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) clusterDomain: cluster.local auth: - ## @param auth.plugins [object] Plugins to load - plugins: [] - ## @param auth.stacks [object] Stacks to load - stacks: [] + plugins: + k8s_psat: + ## @param auth.plugins.k8s_psat.enabled Enable the k8s psat plugin + enabled: true + config: + ## @param auth.plugins.k8s_psat.config.audiences [array] The audiences to allow + audiences: + - spire-identity-exchange + ## @param auth.plugins.k8s_psat.config.allowedServiceAccounts [array] The service accounts that are allowed + allowedServiceAccounts: + - "*" + spiffe: + ## @param auth.plugins.spiffe.enabled Enable the spiffe plugin + enabled: true + ## @param auth.plugins.spiffe.keySource What source to use to fetch the keys. Can be oidc or oidcLocal. oidcLocal forces discoveryURL to be the internal discovery address. + keySource: oidcLocal + ## @extra auth.plugins.spiffe.csiDriverName The CSI driver providing the SPIRE Agent workload socket this plugin attests against. Defaults to the chart level csiDriverName. Requires config.connectWithTrustBundle. + config: + ## @param auth.plugins.spiffe.config.issuerURL The url to connect to for JWKS discovery + issuerURL: "${SPIFFE_JWT_ISSUER}" + ## @param auth.plugins.spiffe.config.trustDomain The trust domain to use + trustDomain: "${SPIFFE_TRUST_DOMAIN}" + ## @param auth.plugins.spiffe.config.pathPatterns [array] The service accounts that are allowed + pathPatterns: + - "^/k8s/${K8S_CLUSTER_NAME}/node/[^/]+" + ## @param auth.plugins.spiffe.config.audiences [array] The audiences to allow + audiences: + - spire-identity-exchange + ## @param auth.plugins.spiffe.config.connectWithTrustBundle Use the trust bundle to validate the issuerURL + connectWithTrustBundle: true -rest: - ## @param rest.enabled Enable the rest service - enabled: true - ## @param rest.service.type Service type - ## @param rest.service.port port for the service - ## @param rest.service.annotations Annotations for service resource - ## - service: - type: ClusterIP - port: 443 - annotations: {} - # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-rest.example.org - ## @param rest.service.loadBalancerIP IP address to assign to load balancer (if supported) - loadBalancerIP: "" - ingress: - ## @param rest.ingress.enabled Flag to enable ingress + stacks: + image_pull: + ## @param auth.stacks.image_pull.enabled Enable the image_pull stack + enabled: true + ## @param auth.stacks.image_pull.plugins [array] List of plugins that are required by this stack + plugins: + - spiffe + - k8s_psat + + ## @param auth.unsupportedBuiltInPlugins [object] Unsupported mechanism to use plugins not yet supported by the chart. + unsupportedBuiltInPlugins: {} + + ## @param auth.passthroughPlugins Address each plugin as a stack of its own, in addition to any stacks defined + passthroughPlugins: false + +# Listeners served with a certificate from disk. Select one of the options below to be the source of that certificate. +tls: + + externalSecret: + ## @param tls.externalSecret.enabled Provide your own certificate/key via tls style Kubernetes Secret enabled: false - ## @param rest.ingress.className Ingress class name - className: "" - ## @param rest.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. - controllerType: "" - ## @param rest.ingress.annotations [object] Annotations for ingress object - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - # nginx.ingress.kubernetes.io/ssl-redirect: "true" - # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + ## @param tls.externalSecret.secretName Specify which Secret to use + secretName: "" - ## @param rest.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. - host: "spire-identity-exchange-rest" - - ## @param rest.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. - tlsSecret: "" - - ## @param rest.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. - hosts: [] - # - host: spire-identity-exchange-rest.example.org - # paths: - # - path: / - # pathType: Prefix - - ## @param rest.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. - tls: [] - # - secretName: chart-example-tls - # hosts: - # - spire-identity-exchange-rest.example.org - - ## Gateway API exposure for the REST endpoint. Independent of rest.ingress. The - ## backend serves HTTPS, so a set tlsSecret => HTTPRoute + BackendTLSPolicy - ## (reencrypt); blank tlsSecret => TLSRoute (SNI passthrough). - gatewayAPI: - ## @param rest.gatewayAPI.enabled Flag to expose the REST endpoint via Gateway API + certManager: + ## @param tls.certManager.enabled Use certificateManager to create the certificate enabled: false - ## @param rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. - host: "spire-identity-exchange-rest" - ## @param rest.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. - tlsSecret: "" - ## @param rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) - annotations: {} - listenerSet: - ## @param rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. - enabled: null - ## @param rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) - parentRefs: [] - ## @param rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway - sectionName: "" - # BackendTLSPolicy (reencrypt) is emitted automatically for the terminated - # HTTPS backend when gatewayAPI.tlsSecret is set. - backendTLS: - ## @param rest.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. - caCertificateRefs: [] + issuer: + ## @param tls.certManager.issuer.create Create an issuer to use to issue the certificate + create: true + acme: + ## @param tls.certManager.issuer.acme.email Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service + email: "" + ## @param tls.certManager.issuer.acme.server Server to use to get certificate. Defaults to LetsEncrypt + server: https://acme-v02.api.letsencrypt.org/directory + # Testing server: https://acme-staging-v02.api.letsencrypt.org/directory + ## @param tls.certManager.issuer.acme.solvers [object] Configure the issuer solvers. Defaults to http01 via ingress. + solvers: {} + # - http01: + # ingress: + # ingressClassName: nginx + certificate: + ## @param tls.certManager.certificate.dnsNames Override the dnsNames on the certificate request. Defaults to the same settings as Ingress + dnsNames: [] + ## @param tls.certManager.certificate.issuerRef.group If you are using an external plugin, specify the group for it here + ## @param tls.certManager.certificate.issuerRef.kind Kind of the issuer reference. Override if you want to use a ClusterIssuer + ## @param tls.certManager.certificate.issuerRef.name Name of the issuer to use. If unset, it will use the name of the built in issuer + issuerRef: + group: "" + kind: Issuer + name: "" -grpc: - ## @param grpc.enabled Enable the grpc service - enabled: false - ## @param grpc.service.type Service type - ## @param grpc.service.port port for the service - ## @param grpc.service.annotations Annotations for service resource - ## - service: - type: ClusterIP - port: 443 - annotations: {} - # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-grpc.example.org - ## @param grpc.service.loadBalancerIP IP address to assign to load balancer (if supported) - loadBalancerIP: "" - ingress: - ## @param grpc.ingress.enabled Flag to enable ingress + rest: + ## @param tls.rest.enabled Enable the REST listener served with the certificate from disk enabled: false - ## @param grpc.ingress.className Ingress class name - className: "" - ## @param grpc.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. - controllerType: "" - ## @param grpc.ingress.annotations [object] Annotations for ingress object - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - # nginx.ingress.kubernetes.io/ssl-redirect: "true" - # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + ## @param tls.rest.port Container port for the REST listener served with the certificate from disk + port: 8444 + ## @param tls.rest.service.type Service type + ## @param tls.rest.service.port port for the service + ## @param tls.rest.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-rest.example.org + ## @param tls.rest.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param tls.rest.ingress.enabled Flag to enable ingress + enabled: false + ## @param tls.rest.ingress.className Ingress class name + className: "" + ## @param tls.rest.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param tls.rest.ingress.annotations [object] Annotations for ingress object + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/ssl-redirect: "true" + # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - ## @param grpc.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. - host: "spire-identity-exchange-grpc" + ## @param tls.rest.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. + host: "spire-identity-exchange-rest" - ## @param grpc.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. - tlsSecret: "" + ## @param tls.rest.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" - ## @param grpc.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. - hosts: [] - # - host: spire-identity-exchange-grpc.example.org - # paths: - # - path: / - # pathType: Prefix + ## @param tls.rest.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + # - host: spire-identity-exchange-rest.example.org + # paths: + # - path: / + # pathType: Prefix - ## @param grpc.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. - tls: [] - # - secretName: chart-example-tls - # hosts: - # - spire-identiy-exchange-grpc.example.org + ## @param tls.rest.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - spire-identity-exchange-rest.example.org - ## Gateway API exposure for the gRPC endpoint. Independent of grpc.ingress. The - ## backend serves HTTPS, so a set tlsSecret => HTTPRoute + BackendTLSPolicy - ## (reencrypt); blank tlsSecret => TLSRoute (SNI passthrough). - gatewayAPI: - ## @param grpc.gatewayAPI.enabled Flag to expose the gRPC endpoint via Gateway API + ## Gateway API exposure for this endpoint. A set tlsSecret gives HTTPRoute (reencrypt); blank gives TLSRoute (SNI passthrough). + gatewayAPI: + ## @param tls.rest.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param tls.rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-rest" + ## @param tls.rest.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. + tlsSecret: "" + ## @param tls.rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param tls.rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param tls.rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param tls.rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + # BackendTLSPolicy (reencrypt) is emitted automatically for the terminated + # HTTPS backend when gatewayAPI.tlsSecret is set. + backendTLS: + ## @param tls.rest.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. + caCertificateRefs: [] + + grpc: + ## @param tls.grpc.enabled Enable the gRPC listener served with the certificate from disk enabled: false - ## @param grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. - host: "spire-identity-exchange-grpc" - ## @param grpc.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. - tlsSecret: "" - ## @param grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) - annotations: {} - listenerSet: - ## @param grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. - enabled: null - ## @param grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) - parentRefs: [] - ## @param grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway - sectionName: "" - # BackendTLSPolicy (reencrypt) is emitted automatically for the terminated - # HTTPS backend when gatewayAPI.tlsSecret is set. - backendTLS: - ## @param grpc.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. - caCertificateRefs: [] + ## @param tls.grpc.port Container port for the gRPC listener served with the certificate from disk + port: 8443 + ## @param tls.grpc.service.type Service type + ## @param tls.grpc.service.port port for the service + ## @param tls.grpc.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-grpc.example.org + ## @param tls.grpc.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param tls.grpc.ingress.enabled Flag to enable ingress + enabled: false + ## @param tls.grpc.ingress.className Ingress class name + className: "" + ## @param tls.grpc.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param tls.grpc.ingress.annotations [object] Annotations for ingress object + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/ssl-redirect: "true" + # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + + ## @param tls.grpc.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. + host: "spire-identity-exchange-grpc" + + ## @param tls.grpc.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + + ## @param tls.grpc.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + # - host: spire-identity-exchange-grpc.example.org + # paths: + # - path: / + # pathType: Prefix + + ## @param tls.grpc.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - spire-identiy-exchange-grpc.example.org + + ## Gateway API exposure for this endpoint. A set tlsSecret gives HTTPRoute (reencrypt); blank gives TLSRoute (SNI passthrough). + gatewayAPI: + ## @param tls.grpc.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param tls.grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-grpc" + ## @param tls.grpc.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. + tlsSecret: "" + ## @param tls.grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param tls.grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param tls.grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param tls.grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + # BackendTLSPolicy (reencrypt) is emitted automatically for the terminated + # HTTPS backend when gatewayAPI.tlsSecret is set. + backendTLS: + ## @param tls.grpc.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. + caCertificateRefs: [] + +# Listeners served with this deployment's own X509-SVID from the Workload API. No certificate files needed. +spiffe: + + rest: + ## @param spiffe.rest.enabled Enable the REST listener served with this deployment's own X509-SVID + enabled: true + ## @param spiffe.rest.port Container port for the REST listener served with this deployment's own X509-SVID + port: 8544 + ## @param spiffe.rest.service.type Service type + ## @param spiffe.rest.service.port port for the service + ## @param spiffe.rest.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-rest-spiffe.example.org + ## @param spiffe.rest.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spiffe.rest.ingress.enabled Flag to enable ingress + enabled: false + ## @param spiffe.rest.ingress.className Ingress class name + className: "" + ## @param spiffe.rest.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spiffe.rest.ingress.annotations [object] Annotations for ingress object + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/ssl-redirect: "true" + # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + + ## @param spiffe.rest.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. + host: "spire-identity-exchange-rest-spiffe" + + ## @param spiffe.rest.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + + ## @param spiffe.rest.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + # - host: spire-identity-exchange-rest-spiffe.example.org + # paths: + # - path: / + # pathType: Prefix + + ## @param spiffe.rest.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - spire-identity-exchange-rest-spiffe.example.org + + ## Gateway API exposure for this endpoint. Always a TLSRoute (SNI passthrough): an X509-SVID has no DNS SAN, so edge termination cannot validate this backend. + gatewayAPI: + ## @param spiffe.rest.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spiffe.rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-rest-spiffe" + ## @param spiffe.rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spiffe.rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spiffe.rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spiffe.rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + + grpc: + ## @param spiffe.grpc.enabled Enable the gRPC listener served with this deployment's own X509-SVID + enabled: false + ## @param spiffe.grpc.port Container port for the gRPC listener served with this deployment's own X509-SVID + port: 8543 + ## @param spiffe.grpc.service.type Service type + ## @param spiffe.grpc.service.port port for the service + ## @param spiffe.grpc.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + # external-dns.alpha.kubernetes.io/hostname: spire-identity-exchange-grpc-spiffe.example.org + ## @param spiffe.grpc.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spiffe.grpc.ingress.enabled Flag to enable ingress + enabled: false + ## @param spiffe.grpc.ingress.className Ingress class name + className: "" + ## @param spiffe.grpc.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spiffe.grpc.ingress.annotations [object] Annotations for ingress object + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/ssl-redirect: "true" + # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + + ## @param spiffe.grpc.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. + host: "spire-identity-exchange-grpc-spiffe" + + ## @param spiffe.grpc.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + + ## @param spiffe.grpc.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + # - host: spire-identity-exchange-grpc-spiffe.example.org + # paths: + # - path: / + # pathType: Prefix + + ## @param spiffe.grpc.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - spire-identiy-exchange-grpc-spiffe.example.org + + ## Gateway API exposure for this endpoint. Always a TLSRoute (SNI passthrough): an X509-SVID has no DNS SAN, so edge termination cannot validate this backend. + gatewayAPI: + ## @param spiffe.grpc.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spiffe.grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-grpc-spiffe" + ## @param spiffe.grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spiffe.grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spiffe.grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spiffe.grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" tools: kubectl: diff --git a/charts/spire-nested/README.md b/charts/spire-nested/README.md index 4318c19..de38714 100644 --- a/charts/spire-nested/README.md +++ b/charts/spire-nested/README.md @@ -199,43 +199,134 @@ Now you can interact with the Spire agent socket from your own application. The ### Global parameters -| Name | Description | Value | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | -| `global.k8s.clusterDomain` | Cluster domain name configured for Spire install | `cluster.local` | -| `global.spire.clusterName` | The name of the k8s cluster for Spire install | `example-cluster` | -| `global.spire.jwtIssuer` | The issuer for Spire JWT tokens. Defaults to oidc-discovery.$trustDomain if unset | `""` | -| `global.spire.trustDomain` | The trust domain for Spire install | `example.org` | -| `global.spire.caSubject.country` | Country for Spire server CA | `""` | -| `global.spire.caSubject.organization` | Organization for Spire server CA | `""` | -| `global.spire.caSubject.commonName` | Common Name for Spire server CA | `""` | -| `global.spire.recommendations.enabled` | Use recommended settings for production deployments. Default is off. | `false` | -| `global.spire.recommendations.namespaceLayout` | Set to true to use recommended values for installing across namespaces | `true` | -| `global.spire.recommendations.namespacePSS` | When chart namespace creation is enabled, label them with preffered Pod Security Standard labels | `true` | -| `global.spire.recommendations.priorityClassName` | Set to true to use recommended values for Pod Priority Class Names | `true` | -| `global.spire.recommendations.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `true` | -| `global.spire.recommendations.securityContexts` | Set to true to use recommended values for Pod and Container Security Contexts | `true` | -| `global.spire.recommendations.prometheus` | Enable prometheus exporters for monitoring | `true` | -| `global.spire.image.registry` | Override all Spire image registries at once | `""` | -| `global.spire.namespaces.create` | Set to true to Create all namespaces. If this or either of the namespace specific create flags is set, the namespace will be created. | `false` | -| `global.spire.namespaces.system.name` | Name of the Spire system Namespace. | `spire-system` | -| `global.spire.namespaces.system.create` | Create a Namespace for Spire system resources. | `false` | -| `global.spire.namespaces.system.annotations` | Annotations to apply to the Spire system Namespace. | `{}` | -| `global.spire.namespaces.system.labels` | Labels to apply to the Spire system Namespace. | `{}` | -| `global.spire.namespaces.server.name` | Name of the Spire server Namespace. | `spire-server` | -| `global.spire.namespaces.server.create` | Create a Namespace for Spire server resources. | `false` | -| `global.spire.namespaces.server.annotations` | Annotations to apply to the Spire server Namespace. | `{}` | -| `global.spire.namespaces.server.labels` | Labels to apply to the Spire server Namespace. | `{}` | -| `global.spire.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `false` | -| `global.spire.ingressControllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `global.spire.tools.kubectl.tag` | Set to force the tag to use for all kubectl instances | `""` | -| `global.installAndUpgradeHooks.enabled` | Enable Helm hooks to autofix common install/upgrade issues (should be disabled when using `helm template`) | `true` | -| `global.deleteHooks.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` | -| `tags.nestedRoot` | Set the chart architecture to root nested | `false` | -| `tags.nestedChildFull` | Set the chart mode to a child cluster with its own nested server | `false` | -| `tags.nestedChildSecurity` | Set the chart mode to a child cluster for use with a security cluster | `false` | -| `tags.haAgentCommon` | Set the chart mode to deploy the common portion of a spire-ha-agent setup | `false` | -| `tags.bottomTurtleHAA` | Setup HA side A for use with a Bottom Turtle architecture | `false` | -| `tags.bottomTurtleHAB` | Setup HA side B for use with a Bottom Turtle architecture | `false` | +| Name | Description | Value | +| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | +| `global.k8s.clusterDomain` | Cluster domain name configured for Spire install | `cluster.local` | +| `global.spire.clusterName` | The name of the k8s cluster for Spire install | `example-cluster` | +| `global.spire.jwtIssuer` | The issuer for Spire JWT tokens. Defaults to oidc-discovery.$trustDomain if unset | `""` | +| `global.spire.trustDomain` | The trust domain for Spire install | `example.org` | +| `global.spire.caSubject.country` | Country for Spire server CA | `""` | +| `global.spire.caSubject.organization` | Organization for Spire server CA | `""` | +| `global.spire.caSubject.commonName` | Common Name for Spire server CA | `""` | +| `global.spire.recommendations.enabled` | Use recommended settings for production deployments. Default is off. | `false` | +| `global.spire.recommendations.namespaceLayout` | Set to true to use recommended values for installing across namespaces | `true` | +| `global.spire.recommendations.namespacePSS` | When chart namespace creation is enabled, label them with preffered Pod Security Standard labels | `true` | +| `global.spire.recommendations.priorityClassName` | Set to true to use recommended values for Pod Priority Class Names | `true` | +| `global.spire.recommendations.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `true` | +| `global.spire.recommendations.securityContexts` | Set to true to use recommended values for Pod and Container Security Contexts | `true` | +| `global.spire.recommendations.prometheus` | Enable prometheus exporters for monitoring | `true` | +| `global.spire.image.registry` | Override all Spire image registries at once | `""` | +| `global.spire.namespaces.create` | Set to true to Create all namespaces. If this or either of the namespace specific create flags is set, the namespace will be created. | `false` | +| `global.spire.namespaces.system.name` | Name of the Spire system Namespace. | `spire-system` | +| `global.spire.namespaces.system.create` | Create a Namespace for Spire system resources. | `false` | +| `global.spire.namespaces.system.annotations` | Annotations to apply to the Spire system Namespace. | `{}` | +| `global.spire.namespaces.system.labels` | Labels to apply to the Spire system Namespace. | `{}` | +| `global.spire.namespaces.server.name` | Name of the Spire server Namespace. | `spire-server` | +| `global.spire.namespaces.server.create` | Create a Namespace for Spire server resources. | `false` | +| `global.spire.namespaces.server.annotations` | Annotations to apply to the Spire server Namespace. | `{}` | +| `global.spire.namespaces.server.labels` | Labels to apply to the Spire server Namespace. | `{}` | +| `global.spire.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `false` | +| `global.spire.ingressControllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `global.spire.gatewayAPI.manageListenerSets` | Default policy for whether services render a ListenerSet for their SNI listener. Each service may override via its gatewayAPI.listenerSet.enabled. | `true` | +| `global.spire.gatewayAPI.gateway.name` | Name of the shared Gateway object that routes and ListenerSets attach to | `spire` | +| `global.spire.gatewayAPI.gateway.namespace` | Namespace of the shared Gateway object. Defaults to the release namespace if blank. | `spire-server` | +| `global.spire.gatewayAPI.gateway.port` | Port the shared Gateway listens on. ListenerSet listeners must match this. | `443` | +| `global.spire.tools.kubectl.tag` | Set to force the tag to use for all kubectl instances | `""` | +| `global.installAndUpgradeHooks.enabled` | Enable Helm hooks to autofix common install/upgrade issues (should be disabled when using `helm template`) | `true` | +| `global.deleteHooks.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` | +| `tags.nestedRoot` | Set the chart architecture to root nested | `false` | +| `tags.nestedChildFull` | Set the chart mode to a child cluster with its own nested server | `false` | +| `tags.nestedChildSecurity` | Set the chart mode to a child cluster for use with a security cluster | `false` | +| `tags.haAgentCommon` | Set the chart mode to deploy the common portion of a spire-ha-agent setup | `false` | +| `tags.bottomTurtleHAA` | Setup HA side A for use with a Bottom Turtle architecture | `false` | +| `tags.bottomTurtleHAB` | Setup HA side B for use with a Bottom Turtle architecture | `false` | +| `gatewayAPI.gateway.enabled` | Render the shared Gateway object | `false` | +| `gatewayAPI.gateway.className` | gatewayClassName for the shared Gateway (e.g. "eg"). Required when enabled. | `""` | +| `gatewayAPI.gateway.annotations` | Annotations for the Gateway object | `{}` | +| `gatewayAPI.gateway.allowedListenersNamespaces` | From which namespaces ListenerSets may attach to the Gateway. One of All, Same, Selector. | `All` | +| `gatewayAPI.gateway.allowedRoutesNamespaces` | From which namespaces routes may attach directly to the base listener (used when ListenerSet management is off). One of All, Same, Selector. | `All` | +| `gatewayAPI.gateway.extraListeners` | Additional listeners to add to the Gateway | `[]` | +| `spireIdentityExchange.podSelector` | Labels selecting the exchange pods of both sides. Narrow it (for example by adding release-namespace) when other exchanges share the namespace. | `{}` | +| `spireIdentityExchange.tls.rest.enabled` | Expose the combined REST endpoint served with the on-disk certificate | `false` | +| `spireIdentityExchange.tls.rest.service.type` | Service type | `ClusterIP` | +| `spireIdentityExchange.tls.rest.service.port` | port for the service | `443` | +| `spireIdentityExchange.tls.rest.service.annotations` | Annotations for service resource | `{}` | +| `spireIdentityExchange.tls.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spireIdentityExchange.tls.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `spireIdentityExchange.tls.rest.ingress.className` | Ingress class name | `""` | +| `spireIdentityExchange.tls.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spireIdentityExchange.tls.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `spireIdentityExchange.tls.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. | `spire-identity-exchange-rest` | +| `spireIdentityExchange.tls.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spireIdentityExchange.tls.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spireIdentityExchange.tls.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spireIdentityExchange.tls.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spireIdentityExchange.tls.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` | +| `spireIdentityExchange.tls.rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `spireIdentityExchange.tls.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spireIdentityExchange.tls.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spireIdentityExchange.tls.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spireIdentityExchange.tls.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `spireIdentityExchange.tls.rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `spireIdentityExchange.tls.grpc.enabled` | Expose the combined gRPC endpoint served with the on-disk certificate | `false` | +| `spireIdentityExchange.tls.grpc.service.type` | Service type | `ClusterIP` | +| `spireIdentityExchange.tls.grpc.service.port` | port for the service | `443` | +| `spireIdentityExchange.tls.grpc.service.annotations` | Annotations for service resource | `{}` | +| `spireIdentityExchange.tls.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spireIdentityExchange.tls.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `spireIdentityExchange.tls.grpc.ingress.className` | Ingress class name | `""` | +| `spireIdentityExchange.tls.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spireIdentityExchange.tls.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `spireIdentityExchange.tls.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. | `spire-identity-exchange-grpc` | +| `spireIdentityExchange.tls.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spireIdentityExchange.tls.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spireIdentityExchange.tls.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `spireIdentityExchange.tls.grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `spireIdentityExchange.spiffe.rest.enabled` | Expose the combined REST endpoint served with each side's own X509-SVID | `false` | +| `spireIdentityExchange.spiffe.rest.service.type` | Service type | `ClusterIP` | +| `spireIdentityExchange.spiffe.rest.service.port` | port for the service | `443` | +| `spireIdentityExchange.spiffe.rest.service.annotations` | Annotations for service resource | `{}` | +| `spireIdentityExchange.spiffe.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spireIdentityExchange.spiffe.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `spireIdentityExchange.spiffe.rest.ingress.className` | Ingress class name | `""` | +| `spireIdentityExchange.spiffe.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spireIdentityExchange.spiffe.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `spireIdentityExchange.spiffe.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. | `spire-identity-exchange-rest-spiffe` | +| `spireIdentityExchange.spiffe.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spireIdentityExchange.spiffe.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spireIdentityExchange.spiffe.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest-spiffe` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spireIdentityExchange.spiffe.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `spireIdentityExchange.spiffe.grpc.enabled` | Expose the combined gRPC endpoint served with each side's own X509-SVID | `false` | +| `spireIdentityExchange.spiffe.grpc.service.type` | Service type | `ClusterIP` | +| `spireIdentityExchange.spiffe.grpc.service.port` | port for the service | `443` | +| `spireIdentityExchange.spiffe.grpc.service.annotations` | Annotations for service resource | `{}` | +| `spireIdentityExchange.spiffe.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spireIdentityExchange.spiffe.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `spireIdentityExchange.spiffe.grpc.ingress.className` | Ingress class name | `""` | +| `spireIdentityExchange.spiffe.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spireIdentityExchange.spiffe.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `spireIdentityExchange.spiffe.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. | `spire-identity-exchange-grpc-spiffe` | +| `spireIdentityExchange.spiffe.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spireIdentityExchange.spiffe.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spireIdentityExchange.spiffe.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc-spiffe` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spireIdentityExchange.spiffe.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | ### Spire agent parameters @@ -384,130 +475,138 @@ Now you can interact with the Spire agent socket from your own application. The ### Spire server parameters -| Name | Description | Value | -| ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------- | -| `internal-spire-server-bottom-turtle-ha-a.nameOverride` | Overrides the name of Spire server pods | `internal-server` | -| `internal-spire-server-bottom-turtle-ha-a.caKeyType` | Key type to use for the ca | `ec-p256` | -| `internal-spire-server-bottom-turtle-ha-a.experimental.enabled` | enable experimental features | `true` | -| `internal-spire-server-bottom-turtle-ha-a.experimental.agentSPIFFEIDAsSelector` | enable adding spiffe_id selectors to all agents | `true` | -| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.enabled` | Enable dynamic registration | `true` | -| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.allowedIDPrefix` | The allowed ID prefix | `spire/agent/x509pop/k8s` | -| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.serviceAccount` | The service account to allow in for dynamic registration | `spire-a-agent` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.parentIDTemplate` | parent id template | `spiffe://{{ .TrustDomain }}/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate dns entries | `false` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.type` | The type of the entry | `oidc-discovery-provider-common` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled` | Enables the spire-ha-agent identity | `true` | -| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.spire-identity-exchange-service.federatesWith` | List of trust domains to federate with | `[]` | -| `internal-spire-server-bottom-turtle-ha-a.persistence.type` | What type to use for peristence | `emptyDir` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.spiffePrefix` | What prefix to use when mode is spiffe | `/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.agentPathTemplate` | Override the default agent path template | `/{{ .PluginName }}/k8s${HELM_ADD_CLUSTER_NAME}/{{ .SVIDPathTrimmed }}` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.addClusterName.spiffePrefix` | Suffix the cluster name onto the spiffePrefix | `true` | -| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.addClusterName.agentPathTemplate` | Suffix the cluster name onto the agentPathTemplate | `true` | -| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | -| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream-a.csi.spiffe.io` | -| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | -| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.address` | Address for upstream Spire server | `spire-server-a` | -| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.port` | The port setting of the root SPIRE server | `8081` | -| `internal-spire-server-bottom-turtle-ha-a.bundleConfigMap` | The name of the configmap to store the downstream bundle | `spire-server-a-bundle` | -| `internal-spire-server-bottom-turtle-ha-a.trustSync.enabled` | Enable trust syncing | `true` | -| `internal-spire-server-bottom-turtle-ha-a.trustSync.domains` | the trust domains to sync | `["spire-ha"]` | +| Name | Description | Value | +| -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `internal-spire-server-bottom-turtle-ha-a.nameOverride` | Overrides the name of Spire server pods | `internal-server` | +| `internal-spire-server-bottom-turtle-ha-a.caKeyType` | Key type to use for the ca | `ec-p256` | +| `internal-spire-server-bottom-turtle-ha-a.experimental.enabled` | enable experimental features | `true` | +| `internal-spire-server-bottom-turtle-ha-a.experimental.agentSPIFFEIDAsSelector` | enable adding spiffe_id selectors to all agents | `true` | +| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.enabled` | Enable dynamic registration | `true` | +| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.allowedIDPrefix` | The allowed ID prefix | `spire/agent/x509pop/k8s` | +| `internal-spire-server-bottom-turtle-ha-a.dynamicRegistration.serviceAccount` | The service account to allow in for dynamic registration | `spire-a-agent` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.parentIDTemplate` | parent id template | `spiffe://{{ .TrustDomain }}/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate dns entries | `false` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.type` | The type of the entry | `oidc-discovery-provider-common` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled` | Enables the spire-ha-agent identity | `true` | +| `internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-identity-exchange-service.federatesWith` | List of trust domains to federate with | `[]` | +| `internal-spire-server-bottom-turtle-ha-a.persistence.type` | What type to use for peristence | `emptyDir` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.spiffePrefix` | What prefix to use when mode is spiffe | `/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.agentPathTemplate` | Override the default agent path template | `/{{ .PluginName }}/k8s${HELM_ADD_CLUSTER_NAME}/{{ .SVIDPathTrimmed }}` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.addClusterName.spiffePrefix` | Suffix the cluster name onto the spiffePrefix | `true` | +| `internal-spire-server-bottom-turtle-ha-a.nodeAttestor.x509POP.addClusterName.agentPathTemplate` | Suffix the cluster name onto the agentPathTemplate | `true` | +| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | +| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream-a.csi.spiffe.io` | +| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | +| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.address` | Address for upstream Spire server | `spire-server-a` | +| `internal-spire-server-bottom-turtle-ha-a.upstreamAuthority.spire.server.port` | The port setting of the root SPIRE server | `8081` | +| `internal-spire-server-bottom-turtle-ha-a.bundleConfigMap` | The name of the configmap to store the downstream bundle | `spire-server-a-bundle` | +| `internal-spire-server-bottom-turtle-ha-a.trustSync.enabled` | Enable trust syncing | `true` | +| `internal-spire-server-bottom-turtle-ha-a.trustSync.domains` | the trust domains to sync | `["spire-ha"]` | ### Spire server parameters -| Name | Description | Value | -| ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| `internal-spire-server-bottom-turtle-ha-b.nameOverride` | Overrides the name of Spire server pods | `internal-server` | -| `internal-spire-server-bottom-turtle-ha-b.caKeyType` | Key type to use for the ca | `ec-p256` | -| `internal-spire-server-bottom-turtle-ha-b.experimental.enabled` | enable experimental features | `true` | -| `internal-spire-server-bottom-turtle-ha-b.experimental.agentSPIFFEIDAsSelector` | enable adding spiffe_id selectors to all agents | `true` | -| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.enabled` | Enable dynamic registration | `true` | -| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.allowedIDPrefix` | The allowed ID prefix | `spire/agent/x509pop/k8s` | -| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.serviceAccount` | The service account to allow in for dynamic registration | `spire-b-agent` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.parentIDTemplate` | parent id template | `spiffe://{{ .TrustDomain }}/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate dns entries | `false` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.type` | The type of the entry | `oidc-discovery-provider-common` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled` | Enables the spire-ha-agent identity | `true` | -| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.spire-identity-exchange-service.federatesWith` | List of trust domains to federate with | `[]` | -| `internal-spire-server-bottom-turtle-ha-b.persistence.type` | What type to use for peristence | `emptyDir` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.spiffePrefix` | What prefix to use when mode is spiffe | `/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.agentPathTemplate` | Override the default agent path template | `/{{ .PluginName }}/k8s${HELM_ADD_CLUSTER_NAME}/{{ .SVIDPathTrimmed }}` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.addClusterName.spiffePrefix` | Suffix the cluster name onto the spiffePrefix | `true` | -| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.addClusterName.agentPathTemplate` | Suffix the cluster name onto the agentPathTemplate | `true` | -| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | -| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream-b.csi.spiffe.io` | -| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | -| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.address` | Address for upstream Spire server | `spire-server-b` | -| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.port` | The port setting of the root SPIRE server | `8081` | -| `internal-spire-server-bottom-turtle-ha-b.bundleConfigMap` | The name of the configmap to store the downstream bundle | `spire-server-b-bundle` | -| `internal-spire-server-bottom-turtle-ha-b.trustSync.enabled` | Enable trust syncing | `true` | -| `internal-spire-server-bottom-turtle-ha-b.trustSync.domains` | the trust domains to sync | `["spire-ha"]` | -| `downstream-spire-agent-bottom-turtle-ha-a.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | -| `downstream-spire-agent-bottom-turtle-ha-a.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | -| `downstream-spire-agent-bottom-turtle-ha-a.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-server-a-bundle` | -| `downstream-spire-agent-bottom-turtle-ha-a.persistence.hostPath` | Which path to use on the host when persistence.type = hostPath | `/var/lib/spire/k8s/downstream-agent-a` | -| `downstream-spire-agent-bottom-turtle-ha-a.dynamicRegistration.enabled` | Enable dynamic registration | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.dynamicRegistration.nameOverride` | The name override to use to contact the server | `internal-server` | -| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | -| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.x509POP.spiffeEndpointSocket` | Where the socket is to use for mode spiffe | `/var/run/spiffe/socat/unix/k8s-spire-agent-a/public/api.sock` | -| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.memory.enabled` | Enable the memory based Key Manager | `false` | -| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.disk.enabled` | Enable the disk key manager | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.disk.mode` | Where the disk plugin will write out its data | `emptyDir` | -| `downstream-spire-agent-bottom-turtle-ha-a.healthChecks.port` | Health check port | `9981` | -| `downstream-spire-agent-bottom-turtle-ha-a.telemetry.prometheus.port` | Prometheus port to use | `9989` | -| `downstream-spire-agent-bottom-turtle-ha-a.socketPath` | Socket path to use | `/var/run/spire/agent/sockets/a/csi.spiffe.io/public/spire-agent.sock` | -| `downstream-spire-agent-bottom-turtle-ha-a.sockets.hostBasePath` | Path on the host to place sockets | `/var/run/spire/agent/sockets/a` | -| `downstream-spire-agent-bottom-turtle-ha-a.sockets.admin.enabled` | Enable admin socket | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.sockets.admin.mountOnHost` | Mount admin socket on host | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.authorizedDelegates` | List of workloads able to use the delegation api | `["/spire-ha-agent"]` | -| `downstream-spire-agent-bottom-turtle-ha-a.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | -| `downstream-spire-agent-bottom-turtle-ha-a.workloadAttestors.k8s.brokerAPI.accessPolicy` | The default accessPolicy | `permissive` | -| `downstream-spire-agent-bottom-turtle-ha-a.workloadAttestors.k8s.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | -| `downstream-spire-agent-bottom-turtle-ha-b.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | -| `downstream-spire-agent-bottom-turtle-ha-b.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-server-b-bundle` | -| `downstream-spire-agent-bottom-turtle-ha-b.persistence.hostPath` | Which path to use on the host when persistence.type = hostPath | `/var/lib/spire/k8s/downstream-agent-b` | -| `downstream-spire-agent-bottom-turtle-ha-b.dynamicRegistration.enabled` | Enable dynamic registration | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.dynamicRegistration.nameOverride` | The name override to use to contact the server | `internal-server` | -| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | -| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.x509POP.spiffeEndpointSocket` | Where the socket is to use for mode spiffe | `/var/run/spiffe/socat/unix/k8s-spire-agent-b/public/api.sock` | -| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.memory.enabled` | Enable the memory based Key Manager | `false` | -| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.disk.enabled` | Enable the disk key manager | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.disk.mode` | Where the disk plugin will write out its data | `emptyDir` | -| `downstream-spire-agent-bottom-turtle-ha-b.healthChecks.port` | Health check port | `9982` | -| `downstream-spire-agent-bottom-turtle-ha-b.telemetry.prometheus.port` | Prometheus port to use | `9990` | -| `downstream-spire-agent-bottom-turtle-ha-b.socketPath` | Socket path to use | `/var/run/spire/agent/sockets/b/csi.spiffe.io/public/spire-agent.sock` | -| `downstream-spire-agent-bottom-turtle-ha-b.sockets.hostBasePath` | Path on the host to place sockets | `/var/run/spire/agent/sockets/b` | -| `downstream-spire-agent-bottom-turtle-ha-b.sockets.admin.enabled` | Enable admin socket | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.sockets.admin.mountOnHost` | Mount admin socket on host | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.authorizedDelegates` | List of workloads able to use the delegation api | `["/spire-ha-agent"]` | -| `downstream-spire-agent-bottom-turtle-ha-b.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | -| `downstream-spire-agent-bottom-turtle-ha-b.workloadAttestors.k8s.brokerAPI.accessPolicy` | The default accessPolicy | `permissive` | -| `downstream-spire-agent-bottom-turtle-ha-b.workloadAttestors.k8s.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.fullnameOverride` | Fullname override | `spiffe-csi-driver-downstream-a` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.agentSocketPath` | path to agent socket | `/var/run/spire/agent/sockets/a/csi.spiffe.io/public/spire-agent.sock` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.pluginName` | The name of the plugin instance | `a.csi.spiffe.io` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.healthChecks.port` | The health check port | `9814` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.fullnameOverride` | Fullname override | `spiffe-csi-driver-downstream-b` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.agentSocketPath` | path to agent socket | `/var/run/spire/agent/sockets/b/csi.spiffe.io/public/spire-agent.sock` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.pluginName` | The name of the plugin instance | `b.csi.spiffe.io` | -| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.healthChecks.port` | The health check port | `9816` | -| `spire-identity-exchange-bottom-turtle-ha-a.enabled` | Enable the spire-identity-exchange | `false` | -| `spire-identity-exchange-bottom-turtle-ha-a.nameOverride` | name override | `identity-exchange` | -| `spire-identity-exchange-bottom-turtle-ha-a.csiDriverName` | CSI driver name to use | `a.csi.spiffe.io` | -| `spire-identity-exchange-bottom-turtle-ha-a.rest.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-a-rest` | -| `spire-identity-exchange-bottom-turtle-ha-a.grpc.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-a-grpc` | -| `spire-identity-exchange-bottom-turtle-ha-a.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | -| `spire-identity-exchange-bottom-turtle-ha-b.enabled` | Enable the spire-identity-exchange | `false` | -| `spire-identity-exchange-bottom-turtle-ha-b.nameOverride` | name override | `identity-exchange` | -| `spire-identity-exchange-bottom-turtle-ha-b.csiDriverName` | CSI driver name to use | `b.csi.spiffe.io` | -| `spire-identity-exchange-bottom-turtle-ha-b.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | -| `spire-identity-exchange-bottom-turtle-ha-b.rest.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-b-rest` | -| `spire-identity-exchange-bottom-turtle-ha-b.grpc.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-b-grpc` | +| Name | Description | Value | +| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `internal-spire-server-bottom-turtle-ha-b.nameOverride` | Overrides the name of Spire server pods | `internal-server` | +| `internal-spire-server-bottom-turtle-ha-b.caKeyType` | Key type to use for the ca | `ec-p256` | +| `internal-spire-server-bottom-turtle-ha-b.experimental.enabled` | enable experimental features | `true` | +| `internal-spire-server-bottom-turtle-ha-b.experimental.agentSPIFFEIDAsSelector` | enable adding spiffe_id selectors to all agents | `true` | +| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.enabled` | Enable dynamic registration | `true` | +| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.allowedIDPrefix` | The allowed ID prefix | `spire/agent/x509pop/k8s` | +| `internal-spire-server-bottom-turtle-ha-b.dynamicRegistration.serviceAccount` | The service account to allow in for dynamic registration | `spire-b-agent` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.parentIDTemplate` | parent id template | `spiffe://{{ .TrustDomain }}/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate dns entries | `false` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.type` | The type of the entry | `oidc-discovery-provider-common` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled` | Enables the spire-ha-agent identity | `true` | +| `internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-identity-exchange-service.federatesWith` | List of trust domains to federate with | `[]` | +| `internal-spire-server-bottom-turtle-ha-b.persistence.type` | What type to use for peristence | `emptyDir` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.spiffePrefix` | What prefix to use when mode is spiffe | `/spire-exchange/k8s${HELM_ADD_CLUSTER_NAME}/` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.agentPathTemplate` | Override the default agent path template | `/{{ .PluginName }}/k8s${HELM_ADD_CLUSTER_NAME}/{{ .SVIDPathTrimmed }}` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.addClusterName.spiffePrefix` | Suffix the cluster name onto the spiffePrefix | `true` | +| `internal-spire-server-bottom-turtle-ha-b.nodeAttestor.x509POP.addClusterName.agentPathTemplate` | Suffix the cluster name onto the agentPathTemplate | `true` | +| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | +| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream-b.csi.spiffe.io` | +| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | +| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.address` | Address for upstream Spire server | `spire-server-b` | +| `internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.port` | The port setting of the root SPIRE server | `8081` | +| `internal-spire-server-bottom-turtle-ha-b.bundleConfigMap` | The name of the configmap to store the downstream bundle | `spire-server-b-bundle` | +| `internal-spire-server-bottom-turtle-ha-b.trustSync.enabled` | Enable trust syncing | `true` | +| `internal-spire-server-bottom-turtle-ha-b.trustSync.domains` | the trust domains to sync | `["spire-ha"]` | +| `downstream-spire-agent-bottom-turtle-ha-a.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | +| `downstream-spire-agent-bottom-turtle-ha-a.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | +| `downstream-spire-agent-bottom-turtle-ha-a.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-server-a-bundle` | +| `downstream-spire-agent-bottom-turtle-ha-a.persistence.hostPath` | Which path to use on the host when persistence.type = hostPath | `/var/lib/spire/k8s/downstream-agent-a` | +| `downstream-spire-agent-bottom-turtle-ha-a.dynamicRegistration.enabled` | Enable dynamic registration | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.dynamicRegistration.nameOverride` | The name override to use to contact the server | `internal-server` | +| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | +| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.nodeAttestor.x509POP.spiffeEndpointSocket` | Where the socket is to use for mode spiffe | `/var/run/spiffe/socat/unix/k8s-spire-agent-a/public/api.sock` | +| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.memory.enabled` | Enable the memory based Key Manager | `false` | +| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.disk.enabled` | Enable the disk key manager | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.keyManager.disk.mode` | Where the disk plugin will write out its data | `emptyDir` | +| `downstream-spire-agent-bottom-turtle-ha-a.healthChecks.port` | Health check port | `9981` | +| `downstream-spire-agent-bottom-turtle-ha-a.telemetry.prometheus.port` | Prometheus port to use | `9989` | +| `downstream-spire-agent-bottom-turtle-ha-a.socketPath` | Socket path to use | `/var/run/spire/agent/sockets/a/csi.spiffe.io/public/spire-agent.sock` | +| `downstream-spire-agent-bottom-turtle-ha-a.sockets.hostBasePath` | Path on the host to place sockets | `/var/run/spire/agent/sockets/a` | +| `downstream-spire-agent-bottom-turtle-ha-a.sockets.admin.enabled` | Enable admin socket | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.sockets.admin.mountOnHost` | Mount admin socket on host | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.authorizedDelegates` | List of workloads able to use the delegation api | `["/spire-ha-agent"]` | +| `downstream-spire-agent-bottom-turtle-ha-a.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | +| `downstream-spire-agent-bottom-turtle-ha-a.workloadAttestors.k8s.brokerAPI.accessPolicy` | The default accessPolicy | `permissive` | +| `downstream-spire-agent-bottom-turtle-ha-a.workloadAttestors.k8s.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | +| `downstream-spire-agent-bottom-turtle-ha-b.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | +| `downstream-spire-agent-bottom-turtle-ha-b.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-server-b-bundle` | +| `downstream-spire-agent-bottom-turtle-ha-b.persistence.hostPath` | Which path to use on the host when persistence.type = hostPath | `/var/lib/spire/k8s/downstream-agent-b` | +| `downstream-spire-agent-bottom-turtle-ha-b.dynamicRegistration.enabled` | Enable dynamic registration | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.dynamicRegistration.nameOverride` | The name override to use to contact the server | `internal-server` | +| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.k8sPSAT.enabled` | Enable the k8s projected access token node attestor | `false` | +| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.x509POP.enabled` | Enable the x509 pop node attestor | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.nodeAttestor.x509POP.spiffeEndpointSocket` | Where the socket is to use for mode spiffe | `/var/run/spiffe/socat/unix/k8s-spire-agent-b/public/api.sock` | +| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.memory.enabled` | Enable the memory based Key Manager | `false` | +| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.disk.enabled` | Enable the disk key manager | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.keyManager.disk.mode` | Where the disk plugin will write out its data | `emptyDir` | +| `downstream-spire-agent-bottom-turtle-ha-b.healthChecks.port` | Health check port | `9982` | +| `downstream-spire-agent-bottom-turtle-ha-b.telemetry.prometheus.port` | Prometheus port to use | `9990` | +| `downstream-spire-agent-bottom-turtle-ha-b.socketPath` | Socket path to use | `/var/run/spire/agent/sockets/b/csi.spiffe.io/public/spire-agent.sock` | +| `downstream-spire-agent-bottom-turtle-ha-b.sockets.hostBasePath` | Path on the host to place sockets | `/var/run/spire/agent/sockets/b` | +| `downstream-spire-agent-bottom-turtle-ha-b.sockets.admin.enabled` | Enable admin socket | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.sockets.admin.mountOnHost` | Mount admin socket on host | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.authorizedDelegates` | List of workloads able to use the delegation api | `["/spire-ha-agent"]` | +| `downstream-spire-agent-bottom-turtle-ha-b.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | +| `downstream-spire-agent-bottom-turtle-ha-b.workloadAttestors.k8s.brokerAPI.accessPolicy` | The default accessPolicy | `permissive` | +| `downstream-spire-agent-bottom-turtle-ha-b.workloadAttestors.k8s.brokerAPI.brokers.spire-ha-agent.enabled` | Enable the spire-ha-agent by default | `true` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.fullnameOverride` | Fullname override | `spiffe-csi-driver-downstream-a` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.agentSocketPath` | path to agent socket | `/var/run/spire/agent/sockets/a/csi.spiffe.io/public/spire-agent.sock` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.pluginName` | The name of the plugin instance | `a.csi.spiffe.io` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-a.healthChecks.port` | The health check port | `9814` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.fullnameOverride` | Fullname override | `spiffe-csi-driver-downstream-b` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.agentSocketPath` | path to agent socket | `/var/run/spire/agent/sockets/b/csi.spiffe.io/public/spire-agent.sock` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.pluginName` | The name of the plugin instance | `b.csi.spiffe.io` | +| `downstream-spiffe-csi-driver-bottom-turtle-ha-b.healthChecks.port` | The health check port | `9816` | +| `spire-identity-exchange-bottom-turtle-ha-a.enabled` | Enable the spire-identity-exchange | `false` | +| `spire-identity-exchange-bottom-turtle-ha-a.nameOverride` | name override | `identity-exchange` | +| `spire-identity-exchange-bottom-turtle-ha-a.csiDriverName` | CSI driver name to use | `a.csi.spiffe.io` | +| `spire-identity-exchange-bottom-turtle-ha-a.tls.rest.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-a-rest` | +| `spire-identity-exchange-bottom-turtle-ha-a.tls.grpc.ingress.host` | Hostname override for the grpc ingress service | `spire-identity-exchange-a-grpc` | +| `spire-identity-exchange-bottom-turtle-ha-a.spiffe.rest.ingress.host` | Hostname override for the SVID-served rest ingress service | `spire-identity-exchange-a-rest-spiffe` | +| `spire-identity-exchange-bottom-turtle-ha-a.spiffe.grpc.ingress.host` | Hostname override for the SVID-served grpc ingress service | `spire-identity-exchange-a-grpc-spiffe` | +| `spire-identity-exchange-bottom-turtle-ha-a.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | +| `spire-identity-exchange-bottom-turtle-ha-a.auth.plugins.spiffe.csiDriverName` | The csi driver the spiffe plugin reads its trust bundle from. The shared ha-agent, since that is what mints the oidc discovery provider's serving svid. | `csi.spiffe.io` | +| `spire-identity-exchange-bottom-turtle-ha-a.auth.plugins.spiffe.config.discoveryURL` | The OIDC discovery provider to fetch keys from. This chart gives it a fullnameOverride, so the keySource convention does not apply. | `https://spiffe-oidc-discovery-provider` | +| `spire-identity-exchange-bottom-turtle-ha-b.enabled` | Enable the spire-identity-exchange | `false` | +| `spire-identity-exchange-bottom-turtle-ha-b.nameOverride` | name override | `identity-exchange` | +| `spire-identity-exchange-bottom-turtle-ha-b.csiDriverName` | CSI driver name to use | `b.csi.spiffe.io` | +| `spire-identity-exchange-bottom-turtle-ha-b.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | +| `spire-identity-exchange-bottom-turtle-ha-b.tls.rest.ingress.host` | Hostname override for the rest ingress service | `spire-identity-exchange-b-rest` | +| `spire-identity-exchange-bottom-turtle-ha-b.tls.grpc.ingress.host` | Hostname override for the grpc ingress service | `spire-identity-exchange-b-grpc` | +| `spire-identity-exchange-bottom-turtle-ha-b.spiffe.rest.ingress.host` | Hostname override for the SVID-served rest ingress service | `spire-identity-exchange-b-rest-spiffe` | +| `spire-identity-exchange-bottom-turtle-ha-b.spiffe.grpc.ingress.host` | Hostname override for the SVID-served grpc ingress service | `spire-identity-exchange-b-grpc-spiffe` | +| `spire-identity-exchange-bottom-turtle-ha-b.auth.plugins.spiffe.csiDriverName` | The csi driver the spiffe plugin reads its trust bundle from. The shared ha-agent, since that is what mints the oidc discovery provider's serving svid. | `csi.spiffe.io` | +| `spire-identity-exchange-bottom-turtle-ha-b.auth.plugins.spiffe.config.discoveryURL` | The OIDC discovery provider to fetch keys from. This chart gives it a fullnameOverride, so the keySource convention does not apply. | `https://spiffe-oidc-discovery-provider` | diff --git a/charts/spire-nested/templates/_helpers.tpl b/charts/spire-nested/templates/_helpers.tpl new file mode 100644 index 0000000..4149892 --- /dev/null +++ b/charts/spire-nested/templates/_helpers.tpl @@ -0,0 +1,64 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "spire-nested.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "spire-nested.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "spire-nested.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "spire-nested.labels" -}} +helm.sh/chart: {{ include "spire-nested.chart" . }} +app.kubernetes.io/name: {{ include "spire-nested.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +The namespace the SPIRE server side components land in. Resolved the same way as +spire-identity-exchange.namespace, so the combined exposure lands beside the exchange +pods it selects — a Service selector is namespace scoped. +*/}} +{{- define "spire-nested.server-namespace" -}} + {{- if and (dig "spire" "recommendations" "enabled" false .Values.global) (dig "spire" "recommendations" "namespaceLayout" true .Values.global) }} + {{- if ne (len (dig "spire" "namespaces" "server" "name" "" .Values.global)) 0 }} + {{- .Values.global.spire.namespaces.server.name }} + {{- else }} + {{- printf "spire-server" }} + {{- end }} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Base name for the combined identity exchange objects. Keyed on the release name so it +reads like the per-side exchanges, which the sides' own releases name spire-a-identity- +exchange / spire-b-identity-exchange — so these never collide with them either. +*/}} +{{- define "spire-nested.identity-exchange-name" -}} +{{- printf "%s-identity-exchange" .Release.Name | trunc 63 | trimSuffix "-" }} +{{- end }} diff --git a/charts/spire-nested/templates/gateway.yaml b/charts/spire-nested/templates/gateway.yaml new file mode 100644 index 0000000..428f33a --- /dev/null +++ b/charts/spire-nested/templates/gateway.yaml @@ -0,0 +1,3 @@ +{{- if .Values.gatewayAPI.gateway.enabled }} +{{- include "spire-lib.gateway-resource" (dict "root" . "gatewayObject" .Values.gatewayAPI.gateway) }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-grpc-gateway.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-gateway.yaml new file mode 100644 index 0000000..1eb4770 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-gateway.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.grpc.enabled .Values.spireIdentityExchange.spiffe.grpc.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-grpc-spiffe" (include "spire-nested.identity-exchange-name" .) -}} +{{/* Passthrough only. These backends serve an X509-SVID, whose only SAN is a + spiffe:// URI, so a BackendTLSPolicy hostname check could never match. */}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spireIdentityExchange.spiffe.grpc.gatewayAPI + "name" $fullName + "namespace" (include "spire-nested.server-namespace" .) + "svcName" $fullName + "port" .Values.spireIdentityExchange.spiffe.grpc.service.port + "labels" (include "spire-nested.labels" .) + "routeKind" "TLSRoute" + "backendTLS" false) }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-grpc-ingress.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-ingress.yaml new file mode 100644 index 0000000..816d1be --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.grpc.enabled .Values.spireIdentityExchange.spiffe.grpc.ingress.enabled -}} +{{- $port := .Values.spireIdentityExchange.spiffe.grpc.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spireIdentityExchange.spiffe.grpc.ingress) }} +{{- $fullName := printf "%s-grpc-spiffe" (include "spire-nested.identity-exchange-name" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spireIdentityExchange.spiffe.grpc.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spireIdentityExchange.spiffe.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spireIdentityExchange.spiffe.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{ include "spire-nested.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spireIdentityExchange.spiffe.grpc.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-grpc-service.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-service.yaml new file mode 100644 index 0000000..73b4c14 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-grpc-service.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.grpc.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-nested.identity-exchange-name" . }}-grpc-spiffe + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{- include "spire-nested.labels" . | nindent 4 }} + {{- with .Values.spireIdentityExchange.spiffe.grpc.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spireIdentityExchange.spiffe.grpc.service.type }} + {{- if and (eq .Values.spireIdentityExchange.spiffe.grpc.service.type "LoadBalancer") .Values.spireIdentityExchange.spiffe.grpc.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spireIdentityExchange.spiffe.grpc.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spireIdentityExchange.spiffe.grpc.service.port }} + targetPort: grpc-spiffe + protocol: TCP + selector: + {{- toYaml .Values.spireIdentityExchange.podSelector | nindent 4 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-rest-gateway.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-rest-gateway.yaml new file mode 100644 index 0000000..a49e880 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-rest-gateway.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.rest.enabled .Values.spireIdentityExchange.spiffe.rest.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-rest-spiffe" (include "spire-nested.identity-exchange-name" .) -}} +{{/* Passthrough only. These backends serve an X509-SVID, whose only SAN is a + spiffe:// URI, so a BackendTLSPolicy hostname check could never match. */}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spireIdentityExchange.spiffe.rest.gatewayAPI + "name" $fullName + "namespace" (include "spire-nested.server-namespace" .) + "svcName" $fullName + "port" .Values.spireIdentityExchange.spiffe.rest.service.port + "labels" (include "spire-nested.labels" .) + "routeKind" "TLSRoute" + "backendTLS" false) }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-rest-ingress.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-rest-ingress.yaml new file mode 100644 index 0000000..e5e4eaa --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-rest-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.rest.enabled .Values.spireIdentityExchange.spiffe.rest.ingress.enabled -}} +{{- $port := .Values.spireIdentityExchange.spiffe.rest.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spireIdentityExchange.spiffe.rest.ingress) }} +{{- $fullName := printf "%s-rest-spiffe" (include "spire-nested.identity-exchange-name" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spireIdentityExchange.spiffe.rest.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spireIdentityExchange.spiffe.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spireIdentityExchange.spiffe.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{ include "spire-nested.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spireIdentityExchange.spiffe.rest.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-spiffe-rest-service.yaml b/charts/spire-nested/templates/identity-exchange-spiffe-rest-service.yaml new file mode 100644 index 0000000..0ba9422 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-spiffe-rest-service.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.spiffe.rest.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-nested.identity-exchange-name" . }}-rest-spiffe + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{- include "spire-nested.labels" . | nindent 4 }} + {{- with .Values.spireIdentityExchange.spiffe.rest.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spireIdentityExchange.spiffe.rest.service.type }} + {{- if and (eq .Values.spireIdentityExchange.spiffe.rest.service.type "LoadBalancer") .Values.spireIdentityExchange.spiffe.rest.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spireIdentityExchange.spiffe.rest.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spireIdentityExchange.spiffe.rest.service.port }} + targetPort: rest-spiffe + protocol: TCP + selector: + {{- toYaml .Values.spireIdentityExchange.podSelector | nindent 4 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-grpc-gateway.yaml b/charts/spire-nested/templates/identity-exchange-tls-grpc-gateway.yaml new file mode 100644 index 0000000..da92ef5 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-grpc-gateway.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.grpc.enabled .Values.spireIdentityExchange.tls.grpc.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-grpc" (include "spire-nested.identity-exchange-name" .) -}} +{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.spireIdentityExchange.tls.grpc.gatewayAPI) -}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spireIdentityExchange.tls.grpc.gatewayAPI + "name" $fullName + "namespace" (include "spire-nested.server-namespace" .) + "svcName" $fullName + "port" .Values.spireIdentityExchange.tls.grpc.service.port + "labels" (include "spire-nested.labels" .) + "routeKind" $routeKind + "backendTLS" (eq $routeKind "HTTPRoute")) }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-grpc-ingress.yaml b/charts/spire-nested/templates/identity-exchange-tls-grpc-ingress.yaml new file mode 100644 index 0000000..b415674 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-grpc-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.grpc.enabled .Values.spireIdentityExchange.tls.grpc.ingress.enabled -}} +{{- $port := .Values.spireIdentityExchange.tls.grpc.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spireIdentityExchange.tls.grpc.ingress) }} +{{- $fullName := printf "%s-grpc" (include "spire-nested.identity-exchange-name" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spireIdentityExchange.tls.grpc.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spireIdentityExchange.tls.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spireIdentityExchange.tls.grpc.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{ include "spire-nested.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spireIdentityExchange.tls.grpc.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-grpc-service.yaml b/charts/spire-nested/templates/identity-exchange-tls-grpc-service.yaml new file mode 100644 index 0000000..21b229a --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-grpc-service.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.grpc.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-nested.identity-exchange-name" . }}-grpc + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{- include "spire-nested.labels" . | nindent 4 }} + {{- with .Values.spireIdentityExchange.tls.grpc.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spireIdentityExchange.tls.grpc.service.type }} + {{- if and (eq .Values.spireIdentityExchange.tls.grpc.service.type "LoadBalancer") .Values.spireIdentityExchange.tls.grpc.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spireIdentityExchange.tls.grpc.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spireIdentityExchange.tls.grpc.service.port }} + targetPort: grpc + protocol: TCP + selector: + {{- toYaml .Values.spireIdentityExchange.podSelector | nindent 4 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-rest-gateway.yaml b/charts/spire-nested/templates/identity-exchange-tls-rest-gateway.yaml new file mode 100644 index 0000000..bfa9598 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-rest-gateway.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.rest.enabled .Values.spireIdentityExchange.tls.rest.gatewayAPI.enabled -}} +{{- $fullName := printf "%s-rest" (include "spire-nested.identity-exchange-name" .) -}} +{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.spireIdentityExchange.tls.rest.gatewayAPI) -}} +{{- include "spire-lib.gateway-routes" (dict + "root" . + "gatewayAPI" .Values.spireIdentityExchange.tls.rest.gatewayAPI + "name" $fullName + "namespace" (include "spire-nested.server-namespace" .) + "svcName" $fullName + "port" .Values.spireIdentityExchange.tls.rest.service.port + "labels" (include "spire-nested.labels" .) + "routeKind" $routeKind + "backendTLS" (eq $routeKind "HTTPRoute")) }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-rest-ingress.yaml b/charts/spire-nested/templates/identity-exchange-tls-rest-ingress.yaml new file mode 100644 index 0000000..e0f1db4 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-rest-ingress.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.rest.enabled .Values.spireIdentityExchange.tls.rest.ingress.enabled -}} +{{- $port := .Values.spireIdentityExchange.tls.rest.service.port }} +{{- $ingressControllerType := include "spire-lib.ingress-controller-type" (dict "global" .Values.global "ingress" .Values.spireIdentityExchange.tls.rest.ingress) }} +{{- $fullName := printf "%s-rest" (include "spire-nested.identity-exchange-name" .) }} +{{- $path := "/" }} +{{- $pathType := "Prefix" }} +{{- $tlsSection := true }} +{{- $annotations := deepCopy .Values.spireIdentityExchange.tls.rest.ingress.annotations }} +{{- if eq $ingressControllerType "ingress-nginx" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/force-ssl-redirect" "true" }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/backend-protocol" "HTTPS" }} +{{- if not .Values.spireIdentityExchange.tls.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "nginx.ingress.kubernetes.io/ssl-passthrough" "true" }} +{{- end }} +{{- else if eq $ingressControllerType "openshift" }} +{{- if .Values.spireIdentityExchange.tls.rest.ingress.tlsSecret }} +{{- $_ := set $annotations "route.openshift.io/termination" "reencrypt" }} +{{- else }} +{{- $_ := set $annotations "route.openshift.io/termination" "passthrough" }} +{{- end }} +{{- $path = "" }} +{{- $pathType = "ImplementationSpecific" }} +{{- $tlsSection = false }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{ include "spire-nested.labels" . | nindent 4 }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{ include "spire-lib.ingress-spec" (dict "ingress" .Values.spireIdentityExchange.tls.rest.ingress "svcName" $fullName "port" $port "path" $path "pathType" $pathType "tlsSection" $tlsSection "Values" .Values) | nindent 2 }} +{{- end }} diff --git a/charts/spire-nested/templates/identity-exchange-tls-rest-service.yaml b/charts/spire-nested/templates/identity-exchange-tls-rest-service.yaml new file mode 100644 index 0000000..869ee85 --- /dev/null +++ b/charts/spire-nested/templates/identity-exchange-tls-rest-service.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.tags.haAgentCommon .Values.spireIdentityExchange.tls.rest.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "spire-nested.identity-exchange-name" . }}-rest + namespace: {{ include "spire-nested.server-namespace" . }} + labels: + {{- include "spire-nested.labels" . | nindent 4 }} + {{- with .Values.spireIdentityExchange.tls.rest.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.spireIdentityExchange.tls.rest.service.type }} + {{- if and (eq .Values.spireIdentityExchange.tls.rest.service.type "LoadBalancer") .Values.spireIdentityExchange.tls.rest.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.spireIdentityExchange.tls.rest.service.loadBalancerIP }} + {{- end }} + ports: + - name: https + port: {{ .Values.spireIdentityExchange.tls.rest.service.port }} + targetPort: rest + protocol: TCP + selector: + {{- toYaml .Values.spireIdentityExchange.podSelector | nindent 4 }} +{{- end }} diff --git a/charts/spire-nested/values.yaml b/charts/spire-nested/values.yaml index b551356..c5406d9 100644 --- a/charts/spire-nested/values.yaml +++ b/charts/spire-nested/values.yaml @@ -73,6 +73,24 @@ global: ## @param global.spire.ingressControllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. ingressControllerType: "" + ## Shared Gateway that routes and ListenerSets attach to. The Gateway object itself is + ## rendered by the `gatewayAPI.gateway` block of this chart. Gateway API support is + ## independent of ingress and can be enabled alongside it. + gatewayAPI: + ## @param global.spire.gatewayAPI.manageListenerSets Default policy for whether services render a ListenerSet for their SNI listener. Each service may override via its gatewayAPI.listenerSet.enabled. + manageListenerSets: true + gateway: + ## @param global.spire.gatewayAPI.gateway.name Name of the shared Gateway object that routes and ListenerSets attach to + name: spire + ## The Gateway object and every route's parentRef both read this value, so they + ## cannot disagree. It defaults to the server namespace so the Gateway sits with + ## the workloads it fronts; a ListenerSet in another namespace still attaches, as + ## the Gateway allows listeners from all namespaces by default. + ## @param global.spire.gatewayAPI.gateway.namespace Namespace of the shared Gateway object. Defaults to the release namespace if blank. + namespace: spire-server + ## @param global.spire.gatewayAPI.gateway.port Port the shared Gateway listens on. ListenerSet listeners must match this. + port: 443 + tools: kubectl: ## @param global.spire.tools.kubectl.tag Set to force the tag to use for all kubectl instances @@ -108,6 +126,233 @@ tags: ## @param tags.bottomTurtleHAB Setup HA side B for use with a Bottom Turtle architecture bottomTurtleHAB: false +## The shared Gateway that routes and ListenerSets attach to. Only one release in a cluster +## should render it. name/namespace/port come from global.spire.gatewayAPI.gateway; the +## class and listener policy are local. +## +gatewayAPI: + gateway: + ## @param gatewayAPI.gateway.enabled Render the shared Gateway object + enabled: false + ## @param gatewayAPI.gateway.className gatewayClassName for the shared Gateway (e.g. "eg"). Required when enabled. + className: "" + ## @param gatewayAPI.gateway.annotations [object] Annotations for the Gateway object + annotations: {} + ## @param gatewayAPI.gateway.allowedListenersNamespaces From which namespaces ListenerSets may attach to the Gateway. One of All, Same, Selector. + allowedListenersNamespaces: All + ## @param gatewayAPI.gateway.allowedRoutesNamespaces From which namespaces routes may attach directly to the base listener (used when ListenerSet management is off). One of All, Same, Selector. + allowedRoutesNamespaces: All + ## @param gatewayAPI.gateway.extraListeners [array] Additional listeners to add to the Gateway + extraListeners: [] + +## Combined exposure fronting the spire-identity-exchange of both HA sides. Only rendered +## with tags.haAgentCommon; the per-side exposures under bottomTurtleHAA/B are untouched. +## Each endpoint gets one Service selecting both sides' exchange pods, so the Ingress and +## the Gateway API route need only a single backend. The matching listener must be enabled +## on the sides themselves; if it is not, the Service simply has no endpoints. +spireIdentityExchange: + ## @param spireIdentityExchange.podSelector [object] Labels selecting the exchange pods of both sides. Narrow it (for example by adding release-namespace) when other exchanges share the namespace. + podSelector: + component: spire-identity-exchange + + ## Endpoints served with the certificate each side loads from disk. + tls: + rest: + ## @param spireIdentityExchange.tls.rest.enabled Expose the combined REST endpoint served with the on-disk certificate + enabled: false + ## @param spireIdentityExchange.tls.rest.service.type Service type + ## @param spireIdentityExchange.tls.rest.service.port port for the service + ## @param spireIdentityExchange.tls.rest.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + ## @param spireIdentityExchange.tls.rest.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spireIdentityExchange.tls.rest.ingress.enabled Flag to enable ingress + enabled: false + ## @param spireIdentityExchange.tls.rest.ingress.className Ingress class name + className: "" + ## @param spireIdentityExchange.tls.rest.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spireIdentityExchange.tls.rest.ingress.annotations [object] Annotations for ingress object + annotations: {} + ## @param spireIdentityExchange.tls.rest.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. + host: "spire-identity-exchange-rest" + ## @param spireIdentityExchange.tls.rest.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + ## @param spireIdentityExchange.tls.rest.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + ## @param spireIdentityExchange.tls.rest.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + ## Gateway API exposure for this endpoint. A set tlsSecret gives HTTPRoute (reencrypt); blank gives TLSRoute (SNI passthrough). + gatewayAPI: + ## @param spireIdentityExchange.tls.rest.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spireIdentityExchange.tls.rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-rest" + ## @param spireIdentityExchange.tls.rest.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. + tlsSecret: "" + ## @param spireIdentityExchange.tls.rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spireIdentityExchange.tls.rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spireIdentityExchange.tls.rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spireIdentityExchange.tls.rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + backendTLS: + ## @param spireIdentityExchange.tls.rest.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. + caCertificateRefs: [] + grpc: + ## @param spireIdentityExchange.tls.grpc.enabled Expose the combined gRPC endpoint served with the on-disk certificate + enabled: false + ## @param spireIdentityExchange.tls.grpc.service.type Service type + ## @param spireIdentityExchange.tls.grpc.service.port port for the service + ## @param spireIdentityExchange.tls.grpc.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + ## @param spireIdentityExchange.tls.grpc.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spireIdentityExchange.tls.grpc.ingress.enabled Flag to enable ingress + enabled: false + ## @param spireIdentityExchange.tls.grpc.ingress.className Ingress class name + className: "" + ## @param spireIdentityExchange.tls.grpc.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spireIdentityExchange.tls.grpc.ingress.annotations [object] Annotations for ingress object + annotations: {} + ## @param spireIdentityExchange.tls.grpc.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. + host: "spire-identity-exchange-grpc" + ## @param spireIdentityExchange.tls.grpc.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + ## @param spireIdentityExchange.tls.grpc.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + ## @param spireIdentityExchange.tls.grpc.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + ## Gateway API exposure for this endpoint. A set tlsSecret gives HTTPRoute (reencrypt); blank gives TLSRoute (SNI passthrough). + gatewayAPI: + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-grpc" + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough. + tlsSecret: "" + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + backendTLS: + ## @param spireIdentityExchange.tls.grpc.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. + caCertificateRefs: [] + + ## Endpoints served with each side's own X509-SVID. Gateway API is always a TLSRoute + ## (SNI passthrough) here: an X509-SVID has no DNS SAN, so edge termination could never + ## validate these backends. + spiffe: + rest: + ## @param spireIdentityExchange.spiffe.rest.enabled Expose the combined REST endpoint served with each side's own X509-SVID + enabled: false + ## @param spireIdentityExchange.spiffe.rest.service.type Service type + ## @param spireIdentityExchange.spiffe.rest.service.port port for the service + ## @param spireIdentityExchange.spiffe.rest.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + ## @param spireIdentityExchange.spiffe.rest.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spireIdentityExchange.spiffe.rest.ingress.enabled Flag to enable ingress + enabled: false + ## @param spireIdentityExchange.spiffe.rest.ingress.className Ingress class name + className: "" + ## @param spireIdentityExchange.spiffe.rest.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spireIdentityExchange.spiffe.rest.ingress.annotations [object] Annotations for ingress object + annotations: {} + ## @param spireIdentityExchange.spiffe.rest.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. + host: "spire-identity-exchange-rest-spiffe" + ## @param spireIdentityExchange.spiffe.rest.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + ## @param spireIdentityExchange.spiffe.rest.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + ## @param spireIdentityExchange.spiffe.rest.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + ## Gateway API exposure for this endpoint. Always a TLSRoute (SNI passthrough). + gatewayAPI: + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-rest-spiffe" + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spireIdentityExchange.spiffe.rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + grpc: + ## @param spireIdentityExchange.spiffe.grpc.enabled Expose the combined gRPC endpoint served with each side's own X509-SVID + enabled: false + ## @param spireIdentityExchange.spiffe.grpc.service.type Service type + ## @param spireIdentityExchange.spiffe.grpc.service.port port for the service + ## @param spireIdentityExchange.spiffe.grpc.service.annotations Annotations for service resource + ## + service: + type: ClusterIP + port: 443 + annotations: {} + ## @param spireIdentityExchange.spiffe.grpc.service.loadBalancerIP IP address to assign to load balancer (if supported) + loadBalancerIP: "" + ingress: + ## @param spireIdentityExchange.spiffe.grpc.ingress.enabled Flag to enable ingress + enabled: false + ## @param spireIdentityExchange.spiffe.grpc.ingress.className Ingress class name + className: "" + ## @param spireIdentityExchange.spiffe.grpc.ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + controllerType: "" + ## @param spireIdentityExchange.spiffe.grpc.ingress.annotations [object] Annotations for ingress object + annotations: {} + ## @param spireIdentityExchange.spiffe.grpc.ingress.host Host name for the ingress. If no '.' in host, trustDomain is automatically appended. Must differ from the per-side hosts. + host: "spire-identity-exchange-grpc-spiffe" + ## @param spireIdentityExchange.spiffe.grpc.ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. + tlsSecret: "" + ## @param spireIdentityExchange.spiffe.grpc.ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + hosts: [] + ## @param spireIdentityExchange.spiffe.grpc.ingress.tls [array] Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + tls: [] + ## Gateway API exposure for this endpoint. Always a TLSRoute (SNI passthrough). + gatewayAPI: + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.enabled Flag to expose the endpoint via Gateway API + enabled: false + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended. + host: "spire-identity-exchange-grpc-spiffe" + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet) + annotations: {} + listenerSet: + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. + enabled: null + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach) + parentRefs: [] + ## @param spireIdentityExchange.spiffe.grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway + sectionName: "" + ## subcharts ## @section Spire agent parameters @@ -497,10 +742,10 @@ internal-spire-server-bottom-turtle-ha-a: spire-ha-agent: ## @param internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled Enables the spire-ha-agent identity enabled: true - spire-identity-exchange-service: - ## @param internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.spire-identity-exchange-service.federatesWith [array] List of trust domains to federate with - federatesWith: - - spire-ha + spire-identity-exchange-service: + ## @param internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-identity-exchange-service.federatesWith [array] List of trust domains to federate with + federatesWith: + - spire-ha persistence: ## @param internal-spire-server-bottom-turtle-ha-a.persistence.type What type to use for peristence type: emptyDir @@ -579,10 +824,10 @@ internal-spire-server-bottom-turtle-ha-b: spire-ha-agent: ## @param internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.enabled Enables the spire-ha-agent identity enabled: true - spire-identity-exchange-service: - ## @param internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.spire-identity-exchange-service.federatesWith [array] List of trust domains to federate with - federatesWith: - - spire-ha + spire-identity-exchange-service: + ## @param internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-identity-exchange-service.federatesWith [array] List of trust domains to federate with + federatesWith: + - spire-ha persistence: ## @param internal-spire-server-bottom-turtle-ha-b.persistence.type What type to use for peristence type: emptyDir @@ -799,17 +1044,35 @@ spire-identity-exchange-bottom-turtle-ha-a: nameOverride: identity-exchange ## @param spire-identity-exchange-bottom-turtle-ha-a.csiDriverName CSI driver name to use csiDriverName: a.csi.spiffe.io - rest: - ingress: - ## @param spire-identity-exchange-bottom-turtle-ha-a.rest.ingress.host Hostname override for the rest ingress service - host: "spire-identity-exchange-a-rest" - grpc: - ingress: - ## @param spire-identity-exchange-bottom-turtle-ha-a.grpc.ingress.host Hostname override for the rest ingress service - host: "spire-identity-exchange-a-grpc" + tls: + rest: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-a.tls.rest.ingress.host Hostname override for the rest ingress service + host: "spire-identity-exchange-a-rest" + grpc: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-a.tls.grpc.ingress.host Hostname override for the grpc ingress service + host: "spire-identity-exchange-a-grpc" + spiffe: + rest: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-a.spiffe.rest.ingress.host Hostname override for the SVID-served rest ingress service + host: "spire-identity-exchange-a-rest-spiffe" + grpc: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-a.spiffe.grpc.ingress.host Hostname override for the SVID-served grpc ingress service + host: "spire-identity-exchange-a-grpc-spiffe" server: ## @param spire-identity-exchange-bottom-turtle-ha-a.server.nameOverride The name override setting of the internal SPIRE server nameOverride: internal-server + auth: + plugins: + spiffe: + ## @param spire-identity-exchange-bottom-turtle-ha-a.auth.plugins.spiffe.csiDriverName The csi driver the spiffe plugin reads its trust bundle from. The shared ha-agent, since that is what mints the oidc discovery provider's serving svid. + csiDriverName: csi.spiffe.io + config: + ## @param spire-identity-exchange-bottom-turtle-ha-a.auth.plugins.spiffe.config.discoveryURL The OIDC discovery provider to fetch keys from. This chart gives it a fullnameOverride, so the keySource convention does not apply. + discoveryURL: https://spiffe-oidc-discovery-provider spire-identity-exchange-bottom-turtle-ha-b: ## @param spire-identity-exchange-bottom-turtle-ha-b.enabled Enable the spire-identity-exchange @@ -821,11 +1084,29 @@ spire-identity-exchange-bottom-turtle-ha-b: server: ## @param spire-identity-exchange-bottom-turtle-ha-b.server.nameOverride The name override setting of the internal SPIRE server nameOverride: internal-server - rest: - ingress: - ## @param spire-identity-exchange-bottom-turtle-ha-b.rest.ingress.host Hostname override for the rest ingress service - host: "spire-identity-exchange-b-rest" - grpc: - ingress: - ## @param spire-identity-exchange-bottom-turtle-ha-b.grpc.ingress.host Hostname override for the rest ingress service - host: "spire-identity-exchange-b-grpc" + tls: + rest: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-b.tls.rest.ingress.host Hostname override for the rest ingress service + host: "spire-identity-exchange-b-rest" + grpc: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-b.tls.grpc.ingress.host Hostname override for the grpc ingress service + host: "spire-identity-exchange-b-grpc" + spiffe: + rest: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-b.spiffe.rest.ingress.host Hostname override for the SVID-served rest ingress service + host: "spire-identity-exchange-b-rest-spiffe" + grpc: + ingress: + ## @param spire-identity-exchange-bottom-turtle-ha-b.spiffe.grpc.ingress.host Hostname override for the SVID-served grpc ingress service + host: "spire-identity-exchange-b-grpc-spiffe" + auth: + plugins: + spiffe: + ## @param spire-identity-exchange-bottom-turtle-ha-b.auth.plugins.spiffe.csiDriverName The csi driver the spiffe plugin reads its trust bundle from. The shared ha-agent, since that is what mints the oidc discovery provider's serving svid. + csiDriverName: csi.spiffe.io + config: + ## @param spire-identity-exchange-bottom-turtle-ha-b.auth.plugins.spiffe.config.discoveryURL The OIDC discovery provider to fetch keys from. This chart gives it a fullnameOverride, so the keySource convention does not apply. + discoveryURL: https://spiffe-oidc-discovery-provider diff --git a/examples/bottom-turtle-ha/federation-test-job.yaml b/examples/bottom-turtle-ha/federation-test-job.yaml index 26923d3..459ecf9 100644 --- a/examples/bottom-turtle-ha/federation-test-job.yaml +++ b/examples/bottom-turtle-ha/federation-test-job.yaml @@ -36,10 +36,10 @@ spec: i=0 while [ "$i" -lt 60 ]; do if XOUT=$(/opt/spire/bin/spire-agent api fetch x509 -socketPath "$SOCK" -write /data -timeout 5s 2>&1) && - echo "$XOUT" | /data/busybox grep -q "for trust domain other.org" && + echo "$XOUT" | /data/busybox grep -q "for trust domain other.invalid" && JOUT=$(/opt/spire/bin/spire-agent api fetch jwt -audience test -socketPath "$SOCK" -timeout 5s 2>&1) && - echo "$JOUT" | /data/busybox grep -q "bundle(other.org)"; then - # The other.org bundle was statically set to the same single CA on both sides, + echo "$JOUT" | /data/busybox grep -q "bundle(other.invalid)"; then + # Both sides were seeded with the same single-CA other.invalid bundle at install, # so every federated bundle delivered must contain exactly one certificate. for f in /data/federated_bundle.*.pem; do COUNT=$(/data/busybox grep -c "BEGIN CERTIFICATE" "$f") diff --git a/examples/bottom-turtle-ha/run-tests.sh b/examples/bottom-turtle-ha/run-tests.sh index 1b6428f..e5a69e1 100755 --- a/examples/bottom-turtle-ha/run-tests.sh +++ b/examples/bottom-turtle-ha/run-tests.sh @@ -31,8 +31,13 @@ done # With -b, test the spire-ha-agent broker api instead of the delegated api. # Broker mode also supports federated trust bundles, so federate the ha-agent's own entry and a -# dedicated federation-test workload entry with the other.org trust domain on both sides. Delegated +# dedicated federation-test workload entry with the other.invalid trust domain on both sides. Delegated # mode only tolerates the local and spire-ha bundles, so none of this may apply without -b. + +# Placeholder bundle endpoint for other.invalid. Its ClusterFederatedTrustDomain carries the bundle +# verbatim, but the CRD requires an endpoint alongside it. This name is never meant to answer, it +# just has to be ours: .invalid can never be registered, and coredns pins it to 127.0.0.1 below. +FEDERATION_ENDPOINT_HOST=spire-server-federation.other.invalid BROKER_MODE_ARGS=() BROKER_SOCKET_ARGS_A=() BROKER_SOCKET_ARGS_B=() @@ -41,15 +46,15 @@ if [ "${BROKER}" -eq 1 ]; then BROKER_SOCKET_ARGS_A=( --set downstream-spire-agent-bottom-turtle-ha-a.sockets.broker.enabled=true --set downstream-spire-agent-bottom-turtle-ha-a.sockets.broker.mountOnHost=true - --set 'internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.federatesWith={spire-ha,other.org}' - --set 'internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.federation-test.federatesWith={other.org}' + --set 'internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.federatesWith={spire-ha,other.invalid}' + --set 'internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.federation-test.federatesWith={other.invalid}' --set 'internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterSPIFFEIDs.federation-test.podSelector.matchLabels.app=federation-test' ) BROKER_SOCKET_ARGS_B=( --set downstream-spire-agent-bottom-turtle-ha-b.sockets.broker.enabled=true --set downstream-spire-agent-bottom-turtle-ha-b.sockets.broker.mountOnHost=true - --set 'internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.federatesWith={spire-ha,other.org}' - --set 'internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.federation-test.federatesWith={other.org}' + --set 'internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.spire-ha-agent.federatesWith={spire-ha,other.invalid}' + --set 'internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.federation-test.federatesWith={other.invalid}' --set 'internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterSPIFFEIDs.federation-test.podSelector.matchLabels.app=federation-test' ) fi @@ -95,6 +100,7 @@ teardown() { kubectl describe daemonset pods -n spire-system || true kubectl get configmap -n spire-system || true kubectl get configmap -n spire-system spire-a-agent-downstream -o yaml || true + kubectl get endpoints -n spire-server -o yaml || true print_helm_releases @@ -204,17 +210,38 @@ if [ "${BROKER}" -eq 1 ]; then BUSYBOX_IMAGE=$(helm template t charts/spire -s charts/spiffe-oidc-discovery-provider/templates/tests/test-keys.yaml --values "${COMMON_TEST_YOUR_VALUES}" --set spiffe-oidc-discovery-provider.enabled=true | yq e 'select(.kind=="Pod") | .spec.initContainers[] | select(.name=="static-busybox") | .image' -) echo "federation test job images: ${AGENT_IMAGE} ${BUSYBOX_IMAGE}" - # Mint a trust bundle for a foreign trust domain (other.org) to test federated trust bundle + # Mint a trust bundle for a foreign trust domain (other.invalid) to test federated trust bundle # support. A throwaway third spire-server instance produces a genuine spiffe format bundle # carrying both x509 and jwt authorities. The instance env file overrides the global trust # domain since systemd applies later EnvironmentFiles last. - sudo /bin/bash -c '(echo SPIFFE_TRUST_DOMAIN=other.org; echo SPIRE_BIND_PORT=8083) > /etc/spire/server/other.env' + sudo /bin/bash -c '(echo SPIFFE_TRUST_DOMAIN=other.invalid; echo SPIRE_BIND_PORT=8083) > /etc/spire/server/other.env' sudo systemctl start spire-server@other wait_for_healthcheck spire-server /run/spire/server/sockets/other/private/api.sock - sudo spire-server bundle show -format spiffe -socketPath /run/spire/server/sockets/other/private/api.sock | sudo tee /tmp/other-org-bundle.json > /dev/null + sudo spire-server bundle show -format spiffe -socketPath /run/spire/server/sockets/other/private/api.sock | sudo tee /tmp/other-invalid-bundle.json > /dev/null sudo systemctl stop spire-server@other - grep -q '"x509-svid"' /tmp/other-org-bundle.json - grep -q '"jwt-svid"' /tmp/other-org-bundle.json + grep -q '"x509-svid"' /tmp/other-invalid-bundle.json + grep -q '"jwt-svid"' /tmp/other-invalid-bundle.json + + # Seed the bundle into each server's ClusterFederatedTrustDomain so the controller manager can + # create the entries that federate with other.invalid on its first reconcile. Loading it after + # the install instead leaves the ha-agent without an SVID for the whole helm --wait window. + # The CRD requires an endpoint even when the bundle is supplied verbatim; .invalid can never be + # registered and coredns pins the name locally, so the endpoint never answers. That is fine, + # spire keeps a federated bundle when a refresh fails. + FTD_A=internal-spire-server-bottom-turtle-ha-a.controllerManager.identities.clusterFederatedTrustDomains.other + FTD_B=internal-spire-server-bottom-turtle-ha-b.controllerManager.identities.clusterFederatedTrustDomains.other + BROKER_SOCKET_ARGS_A+=( + --set "${FTD_A}.trustDomain=other.invalid" + --set "${FTD_A}.bundleEndpointProfile.type=https_web" + --set "${FTD_A}.bundleEndpointURL=https://${FEDERATION_ENDPOINT_HOST}" + --set-file "${FTD_A}.trustDomainBundle=/tmp/other-invalid-bundle.json" + ) + BROKER_SOCKET_ARGS_B+=( + --set "${FTD_B}.trustDomain=other.invalid" + --set "${FTD_B}.bundleEndpointProfile.type=https_web" + --set "${FTD_B}.bundleEndpointURL=https://${FEDERATION_ENDPOINT_HOST}" + --set-file "${FTD_B}.trustDomainBundle=/tmp/other-invalid-bundle.json" + ) fi # register some workloads with the spire server using manifests @@ -305,7 +332,7 @@ common_test_url "$IP" # Get the host IP And add spire-server-[ab].${trust_domain} records to it so the spire-servers can talk back to root servers running on the host HOSTIP=$(ip addr show docker0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) kubectl get configmap -n kube-system coredns -o yaml | grep hosts || kubectl get configmap -n kube-system coredns -o yaml | sed "/ready/a\ hosts {\n fallthrough\n }" | kubectl apply -f - -kubectl get configmap -n kube-system coredns -o yaml | grep production.other || kubectl get configmap -n kube-system coredns -o yaml | sed "/hosts/a\ $HOSTIP spire-server-a.production.other\n $HOSTIP oidc-discovery.production.other\n $HOSTIP spire-server-b.production.other\n" | kubectl apply -f - +kubectl get configmap -n kube-system coredns -o yaml | grep production.other || kubectl get configmap -n kube-system coredns -o yaml | sed "/hosts/a\ $HOSTIP spire-server-a.production.other\n $IP oidc-discovery.production.other\n $HOSTIP spire-server-b.production.other\n 127.0.0.1 $FEDERATION_ENDPOINT_HOST\n" | kubectl apply -f - kubectl rollout restart -n kube-system deployment/coredns kubectl rollout status -n kube-system -w --timeout=1m deploy/coredns @@ -330,19 +357,13 @@ kubectl create secret tls -n spire-server spire-identity-exchange --key=certs/se # Install server side a helm upgrade --install --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/spire-values.yaml" \ - --wait spire-a charts/spire-nested \ + --wait --timeout 7m spire-a charts/spire-nested \ --set tags.bottomTurtleHAA=true \ --values "${SCRIPTPATH}/spire-identity-exchange-values.yaml" \ --set "spire-identity-exchange-bottom-turtle-ha-a.enabled=true" \ --set "global.spire.ingressControllerType=ingress-nginx" \ "${BROKER_SOCKET_ARGS_A[@]}" -if [ "${BROKER}" -eq 1 ]; then - # Install the other.org bundle so the controller manager can create the entries that federate - # with it. It retries any entries that failed with "unable to find federated bundle". - kubectl exec -i -n spire-server spire-a-internal-server-0 -- spire-server bundle set -format spiffe -id spiffe://other.org < /tmp/other-org-bundle.json -fi - docker exec -i chart-testing-worker /bin/bash -c "more /var/lib/kubelet/pods/*/volumes/kubernetes.io~empty-dir/disk-keymanager/keys.json /var/lib/kubelet/pods/*/volumes/kubernetes.io~empty-dir/spire-agent-persistence/agent-data.json | cat" # Rollout just to sped up the tests @@ -356,7 +377,7 @@ curl -k --resolve "oidc-discovery.production.other:443:$IP" "https://oidc-discov # Install server side b helm upgrade --install --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/spire-values.yaml" \ - --wait spire-b charts/spire-nested \ + --wait --timeout 7m spire-b charts/spire-nested \ --set tags.bottomTurtleHAB=true \ --set internal-spire-server-bottom-turtle-ha-b.upstreamAuthority.spire.server.port=8082 \ --values "${SCRIPTPATH}/spire-identity-exchange-values.yaml" \ @@ -365,10 +386,10 @@ helm upgrade --install --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUE "${BROKER_SOCKET_ARGS_B[@]}" if [ "${BROKER}" -eq 1 ]; then - kubectl exec -i -n spire-server spire-b-internal-server-0 -- spire-server bundle set -format spiffe -id spiffe://other.org < /tmp/other-org-bundle.json - # Both sides' spire-ha-agent entries must federate with other.org before the workload test. - wait_for_entry_federation spire-a-internal-server-0 other.org - wait_for_entry_federation spire-b-internal-server-0 other.org + # Both sides' spire-ha-agent entries must federate with other.invalid before the workload test. + # The bundle came in with the install, so this should already be true rather than waited on. + wait_for_entry_federation spire-a-internal-server-0 other.invalid + wait_for_entry_federation spire-b-internal-server-0 other.invalid fi docker ps @@ -398,11 +419,11 @@ curl -k --resolve "oidc-discovery.production.other:443:$IP" "https://oidc-discov kubectl apply -f "${SCRIPTPATH}/test-job.yaml" kubectl wait --for=condition=complete --timeout=60s job/test && \ TOKEN=$(kubectl logs job/test) -curl -f -H "Authorization: Bearer ${TOKEN}" -X POST --resolve "spire-identity-exchange-a-rest.production.other:443:$IP" "https://spire-identity-exchange-a-rest.production.other/api/v1/svid/k8s_psat/x509" -k -sS -q -curl -f -H "Authorization: Bearer ${TOKEN}" -X POST --resolve "spire-identity-exchange-b-rest.production.other:443:$IP" "https://spire-identity-exchange-b-rest.production.other/api/v1/svid/k8s_psat/x509" -k -sS -q +curl --fail-with-body -H "Authorization: Bearer ${TOKEN}" -X POST --resolve "spire-identity-exchange-a-rest.production.other:443:$IP" "https://spire-identity-exchange-a-rest.production.other/api/v1/svid/k8s_psat/x509" -k -sS -q +curl --fail-with-body -H "Authorization: Bearer ${TOKEN}" -X POST --resolve "spire-identity-exchange-b-rest.production.other:443:$IP" "https://spire-identity-exchange-b-rest.production.other/api/v1/svid/k8s_psat/x509" -k -sS -q if [ "${BROKER}" -eq 1 ]; then - # Verify a workload on the ha-agent socket receives the other.org federated trust bundles, + # Verify a workload on the ha-agent socket receives the other.invalid federated trust bundles, # x509 and jwt, merged from both sides. run_federation_test_job fi @@ -416,7 +437,7 @@ kubectl rollout status deployment -n spire-server spiffe-oidc-discovery-provider curl -k --resolve "oidc-discovery.production.other:443:$IP" "https://oidc-discovery.production.other/.well-known/openid-configuration" -s --fail if [ "${BROKER}" -eq 1 ]; then - # Verify the other.org federated trust bundles still serve with only side b running. + # Verify the other.invalid federated trust bundles still serve with only side b running. run_federation_test_job fi diff --git a/examples/bottom-turtle-ha/spire-identity-exchange-values.yaml b/examples/bottom-turtle-ha/spire-identity-exchange-values.yaml index d345a6b..d6a93f9 100644 --- a/examples/bottom-turtle-ha/spire-identity-exchange-values.yaml +++ b/examples/bottom-turtle-ha/spire-identity-exchange-values.yaml @@ -1,4 +1,4 @@ -internal-spire-server-bottom-turtle-ha-a: +internal-spire-server-bottom-turtle-ha-a: &server controllerManager: identities: clusterStaticEntries: @@ -11,49 +11,25 @@ internal-spire-server-bottom-turtle-ha-a: spireIdentityExchange: enabled: true -internal-spire-server-bottom-turtle-ha-b: - controllerManager: - identities: - clusterStaticEntries: - test: - parentID: spiffe://production.other/spire-identity-exchange - spiffeID: spiffe://production.other/k8s-psat/test - selectors: - - k8s_psat:namespace:default - - k8s_psat:service_account_name:default - spireIdentityExchange: - enabled: true +#Set the same settings on the B side +internal-spire-server-bottom-turtle-ha-b: *server -spire-identity-exchange-bottom-turtle-ha-a: - rest: - ingress: - enabled: true +spire-identity-exchange-bottom-turtle-ha-a: &six tls: externalSecret: enabled: true secretName: spire-identity-exchange + rest: + enabled: true + ingress: + enabled: true auth: + passthroughPlugins: true plugins: - - plugin: k8s_psat + k8s_psat: config: - audiences: - - spire-identity-exchange allowedServiceAccounts: - default/default -spire-identity-exchange-bottom-turtle-ha-b: - rest: - ingress: - enabled: true - tls: - externalSecret: - enabled: true - secretName: spire-identity-exchange - auth: - plugins: - - plugin: k8s_psat - config: - audiences: - - spire-identity-exchange - allowedServiceAccounts: - - default/default +#Set the same settings on the B side +spire-identity-exchange-bottom-turtle-ha-b: *six From e46ad1594ae0c4d4e9d9f81f645e6a801659f72a Mon Sep 17 00:00:00 2001 From: Daniel Schlatter Date: Tue, 18 Aug 2026 12:47:36 -0600 Subject: [PATCH 12/22] Make spire-server rollout strategy configurable (#924) Signed-off-by: Daniel Schlatter Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 1 + .../templates/server-resource.yaml | 8 ++++ charts/spire/charts/spire-server/values.yaml | 3 ++ tests/unit/spire_test.go | 48 +++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index ba80fab..7c17a2b 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -87,6 +87,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `image.pullPolicy` | The image pull policy | `IfNotPresent` | | `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | | `kind` | Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. | `statefulset` | +| `updateStrategy` | Rollout strategy for the server, mapped to spec.updateStrategy when kind is "statefulset" and to spec.strategy when kind is "deployment". Left empty the Kubernetes default applies, which for a Deployment surges a second server before the old one goes away. Set `{type: Recreate}` to keep at most one server running, as a memory keyManager or an in-memory datastore requires. | `{}` | | `externalServer` | Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. | `false` | | `externalServerSubject.kind` | RBAC subject kind the external (nested) server's downstream bindings are granted to. One of "User" (client-certificate identity, the historical default), "Group", or "ServiceAccount" (e.g. for a static-token kubeconfig). Only used when externalServer is true. | `User` | | `externalServerSubject.name` | Name of the subject. For kind "User" it must match the CN of the client certificate the external server presents; for kind "Group" it is the group name (e.g. a certificate O value); for kind "ServiceAccount" it is the name of the (operator-managed, out-of-band) ServiceAccount. | `spire-root` | diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index be1a6c7..64307bd 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -90,6 +90,14 @@ spec: {{- end }} replicas: {{ .Values.replicaCount }} {{- end }} + {{- with .Values.updateStrategy }} + {{- if eq $.Values.kind "statefulset" }} + updateStrategy: + {{- else }} + strategy: + {{- end }} + {{- toYaml . | nindent 4 }} + {{- end }} {{- if eq .Values.kind "statefulset" }} serviceName: {{ include "spire-server.fullname" . }} {{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index da39180..1fdfeb0 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -23,6 +23,9 @@ image: ## @param kind Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. kind: statefulset +## @param updateStrategy [object] Rollout strategy for the server, mapped to spec.updateStrategy when kind is "statefulset" and to spec.strategy when kind is "deployment". Left empty the Kubernetes default applies, which for a Deployment surges a second server before the old one goes away. Set `{type: Recreate}` to keep at most one server running, as a memory keyManager or an in-memory datastore requires. +updateStrategy: {} + ## @param externalServer Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. externalServer: false diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 24964e3..1eccff9 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -380,4 +380,52 @@ spire-server: Expect(roles).Should(ContainSubstring(`name: "spire-admins"`)) }) }) + Describe("spire-server.updateStrategy", func() { + It("maps to spec.strategy when kind is deployment", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + kind: deployment + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + dataStore: + sql: + databaseType: postgres + host: db.example.org + updateStrategy: + type: Recreate +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("kind: Deployment")) + Expect(serverResource).Should(ContainSubstring("\n strategy:\n type: Recreate\n")) + }) + + It("maps to spec.updateStrategy when kind is statefulset", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + updateStrategy: + type: OnDelete +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("kind: StatefulSet")) + Expect(serverResource).Should(ContainSubstring("\n updateStrategy:\n type: OnDelete\n")) + }) + + It("renders neither field when left unset", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + replicaCount: 1 +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).ShouldNot(ContainSubstring("\n strategy:")) + Expect(serverResource).ShouldNot(ContainSubstring("\n updateStrategy:")) + }) + }) }) From 59bb8a774cfdaf7178bb71ddd7392893f2de5164 Mon Sep 17 00:00:00 2001 From: Daniel Schlatter Date: Tue, 18 Aug 2026 15:22:50 -0600 Subject: [PATCH 13/22] Allow sqlite3 in memory when kind is deployment (#923) * Allow sqlite3 in memory when kind is deployment Signed-off-by: Daniel Schlatter * Warn on unsafe in-memory datastore combinations Signed-off-by: Daniel Schlatter --------- Signed-off-by: Daniel Schlatter --- charts/spire/charts/spire-server/README.md | 3 +- .../spire-server/templates/_helpers.tpl | 7 + .../templates/server-resource.yaml | 10 +- charts/spire/charts/spire-server/values.yaml | 4 +- charts/spire/templates/NOTES.txt | 18 ++ tests/unit/spire_test.go | 198 ++++++++++++++++++ 6 files changed, 236 insertions(+), 4 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 7c17a2b..5a867cc 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -142,7 +142,8 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `dataStore.sql.port` | If 0 (default), it will auto set to 5432 for postgres and 3306 for mysql. Only used by those databases. | `0` | | `dataStore.sql.username` | Only used when type != "sqlite3" | `spire` | | `dataStore.sql.password` | Only used when type != "sqlite3" | `""` | -| `dataStore.sql.file` | Data source file. Only used when type == "sqlite3" | `/run/spire/data/datastore.sqlite3` | +| `dataStore.sql.file` | Data source file. Only used when type == "sqlite3" and inMemory is false | `/run/spire/data/datastore.sqlite3` | +| `dataStore.sql.inMemory` | Hold the sqlite3 datastore in memory instead of in a file, in which case `file` is unused. The datastore starts empty on every restart, so this only suits a single replica whose registration entries are recreated at startup, for example by the controller manager writing static entries. Required to run as a deployment on sqlite3, since a deployment has no durable per-pod storage. | `false` | | `dataStore.sql.options` | takes an array of objects of form {: } to use when building the database connection string | `[]` | | `dataStore.sql.rootCAPath` | Path to Root CA bundle (MySQL only) | `""` | | `dataStore.sql.clientCertPath` | Path to client certificate (MySQL only) | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index e2c0019..80c53f4 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -307,8 +307,15 @@ current-context: cluster {{- $ropw := "" }} {{- if eq .Values.dataStore.sql.databaseType "sqlite3" }} {{- $_ := set $config "database_type" "sqlite3" }} + {{- if .Values.dataStore.sql.inMemory }} + {{- /* cache=shared is not optional: without it every pooled connection opens its own + empty database, so the server silently loses every write it did not make itself. */}} + {{- $query := include "spire-server.config-sqlite-query" (concat (list (dict "mode" "memory") (dict "cache" "shared")) .Values.dataStore.sql.options) }} + {{- $_ := set $config "connection_string" (printf "memdb%s" $query) }} + {{- else }} {{- $query := include "spire-server.config-sqlite-query" .Values.dataStore.sql.options }} {{- $_ := set $config "connection_string" (printf "%s%s" .Values.dataStore.sql.file $query) }} + {{- end }} {{- else if or (eq .Values.dataStore.sql.databaseType "mysql") (eq .Values.dataStore.sql.databaseType "aws_mysql") (eq .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") }} {{- if eq .Values.dataStore.sql.databaseType "mysql" }} {{- $_ := set $config "database_type" "mysql" }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index 64307bd..ea6a428 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -41,8 +41,14 @@ {{- if (has .Values.persistence.type (list "pvc" "hostPath")) }} {{- fail "When running as deployment, persistence can't be set. 'persistence.type' must be [\"emptyDir\"]" }} {{- end }} -{{- if (eq .Values.dataStore.sql.databaseType "sqlite3") }} -{{- fail "When running as deployment, sqlite3 can't be used." }} +{{- if and (eq .Values.dataStore.sql.databaseType "sqlite3") (not .Values.dataStore.sql.inMemory) }} +{{- fail "When running as deployment, sqlite3 can only be used in memory. Set 'dataStore.sql.inMemory' to true." }} +{{- end }} +{{- if and (eq .Values.dataStore.sql.databaseType "sqlite3") .Values.dataStore.sql.inMemory }} +{{- $surge := dig "rollingUpdate" "maxSurge" "" .Values.updateStrategy | toString }} +{{- if not (or (eq (dig "type" "" .Values.updateStrategy) "Recreate") (eq $surge "0") (eq $surge "0%")) }} +{{- fail "An in-memory datastore on a deployment must not surge, or two servers run with separate datastores. Set 'updateStrategy' to {type: Recreate} or {rollingUpdate: {maxSurge: 0}}." }} +{{- end }} {{- end }} {{- if (eq (.Values.keyManager.disk.enabled | toString) "true") }} {{- fail "When running as deployment, disk keymanager can't be used. 'keyManager.disk.enabled' must be false." }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 1fdfeb0..47b0fbc 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -196,8 +196,10 @@ dataStore: username: spire ## @param dataStore.sql.password Only used when type != "sqlite3" password: "" - ## @param dataStore.sql.file Data source file. Only used when type == "sqlite3" + ## @param dataStore.sql.file Data source file. Only used when type == "sqlite3" and inMemory is false file: "/run/spire/data/datastore.sqlite3" + ## @param dataStore.sql.inMemory Hold the sqlite3 datastore in memory instead of in a file, in which case `file` is unused. The datastore starts empty on every restart, so this only suits a single replica whose registration entries are recreated at startup, for example by the controller manager writing static entries. Required to run as a deployment on sqlite3, since a deployment has no durable per-pod storage. + inMemory: false ## @param dataStore.sql.options [array] takes an array of objects of form {: } to use when building the database connection string options: [] diff --git a/charts/spire/templates/NOTES.txt b/charts/spire/templates/NOTES.txt index 0b8a729..de8cc74 100644 --- a/charts/spire/templates/NOTES.txt +++ b/charts/spire/templates/NOTES.txt @@ -22,6 +22,24 @@ Warning: You're using an unsupported plugin. Functionality of this release and f Warning: You're using an experimental config. Functionality of this release and future upgrades aren't guaranteed to work smoothly. {{- end }} {{- if (index .Values "spire-server").enabled }} +{{- $ss := index .Values "spire-server" }} +{{- if and (eq $ss.dataStore.sql.databaseType "sqlite3") $ss.dataStore.sql.inMemory }} +{{- $upstream := false }} +{{- range $name, $cfg := $ss.upstreamAuthority }} +{{- if kindIs "map" $cfg }}{{ if eq ($cfg.enabled | toString) "true" }}{{ $upstream = true }}{{ end }}{{ end }} +{{- end }} +{{- $reconciled := and $ss.controllerManager.enabled (or $ss.controllerManager.reconcile.clusterSPIFFEIDs $ss.controllerManager.reconcile.clusterStaticEntries) }} +{{- if not $reconciled }} + +Warning: dataStore.sql.inMemory is set, but no controller manager reconciler is enabled. Registration entries live only in memory and nothing recreates them, so every entry is lost when the server restarts. Enable controllerManager with reconcile.clusterSPIFFEIDs or reconcile.clusterStaticEntries. +{{- end }} +{{- if and $ss.keyManager.memory.enabled (not $upstream) }} + +Warning: dataStore.sql.inMemory is set with keyManager.memory and no upstreamAuthority. The server mints a new CA on every restart, so the whole trust domain has to re-attest and previously issued SVIDs stop verifying. Suitable for testing only; configure a KMS key manager or an upstream authority for anything else. +{{- end }} +{{- end }} +{{- end }} +{{- if (index .Values "spire-server").enabled }} {{- $className := include "spire-server.controller-manager-class-name" (dict "Values" (index .Values "spire-server") "Release" .Release) }} {{- if (index .Values "spire-server").controllerManager.enabled }} {{- if (index .Values "spire-server").controllerManager.watchClassless }} diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 1eccff9..c383e7a 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -428,4 +428,202 @@ spire-server: Expect(serverResource).ShouldNot(ContainSubstring("\n updateStrategy:")) }) }) + Describe("spire-server.kind.deployment.sqlite3", func() { + deployment := func(sql string) string { + return ` +spire-server: + kind: deployment + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + updateStrategy: + type: Recreate + dataStore: + sql: +` + sql + } + + It("renders a Deployment when the sqlite3 datastore is in memory", func() { + objs, err := ValueStringRender(chart, deployment(` inMemory: true +`)) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("kind: Deployment")) + Expect(serverResource).ShouldNot(ContainSubstring("kind: StatefulSet")) + }) + + It("rejects a file backed sqlite3 datastore", func() { + _, err := ValueStringRender(chart, deployment(` inMemory: false +`)) + Expect(err).Should(MatchError(ContainSubstring("sqlite3 can only be used in memory"))) + }) + }) + Describe("spire-server.dataStore.sql.inMemory", func() { + It("builds a shared cache connection string and ignores file", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + dataStore: + sql: + inMemory: true + file: /run/spire/data/datastore.sqlite3 +`) + Expect(err).Should(Succeed()) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + Should(ContainSubstring(`"connection_string": "memdb?mode=memory\u0026cache=shared"`)) + }) + + It("keeps the file connection string when left off", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + dataStore: + sql: + file: /run/spire/data/datastore.sqlite3 +`) + Expect(err).Should(Succeed()) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + Should(ContainSubstring(`"connection_string": "/run/spire/data/datastore.sqlite3"`)) + }) + }) + Describe("spire-server.dataStore.sql.inMemory warnings", func() { + notes := func(values string) string { + objs, err := ValueStringRender(chart, values) + ExpectWithOffset(1, err).Should(Succeed()) + return objs["spire/templates/NOTES.txt"] + } + safe := ` +spire-server: + dataStore: + sql: + inMemory: true + controllerManager: + enabled: true + reconcile: + clusterStaticEntries: true + upstreamAuthority: + vault: + enabled: true +` + + It("stays quiet on the default values", func() { + Expect(notes(`spire-server: {}`)).ShouldNot(ContainSubstring("Warning: dataStore.sql.inMemory")) + }) + + It("stays quiet when entries are reconciled and a CA is upstream", func() { + Expect(notes(safe)).ShouldNot(ContainSubstring("Warning: dataStore.sql.inMemory")) + }) + + It("warns when nothing recreates the registration entries", func() { + Expect(notes(` +spire-server: + dataStore: + sql: + inMemory: true + controllerManager: + enabled: false +`)).Should(ContainSubstring("nothing recreates them")) + }) + + It("warns when the CA is also in memory with no upstream authority", func() { + Expect(notes(` +spire-server: + dataStore: + sql: + inMemory: true + controllerManager: + enabled: true + reconcile: + clusterStaticEntries: true + keyManager: + disk: + enabled: false + memory: + enabled: true +`)).Should(ContainSubstring("mints a new CA on every restart")) + }) + + It("stays quiet on a deployment that cannot surge", func() { + Expect(notes(safe + ` + kind: deployment + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + updateStrategy: + type: Recreate +`)).ShouldNot(ContainSubstring("Warning: dataStore.sql.inMemory")) + }) + }) + Describe("spire-server.updateStrategy surge guard", func() { + deployment := func(strategy string) string { + return ` +spire-server: + kind: deployment + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + dataStore: + sql: + inMemory: true +` + strategy + } + + It("rejects an in-memory deployment that can surge", func() { + _, err := ValueStringRender(chart, deployment(``)) + Expect(err).Should(MatchError(ContainSubstring("must not surge"))) + }) + + It("rejects an explicit rolling update that can surge", func() { + _, err := ValueStringRender(chart, deployment(` updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 +`)) + Expect(err).Should(MatchError(ContainSubstring("must not surge"))) + }) + + It("accepts Recreate", func() { + _, err := ValueStringRender(chart, deployment(` updateStrategy: + type: Recreate +`)) + Expect(err).Should(Succeed()) + }) + + It("accepts a rolling update pinned to maxSurge 0", func() { + _, err := ValueStringRender(chart, deployment(` updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 +`)) + Expect(err).Should(Succeed()) + }) + + It("accepts maxSurge expressed as a percentage", func() { + _, err := ValueStringRender(chart, deployment(` updateStrategy: + rollingUpdate: + maxSurge: 0% +`)) + Expect(err).Should(Succeed()) + }) + + It("leaves a file backed statefulset alone", func() { + _, err := ValueStringRender(chart, ` +spire-server: + updateStrategy: + type: RollingUpdate +`) + Expect(err).Should(Succeed()) + }) + }) }) From f1dddf2e85a5c062eb0f827181e18240c611cb1e Mon Sep 17 00:00:00 2001 From: Saumil Macwan Date: Tue, 18 Aug 2026 18:03:28 -0400 Subject: [PATCH 14/22] feat: add first-class support for gcp_cas UpstreamAuthority plugin (#914) The gcp_cas built-in plugin was not wired into the spire-server chart. This adds values, configmap rendering, and generated docs to support GCP Certificate Authority Service as an upstream authority, matching the existing awsPCA pattern. Signed-off-by: saumil Macwan Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 6 ++++++ .../charts/spire-server/templates/configmap.yaml | 16 ++++++++++++++++ charts/spire/charts/spire-server/values.yaml | 13 +++++++++++++ 3 files changed, 35 insertions(+) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 5a867cc..45f0241 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -272,6 +272,12 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `upstreamAuthority.awsSecret.keyFileArn` | ARN or name of the secret containing the intermediate CA private key | `""` | | `upstreamAuthority.awsSecret.bundleFileArn` | (Optional) ARN or name of the secret containing the root CA bundle | `""` | | `upstreamAuthority.awsSecret.assumeRoleArn` | (Optional) ARN of an IAM role to assume | `""` | +| `upstreamAuthority.gcpCAS.enabled` | Flag to enable upstream authority plugin with GCP Certificate Authority Service | `false` | +| `upstreamAuthority.gcpCAS.projectName` | GCP project containing the root CA certificate | `""` | +| `upstreamAuthority.gcpCAS.regionName` | GCP region name (e.g., us-central1) | `""` | +| `upstreamAuthority.gcpCAS.caPool` | Name of the CA Pool that has the root CA certificate | `""` | +| `upstreamAuthority.gcpCAS.labelKey` | Label key used to filter and select the relevant CA certificate | `""` | +| `upstreamAuthority.gcpCAS.labelValue` | Label value used to filter and select the relevant CA certificate | `""` | | `upstreamAuthority.certManager.enabled` | Flag to enable upstream authority plugin with cert manager | `false` | | `upstreamAuthority.certManager.rbac.create` | Flag to create RBAC roles | `true` | | `upstreamAuthority.certManager.issuerName` | Defaults to the release name, override if CA is provided outside of the chart | `""` | diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index 63d9f96..befedbc 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -656,6 +656,22 @@ plugins: {{- end }} {{- end }} {{- end }} + + {{- with .Values.upstreamAuthority.gcpCAS }} + {{- if eq (.enabled | toString) "true" }} + {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} + UpstreamAuthority: + gcp_cas: + plugin_data: + root_cert_spec: + project_name: {{ .projectName | quote }} + region_name: {{ .regionName | quote }} + ca_pool: {{ .caPool | quote }} + label_key: {{ .labelKey | quote }} + label_value: {{ .labelValue | quote }} + {{- end }} + {{- end }} + {{- if gt $upstreamAuthorityUsed 1 }} {{- fail "You can only enable a single Upstream Authority." }} {{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 47b0fbc..dd5e7cc 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -551,6 +551,19 @@ upstreamAuthority: bundleFileArn: "" ## @param upstreamAuthority.awsSecret.assumeRoleArn (Optional) ARN of an IAM role to assume assumeRoleArn: "" + gcpCAS: + ## @param upstreamAuthority.gcpCAS.enabled Flag to enable upstream authority plugin with GCP Certificate Authority Service + enabled: false + ## @param upstreamAuthority.gcpCAS.projectName GCP project containing the root CA certificate + projectName: "" + ## @param upstreamAuthority.gcpCAS.regionName GCP region name (e.g., us-central1) + regionName: "" + ## @param upstreamAuthority.gcpCAS.caPool Name of the CA Pool that has the root CA certificate + caPool: "" + ## @param upstreamAuthority.gcpCAS.labelKey Label key used to filter and select the relevant CA certificate + labelKey: "" + ## @param upstreamAuthority.gcpCAS.labelValue Label value used to filter and select the relevant CA certificate + labelValue: "" certManager: ## @param upstreamAuthority.certManager.enabled Flag to enable upstream authority plugin with cert manager enabled: false From 0726faa07670dada923dfa23256a7bba6f4ca3f8 Mon Sep 17 00:00:00 2001 From: Michael Munch Date: Thu, 20 Aug 2026 01:30:25 +0200 Subject: [PATCH 15/22] :sparkles: add topologySpreadConstraints support to OIDC discovery provider (#925) Add optional topologySpreadConstraints to the spiffe-oidc-discovery-provider Deployment, matching the pattern used by spire-server and the spike-* charts. Signed-off-by: Michael Munch Co-authored-by: kfox1111 --- charts/spire/charts/spiffe-oidc-discovery-provider/README.md | 1 + .../spiffe-oidc-discovery-provider/templates/deployment.yaml | 4 ++++ .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 3 +++ 3 files changed, 8 insertions(+) diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index 9b764bc..be48a7e 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -102,6 +102,7 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `nodeSelector` | Node selector | `{}` | | `tolerations` | iist of tolerations | `[]` | | `affinity` | Node affinity | `{}` | +| `topologySpreadConstraints` | Topology spread constraints for resilience | `[]` | | `trustDomain` | Set the trust domain to be used for the SPIFFE identifiers | `example.org` | | `clusterDomain` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `cluster.local` | | `telemetry.prometheus.enabled` | Flag to enable prometheus monitoring | `false` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/templates/deployment.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/templates/deployment.yaml index 044a3b7..7873ab2 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/templates/deployment.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/templates/deployment.yaml @@ -220,3 +220,7 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 7035bf6..48b3f73 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -267,6 +267,9 @@ tolerations: [] ## @param affinity [object] Node affinity affinity: {} +## @param topologySpreadConstraints [array] Topology spread constraints for resilience +topologySpreadConstraints: [] + ## @param trustDomain Set the trust domain to be used for the SPIFFE identifiers trustDomain: example.org From ab5e5d86775b3ce483beff469869aef2a60801e3 Mon Sep 17 00:00:00 2001 From: Michael Munch Date: Thu, 20 Aug 2026 19:51:35 +0200 Subject: [PATCH 16/22] fix(spiffe-oidc-discovery-provider): run under restricted PSA/SCC on OpenShift (#920) * fix(spiffe-oidc-discovery-provider): run under restricted PSA/SCC on OpenShift The OIDC discovery provider does not require any elevated privileges: it runs fine under OpenShift's built-in restricted-v2 SCC (non-root, no privilege escalation, all capabilities dropped, RuntimeDefault seccomp, read-only root filesystem) and mounts only restricted-compatible volumes (csi, configMap, emptyDir, secret, projected, downwardAPI). Despite this, on OpenShift the chart: - downgraded the spire-server namespace from restricted to privileged PSA whenever the OIDC provider was enabled, and - created a fully privileged SecurityContextConstraints (host network/IPC/ PID, privileged container, hostPath, arbitrary seccomp, RunAsAny) bound to the provider's ServiceAccount. Both contradict the chart's own Namespaces documentation, which specifies restricted PSA for spire-server, and violate least privilege for an internet-facing OIDC endpoint. Remove the privileged PSA override for the OIDC provider (spire-server stays restricted; the scc.podSecurityLabelSync=false label is retained) and drop the privileged SCC so the provider falls through to restricted-v2. With spire-server enforcing restricted PSA, the inline-CSI PodSecurity check reads the cluster-scoped CSIDriver's security.openshift.io/csi-ephemeral-volume-profile label. If the CSIDriver is not committed before the spire-server StatefulSet (which mounts the inline upstream.csi.spiffe.io volume) is admitted, the profile defaults to privileged and admission is denied. Under ArgoCD the CSIDriver and the server StatefulSet can land in the same sync wave, racing admission. Annotate the CSIDriver with argocd.argoproj.io/sync-wave: "-1" (OpenShift only) so it is applied before the default-wave server workloads; the annotation is inert for plain helm installs. Signed-off-by: Michael Munch * :sparkles: make CSIDriver sync-wave ordering configurable Add syncWave and csiDriverAnnotations values to the spiffe-csi-driver chart so the OpenShift argocd.argoproj.io/sync-wave annotation number can be overridden (e.g. when the chart is nested) and arbitrary annotations can be applied to the CSIDriver. Signed-off-by: Michael Munch --------- Signed-off-by: Michael Munch Co-authored-by: kfox1111 --- .../templates/_spire-server-namespace.yaml | 3 - .../spire/charts/spiffe-csi-driver/README.md | 102 +++++++++--------- .../templates/spiffe-csi-driver.yaml | 9 ++ .../charts/spiffe-csi-driver/values.yaml | 6 ++ .../scc-spire-oidc-discovery-provider.yaml | 42 -------- tests/unit/spire_test.go | 37 +++++++ 6 files changed, 104 insertions(+), 95 deletions(-) delete mode 100644 charts/spire/charts/spiffe-oidc-discovery-provider/templates/scc-spire-oidc-discovery-provider.yaml diff --git a/charts/spire-lib/templates/_spire-server-namespace.yaml b/charts/spire-lib/templates/_spire-server-namespace.yaml index 37a959b..f933d38 100644 --- a/charts/spire-lib/templates/_spire-server-namespace.yaml +++ b/charts/spire-lib/templates/_spire-server-namespace.yaml @@ -10,9 +10,6 @@ {{- $labels = mergeOverwrite $labels (include "spire-lib.namespace.default_server_labels" . | fromYaml) }} {{- if (dig "openshift" false .Values.global) }} {{- $_ := set $labels "security.openshift.io/scc.podSecurityLabelSync" "false" }} -{{- if (index .Values "spiffe-oidc-discovery-provider").enabled }} -{{- $_ := set $labels "pod-security.kubernetes.io/enforce" "privileged" }} -{{- end }} {{- end }} {{- end }} {{- $labels = mergeOverwrite $labels .Values.global.spire.namespaces.server.labels }} diff --git a/charts/spire/charts/spiffe-csi-driver/README.md b/charts/spire/charts/spiffe-csi-driver/README.md index a43873f..462826d 100644 --- a/charts/spire/charts/spiffe-csi-driver/README.md +++ b/charts/spire/charts/spiffe-csi-driver/README.md @@ -25,54 +25,56 @@ A Helm chart to install the SPIFFE CSI driver. ### SPIFFE CSI Driver Chart parameters -| Name | Description | Value | -| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | -| `pluginName` | Set the csi driver name deployed to Kubernetes. | `csi.spiffe.io` | -| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `image.repository` | The repository within the registry | `spiffe/spiffe-csi-driver` | -| `image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `resources` | Resource requests and limits for spiffe-csi-driver and its initContainers | `{}` | -| `extraEnvVars` | Extra environment variables to be added to the spiffe-csi-driver container | `[]` | -| `healthChecks.port` | The healthcheck port for spiffe-csi-driver | `9809` | -| `updateStrategy.type` | The update strategy to use to replace existing DaemonSet pods with new pods. Can be RollingUpdate or OnDelete. | `RollingUpdate` | -| `updateStrategy.rollingUpdate.maxUnavailable` | Max unavailable pods during update. Can be a number or a percentage. | `1` | -| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | -| `livenessProbe.timeoutSeconds` | Timeout value in seconds for livenessProbe | `5` | -| `imagePullSecrets` | Image pull secret details for spiffe-csi-driver | `[]` | -| `nameOverride` | Name override for spiffe-csi-driver | `""` | -| `namespaceOverride` | Namespace to install spiffe-csi-driver | `""` | -| `serverNamespaceOverride` | Override the namespace that the spire-server is installed into | `""` | -| `validatingAdmissionPolicy.enabled` | When set to auto, the validatingAdmissionPolicy will be enabled when the pluginName == "upstream.csi.spiffe.io" and k8s >= 1.30.0. Valid options are [auto, true, false] | `auto` | -| `fullnameOverride` | Full name override for spiffe-csi-driver | `""` | -| `csiDriverLabels` | Labels to apply to the CSIDriver | `{}` | -| `initContainers` | Init Containers to apply to the CSI Driver DaemonSet | `[]` | -| `serviceAccount.create` | Specifies whether a service account should be created | `true` | -| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | -| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | -| `podAnnotations` | Pod annotations for spiffe-csi-driver | `{}` | -| `podLabels` | Labels to add to pods | `{}` | -| `podSecurityContext` | Security context for CSI driver pods | `{}` | -| `securityContext` | Security context for CSI driver containers | `{}` | -| `hostNetwork` | Enable hostNetwork for the DaemonSet | `false` | -| `nodeSelector` | Node selector for CSI driver pods | `{}` | -| `tolerations` | Tolerations for CSI driver pods | `[]` | -| `affinity` | Node affinity | `{}` | -| `nodeDriverRegistrar.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` | -| `nodeDriverRegistrar.image.repository` | The repository within the registry | `sig-storage/csi-node-driver-registrar` | -| `nodeDriverRegistrar.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `nodeDriverRegistrar.image.tag` | Overrides the image tag | `v2.15.0` | -| `nodeDriverRegistrar.extraEnvVars` | Extra environment variables to be added to the nodeDriverRegistrar container | `[]` | -| `agentSocketPath` | The unix socket path to the spire-agent | `/run/spire/agent-sockets/spire-agent.sock` | -| `kubeletPath` | Path to kubelet file | `/var/lib/kubelet` | -| `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | -| `restrictedScc.enabled` | Enables the creation of a SecurityContextConstraint based on the restricted SCC with CSI volume support | `false` | -| `restrictedScc.name` | Set the name of the restricted SCC with CSI support | `""` | -| `restrictedScc.version` | Version of the restricted SCC | `2` | -| `selinux.enabled` | Enable selinux support | `false` | -| `selinux.context` | Which selinux context to use | `container_file_t` | -| `selinux.image.registry` | The OCI registry to pull the image from | `registry.access.redhat.com` | -| `selinux.image.repository` | The repository within the registry | `ubi10/ubi-minimal` | -| `selinux.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `selinux.image.tag` | Overrides the image tag whose default is the chart appVersion | `10.1-1776834797` | +| Name | Description | Value | +| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `pluginName` | Set the csi driver name deployed to Kubernetes. | `csi.spiffe.io` | +| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | +| `image.repository` | The repository within the registry | `spiffe/spiffe-csi-driver` | +| `image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `resources` | Resource requests and limits for spiffe-csi-driver and its initContainers | `{}` | +| `extraEnvVars` | Extra environment variables to be added to the spiffe-csi-driver container | `[]` | +| `healthChecks.port` | The healthcheck port for spiffe-csi-driver | `9809` | +| `updateStrategy.type` | The update strategy to use to replace existing DaemonSet pods with new pods. Can be RollingUpdate or OnDelete. | `RollingUpdate` | +| `updateStrategy.rollingUpdate.maxUnavailable` | Max unavailable pods during update. Can be a number or a percentage. | `1` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | +| `livenessProbe.timeoutSeconds` | Timeout value in seconds for livenessProbe | `5` | +| `imagePullSecrets` | Image pull secret details for spiffe-csi-driver | `[]` | +| `nameOverride` | Name override for spiffe-csi-driver | `""` | +| `namespaceOverride` | Namespace to install spiffe-csi-driver | `""` | +| `serverNamespaceOverride` | Override the namespace that the spire-server is installed into | `""` | +| `validatingAdmissionPolicy.enabled` | When set to auto, the validatingAdmissionPolicy will be enabled when the pluginName == "upstream.csi.spiffe.io" and k8s >= 1.30.0. Valid options are [auto, true, false] | `auto` | +| `fullnameOverride` | Full name override for spiffe-csi-driver | `""` | +| `csiDriverLabels` | Labels to apply to the CSIDriver | `{}` | +| `csiDriverAnnotations` | Annotations to apply to the CSIDriver | `{}` | +| `syncWave` | The argocd.argoproj.io/sync-wave value applied to the CSIDriver on OpenShift, ensuring the CSI driver reconciles before workloads that depend on its csi-ephemeral-volume-profile label | `-1` | +| `initContainers` | Init Containers to apply to the CSI Driver DaemonSet | `[]` | +| `serviceAccount.create` | Specifies whether a service account should be created | `true` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | +| `podAnnotations` | Pod annotations for spiffe-csi-driver | `{}` | +| `podLabels` | Labels to add to pods | `{}` | +| `podSecurityContext` | Security context for CSI driver pods | `{}` | +| `securityContext` | Security context for CSI driver containers | `{}` | +| `hostNetwork` | Enable hostNetwork for the DaemonSet | `false` | +| `nodeSelector` | Node selector for CSI driver pods | `{}` | +| `tolerations` | Tolerations for CSI driver pods | `[]` | +| `affinity` | Node affinity | `{}` | +| `nodeDriverRegistrar.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` | +| `nodeDriverRegistrar.image.repository` | The repository within the registry | `sig-storage/csi-node-driver-registrar` | +| `nodeDriverRegistrar.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `nodeDriverRegistrar.image.tag` | Overrides the image tag | `v2.15.0` | +| `nodeDriverRegistrar.extraEnvVars` | Extra environment variables to be added to the nodeDriverRegistrar container | `[]` | +| `agentSocketPath` | The unix socket path to the spire-agent | `/run/spire/agent-sockets/spire-agent.sock` | +| `kubeletPath` | Path to kubelet file | `/var/lib/kubelet` | +| `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | +| `restrictedScc.enabled` | Enables the creation of a SecurityContextConstraint based on the restricted SCC with CSI volume support | `false` | +| `restrictedScc.name` | Set the name of the restricted SCC with CSI support | `""` | +| `restrictedScc.version` | Version of the restricted SCC | `2` | +| `selinux.enabled` | Enable selinux support | `false` | +| `selinux.context` | Which selinux context to use | `container_file_t` | +| `selinux.image.registry` | The OCI registry to pull the image from | `registry.access.redhat.com` | +| `selinux.image.repository` | The repository within the registry | `ubi10/ubi-minimal` | +| `selinux.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `selinux.image.tag` | Overrides the image tag whose default is the chart appVersion | `10.1-1776834797` | diff --git a/charts/spire/charts/spiffe-csi-driver/templates/spiffe-csi-driver.yaml b/charts/spire/charts/spiffe-csi-driver/templates/spiffe-csi-driver.yaml index cd17fdd..3027602 100644 --- a/charts/spire/charts/spiffe-csi-driver/templates/spiffe-csi-driver.yaml +++ b/charts/spire/charts/spiffe-csi-driver/templates/spiffe-csi-driver.yaml @@ -3,10 +3,19 @@ {{- $_ := set $labels "security.openshift.io/csi-ephemeral-volume-profile" "restricted" }} {{- end }} {{- $labels = mergeOverwrite $labels .Values.csiDriverLabels }} +{{- $annotations := dict }} +{{- if (dig "openshift" false .Values.global) }} +{{- $_ := set $annotations "argocd.argoproj.io/sync-wave" (toString .Values.syncWave) }} +{{- end }} +{{- $annotations = mergeOverwrite $annotations .Values.csiDriverAnnotations }} apiVersion: storage.k8s.io/v1 kind: CSIDriver metadata: name: {{ .Values.pluginName | quote }} + {{- with $annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} {{- with $labels }} labels: {{- toYaml . | nindent 4 }} diff --git a/charts/spire/charts/spiffe-csi-driver/values.yaml b/charts/spire/charts/spiffe-csi-driver/values.yaml index 56a4a2f..d21d4c8 100644 --- a/charts/spire/charts/spiffe-csi-driver/values.yaml +++ b/charts/spire/charts/spiffe-csi-driver/values.yaml @@ -76,6 +76,12 @@ fullnameOverride: "" ## @param csiDriverLabels Labels to apply to the CSIDriver csiDriverLabels: {} +## @param csiDriverAnnotations Annotations to apply to the CSIDriver +csiDriverAnnotations: {} + +## @param syncWave The argocd.argoproj.io/sync-wave value applied to the CSIDriver on OpenShift, ensuring the CSI driver reconciles before workloads that depend on its csi-ephemeral-volume-profile label +syncWave: -1 + ## @param initContainers Init Containers to apply to the CSI Driver DaemonSet initContainers: [] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/templates/scc-spire-oidc-discovery-provider.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/templates/scc-spire-oidc-discovery-provider.yaml deleted file mode 100644 index 6916687..0000000 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/templates/scc-spire-oidc-discovery-provider.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{- if eq (.Values.global.openshift | toString) "true" }} -apiVersion: security.openshift.io/v1 -kind: SecurityContextConstraints -metadata: - name: {{ include "spiffe-oidc-discovery-provider.fullname" . }} -readOnlyRootFilesystem: true -runAsUser: - type: RunAsAny -seLinuxContext: - type: RunAsAny -supplementalGroups: - type: RunAsAny -users: - - system:serviceaccount:{{ include "spiffe-oidc-discovery-provider.namespace" . }}:{{ include "spiffe-oidc-discovery-provider.serviceAccountName" . }} - - system:serviceaccount:{{ include "spiffe-oidc-discovery-provider.namespace" . }}:{{ include "spiffe-oidc-discovery-provider.serviceAccountName" . }}-pre-delete -volumes: - - configMap - - csi - - downwardAPI - - emptyDir - - ephemeral - - hostPath - - projected - - secret -allowedCapabilities: null -allowHostDirVolumePlugin: true -allowHostIPC: true -allowHostNetwork: true -allowHostPID: true -allowHostPorts: true -allowPrivilegeEscalation: true -allowPrivilegedContainer: true -defaultAddCapabilities: null -fsGroup: - type: RunAsAny -groups: [] -priority: null -requiredDropCapabilities: null -seccompProfiles: - - '*' - -{{ end }} diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index c383e7a..e446a24 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -349,6 +349,43 @@ spire-server: Expect(objs[serverTmpl]).Should(ContainSubstring("init-jwt-svid-exec")) }) }) + Describe("spiffe-csi-driver.syncWave", func() { + csiTmpl := "spire/charts/spiffe-csi-driver/templates/spiffe-csi-driver.yaml" + It("renders the default sync-wave annotation on OpenShift", func() { + objs, err := ValueStringRender(chart, ` +global: + openshift: true +`) + Expect(err).Should(Succeed()) + Expect(objs[csiTmpl]).Should(ContainSubstring(`argocd.argoproj.io/sync-wave: "-1"`)) + }) + It("allows overriding the sync-wave number", func() { + objs, err := ValueStringRender(chart, ` +global: + openshift: true +spiffe-csi-driver: + syncWave: -2 +`) + Expect(err).Should(Succeed()) + Expect(objs[csiTmpl]).Should(ContainSubstring(`argocd.argoproj.io/sync-wave: "-2"`)) + }) + It("allows overriding the annotation via csiDriverAnnotations", func() { + objs, err := ValueStringRender(chart, ` +global: + openshift: true +spiffe-csi-driver: + csiDriverAnnotations: + argocd.argoproj.io/sync-wave: "-5" +`) + Expect(err).Should(Succeed()) + Expect(objs[csiTmpl]).Should(ContainSubstring(`argocd.argoproj.io/sync-wave: "-5"`)) + }) + It("does not render the sync-wave annotation when not on OpenShift", func() { + objs, err := ValueStringRender(chart, ``) + Expect(err).Should(Succeed()) + Expect(objs[csiTmpl]).ShouldNot(ContainSubstring("argocd.argoproj.io/sync-wave")) + }) + }) Describe("spire-server.externalServerSubject", func() { It("binds the external server's downstream RBAC to a ServiceAccount subject", func() { objs, err := ValueStringRender(chart, ` From 1ce42d587abc0f107fb37e60bcff73d49002d2ba Mon Sep 17 00:00:00 2001 From: Michael Munch Date: Thu, 20 Aug 2026 20:15:26 +0200 Subject: [PATCH 17/22] fix(spire-server): support postgres TLS client-certificate (passwordless) auth (#922) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(spire-server): support postgres TLS client-certificate (passwordless) auth The postgres datastore always injected a password into the connection string, always created the -dbpw Secret, and always set the DBPW env var, with no way to use TLS client-certificate (or IAM) authentication. This forced a dummy password (e.g. "unused") when authenticating with certs. - Map dataStore.sql.rootCAPath / clientCertPath / clientKeyPath to the postgres connection-string options sslrootcert / sslcert / sslkey (previously these were mysql-only and rejected for postgres). MySQL keeps using the root_ca_path / client_cert_path / client_key_path plugin fields, now correctly gated to mysql/aws_mysql only. - For postgres/aws_postgres, when dataStore.sql.password is empty, omit "password=${DBPW}" from the connection string and skip creating the -dbpw Secret and the DBPW/RODBPW env vars (mirrors the existing gcp_mysql_sa_iam passwordless behavior). - Add a guard: for postgres, dataStore.sql.password and clientCertPath are mutually exclusive. - Fix a stray tab in the mysql client_key_path config field. - Update value docs and regenerate the README. Existing configurations with a password set are unaffected. Signed-off-by: Michael Munch * 🐛 fix(spire-server): keep postgres password when external secret is used The postgres passwordless path keyed only on an empty password, so enabling dataStore.sql.externalSecret (or readOnly.externalSecret) with an empty password dropped the password token from the connection string and skipped the DBPW/RODBPW env vars, breaking external-secret auth. - Add shared passwordless predicates that also require external secrets to be disabled, evaluated independently for read-write and read-only. - Use the predicates in datastore-config, secret.yaml, and server-resource.yaml so the gating cannot drift. - Add unit tests for postgres with read-write and read-only external secrets plus the cert-auth passwordless case. Signed-off-by: Michael Munch * 🔁 ci: re-trigger checks Re-run CI; the previous spiffe-step-ssh integration job failed on an unrelated flaky SSH host-key verification on k8s v1.35.1 (passed on v1.33.7 and v1.34.3). Signed-off-by: Michael Munch --------- Signed-off-by: Michael Munch Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 8 +- .../spire-server/templates/_helpers.tpl | 36 ++++++++- .../spire-server/templates/configmap.yaml | 4 +- .../charts/spire-server/templates/secret.yaml | 3 +- .../templates/server-resource.yaml | 20 +++-- charts/spire/charts/spire-server/values.yaml | 8 +- tests/unit/spire_test.go | 77 +++++++++++++++++++ 7 files changed, 135 insertions(+), 21 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 45f0241..92461b6 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -141,13 +141,13 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `dataStore.sql.host` | Only used when type != "sqlite3" | `""` | | `dataStore.sql.port` | If 0 (default), it will auto set to 5432 for postgres and 3306 for mysql. Only used by those databases. | `0` | | `dataStore.sql.username` | Only used when type != "sqlite3" | `spire` | -| `dataStore.sql.password` | Only used when type != "sqlite3" | `""` | +| `dataStore.sql.password` | Only used when type != "sqlite3". For postgres/aws_postgres, leave empty to omit the password from the connection string (e.g. TLS client-certificate or IAM authentication). | `""` | | `dataStore.sql.file` | Data source file. Only used when type == "sqlite3" and inMemory is false | `/run/spire/data/datastore.sqlite3` | | `dataStore.sql.inMemory` | Hold the sqlite3 datastore in memory instead of in a file, in which case `file` is unused. The datastore starts empty on every restart, so this only suits a single replica whose registration entries are recreated at startup, for example by the controller manager writing static entries. Required to run as a deployment on sqlite3, since a deployment has no durable per-pod storage. | `false` | | `dataStore.sql.options` | takes an array of objects of form {: } to use when building the database connection string | `[]` | -| `dataStore.sql.rootCAPath` | Path to Root CA bundle (MySQL only) | `""` | -| `dataStore.sql.clientCertPath` | Path to client certificate (MySQL only) | `""` | -| `dataStore.sql.clientKeyPath` | Path to private key for client certificate (MySQL only) | `""` | +| `dataStore.sql.rootCAPath` | Path to Root CA bundle. Supports MySQL and postgres. | `""` | +| `dataStore.sql.clientCertPath` | Path to client certificate. Supports MySQL and postgres. | `""` | +| `dataStore.sql.clientKeyPath` | Path to private key for client certificate. Supports MySQL and postgres. | `""` | | `dataStore.sql.externalSecret.enabled` | Enable external secret for datastore creds | `false` | | `dataStore.sql.externalSecret.name` | The name of the secret object | `""` | | `dataStore.sql.externalSecret.key` | The key of the secret object whose value is the dataStore.sql password | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index 80c53f4..5dc537d 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -301,6 +301,20 @@ current-context: cluster {{- end }} {{- end }} +{{- define "spire-server.datastore-is-postgres" -}} +{{- or (eq .Values.dataStore.sql.databaseType "postgres") (eq .Values.dataStore.sql.databaseType "aws_postgres") -}} +{{- end }} + +{{- define "spire-server.datastore-postgres-passwordless" -}} +{{- $isPostgres := eq (include "spire-server.datastore-is-postgres" .) "true" -}} +{{- and $isPostgres (eq .Values.dataStore.sql.password "") (not .Values.dataStore.sql.externalSecret.enabled) -}} +{{- end }} + +{{- define "spire-server.datastore-postgres-ro-passwordless" -}} +{{- $isPostgres := eq (include "spire-server.datastore-is-postgres" .) "true" -}} +{{- and $isPostgres (eq .Values.dataStore.sql.readOnly.password "") (not .Values.dataStore.sql.readOnly.externalSecret.enabled) -}} +{{- end }} + {{- define "spire-server.datastore-config" }} {{- $config := dict }} {{- $pw := "" }} @@ -349,18 +363,32 @@ current-context: cluster {{- else if or (eq .Values.dataStore.sql.databaseType "postgres") (eq .Values.dataStore.sql.databaseType "aws_postgres") }} {{- if eq .Values.dataStore.sql.databaseType "postgres" }} {{- $_ := set $config "database_type" "postgres" }} - {{- $pw = " password=${DBPW}" }} - {{- $ropw = " password=${RODBPW}" }} {{- else }} {{- $_ := set $config "database_type" (list (dict "aws_postgres" (dict "region" .Values.dataStore.sql.region))) }} {{- end }} + {{- if ne (include "spire-server.datastore-postgres-passwordless" .) "true" }} + {{- $pw = " password=${DBPW}" }} + {{- end }} + {{- if ne (include "spire-server.datastore-postgres-ro-passwordless" .) "true" }} + {{- $ropw = " password=${RODBPW}" }} + {{- end }} + {{- $sslPaths := "" }} + {{- if ne .Values.dataStore.sql.rootCAPath "" }} + {{- $sslPaths = printf "%s sslrootcert=%s" $sslPaths .Values.dataStore.sql.rootCAPath }} + {{- end }} + {{- if ne .Values.dataStore.sql.clientCertPath "" }} + {{- $sslPaths = printf "%s sslcert=%s" $sslPaths .Values.dataStore.sql.clientCertPath }} + {{- end }} + {{- if ne .Values.dataStore.sql.clientKeyPath "" }} + {{- $sslPaths = printf "%s sslkey=%s" $sslPaths .Values.dataStore.sql.clientKeyPath }} + {{- end }} {{- $port := int .Values.dataStore.sql.port | default 5432 }} {{- $options:= include "spire-server.config-postgresql-options" .Values.dataStore.sql.options }} - {{- $_ := set $config "connection_string" (printf "dbname=%s user=%s%s host=%s port=%d%s" .Values.dataStore.sql.databaseName .Values.dataStore.sql.username $pw .Values.dataStore.sql.host $port $options) }} + {{- $_ := set $config "connection_string" (printf "dbname=%s user=%s%s host=%s port=%d%s%s" .Values.dataStore.sql.databaseName .Values.dataStore.sql.username $pw .Values.dataStore.sql.host $port $options $sslPaths) }} {{- if .Values.dataStore.sql.readOnly.enabled }} {{- $roPort := int .Values.dataStore.sql.readOnly.port | default 5432 }} {{- $roOptions:= include "spire-server.config-postgresql-options" .Values.dataStore.sql.readOnly.options }} - {{- $_ := set $config "ro_connection_string" (printf "dbname=%s user=%s%s host=%s port=%d%s" .Values.dataStore.sql.readOnly.databaseName .Values.dataStore.sql.readOnly.username $ropw .Values.dataStore.sql.readOnly.host $roPort $roOptions) }} + {{- $_ := set $config "ro_connection_string" (printf "dbname=%s user=%s%s host=%s port=%d%s%s" .Values.dataStore.sql.readOnly.databaseName .Values.dataStore.sql.readOnly.username $ropw .Values.dataStore.sql.readOnly.host $roPort $roOptions $sslPaths) }} {{- end }} {{- else }} {{- fail "Unsupported database type" }} diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index befedbc..2c4211b 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -173,6 +173,7 @@ plugins: sql: plugin_data: {{ include "spire-server.datastore-config" . | nindent 8 }} + {{- if or (eq .Values.dataStore.sql.databaseType "mysql") (eq .Values.dataStore.sql.databaseType "aws_mysql") }} {{- if ne .Values.dataStore.sql.rootCAPath "" }} root_ca_path: {{ .Values.dataStore.sql.rootCAPath }} {{- end }} @@ -180,7 +181,8 @@ plugins: client_cert_path: {{ .Values.dataStore.sql.clientCertPath }} {{- end }} {{- if ne .Values.dataStore.sql.clientKeyPath "" }} - client_key_path : {{ .Values.dataStore.sql.clientKeyPath }} + client_key_path: {{ .Values.dataStore.sql.clientKeyPath }} + {{- end }} {{- end }} max_open_conns: {{ .Values.dataStore.sql.maxOpenConns }} max_idle_conns: {{ .Values.dataStore.sql.maxIdleConns }} diff --git a/charts/spire/charts/spire-server/templates/secret.yaml b/charts/spire/charts/spire-server/templates/secret.yaml index b0baa59..ae5cfc7 100644 --- a/charts/spire/charts/spire-server/templates/secret.yaml +++ b/charts/spire/charts/spire-server/templates/secret.yaml @@ -7,7 +7,8 @@ {{- if and (.Values.dataStore.sql.externalSecret.enabled) (eq .Values.dataStore.sql.externalSecret.key "") }} {{- fail "dataStore.sql.externalSecret.key cannot be empty string when dataStore.sql.externalSecret is enabled" }} {{- end }} -{{- if and (ne .Values.dataStore.sql.databaseType "sqlite3") (not .Values.dataStore.sql.externalSecret.enabled) (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") }} +{{- $postgresPasswordless := eq (include "spire-server.datastore-postgres-passwordless" .) "true" }} +{{- if and (ne .Values.dataStore.sql.databaseType "sqlite3") (not .Values.dataStore.sql.externalSecret.enabled) (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") (not $postgresPasswordless) }} apiVersion: v1 kind: Secret metadata: diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index ea6a428..009fc84 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -59,17 +59,21 @@ {{- if hasKey .Values.dataStore.sql "plugin_data" }} {{- fail "The plugin_data setting to the sql data store is no longer supported." }} {{- end }} -{{- if and (ne .Values.dataStore.sql.databaseType "mysql") (ne .Values.dataStore.sql.databaseType "aws_mysql") }} +{{- $certPathDatabaseType := or (eq .Values.dataStore.sql.databaseType "mysql") (eq .Values.dataStore.sql.databaseType "aws_mysql") (eq .Values.dataStore.sql.databaseType "postgres") (eq .Values.dataStore.sql.databaseType "aws_postgres") }} +{{- if not $certPathDatabaseType }} {{- if ne .Values.dataStore.sql.rootCAPath "" }} -{{- fail "rootCAPath can only be set with database type mysql or aws_mysql." }} +{{- fail "rootCAPath can only be set with database type mysql, aws_mysql, postgres or aws_postgres." }} {{- end }} {{- if ne .Values.dataStore.sql.clientCertPath "" }} -{{- fail "clientCertPath can only be set with database type mysql or aws_mysql." }} +{{- fail "clientCertPath can only be set with database type mysql, aws_mysql, postgres or aws_postgres." }} {{- end }} {{- if ne .Values.dataStore.sql.clientKeyPath "" }} -{{- fail "clientKeyPath can only be set with database type mysql or aws_mysql." }} +{{- fail "clientKeyPath can only be set with database type mysql, aws_mysql, postgres or aws_postgres." }} {{- end }} {{- end }} +{{- if and (or (eq .Values.dataStore.sql.databaseType "postgres") (eq .Values.dataStore.sql.databaseType "aws_postgres")) (ne .Values.dataStore.sql.password "") (ne .Values.dataStore.sql.clientCertPath "") }} +{{- fail "dataStore.sql.password and dataStore.sql.clientCertPath are mutually exclusive for postgres; use one authentication method." }} +{{- end }} {{- $pluginsToLoad := include "spire-lib.extract_custom_plugin_images" . | fromYamlArray }} {{- $jwtExecNeeded := false }} {{- range $name, $value := .Values.kubeConfigs }} @@ -316,7 +320,9 @@ spec: {{- with .Values.extraEnv }} {{- . | toYaml | nindent 10 }} {{- end }} - {{- if and (ne .Values.dataStore.sql.databaseType "sqlite3") (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") }} + {{- $postgresPasswordless := eq (include "spire-server.datastore-postgres-passwordless" .) "true" }} + {{- $postgresRoPasswordless := eq (include "spire-server.datastore-postgres-ro-passwordless" .) "true" }} + {{- if and (ne .Values.dataStore.sql.databaseType "sqlite3") (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") (not $postgresPasswordless) }} {{- if .Values.dataStore.sql.externalSecret.enabled }} - name: DBPW valueFrom: @@ -330,7 +336,8 @@ spec: name: {{ $fullname }}-dbpw key: DBPW {{- end }} - {{- if and .Values.dataStore.sql.readOnly.enabled (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") }} + {{- end }} + {{- if and .Values.dataStore.sql.readOnly.enabled (ne .Values.dataStore.sql.databaseType "gcp_mysql_sa_iam") (not $postgresRoPasswordless) }} {{- if .Values.dataStore.sql.readOnly.externalSecret.enabled }} - name: RODBPW valueFrom: @@ -345,7 +352,6 @@ spec: key: RODBPW {{- end }} {{- end }} - {{- end }} {{- if ne .Values.keyManager.awsKMS.accessKeyID "" }} - name: AWS_KMS_ACCESS_KEY_ID valueFrom: diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index dd5e7cc..191623e 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -194,7 +194,7 @@ dataStore: port: 0 ## @param dataStore.sql.username Only used when type != "sqlite3" username: spire - ## @param dataStore.sql.password Only used when type != "sqlite3" + ## @param dataStore.sql.password Only used when type != "sqlite3". For postgres/aws_postgres, leave empty to omit the password from the connection string (e.g. TLS client-certificate or IAM authentication). password: "" ## @param dataStore.sql.file Data source file. Only used when type == "sqlite3" and inMemory is false file: "/run/spire/data/datastore.sqlite3" @@ -203,11 +203,11 @@ dataStore: ## @param dataStore.sql.options [array] takes an array of objects of form {: } to use when building the database connection string options: [] - ## @param dataStore.sql.rootCAPath Path to Root CA bundle (MySQL only) + ## @param dataStore.sql.rootCAPath Path to Root CA bundle. Supports MySQL and postgres. rootCAPath: "" - ## @param dataStore.sql.clientCertPath Path to client certificate (MySQL only) + ## @param dataStore.sql.clientCertPath Path to client certificate. Supports MySQL and postgres. clientCertPath: "" - ## @param dataStore.sql.clientKeyPath Path to private key for client certificate (MySQL only) + ## @param dataStore.sql.clientKeyPath Path to private key for client certificate. Supports MySQL and postgres. clientKeyPath: "" ## When an external source creates the secret. The secret should reside in the same namespace as the spire server diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index e446a24..e76c249 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -663,4 +663,81 @@ spire-server: Expect(err).Should(Succeed()) }) }) + Describe("spire-server.dataStore.sql.postgres passwordless", func() { + It("omits password and the -dbpw Secret for cert auth with an empty password", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + dataStore: + sql: + databaseType: postgres + host: db.example.org + username: spire + password: "" + rootCAPath: /run/spire/db-ca/ca.crt + clientCertPath: /run/spire/db-certs/tls.crt + clientKeyPath: /run/spire/db-certs/tls.key +`) + Expect(err).Should(Succeed()) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + ShouldNot(ContainSubstring("password=${DBPW}")) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + Should(ContainSubstring("sslrootcert=/run/spire/db-ca/ca.crt")) + Expect(objs["spire/charts/spire-server/templates/secret.yaml"]). + ShouldNot(ContainSubstring("kind: Secret")) + Expect(objs["spire/charts/spire-server/templates/server-resource.yaml"]). + ShouldNot(ContainSubstring("name: DBPW")) + }) + + It("keeps the password token and DBPW env when an external secret provides the password", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + dataStore: + sql: + databaseType: postgres + host: db.example.org + username: spire + password: "" + externalSecret: + enabled: true + name: my-db-secret + key: password +`) + Expect(err).Should(Succeed()) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + Should(ContainSubstring("password=${DBPW}")) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("name: DBPW")) + Expect(serverResource).Should(ContainSubstring("name: my-db-secret")) + }) + + It("keeps the RODBPW env when a read-only external secret provides the password", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + dataStore: + sql: + databaseType: postgres + host: db.example.org + username: spire + password: "" + rootCAPath: /run/spire/db-ca/ca.crt + clientCertPath: /run/spire/db-certs/tls.crt + clientKeyPath: /run/spire/db-certs/tls.key + readOnly: + enabled: true + host: ro.example.org + username: spire + password: "" + externalSecret: + enabled: true + name: my-ro-db-secret + key: password +`) + Expect(err).Should(Succeed()) + Expect(objs["spire/charts/spire-server/templates/configmap.yaml"]). + Should(ContainSubstring("password=${RODBPW}")) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("name: RODBPW")) + Expect(serverResource).Should(ContainSubstring("name: my-ro-db-secret")) + }) + }) }) From 27026e4657401502d8898feeb76ccee45294e42b Mon Sep 17 00:00:00 2001 From: RuriRyan <2413490+RuriRyan@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:15:55 +0200 Subject: [PATCH 18/22] fix(spire): preserve webhook order in patch hooks (#918) * fix(spire-server): preserve webhook order in patch hooks Signed-off-by: Christoph Manns <2413490+RuriRyan@users.noreply.github.com> * test(spire): cover webhook patch order Signed-off-by: Christoph Manns <2413490+RuriRyan@users.noreply.github.com> * fix(spire-server): honor effective hook setting Signed-off-by: Christoph Manns <2413490+RuriRyan@users.noreply.github.com> * Apply suggestion from @kfox1111 Signed-off-by: kfox1111 --------- Signed-off-by: Christoph Manns <2413490+RuriRyan@users.noreply.github.com> Signed-off-by: kfox1111 Signed-off-by: kfox1111 Co-authored-by: kfox1111 Co-authored-by: kfox1111 --- .../templates/controller-manager-webhook.yaml | 7 +- .../templates/post-install-hook.yaml | 4 +- .../templates/post-upgrade-hook.yaml | 4 +- .../templates/pre-upgrade-hook.yaml | 4 +- tests/unit/spire_test.go | 147 ++++++++++++++++++ 5 files changed, 159 insertions(+), 7 deletions(-) diff --git a/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml b/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml index 8e80679..5bb5758 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml @@ -1,3 +1,4 @@ +{{- $installAndUpgradeHooksEnabled := dig "installAndUpgradeHooks" "enabled" .Values.controllerManager.installAndUpgradeHook.enabled .Values.global }} {{- if not .Values.externalServer }} {{- if eq .Values.controllerManager.staticManifestMode "off" }} {{- if and (eq (.Values.controllerManager.enabled | toString) "true") .Values.controllerManager.validatingWebhookConfiguration.enabled }} @@ -12,7 +13,7 @@ webhooks: name: {{ include "spire-controller-manager.fullname" . }}-webhook namespace: {{ include "spire-server.namespace" . }} path: /validate-spire-spiffe-io-v1alpha1-clusterfederatedtrustdomain - {{- if eq (.Values.controllerManager.installAndUpgradeHook.enabled | toString) "true" }} + {{- if eq ($installAndUpgradeHooksEnabled | toString) "true" }} failurePolicy: Ignore # Actual value to be set by post install/upgrade hooks {{- else }} failurePolicy: {{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }} @@ -30,7 +31,11 @@ webhooks: name: {{ include "spire-controller-manager.fullname" . }}-webhook namespace: {{ include "spire-server.namespace" . }} path: /validate-spire-spiffe-io-v1alpha1-clusterspiffeid + {{- if eq ($installAndUpgradeHooksEnabled | toString) "true" }} failurePolicy: Ignore # Actual value to be set by post install/upgrade hooks + {{- else }} + failurePolicy: {{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }} + {{- end }} name: vclusterspiffeid.kb.io rules: - apiGroups: ["spire.spiffe.io"] diff --git a/charts/spire/charts/spire-server/templates/post-install-hook.yaml b/charts/spire/charts/spire-server/templates/post-install-hook.yaml index ed00051..ef0026e 100644 --- a/charts/spire/charts/spire-server/templates/post-install-hook.yaml +++ b/charts/spire/charts/spire-server/templates/post-install-hook.yaml @@ -85,11 +85,11 @@ spec: { "webhooks":[ { - "name":"vclusterspiffeid.kb.io", + "name":"vclusterfederatedtrustdomain.kb.io", "failurePolicy":"{{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }}" }, { - "name":"vclusterfederatedtrustdomain.kb.io", + "name":"vclusterspiffeid.kb.io", "failurePolicy":"{{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }}" } ] diff --git a/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml b/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml index fb42cfb..4fc2274 100644 --- a/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml +++ b/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml @@ -85,11 +85,11 @@ spec: { "webhooks":[ { - "name":"vclusterspiffeid.kb.io", + "name":"vclusterfederatedtrustdomain.kb.io", "failurePolicy":"{{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }}" }, { - "name":"vclusterfederatedtrustdomain.kb.io", + "name":"vclusterspiffeid.kb.io", "failurePolicy":"{{ .Values.controllerManager.validatingWebhookConfiguration.failurePolicy }}" } ] diff --git a/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml b/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml index 18a5bc6..f5f94e6 100644 --- a/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml +++ b/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml @@ -85,11 +85,11 @@ spec: { "webhooks":[ { - "name":"vclusterspiffeid.kb.io", + "name":"vclusterfederatedtrustdomain.kb.io", "failurePolicy":"Ignore" }, { - "name":"vclusterfederatedtrustdomain.kb.io", + "name":"vclusterspiffeid.kb.io", "failurePolicy":"Ignore" } ] diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index e76c249..4f11873 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -1,6 +1,10 @@ package unit_test import ( + "encoding/json" + "io" + "strings" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -8,8 +12,71 @@ import ( helmloader "helm.sh/helm/v3/pkg/chart/loader" helmutil "helm.sh/helm/v3/pkg/chartutil" helmengine "helm.sh/helm/v3/pkg/engine" + yamlutil "k8s.io/apimachinery/pkg/util/yaml" ) +type renderedWebhook struct { + Name string `json:"name"` + FailurePolicy string `json:"failurePolicy"` +} + +type renderedDocument struct { + Kind string `json:"kind"` + Metadata struct { + Annotations map[string]string `json:"annotations"` + } `json:"metadata"` + Spec struct { + Template struct { + Spec struct { + Containers []struct { + Args []string `json:"args"` + } `json:"containers"` + } `json:"spec"` + } `json:"template"` + } `json:"spec"` + Webhooks []renderedWebhook `json:"webhooks"` +} + +func decodeRenderedDocuments(rendered string) ([]renderedDocument, error) { + decoder := yamlutil.NewYAMLOrJSONDecoder(strings.NewReader(rendered), 4096) + var documents []renderedDocument + for { + var document renderedDocument + err := decoder.Decode(&document) + if err == io.EOF { + return documents, nil + } + if err != nil { + return nil, err + } + if document.Kind != "" { + documents = append(documents, document) + } + } +} + +func patchWebhookNames(job renderedDocument) ([]string, error) { + for _, container := range job.Spec.Template.Spec.Containers { + for index, arg := range container.Args { + if arg != "-p" || index+1 >= len(container.Args) { + continue + } + var patch struct { + Webhooks []renderedWebhook `json:"webhooks"` + } + if err := json.Unmarshal([]byte(container.Args[index+1]), &patch); err != nil { + return nil, err + } + names := make([]string, 0, len(patch.Webhooks)) + for _, webhook := range patch.Webhooks { + names = append(names, webhook.Name) + } + return names, nil + } + } + return nil, nil +} + func ValueStringRender(chart *helmchart.Chart, values string) (map[string]string, error) { v, err := helmutil.ReadValues([]byte(values)) if err != nil { @@ -663,6 +730,86 @@ spire-server: Expect(err).Should(Succeed()) }) }) + Describe("spire-server webhook patch order", func() { + It("preserves the rendered webhook order in every strategic-merge hook", func() { + objs, err := ValueStringRender(chart, ` +global: + installAndUpgradeHooks: + enabled: true +spire-server: + enabled: true + controllerManager: + enabled: true +`) + Expect(err).Should(Succeed()) + + canonicalDocuments, err := decodeRenderedDocuments(objs["spire/charts/spire-server/templates/controller-manager-webhook.yaml"]) + Expect(err).Should(Succeed()) + Expect(canonicalDocuments).Should(HaveLen(1)) + Expect(canonicalDocuments[0].Kind).Should(Equal("ValidatingWebhookConfiguration")) + canonicalNames := make([]string, 0, len(canonicalDocuments[0].Webhooks)) + for _, webhook := range canonicalDocuments[0].Webhooks { + canonicalNames = append(canonicalNames, webhook.Name) + } + + for _, hook := range []struct { + name string + template string + }{ + {name: "post-install", template: "spire/charts/spire-server/templates/post-install-hook.yaml"}, + {name: "pre-upgrade", template: "spire/charts/spire-server/templates/pre-upgrade-hook.yaml"}, + {name: "post-upgrade", template: "spire/charts/spire-server/templates/post-upgrade-hook.yaml"}, + } { + documents, err := decodeRenderedDocuments(objs[hook.template]) + Expect(err).Should(Succeed()) + var jobs []renderedDocument + for _, document := range documents { + if document.Kind == "Job" && document.Metadata.Annotations["helm.sh/hook"] == hook.name { + jobs = append(jobs, document) + } + } + Expect(jobs).Should(HaveLen(1), hook.name) + actualNames, err := patchWebhookNames(jobs[0]) + Expect(err).Should(Succeed()) + Expect(actualNames).Should(Equal(canonicalNames), hook.name) + } + }) + }) + Describe("spire-server webhook hooks disabled", func() { + It("uses the configured failure policy and omits lifecycle Jobs", func() { + objs, err := ValueStringRender(chart, ` +global: + installAndUpgradeHooks: + enabled: false +spire-server: + controllerManager: + enabled: true + validatingWebhookConfiguration: + failurePolicy: Fail +`) + Expect(err).Should(Succeed()) + + canonicalDocuments, err := decodeRenderedDocuments(objs["spire/charts/spire-server/templates/controller-manager-webhook.yaml"]) + Expect(err).Should(Succeed()) + Expect(canonicalDocuments).Should(HaveLen(1)) + Expect(canonicalDocuments[0].Webhooks).Should(HaveLen(2)) + for _, webhook := range canonicalDocuments[0].Webhooks { + Expect(webhook.FailurePolicy).Should(Equal("Fail")) + } + + for _, template := range []string{ + "spire/charts/spire-server/templates/post-install-hook.yaml", + "spire/charts/spire-server/templates/pre-upgrade-hook.yaml", + "spire/charts/spire-server/templates/post-upgrade-hook.yaml", + } { + documents, err := decodeRenderedDocuments(objs[template]) + Expect(err).Should(Succeed()) + for _, document := range documents { + Expect(document.Kind).ShouldNot(Equal("Job"), template) + } + } + }) + }) Describe("spire-server.dataStore.sql.postgres passwordless", func() { It("omits password and the -dbpw Secret for cert auth with an empty password", func() { objs, err := ValueStringRender(chart, ` From b47ab6c19710192c7470d42baeba51c6946bfe4d Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Fri, 21 Aug 2026 05:22:55 -0700 Subject: [PATCH 19/22] README.md version match check (#916) * README.md version match check Signed-off-by: Kevin Fox * Fix existing version issues Signed-off-by: Kevin Fox * Fix existing version issues Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox --- .github/scripts/check-readme-versions.sh | 114 ++++++++++++++++++++ .github/workflows/helm-chart-ci-ignore.yaml | 1 + .github/workflows/helm-chart-ci.yaml | 17 +++ Makefile | 5 + charts/spire-crds/README.md | 2 +- charts/spire-ha-agent/README.md | 2 +- charts/spire-nested/README.md | 2 +- charts/spire/Chart.yaml | 2 +- charts/spire/README.md | 2 +- 9 files changed, 142 insertions(+), 5 deletions(-) create mode 100755 .github/scripts/check-readme-versions.sh diff --git a/.github/scripts/check-readme-versions.sh b/.github/scripts/check-readme-versions.sh new file mode 100755 index 0000000..5fe195c --- /dev/null +++ b/.github/scripts/check-readme-versions.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +# Verify that the shields.io version badges in each top-level chart README +# agree with the version and appVersion declared in the sibling Chart.yaml. +# Nothing regenerates those badges, so they drift silently as charts are bumped. +# +# READMEs without a Version badge (library charts, hand written docs) are +# skipped. A leading 'v' is ignored when comparing, so 'v0.3.0' matches '0.3.0'. + +set -euo pipefail + +SCRIPT="$(readlink -f "$0")" +SCRIPTPATH="$(dirname "${SCRIPT}")" +REPO_ROOT="$(dirname "${SCRIPTPATH}")/.." + +function print_problem { + echo >&2 " ❌ ${*}" +} + +function require_command { + command -v "$1" >/dev/null 2>&1 || { + print_problem "$2" + exit 1 + } +} + +# Print the value of a shields.io badge as " ", or nothing +# at all when the badge is absent. Each version is spelled twice in the badge +# markup, and both spellings need checking. +function badge_values { + local readme="$1" + local name="$2" + local badge alt url + + badge="$(grep -o "!\[${name}: [^]]*\](https://img.shields.io/badge/${name}-[^)]*)" "${readme}" | head -1 || true)" + if [ -z "${badge}" ]; then + return 0 + fi + + alt="$(printf '%s' "${badge}" | sed "s#^!\[${name}: \([^]]*\)\].*#\1#")" + # shields.io escapes a literal dash in the value as '--' + url="$(printf '%s' "${badge}" | sed "s#.*/badge/${name}-\(.*\)-informational.*#\1#; s#--#-#g")" + + printf '%s %s' "${alt}" "${url}" +} + +# Compare two versions, ignoring a single leading 'v' on either side. +function versions_match { + [ "${1#v}" = "${2#v}" ] +} + +require_command yq 'yq is required to run this script' + +problems=0 + +for chart_yaml in "${REPO_ROOT}"/charts/*/Chart.yaml; do + [ -f "${chart_yaml}" ] || continue + + chart_dir="$(dirname "${chart_yaml}")" + readme="${chart_dir}/README.md" + label="charts/$(basename "${chart_dir}")/README.md" + + [ -f "${readme}" ] || continue + + version_badge="$(badge_values "${readme}" Version)" + if [ -z "${version_badge}" ]; then + # No version badges in this README, nothing to keep in sync. + continue + fi + + chart_version="$(yq e '.version // ""' "${chart_yaml}")" + chart_app_version="$(yq e '.appVersion // ""' "${chart_yaml}")" + + version_alt="${version_badge%% *}" + version_url="${version_badge##* }" + + if ! versions_match "${version_alt}" "${chart_version}"; then + print_problem "${label}: Version badge ${version_alt} does not match Chart.yaml version ${chart_version}" + problems=$((problems + 1)) + fi + if [ "${version_url}" != "${version_alt}" ]; then + print_problem "${label}: Version badge text (${version_alt}) and image URL (${version_url}) disagree" + problems=$((problems + 1)) + fi + + app_badge="$(badge_values "${readme}" AppVersion)" + if [ -z "${app_badge}" ]; then + if [ -n "${chart_app_version}" ]; then + print_problem "${label}: has a Version badge but no AppVersion badge, while Chart.yaml declares appVersion ${chart_app_version}" + problems=$((problems + 1)) + fi + continue + fi + + app_alt="${app_badge%% *}" + app_url="${app_badge##* }" + + if [ -z "${chart_app_version}" ]; then + print_problem "${label}: AppVersion badge is ${app_alt} but Chart.yaml declares no appVersion" + problems=$((problems + 1)) + elif ! versions_match "${app_alt}" "${chart_app_version}"; then + print_problem "${label}: AppVersion badge ${app_alt} does not match Chart.yaml appVersion ${chart_app_version}" + problems=$((problems + 1)) + fi + if [ "${app_url}" != "${app_alt}" ]; then + print_problem "${label}: AppVersion badge text (${app_alt}) and image URL (${app_url}) disagree" + problems=$((problems + 1)) + fi +done + +if [ "${problems}" -ne 0 ]; then + print_problem "${problems} README version badge problem(s) found. Update the badges to match Chart.yaml." + exit 1 +fi diff --git a/.github/workflows/helm-chart-ci-ignore.yaml b/.github/workflows/helm-chart-ci-ignore.yaml index 470c2da..0bf2124 100644 --- a/.github/workflows/helm-chart-ci-ignore.yaml +++ b/.github/workflows/helm-chart-ci-ignore.yaml @@ -10,6 +10,7 @@ on: - '.github/tests/**/*.yaml' - '.github/tests/**/*.sh' - '.github/tests/**/*.json' + - '.github/scripts/check-readme-versions.sh' - 'examples/**/*.yaml' - 'helm-docs.sh' diff --git a/.github/workflows/helm-chart-ci.yaml b/.github/workflows/helm-chart-ci.yaml index 083939d..ec7b9b3 100644 --- a/.github/workflows/helm-chart-ci.yaml +++ b/.github/workflows/helm-chart-ci.yaml @@ -16,6 +16,7 @@ on: - '.github/tests/**/*.yaml' - '.github/tests/**/*.sh' - '.github/tests/**/*.json' + - '.github/scripts/check-readme-versions.sh' - 'examples/**/*.yaml' - 'examples/**/*.sh' - 'tests/**/*' @@ -42,6 +43,22 @@ jobs: - name: Verify Docs updated run: ./helm-docs.sh + - name: Verify README version badges + run: | + set +e + .github/scripts/check-readme-versions.sh 2>/tmp/badge-findings + res=$? + if [ $res -ne 0 ]; then + { + echo "## README version badges" + echo + echo ":x: These chart READMEs have version badges that disagree with their Chart.yaml. Please fix." + echo + cat /tmp/badge-findings + } >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi + - name: Verify Spire appVersion run: | set +e diff --git a/Makefile b/Makefile index e3d7506..c8b0937 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,11 @@ lint-release: ## Lint the charts using chart-testing for release @echo Linting charts… @ct lint --config ct.yaml --target-branch $(TARGET_BRANCH) +.PHONY: check-readme-versions +check-readme-versions: ## Verify chart README version badges match Chart.yaml + @echo Checking README version badges… + @.github/scripts/check-readme-versions.sh + ##@ Testing: (ensure to run on dedicated test cluster) .PHONY: clean-test-leftovers diff --git a/charts/spire-crds/README.md b/charts/spire-crds/README.md index 63134e1..c92c731 100644 --- a/charts/spire-crds/README.md +++ b/charts/spire-crds/README.md @@ -1,6 +1,6 @@ # spire-crds -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.2](https://img.shields.io/badge/AppVersion-1.7.2-informational?style=flat-square) +![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) A Helm chart to install the SPIRE CRDS. diff --git a/charts/spire-ha-agent/README.md b/charts/spire-ha-agent/README.md index 0130833..2c08b19 100644 --- a/charts/spire-ha-agent/README.md +++ b/charts/spire-ha-agent/README.md @@ -1,6 +1,6 @@ # spire-ha-agent -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.2](https://img.shields.io/badge/AppVersion-1.7.2-informational?style=flat-square) +![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) A Helm chart to install the SPIRE HA agent. diff --git a/charts/spire-nested/README.md b/charts/spire-nested/README.md index de38714..b0d1a3d 100644 --- a/charts/spire-nested/README.md +++ b/charts/spire-nested/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.28.5](https://img.shields.io/badge/Version-0.28.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) +![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index 30c3878..5040e65 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -5,7 +5,7 @@ description: > type: application version: 0.30.0 -appVersion: "1.14.5" +appVersion: "1.15.2" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/README.md b/charts/spire/README.md index 83a0a53..72a7646 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.28.5](https://img.shields.io/badge/Version-0.28.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) +![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. From 8a28a1b94fa93c4d7729eedff20e5497604b088f Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Fri, 21 Aug 2026 12:23:46 -0700 Subject: [PATCH 20/22] Update spire to 1.15.3 (#926) --- charts/spire-identity-exchange/README.md | 349 +++++++++--------- .../templates/configmap.yaml | 3 +- .../templates/deployment.yaml | 37 -- charts/spire-identity-exchange/values.yaml | 26 +- charts/spire-nested/Chart.yaml | 2 +- charts/spire-nested/README.md | 2 +- charts/spire/Chart.yaml | 2 +- charts/spire/README.md | 2 +- .../spiffe-oidc-discovery-provider/Chart.yaml | 2 +- charts/spire/charts/spire-agent/Chart.yaml | 2 +- charts/spire/charts/spire-server/Chart.yaml | 2 +- 11 files changed, 181 insertions(+), 248 deletions(-) diff --git a/charts/spire-identity-exchange/README.md b/charts/spire-identity-exchange/README.md index ff8db16..79468c4 100644 --- a/charts/spire-identity-exchange/README.md +++ b/charts/spire-identity-exchange/README.md @@ -72,180 +72,175 @@ controllerManager: ### Chart parameters -| Name | Description | Value | -| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| `agentSocketName` | The name of the spire-agent unix socket | `spire-agent.sock` | -| `csiDriverName` | The csi driver to use | `csi.spiffe.io` | -| `replicaCount` | Replica count | `1` | -| `namespaceOverride` | Namespace override | `""` | -| `annotations` | Annotations for the deployment | `{}` | -| `labels` | Labels for the deployment | `{}` | -| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `image.repository` | The repository within the registry | `spiffe/spire-identity-exchange-server` | -| `image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `spireServerAttestorSPIFFEWorkloadAPI.resources` | Resource requests and limits | `{}` | -| `spireServerAttestorSPIFFEWorkloadAPI.image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `spireServerAttestorSPIFFEWorkloadAPI.image.repository` | The repository within the registry | `spiffe/spire-server-attestor-spiffe-workload-api` | -| `spireServerAttestorSPIFFEWorkloadAPI.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `spireServerAttestorSPIFFEWorkloadAPI.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `spireAgent.resources` | Resource requests and limits | `{}` | -| `spireAgent.image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `spireAgent.image.repository` | The repository within the registry | `spiffe/spire-agent` | -| `spireAgent.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `spireAgent.image.tag` | Overrides the image tag whose default is the chart appVersion | `1.15.2` | -| `extraEnv` | Extra environment variables to add to the spire identity exchange | `[]` | -| `resources` | Resource requests and limits | `{}` | -| `configMap.annotations` | Annotations to add to the SPIRE Identity Exchange ConfigMap | `{}` | -| `podSecurityContext` | Pod security context for SPIRE Identity Exchange pods | `{}` | -| `securityContext` | Security context for SPIRE Identity Exchange deployment | `{}` | -| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | -| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` | -| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | -| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `5` | -| `podAnnotations` | Pod annotations for SPIRE Identity Exchange | `{}` | -| `podLabels` | Labels to add to pods | `{}` | -| `config.logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | -| `config.logFormat` | The log format, valid values are "text" and "json" | `text` | -| `telemetry.prometheus.port` | Port for prometheus metrics | `4950` | -| `telemetry.prometheus.podMonitor.enabled` | Enable podMonitor for prometheus | `false` | -| `telemetry.prometheus.podMonitor.namespace` | Override where to install the podMonitor, if not set will use the same namespace as the helm release | `""` | -| `telemetry.prometheus.podMonitor.labels` | Pod labels to filter for prometheus monitoring | `{}` | -| `imagePullSecrets` | Image pull secret names | `[]` | -| `nameOverride` | Name override | `""` | -| `fullnameOverride` | Full name override | `""` | -| `serviceAccount.create` | Specifies whether a service account should be created | `true` | -| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | -| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | -| `deleteHook.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` | -| `autoscaling.enabled` | Flag to enable autoscaling | `false` | -| `autoscaling.minReplicas` | Minimum replicas for autoscaling | `1` | -| `autoscaling.maxReplicas` | Maximum replicas for autoscaling | `5` | -| `autoscaling.targetCPUUtilizationPercentage` | Target CPU utlization that triggers autoscaling | `80` | -| `autoscaling.targetMemoryUtilizationPercentage` | Target Memory utlization that triggers autoscaling | `80` | -| `nodeSelector` | Node selector | `{}` | -| `tolerations` | list of tolerations | `[]` | -| `affinity` | Node affinity | `{}` | -| `trustDomain` | Set the trust domain to be used for the SPIFFE identifiers | `example.org` | -| `clusterName` | The name of this Kubernetes cluster, as it appears in SPIFFE ID paths | `example-cluster` | -| `jwtIssuer` | The issuer URL for JWT-SVIDs. Defaults to https://oidc-discovery.$trustDomain | `""` | -| `clusterDomain` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `cluster.local` | -| `auth.plugins.k8s_psat.enabled` | Enable the k8s psat plugin | `true` | -| `auth.plugins.k8s_psat.config.audiences` | The audiences to allow | `[]` | -| `auth.plugins.k8s_psat.config.allowedServiceAccounts` | The service accounts that are allowed | `[]` | -| `auth.plugins.spiffe.enabled` | Enable the spiffe plugin | `true` | -| `auth.plugins.spiffe.keySource` | What source to use to fetch the keys. Can be oidc or oidcLocal. oidcLocal forces discoveryURL to be the internal discovery address. | `oidcLocal` | -| `auth.plugins.spiffe.csiDriverName` | The CSI driver providing the SPIRE Agent workload socket this plugin attests against. Defaults to the chart level csiDriverName. Requires config.connectWithTrustBundle. | | -| `auth.plugins.spiffe.config.issuerURL` | The url to connect to for JWKS discovery | `${SPIFFE_JWT_ISSUER}` | -| `auth.plugins.spiffe.config.trustDomain` | The trust domain to use | `${SPIFFE_TRUST_DOMAIN}` | -| `auth.plugins.spiffe.config.pathPatterns` | The service accounts that are allowed | `[]` | -| `auth.plugins.spiffe.config.audiences` | The audiences to allow | `[]` | -| `auth.plugins.spiffe.config.connectWithTrustBundle` | Use the trust bundle to validate the issuerURL | `true` | -| `auth.stacks.image_pull.enabled` | Enable the image_pull stack | `true` | -| `auth.stacks.image_pull.plugins` | List of plugins that are required by this stack | `[]` | -| `auth.unsupportedBuiltInPlugins` | Unsupported mechanism to use plugins not yet supported by the chart. | `{}` | -| `auth.passthroughPlugins` | Address each plugin as a stack of its own, in addition to any stacks defined | `false` | -| `tls.externalSecret.enabled` | Provide your own certificate/key via tls style Kubernetes Secret | `false` | -| `tls.externalSecret.secretName` | Specify which Secret to use | `""` | -| `tls.certManager.enabled` | Use certificateManager to create the certificate | `false` | -| `tls.certManager.issuer.create` | Create an issuer to use to issue the certificate | `true` | -| `tls.certManager.issuer.acme.email` | Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service | `""` | -| `tls.certManager.issuer.acme.server` | Server to use to get certificate. Defaults to LetsEncrypt | `https://acme-v02.api.letsencrypt.org/directory` | -| `tls.certManager.issuer.acme.solvers` | Configure the issuer solvers. Defaults to http01 via ingress. | `{}` | -| `tls.certManager.certificate.dnsNames` | Override the dnsNames on the certificate request. Defaults to the same settings as Ingress | `[]` | -| `tls.certManager.certificate.issuerRef.group` | If you are using an external plugin, specify the group for it here | `""` | -| `tls.certManager.certificate.issuerRef.kind` | Kind of the issuer reference. Override if you want to use a ClusterIssuer | `Issuer` | -| `tls.certManager.certificate.issuerRef.name` | Name of the issuer to use. If unset, it will use the name of the built in issuer | `""` | -| `tls.rest.enabled` | Enable the REST listener served with the certificate from disk | `false` | -| `tls.rest.port` | Container port for the REST listener served with the certificate from disk | `8444` | -| `tls.rest.service.type` | Service type | `ClusterIP` | -| `tls.rest.service.port` | port for the service | `443` | -| `tls.rest.service.annotations` | Annotations for service resource | `{}` | -| `tls.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `tls.rest.ingress.enabled` | Flag to enable ingress | `false` | -| `tls.rest.ingress.className` | Ingress class name | `""` | -| `tls.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `tls.rest.ingress.annotations` | Annotations for ingress object | `{}` | -| `tls.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest` | -| `tls.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `tls.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `tls.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `tls.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | -| `tls.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` | -| `tls.rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | -| `tls.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `tls.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `tls.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `tls.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `tls.rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | -| `tls.grpc.enabled` | Enable the gRPC listener served with the certificate from disk | `false` | -| `tls.grpc.port` | Container port for the gRPC listener served with the certificate from disk | `8443` | -| `tls.grpc.service.type` | Service type | `ClusterIP` | -| `tls.grpc.service.port` | port for the service | `443` | -| `tls.grpc.service.annotations` | Annotations for service resource | `{}` | -| `tls.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `tls.grpc.ingress.enabled` | Flag to enable ingress | `false` | -| `tls.grpc.ingress.className` | Ingress class name | `""` | -| `tls.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `tls.grpc.ingress.annotations` | Annotations for ingress object | `{}` | -| `tls.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc` | -| `tls.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `tls.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `tls.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `tls.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | -| `tls.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` | -| `tls.grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | -| `tls.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `tls.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `tls.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `tls.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `tls.grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | -| `spiffe.rest.enabled` | Enable the REST listener served with this deployment's own X509-SVID | `true` | -| `spiffe.rest.port` | Container port for the REST listener served with this deployment's own X509-SVID | `8544` | -| `spiffe.rest.service.type` | Service type | `ClusterIP` | -| `spiffe.rest.service.port` | port for the service | `443` | -| `spiffe.rest.service.annotations` | Annotations for service resource | `{}` | -| `spiffe.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `spiffe.rest.ingress.enabled` | Flag to enable ingress | `false` | -| `spiffe.rest.ingress.className` | Ingress class name | `""` | -| `spiffe.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `spiffe.rest.ingress.annotations` | Annotations for ingress object | `{}` | -| `spiffe.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest-spiffe` | -| `spiffe.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `spiffe.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `spiffe.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `spiffe.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | -| `spiffe.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest-spiffe` | -| `spiffe.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `spiffe.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `spiffe.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `spiffe.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `spiffe.grpc.enabled` | Enable the gRPC listener served with this deployment's own X509-SVID | `false` | -| `spiffe.grpc.port` | Container port for the gRPC listener served with this deployment's own X509-SVID | `8543` | -| `spiffe.grpc.service.type` | Service type | `ClusterIP` | -| `spiffe.grpc.service.port` | port for the service | `443` | -| `spiffe.grpc.service.annotations` | Annotations for service resource | `{}` | -| `spiffe.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | -| `spiffe.grpc.ingress.enabled` | Flag to enable ingress | `false` | -| `spiffe.grpc.ingress.className` | Ingress class name | `""` | -| `spiffe.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `spiffe.grpc.ingress.annotations` | Annotations for ingress object | `{}` | -| `spiffe.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc-spiffe` | -| `spiffe.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `spiffe.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `spiffe.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `spiffe.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | -| `spiffe.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc-spiffe` | -| `spiffe.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | -| `spiffe.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | -| `spiffe.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | -| `spiffe.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | -| `tools.kubectl.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` | -| `tools.kubectl.image.repository` | The repository within the registry | `kubectl` | -| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tools.kubectl.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `clusterRole.create` | create a k8s cluster role to allow access to token reviews and oidc discovery | `true` | -| `server.address` | Address for Spire server | `""` | -| `server.port` | Port number for Spire server | `443` | -| `server.namespaceOverride` | Override the namespace for Spire server | `""` | -| `server.nameOverride` | Override the name for Spire server. Should only be changed when building your own nested chart to ensure names align. | `""` | +| Name | Description | Value | +| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `agentSocketName` | The name of the spire-agent unix socket | `spire-agent.sock` | +| `csiDriverName` | The csi driver to use | `csi.spiffe.io` | +| `replicaCount` | Replica count | `1` | +| `namespaceOverride` | Namespace override | `""` | +| `annotations` | Annotations for the deployment | `{}` | +| `labels` | Labels for the deployment | `{}` | +| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | +| `image.repository` | The repository within the registry | `spiffe/spire-identity-exchange-server` | +| `image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `spireAgent.resources` | Resource requests and limits | `{}` | +| `spireAgent.image.registry` | The OCI registry to pull the image from | `ghcr.io` | +| `spireAgent.image.repository` | The repository within the registry | `spiffe/spire-agent` | +| `spireAgent.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `spireAgent.image.tag` | Overrides the image tag whose default is the chart appVersion | `1.15.3` | +| `extraEnv` | Extra environment variables to add to the spire identity exchange | `[]` | +| `resources` | Resource requests and limits | `{}` | +| `configMap.annotations` | Annotations to add to the SPIRE Identity Exchange ConfigMap | `{}` | +| `podSecurityContext` | Pod security context for SPIRE Identity Exchange pods | `{}` | +| `securityContext` | Security context for SPIRE Identity Exchange deployment | `{}` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | +| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `5` | +| `podAnnotations` | Pod annotations for SPIRE Identity Exchange | `{}` | +| `podLabels` | Labels to add to pods | `{}` | +| `config.logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | +| `config.logFormat` | The log format, valid values are "text" and "json" | `text` | +| `telemetry.prometheus.port` | Port for prometheus metrics | `4950` | +| `telemetry.prometheus.podMonitor.enabled` | Enable podMonitor for prometheus | `false` | +| `telemetry.prometheus.podMonitor.namespace` | Override where to install the podMonitor, if not set will use the same namespace as the helm release | `""` | +| `telemetry.prometheus.podMonitor.labels` | Pod labels to filter for prometheus monitoring | `{}` | +| `imagePullSecrets` | Image pull secret names | `[]` | +| `nameOverride` | Name override | `""` | +| `fullnameOverride` | Full name override | `""` | +| `serviceAccount.create` | Specifies whether a service account should be created | `true` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | +| `deleteHook.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` | +| `autoscaling.enabled` | Flag to enable autoscaling | `false` | +| `autoscaling.minReplicas` | Minimum replicas for autoscaling | `1` | +| `autoscaling.maxReplicas` | Maximum replicas for autoscaling | `5` | +| `autoscaling.targetCPUUtilizationPercentage` | Target CPU utlization that triggers autoscaling | `80` | +| `autoscaling.targetMemoryUtilizationPercentage` | Target Memory utlization that triggers autoscaling | `80` | +| `nodeSelector` | Node selector | `{}` | +| `tolerations` | list of tolerations | `[]` | +| `affinity` | Node affinity | `{}` | +| `trustDomain` | Set the trust domain to be used for the SPIFFE identifiers | `example.org` | +| `clusterName` | The name of this Kubernetes cluster, as it appears in SPIFFE ID paths | `example-cluster` | +| `jwtIssuer` | The issuer URL for JWT-SVIDs. Defaults to https://oidc-discovery.$trustDomain | `""` | +| `clusterDomain` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `cluster.local` | +| `auth.plugins.k8s_psat.enabled` | Enable the k8s psat plugin | `true` | +| `auth.plugins.k8s_psat.config.audiences` | The audiences to allow | `[]` | +| `auth.plugins.k8s_psat.config.allowedServiceAccounts` | The service accounts that are allowed | `[]` | +| `auth.plugins.spiffe.enabled` | Enable the spiffe plugin | `true` | +| `auth.plugins.spiffe.keySource` | What source to use to fetch the keys. Can be oidc or oidcLocal. oidcLocal forces discoveryURL to be the internal discovery address. | `oidcLocal` | +| `auth.plugins.spiffe.csiDriverName` | The CSI driver providing the SPIRE Agent workload socket this plugin attests against. Defaults to the chart level csiDriverName. Requires config.connectWithTrustBundle. | | +| `auth.plugins.spiffe.config.issuerURL` | The url to connect to for JWKS discovery | `${SPIFFE_JWT_ISSUER}` | +| `auth.plugins.spiffe.config.trustDomain` | The trust domain to use | `${SPIFFE_TRUST_DOMAIN}` | +| `auth.plugins.spiffe.config.pathPatterns` | The service accounts that are allowed | `[]` | +| `auth.plugins.spiffe.config.audiences` | The audiences to allow | `[]` | +| `auth.plugins.spiffe.config.connectWithTrustBundle` | Use the trust bundle to validate the issuerURL | `true` | +| `auth.stacks.image_pull.enabled` | Enable the image_pull stack | `true` | +| `auth.stacks.image_pull.plugins` | List of plugins that are required by this stack | `[]` | +| `auth.unsupportedBuiltInPlugins` | Unsupported mechanism to use plugins not yet supported by the chart. | `{}` | +| `auth.passthroughPlugins` | Address each plugin as a stack of its own, in addition to any stacks defined | `false` | +| `tls.externalSecret.enabled` | Provide your own certificate/key via tls style Kubernetes Secret | `false` | +| `tls.externalSecret.secretName` | Specify which Secret to use | `""` | +| `tls.certManager.enabled` | Use certificateManager to create the certificate | `false` | +| `tls.certManager.issuer.create` | Create an issuer to use to issue the certificate | `true` | +| `tls.certManager.issuer.acme.email` | Must be set in order to register with LetsEncrypt. By setting, you agree to their Terms of Service | `""` | +| `tls.certManager.issuer.acme.server` | Server to use to get certificate. Defaults to LetsEncrypt | `https://acme-v02.api.letsencrypt.org/directory` | +| `tls.certManager.issuer.acme.solvers` | Configure the issuer solvers. Defaults to http01 via ingress. | `{}` | +| `tls.certManager.certificate.dnsNames` | Override the dnsNames on the certificate request. Defaults to the same settings as Ingress | `[]` | +| `tls.certManager.certificate.issuerRef.group` | If you are using an external plugin, specify the group for it here | `""` | +| `tls.certManager.certificate.issuerRef.kind` | Kind of the issuer reference. Override if you want to use a ClusterIssuer | `Issuer` | +| `tls.certManager.certificate.issuerRef.name` | Name of the issuer to use. If unset, it will use the name of the built in issuer | `""` | +| `tls.rest.enabled` | Enable the REST listener served with the certificate from disk | `false` | +| `tls.rest.port` | Container port for the REST listener served with the certificate from disk | `8444` | +| `tls.rest.service.type` | Service type | `ClusterIP` | +| `tls.rest.service.port` | port for the service | `443` | +| `tls.rest.service.annotations` | Annotations for service resource | `{}` | +| `tls.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `tls.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `tls.rest.ingress.className` | Ingress class name | `""` | +| `tls.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `tls.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `tls.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest` | +| `tls.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `tls.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `tls.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `tls.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `tls.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` | +| `tls.rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `tls.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `tls.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `tls.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `tls.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `tls.rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `tls.grpc.enabled` | Enable the gRPC listener served with the certificate from disk | `false` | +| `tls.grpc.port` | Container port for the gRPC listener served with the certificate from disk | `8443` | +| `tls.grpc.service.type` | Service type | `ClusterIP` | +| `tls.grpc.service.port` | port for the service | `443` | +| `tls.grpc.service.annotations` | Annotations for service resource | `{}` | +| `tls.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `tls.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `tls.grpc.ingress.className` | Ingress class name | `""` | +| `tls.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `tls.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `tls.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc` | +| `tls.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `tls.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `tls.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `tls.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `tls.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` | +| `tls.grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` | +| `tls.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `tls.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `tls.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `tls.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `tls.grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` | +| `spiffe.rest.enabled` | Enable the REST listener served with this deployment's own X509-SVID | `true` | +| `spiffe.rest.port` | Container port for the REST listener served with this deployment's own X509-SVID | `8544` | +| `spiffe.rest.service.type` | Service type | `ClusterIP` | +| `spiffe.rest.service.port` | port for the service | `443` | +| `spiffe.rest.service.annotations` | Annotations for service resource | `{}` | +| `spiffe.rest.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spiffe.rest.ingress.enabled` | Flag to enable ingress | `false` | +| `spiffe.rest.ingress.className` | Ingress class name | `""` | +| `spiffe.rest.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spiffe.rest.ingress.annotations` | Annotations for ingress object | `{}` | +| `spiffe.rest.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-rest-spiffe` | +| `spiffe.rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spiffe.rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spiffe.rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spiffe.rest.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spiffe.rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest-spiffe` | +| `spiffe.rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spiffe.rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spiffe.rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spiffe.rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `spiffe.grpc.enabled` | Enable the gRPC listener served with this deployment's own X509-SVID | `false` | +| `spiffe.grpc.port` | Container port for the gRPC listener served with this deployment's own X509-SVID | `8543` | +| `spiffe.grpc.service.type` | Service type | `ClusterIP` | +| `spiffe.grpc.service.port` | port for the service | `443` | +| `spiffe.grpc.service.annotations` | Annotations for service resource | `{}` | +| `spiffe.grpc.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` | +| `spiffe.grpc.ingress.enabled` | Flag to enable ingress | `false` | +| `spiffe.grpc.ingress.className` | Ingress class name | `""` | +| `spiffe.grpc.ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `spiffe.grpc.ingress.annotations` | Annotations for ingress object | `{}` | +| `spiffe.grpc.ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The grpc of the rules will be autogenerated. For more customizability, use hosts[] instead. | `spire-identity-exchange-grpc-spiffe` | +| `spiffe.grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `spiffe.grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | +| `spiffe.grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | +| `spiffe.grpc.gatewayAPI.enabled` | Flag to expose the endpoint via Gateway API | `false` | +| `spiffe.grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc-spiffe` | +| `spiffe.grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` | +| `spiffe.grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` | +| `spiffe.grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` | +| `spiffe.grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` | +| `tools.kubectl.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` | +| `tools.kubectl.image.repository` | The repository within the registry | `kubectl` | +| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `tools.kubectl.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `clusterRole.create` | create a k8s cluster role to allow access to token reviews and oidc discovery | `true` | +| `server.address` | Address for Spire server | `""` | +| `server.port` | Port number for Spire server | `443` | +| `server.namespaceOverride` | Override the namespace for Spire server | `""` | +| `server.nameOverride` | Override the name for Spire server. Should only be changed when building your own nested chart to ensure names align. | `""` | diff --git a/charts/spire-identity-exchange/templates/configmap.yaml b/charts/spire-identity-exchange/templates/configmap.yaml index 87a97e2..d495189 100644 --- a/charts/spire-identity-exchange/templates/configmap.yaml +++ b/charts/spire-identity-exchange/templates/configmap.yaml @@ -206,8 +206,7 @@ data: trust_domain = {{ $trustDomain | quote }} server_address = {{ include "spire-identity-exchange.server-address" . | trim | quote }} server_port = {{ .Values.server.port }} - trust_bundle_url = "http://localhost/trustbundle" - trust_bundle_unix_socket = "/trustbundle/socket" + trust_bundle_spiffe_workload_api = "unix://{{ include "spire-identity-exchange.workload-api-socket-path" . }}" rebootstrap_mode = "always" rebootstrap_delay = "5m" diff --git a/charts/spire-identity-exchange/templates/deployment.yaml b/charts/spire-identity-exchange/templates/deployment.yaml index 0d90193..0efe5ca 100644 --- a/charts/spire-identity-exchange/templates/deployment.yaml +++ b/charts/spire-identity-exchange/templates/deployment.yaml @@ -45,38 +45,6 @@ spec: securityContext: {{- include "spire-identity-exchange.podSecurityContext" . | nindent 8 }} initContainers: - - name: spire-server-attestor - securityContext: - {{- include "spire-lib.securitycontext" . | nindent 12 }} - resources: - {{- toYaml .Values.spireServerAttestorSPIFFEWorkloadAPI.resources | nindent 12 }} - image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.spireServerAttestorSPIFFEWorkloadAPI.image "global" .Values.global) }} - imagePullPolicy: {{ .Values.spireServerAttestorSPIFFEWorkloadAPI.image.pullPolicy }} - restartPolicy: Always - args: - - /trustbundle/socket - env: - - name: SPIFFE_ENDPOINT_SOCKET - value: "unix://{{ include "spire-identity-exchange.workload-api-socket-path" . }}" - - name: SPIFFE_TRUST_DOMAIN - value: {{ $trustDomain }} - readinessProbe: - exec: - command: - - /ko-app/spire-server-attestor-spiffe-workload-api - - --healthcheck - - /trustbundle/socket - initialDelaySeconds: 5 - periodSeconds: 30 - timeoutSeconds: 10 - successThreshold: 1 - failureThreshold: 3 - volumeMounts: - - name: spiffe-workload-api - mountPath: /spiffe-workload-api - readOnly: true - - name: trustbundle - mountPath: /trustbundle - name: spire-agent securityContext: {{- include "spire-lib.securitycontext" . | nindent 12 }} @@ -111,9 +79,6 @@ spec: readOnly: true - name: spire-agent-socket mountPath: /agent - - name: trustbundle - mountPath: /trustbundle - readOnly: true - name: spire-agent-data mountPath: /agent-data containers: @@ -213,8 +178,6 @@ spec: emptyDir: {} - name: spire-agent-data emptyDir: {} - - name: trustbundle - emptyDir: {} - name: spire-identity-exchange-config configMap: name: {{ include "spire-identity-exchange.fullname" . }} diff --git a/charts/spire-identity-exchange/values.yaml b/charts/spire-identity-exchange/values.yaml index 16084bf..ee2994d 100644 --- a/charts/spire-identity-exchange/values.yaml +++ b/charts/spire-identity-exchange/values.yaml @@ -37,30 +37,6 @@ image: pullPolicy: IfNotPresent tag: "" -spireServerAttestorSPIFFEWorkloadAPI: - ## @param spireServerAttestorSPIFFEWorkloadAPI.resources [object] Resource requests and limits - resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # requests: - # cpu: 50m - # memory: 32Mi - # limits: - # cpu: 100m - # memory: 64Mi - image: - ## @param spireServerAttestorSPIFFEWorkloadAPI.image.registry The OCI registry to pull the image from - ## @param spireServerAttestorSPIFFEWorkloadAPI.image.repository The repository within the registry - ## @param spireServerAttestorSPIFFEWorkloadAPI.image.pullPolicy The image pull policy - ## @param spireServerAttestorSPIFFEWorkloadAPI.image.tag Overrides the image tag whose default is the chart appVersion - ## - registry: ghcr.io - repository: spiffe/spire-server-attestor-spiffe-workload-api - pullPolicy: IfNotPresent - tag: "" - spireAgent: ## @param spireAgent.resources [object] Resource requests and limits resources: {} @@ -83,7 +59,7 @@ spireAgent: registry: ghcr.io repository: spiffe/spire-agent pullPolicy: IfNotPresent - tag: "1.15.2" + tag: "1.15.3" ## @param extraEnv [array] Extra environment variables to add to the spire identity exchange extraEnv: [] diff --git a/charts/spire-nested/Chart.yaml b/charts/spire-nested/Chart.yaml index 3e49b90..bf6e887 100644 --- a/charts/spire-nested/Chart.yaml +++ b/charts/spire-nested/Chart.yaml @@ -5,7 +5,7 @@ description: > type: application version: 0.30.0 -appVersion: "1.15.2" +appVersion: "1.15.3" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire-nested/README.md b/charts/spire-nested/README.md index b0d1a3d..524dc80 100644 --- a/charts/spire-nested/README.md +++ b/charts/spire-nested/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) +![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index 5040e65..2f205a3 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -5,7 +5,7 @@ description: > type: application version: 0.30.0 -appVersion: "1.15.2" +appVersion: "1.15.3" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/README.md b/charts/spire/README.md index 72a7646..3436687 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.2](https://img.shields.io/badge/AppVersion-1.15.2-informational?style=flat-square) +![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml index fd6d93b..e6312cc 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml @@ -3,7 +3,7 @@ name: spiffe-oidc-discovery-provider description: A Helm chart to install the SPIFFE OIDC discovery provider. type: application version: 0.1.0 -appVersion: "1.15.2" +appVersion: "1.15.3" keywords: ["spiffe", "oidc"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-agent/Chart.yaml b/charts/spire/charts/spire-agent/Chart.yaml index 17b4048..207c29e 100644 --- a/charts/spire/charts/spire-agent/Chart.yaml +++ b/charts/spire/charts/spire-agent/Chart.yaml @@ -3,7 +3,7 @@ name: spire-agent description: A Helm chart to install the SPIRE agent. type: application version: 0.1.0 -appVersion: "1.15.2" +appVersion: "1.15.3" keywords: ["spiffe", "spire-agent"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-server/Chart.yaml b/charts/spire/charts/spire-server/Chart.yaml index 8a5b7c4..0892a65 100644 --- a/charts/spire/charts/spire-server/Chart.yaml +++ b/charts/spire/charts/spire-server/Chart.yaml @@ -3,7 +3,7 @@ name: spire-server description: A Helm chart to install the SPIRE server. type: application version: 0.1.0 -appVersion: "1.15.2" +appVersion: "1.15.3" keywords: ["spiffe", "spire-server", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: From 0824944320849b5866512488de2bebf4dd9f24aa Mon Sep 17 00:00:00 2001 From: Kevin Fox Date: Fri, 21 Aug 2026 14:26:06 -0700 Subject: [PATCH 21/22] Bump spire-crds and dependent Helm Chart versions (patch) * b47ab6c1 README.md version match check (#916) Signed-off-by: Kevin Fox --- charts/spire-crds/Chart.yaml | 2 +- charts/spire-crds/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/spire-crds/Chart.yaml b/charts/spire-crds/Chart.yaml index 1723dd5..a01e0f5 100644 --- a/charts/spire-crds/Chart.yaml +++ b/charts/spire-crds/Chart.yaml @@ -4,7 +4,7 @@ description: > A Helm chart for deploying the Spire CRDS type: application -version: 0.6.0 +version: 0.6.1 appVersion: "0.0.1" keywords: ["spire-crds"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire diff --git a/charts/spire-crds/README.md b/charts/spire-crds/README.md index c92c731..f1bf16a 100644 --- a/charts/spire-crds/README.md +++ b/charts/spire-crds/README.md @@ -1,6 +1,6 @@ # spire-crds -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) +![Version: 0.6.1](https://img.shields.io/badge/Version-0.6.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) A Helm chart to install the SPIRE CRDS. From 3b4e7a1bd34ebd6fda116164c60c220ad33a6e27 Mon Sep 17 00:00:00 2001 From: Kevin Fox Date: Fri, 21 Aug 2026 14:41:53 -0700 Subject: [PATCH 22/22] Bump spire-lib and dependent Helm Chart versions (patch) * ab5e5d86 fix(spiffe-oidc-discovery-provider): run under restricted PSA/SCC on OpenShift (#920) Signed-off-by: Kevin Fox --- charts/spiffe-step-ssh/Chart.lock | 6 +++--- charts/spiffe-step-ssh/Chart.yaml | 4 ++-- charts/spire-ha-agent/Chart.lock | 6 +++--- charts/spire-ha-agent/Chart.yaml | 4 ++-- charts/spire-ha-agent/README.md | 2 +- charts/spire-identity-exchange/Chart.lock | 6 +++--- charts/spire-identity-exchange/Chart.yaml | 4 ++-- charts/spire-identity-exchange/README.md | 2 +- charts/spire-lib/Chart.yaml | 2 +- charts/spire-lib/README.md | 2 +- charts/spire-nested/Chart.lock | 12 ++++++------ charts/spire-nested/Chart.yaml | 10 +++++----- charts/spire-nested/README.md | 2 +- charts/spire/Chart.lock | 8 ++++---- charts/spire/Chart.yaml | 6 +++--- charts/spire/README.md | 2 +- 16 files changed, 39 insertions(+), 39 deletions(-) diff --git a/charts/spiffe-step-ssh/Chart.lock b/charts/spiffe-step-ssh/Chart.lock index bfb76ee..f9157ef 100644 --- a/charts/spiffe-step-ssh/Chart.lock +++ b/charts/spiffe-step-ssh/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: step-certificates repository: https://smallstep.github.io/helm-charts/ version: 1.27.4 -digest: sha256:b41dcd4ef1a70026a96cbd6b704ae22b7c62c139d3cf699ac746366658698825 -generated: "2026-07-31T16:55:06.703514-07:00" +digest: sha256:11d3748666d0c9aa04a77a01ed3cde99553aae715bc9f00b311247e6a9c6ee91 +generated: "2026-08-21T14:41:49.31784-07:00" diff --git a/charts/spiffe-step-ssh/Chart.yaml b/charts/spiffe-step-ssh/Chart.yaml index 7bc0f4f..53db9ff 100644 --- a/charts/spiffe-step-ssh/Chart.yaml +++ b/charts/spiffe-step-ssh/Chart.yaml @@ -13,7 +13,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.0 +version: 0.3.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. @@ -30,7 +30,7 @@ maintainers: dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: step-certificates alias: step repository: https://smallstep.github.io/helm-charts/ diff --git a/charts/spire-ha-agent/Chart.lock b/charts/spire-ha-agent/Chart.lock index 46d9922..d286b70 100644 --- a/charts/spire-ha-agent/Chart.lock +++ b/charts/spire-ha-agent/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 -digest: sha256:b09e1da391c6df954369ee679f0757522fb39afe9a8e6bdc77c25d048fb30340 -generated: "2026-07-31T16:55:07.148848-07:00" + version: 0.3.1 +digest: sha256:fd4f15738349c83d9a1c60a1529ecc5cb8df6ecd9af21176df54f61f40d8973e +generated: "2026-08-21T14:41:50.554693-07:00" diff --git a/charts/spire-ha-agent/Chart.yaml b/charts/spire-ha-agent/Chart.yaml index 6d9b719..b0b9b1e 100644 --- a/charts/spire-ha-agent/Chart.yaml +++ b/charts/spire-ha-agent/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: spire-ha-agent description: A Helm chart to install the SPIRE HA agent. type: application -version: 0.3.0 +version: 0.3.1 appVersion: "0.3.0" keywords: ["spiffe", "spire-ha-agent"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire-ha-agent @@ -20,4 +20,4 @@ maintainers: dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 diff --git a/charts/spire-ha-agent/README.md b/charts/spire-ha-agent/README.md index 2c08b19..915fdf6 100644 --- a/charts/spire-ha-agent/README.md +++ b/charts/spire-ha-agent/README.md @@ -1,6 +1,6 @@ # spire-ha-agent -![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) +![Version: 0.3.1](https://img.shields.io/badge/Version-0.3.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) A Helm chart to install the SPIRE HA agent. diff --git a/charts/spire-identity-exchange/Chart.lock b/charts/spire-identity-exchange/Chart.lock index ba58ba8..57de7aa 100644 --- a/charts/spire-identity-exchange/Chart.lock +++ b/charts/spire-identity-exchange/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 -digest: sha256:b09e1da391c6df954369ee679f0757522fb39afe9a8e6bdc77c25d048fb30340 -generated: "2026-07-31T16:55:07.22031-07:00" + version: 0.3.1 +digest: sha256:fd4f15738349c83d9a1c60a1529ecc5cb8df6ecd9af21176df54f61f40d8973e +generated: "2026-08-21T14:41:50.632935-07:00" diff --git a/charts/spire-identity-exchange/Chart.yaml b/charts/spire-identity-exchange/Chart.yaml index 84f79ca..f7f3566 100644 --- a/charts/spire-identity-exchange/Chart.yaml +++ b/charts/spire-identity-exchange/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: spire-identity-exchange description: A Helm chart to install the SPIRE Identity Exchange. type: application -version: 0.2.0 +version: 0.2.1 appVersion: "v0.5.0" keywords: ["spiffe", "spire", "identity exchange"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire-identity-exchange @@ -20,4 +20,4 @@ maintainers: dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 diff --git a/charts/spire-identity-exchange/README.md b/charts/spire-identity-exchange/README.md index 79468c4..ceef406 100644 --- a/charts/spire-identity-exchange/README.md +++ b/charts/spire-identity-exchange/README.md @@ -1,6 +1,6 @@ # spire-identity-exchange -![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.5.0](https://img.shields.io/badge/AppVersion-0.5.0-informational?style=flat-square) +![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.5.0](https://img.shields.io/badge/AppVersion-v0.5.0-informational?style=flat-square) A Helm chart to install the SPIRE Identity Exchange. diff --git a/charts/spire-lib/Chart.yaml b/charts/spire-lib/Chart.yaml index 8a40bbc..d3c02a5 100644 --- a/charts/spire-lib/Chart.yaml +++ b/charts/spire-lib/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: spire-lib description: A library of helper templates for SPIRE charts. type: library -version: 0.3.0 +version: 0.3.1 appVersion: "0.1.0" keywords: ["spiffe", "spire", "library"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire-lib diff --git a/charts/spire-lib/README.md b/charts/spire-lib/README.md index 30144e6..c15dc7c 100644 --- a/charts/spire-lib/README.md +++ b/charts/spire-lib/README.md @@ -7,7 +7,7 @@ A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for gro ```yaml dependencies: - name: spire-lib - version: 0.3.0 + version: 0.3.1 repository: https://spiffe.github.io/helm-charts-hardened/ ``` diff --git a/charts/spire-nested/Chart.lock b/charts/spire-nested/Chart.lock index 047972e..25eba26 100644 --- a/charts/spire-nested/Chart.lock +++ b/charts/spire-nested/Chart.lock @@ -1,7 +1,7 @@ dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: spire-server repository: file://../spire/charts/spire-server version: 0.1.0 @@ -43,7 +43,7 @@ dependencies: version: 0.1.0 - name: spire-ha-agent repository: file://../spire-ha-agent - version: 0.3.0 + version: 0.3.1 - name: spire-server repository: file://../spire/charts/spire-server version: 0.1.0 @@ -58,7 +58,7 @@ dependencies: version: 0.1.0 - name: spire-identity-exchange repository: file://../spire-identity-exchange - version: 0.2.0 + version: 0.2.1 - name: spire-server repository: file://../spire/charts/spire-server version: 0.1.0 @@ -73,6 +73,6 @@ dependencies: version: 0.1.0 - name: spire-identity-exchange repository: file://../spire-identity-exchange - version: 0.2.0 -digest: sha256:edf96a8d833dffffc31f0bfdd400ced75f0cf1868e2711a1cd72f51d6f099621 -generated: "2026-07-31T16:55:07.391766-07:00" + version: 0.2.1 +digest: sha256:bd10aa2236190a29056e5b32300d16883bc01635d2446a32c91ead2639b98f1d +generated: "2026-08-21T14:41:50.936953-07:00" diff --git a/charts/spire-nested/Chart.yaml b/charts/spire-nested/Chart.yaml index bf6e887..d13590f 100644 --- a/charts/spire-nested/Chart.yaml +++ b/charts/spire-nested/Chart.yaml @@ -4,7 +4,7 @@ description: > A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. type: application -version: 0.30.0 +version: 0.30.1 appVersion: "1.15.3" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire @@ -23,7 +23,7 @@ kubeVersion: ">=1.21.0-0" dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: spire-server alias: root-spire-server condition: root-spire-server.enabled @@ -121,7 +121,7 @@ dependencies: - haAgentCommon - name: spire-ha-agent repository: file://../spire-ha-agent - version: 0.3.0 + version: 0.3.1 condition: spire-ha-agent.enabled tags: - haAgentCommon @@ -157,7 +157,7 @@ dependencies: alias: spire-identity-exchange-bottom-turtle-ha-a condition: spire-identity-exchange-bottom-turtle-ha-a.enabled repository: file://../spire-identity-exchange - version: 0.2.0 + version: 0.2.1 tags: - bottomTurtleHAA - name: spire-server @@ -192,7 +192,7 @@ dependencies: alias: spire-identity-exchange-bottom-turtle-ha-b condition: spire-identity-exchange-bottom-turtle-ha-b.enabled repository: file://../spire-identity-exchange - version: 0.2.0 + version: 0.2.1 tags: - bottomTurtleHAB annotations: diff --git a/charts/spire-nested/README.md b/charts/spire-nested/README.md index 524dc80..2a7db72 100644 --- a/charts/spire-nested/README.md +++ b/charts/spire-nested/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) +![Version: 0.30.1](https://img.shields.io/badge/Version-0.30.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/Chart.lock b/charts/spire/Chart.lock index 7b55214..750dc36 100644 --- a/charts/spire/Chart.lock +++ b/charts/spire/Chart.lock @@ -1,7 +1,7 @@ dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: spire-server repository: file://./charts/spire-server version: 0.1.0 @@ -34,6 +34,6 @@ dependencies: version: 0.1.0 - name: spire-identity-exchange repository: file://../spire-identity-exchange - version: 0.2.0 -digest: sha256:5bd67eff208fb2918e03b52ab9ac5b2802558106d72a4e4ac94034a1b387c613 -generated: "2026-07-31T16:55:07.012434-07:00" + version: 0.2.1 +digest: sha256:2916c04b61f4813e2e107b84a2168fdf141c67086658e1adbb9501b8425cd6e1 +generated: "2026-08-21T14:41:50.353186-07:00" diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index 2f205a3..8ff4174 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -4,7 +4,7 @@ description: > A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. type: application -version: 0.30.0 +version: 0.30.1 appVersion: "1.15.3" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire @@ -25,7 +25,7 @@ kubeVersion: ">=1.21.0-0" dependencies: - name: spire-lib repository: file://../spire-lib - version: 0.3.0 + version: 0.3.1 - name: spire-server condition: spire-server.enabled repository: file://./charts/spire-server @@ -71,7 +71,7 @@ dependencies: - name: spire-identity-exchange condition: spire-identity-exchange.enabled repository: file://../spire-identity-exchange - version: 0.2.0 + version: 0.2.1 annotations: org.opencontainers.image.source: https://github.com/spiffe/helm-charts-hardened artifacthub.io/category: security diff --git a/charts/spire/README.md b/charts/spire/README.md index 3436687..b6f1423 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) +![Version: 0.30.1](https://img.shields.io/badge/Version-0.30.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.15.3](https://img.shields.io/badge/AppVersion-1.15.3-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager.