Chapter 12. Zero Trust Workload Identity Manager
12.1. Zero Trust Workload Identity Manager overview Copy linkLink copied to clipboard!
The Zero Trust Workload Identity Manager is an OpenShift Container Platform Operator that manages the lifecycle of SPIFFE Runtime Environment (SPIRE) components. It enables workload identity management based on the Secure Production Identity Framework for Everyone (SPIFFE) standard, providing cryptographically verifiable identities (SVIDs) to workloads running in OpenShift Container Platform clusters.
The following are components of the Zero Trust Workload Identity Manager architecture:
12.1.1. SPIFFE Copy linkLink copied to clipboard!
Establish trust between software workloads in distributed systems with Secure Production Identity Framework for Everyone (SPIFFE). SPIFFE assigns unique IDs to workloads, allowing workloads to verify identities and communicate securely. This ensures secure authentication across dynamic environments.
The SPIFFE IDs are contained in the SPIFFE Verifiable Identity Document (SVID). SVIDs are used by workloads to verify their identity to other workloads so that the workloads can communicate with each other. The two main SVID formats are:
- X.509-SVIDs: X.509 certificates where the SPIFFE ID is embedded in the Subject Alternative Name (SAN) field.
-
JWT-SVIDs: JSON Web Tokens (JWTs) where the SPIFFE ID is included as the
subclaim.
For more information, see SPIFFE Overview.
12.1.2. SPIRE Server Copy linkLink copied to clipboard!
The SPIRE Server is the central management component of SPIRE that issues SPIFFE identities and maintains the registration database for a trust domain.
12.1.3. SPIRE Agent Copy linkLink copied to clipboard!
The SPIRE Agent performs workload attestation to ensure that workloads receive a verified identity when requesting authentication through the SPIFFE Workload API. The agent uses configured workload attestor plugins to verify these identities.
SPIRE and the SPIRE Agent perform node attestation via node plugins. The plugins are used to verify the identity of the node on which the agent is running. For more information, see About the SPIRE Agent.
12.1.4. Attestation Copy linkLink copied to clipboard!
The attestation process verifies the identity of nodes and workloads before issuing SPIFFE IDs. By comparing attributes against defined selectors, this process ensures that only legitimate entities within the trust domain receive cryptographic credentials.
The two main types of attestation in SPIFFE/SPIRE are:
- Node attestation: verifies the identity of a machine or a node on a system, before a SPIRE Agent running on that node can be trusted to request identities for workloads.
- Workload attestation: verifies the identity of an application or service running on an attested node before the SPIRE Agent on that node can provide it with a SPIFFE ID and SVID.
For more information, see Attestation.
12.2. Zero Trust Workload Identity Manager components Copy linkLink copied to clipboard!
Review the components available in the initial release of Zero Trust Workload Identity Manager to understand the architecture. These components provide the foundation for identifying and securing your workloads.
12.2.1. SPIFFE CSI Driver Copy linkLink copied to clipboard!
The SPIFFE Container Storage Interface (CSI) driver helps pods securely obtain their SPIFFE Verifiable Identity Document (SVID) by delivering the Workload API socket. By using Kubernetes ephemeral inline volumes, the driver simplifies how applications request temporary storage for identity management.
When the pod starts, the Kubelet calls the SPIFFE CSI driver to provision and mount a volume into the pod’s containers. The SPIFFE CSI driver mounts a directory that contains the SPIFFE Workload API into the pod. Applications in the pod then communicate with the Workload API to obtain their SVIDs. The driver guarantees that each SVID is unique.
12.2.2. SPIRE OpenID Connect Discovery Provider Copy linkLink copied to clipboard!
Use the SPIRE OpenID Connect (OIDC) Discovery Provider to integrate SPIRE workload identities with OIDC-compliant systems. This component exposes endpoints for token verification. It helps ensure compatibility between SPIRE-issued credentials and external APIs requiring standard OIDC tokens.
While SPIRE primarily issues identities for workloads, additional workload-related claims can be embedded into JWT-SVIDs through the configuration of SPIRE, which these claims to be included in the token and verified by OIDC-compliant clients.
12.2.3. SPIRE Controller Manager Copy linkLink copied to clipboard!
Use the SPIRE Controller Manager to automate workload registration with custom resource definitions (CRDs). The manager monitors pods and CRDs to create, update, or delete entries on the SPIRE Server. This process helps ensure that your SPIRE entries accurately reflect your active resources.
The SPIRE Controller Manager is designed to be deployed on the same pod as the SPIRE Server. The manager communicates with the SPIRE Server API using a private UNIX Domain Socket within a shared volume.
12.2.4. SPIRE Server and Agent telemetry Copy linkLink copied to clipboard!
Use the SPIRE Controller Manager to register workloads by using custom resource definitions (CRDs). The manager monitors pods and CRDs for changes and triggers a reconciliation process. This process creates, updates, or deletes SPIRE Server entries to help ensure they match your configuration.
12.2.5. About the Zero Trust Workload Identity Manager workflow Copy linkLink copied to clipboard!
Understand the high-level workflow of Zero Trust Workload Identity Manager to help you manage secure identities. This process relies on SPIRE components and custom resource definitions (CRDs) to validate nodes and workloads.
The following is a high-level workflow of the Zero Trust Workload Identity Manager within the Red Hat OpenShift cluster.
- The SPIRE, SPIRE Agent, SPIFFE CSI Driver, and the SPIRE OIDC Discovery Provider operands are deployed and managed by Zero Trust Workload Identity Manager via associated customer resource definitions (CRDs).
- Watches are then registered for relevant Kubernetes resources and the necessary SPIRE CRDs are applied to the cluster.
-
The CR for the ZeroTrustWorkloadIdentityManager resource named
clusteris deployed and managed by a controller. To deploy the SPIRE Server, SPIRE Agent, SPIFFE CSI Driver, and SPIRE OIDC Discovery Provider, you need to create a custom resource of a each certain type and name it
cluster. The custom resource types are as follows:-
SPIRE Server -
SpireServer -
SPIRE Agent -
SpireAgent -
SPIFFE CSI Driver -
SpiffeCSIDriver -
SPIRE OIDC discovery provider -
SpireOIDCDiscoveryProvider
-
SPIRE Server -
- When a node starts, the SPIRE Agent initializes, and connects to the SPIRE Server.
- The SPIRE Agent begins the node attestation process. The agent collects information on the node’s identity such as label name and namespace. The agent securely provides the information it gathered through the attestation to the SPIRE Server.
- The SPIRE Server then evaluates this information against its configured attestation policies and registration entries. If successful, the server generates an agent SVID and the Trust Bundle (CA Certificate) and securely sends this back to the SPIRE Agent.
- A workload starts on the node and needs a secure identity. The workload connects to the agent’s Workload API and requests a SVID.
- The SPIRE Agent receives the request and begins a workload attestation to gather information about the workload.
- After the SPIRE Agent gathers the information, the information is sent to the SPIRE Server and the server checks its configured registration entries.
- The SPIRE Agent receives the workload SVID and Trust Bundle and passes it on to the workload. The workload can now present their SVIDs to other SPIFFE-aware devices to communicate with them.
12.3. Zero Trust Workload Identity Manager release notes Copy linkLink copied to clipboard!
The Zero Trust Workload Identity Manager leverages Secure Production Identity Framework for Everyone (SPIFFE) and the SPIFFE Runtime Environment (SPIRE) to provide a comprehensive identity management solution for distributed systems.
These release notes track the development of Zero Trust Workload Identity Manager.
12.3.1. Zero Trust Workload Identity Manager 1.1.0 Copy linkLink copied to clipboard!
Issued: 30 Jun 2026
This release adds integration and operational capabilities for workloads that use external certificate authorities, service mesh deployments, or file-based Transport Layer Security (TLS) credentials. The release includes a supported SPIFFE Helper container image, SPIRE UpstreamAuthority plugins for cert-manager and HashiCorp Vault, and Red Hat OpenShift Service Mesh integration with SPIRE for single-cluster and federated multi-cluster mutual Transport Layer Security (mTLS).
The following advisories are available for Zero Trust Workload Identity Manager:
Zero Trust Workload Identity Manager supports the following components and versions:
| Component | Version |
|---|---|
| Zero Trust Workload Identity Manager | 1.1.0 |
| SPIRE Server | 1.14.7 |
| SPIRE Agent | 1.14.7 |
| SPIRE Controller Manager | 0.6.4 |
| SPIRE OIDC Discovery Provider | 1.14.7 |
| SPIFFE CSI Driver | 0.2.8 |
12.3.1.1. New features and enhancements Copy linkLink copied to clipboard!
- Supported SPIFFE Helper container image
- Zero Trust Workload Identity Manager now provides a supported SPIFFE Helper container image for workloads that cannot use the SPIFFE Workload API directly but can read Transport Layer Security (TLS) credentials from a shared volume. The image is based on upstream SPIFFE Helper. The configuration file format, command-line flags, and Workload API behavior remain compatible.
- SPIRE UpstreamAuthority plugins for external certificate authorities
Zero Trust Workload Identity Manager now supports SPIRE Server UpstreamAuthority plugins that obtain intermediate signing certificates from external certificate management systems while preserving Secure Production Identity Framework for Everyone (SPIFFE) identity standards.
Supported plugins:
- cert-manager UpstreamAuthority plugin: integrates SPIRE Server with cert-manager Operator for Red Hat OpenShift.
- Vault UpstreamAuthority plugin: integrates SPIRE Server with the HashiCorp Vault Public Key Infrastructure (PKI) secrets engine.
- cert-manager UpstreamAuthority plugin
-
The cert-manager UpstreamAuthority plugin connects SPIRE Server to cert-manager Operator for Red Hat OpenShift for automated intermediate certificate provisioning. SPIRE Server creates a
CertificateRequestcustom resource, then the configuredIssuerorClusterIssuersigns the request, and then the SPIRE Server uses the signed intermediate certificate to issue workload identities. - Vault UpstreamAuthority plugin
- The Vault UpstreamAuthority plugin connects SPIRE Server to the HashiCorp Vault PKI secrets engine for automated intermediate certificate authority (CA) certificate signing. Use this plugin when PKI is centralized in Vault and SPIRE must obtain intermediate signing certificates through Vault policies and authentication.
- Single-cluster Service Mesh integration with SPIRE
- Zero Trust Workload Identity Manager now supports single-cluster integration with Red Hat OpenShift Service Mesh. SPIRE replaces Istio’s built-in certificate authority (CA) with SPIFFE-compliant identities and short-lived certificates that rotate automatically for workload mutual Transport Layer Security (mTLS).
- Multi-cluster Service Mesh integration with SPIRE federation
Zero Trust Workload Identity Manager now supports multi-cluster integration with Red Hat OpenShift Service Mesh through SPIRE federation, enabling cross-cluster mutual Transport Layer Security (mTLS) authentication and zero trust workload identity across separate OpenShift Container Platform clusters.
Cross-cluster trust requires federation at two layers:
-
SPIRE federation: SPIRE Servers exchange trust bundles through the
https_spiffeprofile. - Istio federation: Istio discovers remote endpoints through remote secrets and routes traffic through East-West Gateways.
-
SPIRE federation: SPIRE Servers exchange trust bundles through the
12.3.1.2. Deprecated features Copy linkLink copied to clipboard!
- Custom SCC
spire-spiffe-csi-driver Starting in Zero Trust Workload Identity Manager 1.1.0, the SPIFFE CSI Driver no longer uses the custom
SecurityContextConstraints(SCC)spire-spiffe-csi-driver.Zero Trust Workload Identity Manager now grants the CSI
ServiceAccountaccess to the platformprivilegedSCC through aRoleBindingnamespace.Zero Trust Workload Identity Manager uses only the existing OpenShift
privilegedSCC. Zero Trust Workload Identity Manager does not create, modify, update, or delete theprivilegedSCC.- Action required after upgrade
Zero Trust Workload Identity Manager does not remove the legacy custom SCC
spire-spiffe-csi-driver. After you upgrade Zero Trust Workload Identity Manager to 1.1.0 from the OpenShift OperatorHub catalog, remove it manually once CSI is healthy on the platform SCC.For more information, see Manually delete the custom security context constraints.
12.3.1.3. Fixed issues Copy linkLink copied to clipboard!
- Managed route TLS secrets no longer require manual RBAC configuration
Before this update, when you configured a managed route with an
externalSecretRefTLS certificate on theSpireOIDCDiscoveryProviderorSpireServercustom resource (CR), Zero Trust Workload Identity Manager did not create theRoleBindingthat grants the OpenShift Ingress router service account permission to read the referenced Secret. As a consequence, route reconciliation failed with aManagedRouteUpdateFailedcondition, and you had to manually createsecret-reader,Role, andRoleBindingCRs. With this release, Zero Trust Workload Identity Manager automatically reconciles the requiredRoleandRoleBindingCRs so that the router service account can access the Secrets referenced by theexternalSecretRefTLS certificate. Managed routes that use externally provided TLS certificates now reconcile without additional manual RBAC steps.
- Pre-existing operand resources are no longer overwritten at installation
Before this update, when you installed Zero Trust Workload Identity Manager on a cluster that already contained Kubernetes resources with the same names as operand objects, Zero Trust Workload Identity Manager reconciled and overwrote those resources during the initial installation without reporting a conflict. As a consequence, manually created or third-party resources could be modified unexpectedly before you had a chance to resolve naming collisions. With this release, Zero Trust Workload Identity Manager checks the
app.kubernetes.io/managed-bylabel before updating any existing resource during installation. If a matching resource is not managed by Zero Trust Workload Identity Manager, Zero Trust Workload Identity Manager sets aResourceConflictstatus condition and stops reconciliation instead of overwriting the resource.
- Unmanaged cluster resources are protected during reconciliation
Before this update, after Zero Trust Workload Identity Manager was already running, operand controllers could still update Kubernetes resources with matching names even when those resources were not owned by Zero Trust Workload Identity Manager. This could occur when a name collision appeared later or when the
app.kubernetes.io/managed-by: zero-trust-workload-identity-managerlabel was removed from a resource that Zero Trust Workload Identity Manager had previously managed. With this release, each operand controller verifies themanaged-bylabel on every reconcile cycle before applying updates. When the label is absent, Zero Trust Workload Identity Manager sets aResourceConflictstatus condition on the custom resource and skips the update to the conflicting object. Operand updates proceed only for resources that Zero Trust Workload Identity Manager currently manages.
- Duplicate health check port names resolved in SPIRE Server StatefulSet
Before this update, the
spire-serverandspire-controller-managercontainers in the SPIRE ServerStatefulSetfield both declared a port namedhealthzon different container ports. Kubernetes treated this as a duplicate port name within the pod, issued a warning, and the services or probes that selected the ports by name could target the wrong container. With this release, Zero Trust Workload Identity Manager assigns unique port names, such asserver-healthzandctrlmgr-healthz, and updates the liveness and readiness probes to reference those names. Health checks and monitoring configurations now resolve to the intended container.
- Create-only mode disabled status now updates on the main custom resource
Before this update, after you disabled create-only mode by setting
CREATE_ONLY_MODEtofalsein the Operator subscription, theCreateOnlyModecondition on the mainZeroTrustWorkloadIdentityManagerCR could remainTruewith the reasonCreateOnlyModeEnabled. Because Zero Trust Workload Identity Manager set that condition from operand status instead of from theCREATE_ONLY_MODEenvironment variable in the subscription, the main CR status did not reflect that create-only mode was disabled even though the operand reconciliation had resumed. With this release, Zero Trust Workload Identity Manager sets theCreateOnlyModecondition on the main CR directly from theCREATE_ONLY_MODEenvironment variable and updates it toFalsewith reasonCreateOnlyModeDisabledwhen create-only mode is turned off.
- Create-only mode status updates reconcile reliably
Before this update, Zero Trust Workload Identity Manager controllers wrote the custom resource (CR) status twice during each reconciliation cycle. The CR status was written at the start through the
SetInitialReconciliationStatuscycle and again at the end through the status manager. Because the informer cache could return a staleResourceVersionafter the first write, the deferred status update was rejected withHTTP 409 Conflict. Status conditions, includingCreateOnlyMode, could fail to transition to the expected state even after you changed configuration in the Operator subscription. With this release, controllers apply the status in a single update at the end of reconciliation, and status update retry logic now complies with theRetryOnConflictcontract. CR status updates, including create-only mode transitions, are now completed.
12.3.2. Zero Trust Workload Identity Manager 1.0.1 Copy linkLink copied to clipboard!
Issued: 17 May 2026
This release fixes some Common Vulnerabilities and Exposures (CVEs).
The following advisories are available for the Zero Trust Workload Identity Manager:
12.3.2.1. CVEs Copy linkLink copied to clipboard!
12.3.3. Zero Trust Workload Identity Manager 1.0.0 (General Availability) Copy linkLink copied to clipboard!
Issued: 12 December 2025
This release introduces capabilities for enterprise readiness, security, and operational flexibility. The release includes SPIRE federation for cross-cluster identity, PostgreSQL support for production persistence, and enhanced security through stricter constraints and API validation.
The following advisories are available for the Zero Trust Workload Identity Manager:
Zero Trust Workload Identity Manager supports the following components and versions:
| Component | Version |
|---|---|
| SPIRE Server | 1.13.3 |
| SPIRE Agent | 1.13.3 |
| SPIRE Controller Manager | 0.6.3 |
| SPIRE OIDC Discovery Provider | 1.13.3 |
| SPIFFE CSI Driver | 0.2.8 |
12.3.3.1. New features and enhancements Copy linkLink copied to clipboard!
- SPIRE federation support
The Operator now includes support for SPIRE federation, enabling workloads across distinct trust domains to securely communicate and authenticate with each other.
Key capabilities:
-
Configuration of bundle endpoints using
https_spiffe(TLS) orhttps_web(Web PKI) profiles. -
Automatic certificate management via the ACME protocol. For example,
Let’s Encrypt. - Automatic OpenShift Container Platform route creation for federation endpoints.
- Ability to configure relationships with multiple federated trust domains.
-
Configuration of bundle endpoints using
Customer action required:
-
Review the
federationconfiguration within theSpireServercustom resource (CR). - Ensure proper DNS resolution and network connectivity to federated trust domains.
-
Review the
- PostgreSQL database support
SPIRE Server now supports PostgreSQL as an external database backend, accommodating production deployments that necessitate enterprise-grade data persistence and high availability.
-
Supported Types:
sqlite3(default),postgres,mysql. Customer action required:
- For production, evaluation of migration from SQLite to PostgreSQL is recommended.
- Creation and configuration of Kubernetes Secrets for database TLS certificates and credentials are required.
-
Supported Types:
- Configurable agent socket path and Container Storage Interface (CSI) plugin name
The SPIRE Agent socket path and the SPIFFE CSI Driver plugin name are now configurable, providing operational flexibility for environments with specific directory requirements or co-existence with multiple SPIFFE deployments.
Key configuration points:
-
SpireAgent.spec.socketPath -
SpiffeCSIDriver.spec.agentSocketPath -
SpiffeCSIDriver.spec.pluginName
-
Customer action required:
-
Ensure consistency between
socketPathin theSpireAgentCR andagentSocketPathin theSpiffeCSIDriverCR.
-
Ensure consistency between
- Workload attestors verification API
A new API has been introduced to configure kubelet certificate verification for workload attestation, enhancing security and supporting various OpenShift Container Platform configurations.
Verification types:
-
auto(default): Verification utilizes OpenShift Container Platform defaults (/etc/kubernetes/kubelet-ca.crt). -
hostCert: Uses a custom CA certificate path. -
skip: Skips TLS verification (not recommended for production use).
-
- Configurable Certificate Authority and JSON Web Token key types
Administrators can now configure the cryptographic key types used for the SPIRE Server Certificate Authority (CA) and JSON Web Token (JWT) signing, ensuring compliance with organizational security policies.
-
Supported Key Types:
rsa-2048(default),rsa-4096,ec-p256,ec-p384. Customer action required:
- Review organizational security policies to determine required key types.
-
Supported Key Types:
- Custom namespace deployment
- The Operator and all associated operands can now be deployed within a custom namespace, providing flexibility for organizations with specific namespace governance requirements.
- Proxy-aware Operator and operands
- The Operator and all managed operands are now proxy-aware and automatically inherit cluster-wide proxy settings when configured.
- Enhanced Security Context Constraints
- SPIRE Agent and SPIFFE CSI Driver now run with Security Context Constraints (SCC) that prevent root user execution, though privileged container mode remains enabled for necessary host-level operations.
-
The Operator and all operand containers are configured with the
ReadOnlyRootFilesystemset totrue.
- Enhanced API validation
Comprehensive Common Expression Language (CEL) validation has been integrated into all Custom Resource Definitions (CRDs) to prevent configuration errors during admission control.
Key validations:
-
All Operator CRDs are enforced as singletons (must be named
cluster). -
Immutable Fields: Fields including
trustDomain,clusterName,bundleConfigMap,federation,bundleEndpointprofile, and allPersistencesettings (size,accessMode, andstorageClass) are now immutable after initial creation.
-
All Operator CRDs are enforced as singletons (must be named
Customer action required:
- Review existing CR configurations to ensure compliance with the new validation rules.
- Common configuration consolidation
-
Standard configuration options (
labels,resources,affinity,tolerations,nodeSelector) are now standardized across all operand CRs via a sharedCommonConfigstructure.
-
Standard configuration options (
- Configuring log level and log format for the operands
This release introduces flexible logging controls to improve observability and debugging across the platform:
-
SPIRE Components: Users can now configure the
logLevel(debug, info, warn, error) andlogFormat(text, JSON) independently forSpireServer,SpireAgent, andSpireOIDCDiscoveryProviderdirectly within their CR specifications. The defaults are set to "info" for thelogLeveland "text" for thelogFormat. -
Operator: The Operator’s log verbosity is now configurable via the
OPERATOR_LOG_LEVELenvironment variable using klog’stextlogger.
-
SPIRE Components: Users can now configure the
- Refactor for create-only mode
-
By setting the
CREATE_ONLY_MODEenvironment variable, users can prevent the Operator from reconciling updates. This allows for manual resource modification without interference. If this mode is disabled, the Operator resumes enforcing the state and overwrites any manual changes.
12.3.3.2. Status and observability improvements Copy linkLink copied to clipboard!
- Enhanced status reporting
- The main CR now aggregates status information from all operand CRs.
- New status conditions include Upgradeable (indicating a safe upgrade path) and Progressing (detailing deployment progress).
- Operator metrics
- Operator metrics are now exposed and secured with appropriate RBAC configuration.
- Integration is supported with the OpenShift Container Platform monitoring stack.
12.3.3.3. Fixed issues Copy linkLink copied to clipboard!
- Enhanced Security Context Constraints for SPIRE Agent
Before this update, the SPIRE Agent and SPIFFE CSI Driver containers were running as root user, leading to potential security violations. With this release, Security Context Constraints (SCC) have been configured to ensure these components no longer run as root. While privileged container mode is still required for necessary capabilities, this change reduces potential security risks for the user.
(SPIRE-60)
- SpireServer updates now propagate without Operator restart
Before this update, the Operator failed to trigger reconciliation after updating the operand CR spec. As a consequence, user updates to
SpireServerCR resources were not propagated to theStatefulSet, causing reconciliation to fail and changes to be ignored, leading to inconsistent resource allocation. With this release, the race condition between the manager and reconciler’s cache to trigger reconciliation after CR updates has been fixed. As a result, post installation patch operations onSpireServerCRs reliably trigger reconciliation, ensuring updated values are applied to the StatefulSet without manual Operator restart.(SPIRE-68)
- Removed unnecessary security context constraint for OpenID Connect discovery provider
Before this update, the system unnecessarily created a custom security context constraint (SCC) for the OpenID Connect (OIDC) discovery provider, which increased the security footprint and configuration complexity even though the deployment did not require it. With this release, the custom SCC creation logic has been removed, resulting in a configuration where the OIDC discovery provider operates successfully without the extra security constraints.
- Fixed ConfigMap Reconciliation for SPIRE Controller Manager
Before this update, Spire-controller manager ConfigMap reconciliation failed due to an unhandled edge case in the previous implementation. As a consequence, users experienced configuration inconsistencies. With this release, the Spire-controller manager ConfigMap reconciliation issue has been resolved. As a result, end users now experience seamless Spire-controller manager configuration.
- OIDC discovery provider now restarts automatically on configuration changes
Before this update, the SPIRE OIDC discovery provider failed to automatically restart following
configmapchanges, leading to persistent authentication failures. With this release, updates to the CR now trigger an automatic pod restart, ensuring thatconfigmapchanges are applied immediately.
- Corrected update rollback for DaemonSets, Deployments, and StatefulSets
Before this update,
daemonset,deployment, andstatefulsetswere not properly reverted to their original form in all valid scenarios due to an oversight in the update logic. As a consequence, user data loss or inconsistency occurred in valid scenarios. With this release, the update logic has been corrected, ensuring all valid scenarios revert to their original form.Other bug fixes included:
- Fixed issues related to continuous reconciliation and unnecessary updates.
- Eliminated requeue logic for user input validation errors.
12.3.4. Zero Trust Workload Identity Manager 0.2.0 (General Availability) Copy linkLink copied to clipboard!
Issued: 09 August 2025
The following advisories are available for the Zero Trust Workload Identity Manager:
12.3.4.1. New features and enhancements Copy linkLink copied to clipboard!
- Support for the managed OIDC Discovery Provider Route
-
The Operator exposes the
SPIREOIDCDiscoveryProviderspec through OpenShift Container Platform Routes under the domain*.apps.<cluster_domain>for the selected default installation. -
The
managedRouteandexternalSecretReffields have been added to thespireOidcDiscoveryProviderspec. -
The
managedRoutefield is boolean and is set totrueby default. If set tofalse, the Operator stops managing the route and the existing route will not be deleted automatically. If set back totrue, the Operator resumes managing the route. If a route does not exist, the Operator creates a new one. If a route already exists, the Operator will override the user configuration if a conflict exists. -
The
externalSecretRefreferences an externally managed Secret that has the TLS certificate for theoidc-discovery-providerRoute host. When provided, this populates the route’s.Spec.TLS.ExternalCertificatefield. For more information, see Creating a route with externally managed certificate
-
The Operator exposes the
- Enabling the custom Certificate Authority Time-To-Live for the SPIRE bundle
The following Time-To-Live (TTL) fields have been added to the
SpireServercustom resource definition (CRD) API for SPIRE Server certificate management:-
CAValidity(default: 24h) -
DefaultX509Validity(default: 1h) -
DefaultJWTValidity(default: 5m)
-
- The default values can be replaced in the server configuration with user-configurable options that give users the flexibility to customize certificate and SPIFFE Verifiable Identity Document (SVID) lifetimes based on their security requirements.
- Enabling Manual User Configurations
-
The Operator controller switches to
create-onlymode once theztwim.openshift.io/create-only=trueannotation is present on the Operator’s APIs. This allows resource creation while skipping the updates. A user can update the resources manually to test their configuration. This annotation supports APIs such asSpireServer,SpireAgents,SpiffeCSIDriver,SpireOIDCDiscoveryProvider, andZeroTrustWorkloadIdentityManager. - When the annotation is applied, all derived resources including resources created and managed by the Operator are created but not updated.
- After the annotation is removed and the pod restarts, the Operator tries to come back to the required state. The annotation is applied only once during start or a restart.
-
The Operator controller switches to
12.3.4.2. Fixed issues Copy linkLink copied to clipboard!
- JSON Web Token Issuer field now requires a valid URL
Before this update, the
JwtIssuerfield for both theSpireServerand theSpireOidcDiscoveryProvidercustom resources did not require the input to be a URL, frequently causing configuration errors. With this release, the validation has been updated, and users must now manually enter a valid issuer URL in theJwtIssuerfield for both custom resources. As a result, misconfigurations caused by a malformed issuer values are prevented, ensuring a stable and reliable setup.
12.3.5. Zero Trust Workload Identity Manager 0.1.0 (General Availability) Copy linkLink copied to clipboard!
Issued: 16 June 2025
The following advisories are available for the Zero Trust Workload Identity Manager:
12.4. Installing the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
Install Zero Trust Workload Identity Manager to help ensure secure communication between your workloads. You can install the Zero Trust Workload Identity Manager by using either the web console or CLI.
If you install the Operator into a custom namespace (for example, my-custom-namespace), all managed operand resources are deployed within that same namespace. All secrets and ConfigMaps referenced by the Custom Resources (CRs) must also exist in that custom namespace.
The Operator installation is not supported in the openshift-* namespaces and the default namespace.
12.4.1. Installing the Zero Trust Workload Identity Manager by using the web console Copy linkLink copied to clipboard!
Use the Software Catalog in the OpenShift Container Platform web console to install the Zero Trust Workload Identity Manager. This process streamlines deployment and helps ensure the Operator is installed in the correct namespace with the appropriate installation mode.
A minimum of 1Gi persistent volume is required to install the SPIRE Server.
Prerequisites
-
You have access to the cluster with
cluster-adminprivileges. - You have access to the OpenShift Container Platform web console.
Procedure
- Log in to the OpenShift Container Platform web console.
-
Go to Ecosystem
Software Catalog. - Search for Zero Trust Workload Identity Manager.
On the Install Operator page:
-
Update the Update channel, if necessary. The channel defaults to
stable-v1, which installs the lateststable-v1release of the Zero Trust Workload Identity Manager. Choose the Installed Namespace for the Operator. The default Operator namespace is
zero-trust-workload-identity-manager.If the
zero-trust-workload-identity-managernamespace does not exist, it is created for you.NoteThe Operator and operands are deployed in the same namespace.
Select an Update Approval strategy
- The Automatic strategy allows Operator Lifecycle Manager (OLM) to automatically update the Operator when a new version is available.
- The Manual strategy requires a user with appropriate credentials to approve the Operator update.
-
Update the Update channel, if necessary. The channel defaults to
- Click Install.
Verification
Navigate to Ecosystem
Installed Operators. -
Verify that Zero Trust Workload Identity Manager is listed with a Status of Succeeded in the
zero-trust-workload-identity-managernamespace. Verify that Zero Trust Workload Identity Manager controller manager deployment is ready and available by running the following command:
$ oc get deployment -l name=zero-trust-workload-identity-manager -n zero-trust-workload-identity-managerExample output
NAME READY UP-TO-DATE AVAILABLE AGE zero-trust-workload-identity-manager-controller-manager-6c4djb 1/1 1 1 43m
-
Verify that Zero Trust Workload Identity Manager is listed with a Status of Succeeded in the
To check the Operator logs, run the following command:
$ oc logs -f deployment/zero-trust-workload-identity-manager -n zero-trust-workload-identity-manager
12.4.2. Installing the Zero Trust Workload Identity Manager by using the CLI Copy linkLink copied to clipboard!
Install the Zero Trust Workload Identity Manager by using the command-line interface (CLI) to create the required project, OperatorGroup, and Subscription objects. You can then deploy the Operator components necessary for managing workload identities on your OpenShift Container Platform cluster.
Prerequisites
-
You have access to the cluster with
cluster-adminprivileges.
A minimum of 1Gi persistent volume is required to install the SPIRE Server.
Procedure
Create a new project named
zero-trust-workload-identity-managerby running the following command:$ oc new-project zero-trust-workload-identity-managerCreate an
OperatorGroupobject:Create a YAML file, for example,
operatorGroup.yaml, with the following content:apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: openshift-zero-trust-workload-identity-manager namespace: zero-trust-workload-identity-manager spec: upgradeStrategy: DefaultCreate the
OperatorGroupobject by running the following command:$ oc create -f operatorGroup.yaml
Create a
Subscriptionobject:Create a YAML file, for example,
subscription.yaml, that defines theSubscriptionobject:apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: openshift-zero-trust-workload-identity-manager namespace: zero-trust-workload-identity-manager spec: channel: stable-v1 name: openshift-zero-trust-workload-identity-manager source: redhat-operators sourceNamespace: openshift-marketplace installPlanApproval: AutomaticCreate the
Subscriptionobject by running the following command:$ oc create -f subscription.yaml
Verification
Verify that the OLM subscription is created by running the following command:
$ oc get subscription -n zero-trust-workload-identity-managerExample output
NAME PACKAGE SOURCE CHANNEL openshift-zero-trust-workload-identity-manager zero-trust-workload-identity-manager redhat-operators stable-v1Verify whether the Operator is successfully installed by running the following command:
$ oc get csv -n zero-trust-workload-identity-managerExample output
NAME DISPLAY VERSION PHASE zero-trust-workload-identity-manager.v1.0.0 Zero Trust Workload Identity Manager 1.0.0 SucceededVerify that the Zero Trust Workload Identity Manager controller manager is ready by running the following command:
$ oc get deployment -l name=zero-trust-workload-identity-manager -n zero-trust-workload-identity-managerExample output
NAME READY UP-TO-DATE AVAILABLE AGE zero-trust-workload-identity-manager-controller-manager 1/1 1 1 43m
12.5. Deploying Zero Trust Workload Identity Manager operands Copy linkLink copied to clipboard!
Deploy the Zero Trust Workload Identity Manager operands by creating their custom resources in a specific order. Adhering to the sequence ensures the successful installation of components, such as the Security Production Identity Framework for Everyone (SPIRE) Server, SPIRE Agent, and Secure Production Identity Framework For Everyone (SPIFFE) CSI driver.
You must deploy the operands in the following sequence to ensure successful installation:
-
ZeroTrustWorkloadIdentityManagerCR - SPIRE Server
- SPIRE Agent
- SPIFFE CSI driver
- SPIRE OIDC discovery provider
12.5.1. About the ZeroTrustWorkloadIdentityManager custom resource Copy linkLink copied to clipboard!
The ZeroTrustWorkloadIdentityManager is the primary custom resource that initializes the SPIRE deployments. This primary resource defines the trust domain and cluster name to help ensure secure workload identity management.
Reference the complete YAML specification to correctly structure the ZeroTrustWorkloadIdentityManager CR. This example helps you identify required fields and immutable parameters for your configuration.
apiVersion: operator.openshift.io/v1alpha1
kind: ZeroTrustWorkloadIdentityManager
metadata:
name: cluster
labels:
app.kubernetes.io/name: zero-trust-workload-identity-manager
app.kubernetes.io/managed-by: zero-trust-workload-identity-manager
spec:
trustDomain: "example.com"
clusterName: "production-cluster"
bundleConfigMap: "spire-bundle"
where:
spec.trustDomain-
Specifies the trust domain to be used for the SPIFFE identifiers. Must be a valid SPIFFE trust domain (lowercase alphanumeric, hyphens, and dots). Maximum length is 255 characters. After setting a value for the field, the field is immutable. Red Hat highly recommends to set this value to match your the base application URL (for example,
apps.mycluster.example.com) of your OpenShift Container Platform cluster. Using a different value might cause automatically generated OpenShift Routes or federation endpoints to be created with incorrect or mismatched hostnames later in the configuration process. spec.clusterName- Specifies the name that identifies this cluster within the trust domain. Must be a valid DNS-1123 subdomain with a maximum length of 63 characters. Once set, this field is immutable.
spec.bundleConfigMap-
Specifies the name of the ConfigMap that stores the SPIRE trust bundle. This ConfigMap contains the root certificates for the trust domain and is created and maintained by the Operator. Must be a valid Kubernetes name with a maximum length of 253 characters. This field is optional (defaults to
spire-bundle) and once set, is immutable.
12.5.2. Deploying the SPIRE Server Copy linkLink copied to clipboard!
Deploy the SPIRE Server by configuring the SpireServer custom resource (CR). This establishes a central authority that manages and issues identities to the workloads in your cluster.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have installed Zero Trust Workload Identity Manager in the cluster.
Procedure
Create the
SpireServerCR:Create a YAML file that defines the
SpireServerCR, for example,SpireServer.yaml:The following is an example of a
SpireServer.yamlfile.apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: logLevel: "info" logFormat: "text" jwtIssuer: "https://oidc-discovery.apps.cluster.example.com" caValidity: "24h" defaultX509Validity: "1h" defaultJWTValidity: "5m" jwtKeyType: "rsa-2048" caSubject: country: "US" organization: "Example Corporation" commonName: "SPIRE Server CA" persistence: size: "5Gi" accessMode: "ReadWriteOnce" storageClass: "gp3-csi" datastore: databaseType: "sqlite3" connectionString: "/run/spire/data/datastore.sqlite3" tlsSecretName: "" maxOpenConns: 100 maxIdleConns: 10 connMaxLifetime: 0 disableMigration: "false"where:
metadata.name-
Specifies that the value must be
cluster. spec.logLevel-
Specifies the logging level for the SPIRE Server. The valid options are
debug,info,warn, anderror. spec.logFormat-
Specifies the logging format for the SPIRE Server. The valid options are
textandjson. spec.jwtIssuer- Specifies the JWT issuer URL. Must be a valid HTTPS or HTTP URL with a maximum length of 512 characters.
spec.caValidity-
Specifies the validity period (Time to Live (TTL)) for the SPIRE Server’s CA certificate. This determines how long the server’s root or intermediate certificate is valid. The format is a duration string (for example,
24h,168h). spec.defaultX509Validity- Specifies the default validity period (TTL) for X.509 SVIDs issued to workloads. This value is used if a specific TTL is not configured for a registration entry.
spec.defaultJWTValidity- Specifies thedefault validity period (TTL) for JWT SVIDs issued to workloads. This value is used if a specific TTL is not configured for a registration entry.
spec.wtKeyType-
Specifies the key type used for JWT signing. The valid options are
rsa-2048,rsa-4096,ec-p256, andec-p384. This field is optional. spec.caSubject.country- Specifies the country for the SPIRE Server certificate authority (CA). Must be an ISO 3166-1 alpha-2 country code (2 characters).
spec.caSubject.organization- Specifies the organization for the SPIRE Server CA. Maximum length is 64 characters.
spec.caSubject.commonName- Specifies the common name for the SPIRE Server CA. Maximum length is 255 characters.
spec.persistence.size-
Specifies the size of the persistent volume (for example,
1Gi,5Gi). Once set, this field is immutable. spec.persistence.accessMode-
Specifies the access mode for the persistent volume. The valid options are
ReadWriteOnce,ReadWriteOncePod, andReadWriteMany. Once set, this field is immutable. spec.persistence.storageClass- Specifies the storage class to be used for the PVC. Once set, this field is immutable.
spec.datastore.databaseType-
Specifies the type of database to use for the datastore. The valid options are
sql,sqlite3,postgres,mysql,aws_postgresql, andaws_mysql. spec.datastore.connectionString-
Specifies the connection string for the database. For PostgreSQL with SSL, include
sslmodeand certificate paths (for example,dbname=spire user=spire host=postgres.example.com sslmode=verify-full). spec.datastore.tlsSecretName-
Specifies the name of a Kubernetes Secret containing TLS certificates for database connections. The Secret will be mounted at
/run/spire/db/certs. This field is optional. spec.datastore.maxOpenConns- Specifies the maximum number of open database connections. Must be between 1 and 10000.
spec.datastore.maxIdleConns- Specifies the maximum number of idle database connections in the pool. Must be between 0 and 10000.
spec.datastore.connMaxLifetime- Specifies the maximum lifetime of a database connection in seconds. A value of 0 means connections are not closed due to age.
spec.datastore.disableMigration-
Specifies whether to disable automatic database migration. The valid options are
trueandfalse.
Apply the configuration by running the following command:
$ oc apply -f SpireServer.yaml
Verification
Verify that the stateful set of SPIRE Server is ready and available by running the following command:
$ oc get statefulset -l app.kubernetes.io/name=server -n zero-trust-workload-identity-managerExample output
NAME READY AGE spire-server 1/1 65sVerify that the status of the SPIRE Server pod is
Runningby running the following command:$ oc get po -l app.kubernetes.io/name=server -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-server-0 2/2 Running 1 (108s ago) 111sVerify that the persistent volume claim (PVC) is bound, by running the following command:
$ oc get pvc -l app.kubernetes.io/name=server -n zero-trust-workload-identity-managerExample output
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTECLASS AGE spire-data-spire-server-0 Bound pvc-27a36535-18a1-4fde-ab6d-e7ee7d3c2744 5Gi RW0 gp3-csi <unset> 22m
12.5.3. Deploying the SPIRE Agent Copy linkLink copied to clipboard!
Use the SpireAgent custom resource to configure the SPIRE Agent DaemonSet on your nodes. This defines how the agent verifies workloads and manages identity attestation across your OpenShift Container Platform cluster.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have installed Zero Trust Workload Identity Manager in the cluster.
Procedure
Create the
SpireAgentCR:Create a YAML file that defines the
SpireAgentCR, for example,SpireAgent.yaml:The following is an example of a
SpireAgent.yamlfile.apiVersion: operator.openshift.io/v1alpha1 kind: SpireAgent metadata: name: cluster spec: socketPath: "/run/spire/agent-sockets" logLevel: "info" logFormat: "text" nodeAttestor: k8sPSATEnabled: "true" workloadAttestors: k8sEnabled: "true" workloadAttestorsVerification: type: "auto" hostCertBasePath: "/etc/kubernetes" hostCertFileName: "kubelet-ca.crt" disableContainerSelectors: "false" useNewContainerLocator: "true"where:
metadata.name-
Specifies that the value must be
cluster. spec.socketPath-
Specifies the directory on the host where the SPIRE agent socket is created. This directory is shared with the SPIFFE CSI driver via the
hostPathvolume. Must match theSpiffeCSIDriver.spec.agentSocketPathfor workloads to access the socket. Must be an absolute path with a maximum length of 256 characters. spec.logLevel-
Specifies the logging level for the SPIRE Server. The valid options are
debug,info,warn, anderror. spec.logFormat-
Specifies the logging format for the SPIRE Server. The valid options are
textandjson. spec.nodeAttestor.k8sPSATEnabled-
Specifies whether Kubernetes Projected Service Account Token (PSAT) node attestation is enabled. When enabled, the SPIRE agent uses K8s PSATs to prove its identity to the SPIRE server during node attestation. The valid options are
trueandfalse. spec.workloadAttestors.k8sEnabled-
Specifies whether the Kubernetes workload attestor is enabled. When enabled, the SPIRE agent can verify workload identities using Kubernetes pod information and service account tokens. The valid options are
trueandfalse. spec.workloadAttestors.workloadAttestorsVerification.type-
Specifies the kubelet certificate verification mode. The valid options are
auto,hostCert, andskip. spec.workloadAttestors.workloadAttestorsVerification.hostCertBasePath-
Specifies the directory containing the kubelet CA certificate. Required when type is
hostCert. Optional when type isauto(defaults to /etc/kubernetes if not specified). spec.workloadAttestors.workloadAttestorsVerification.hostCertFileName-
Specifies the file name for the kubelet’s CA certificate. When combined with
hostCertBasePath, forms the full path. Required when type ishostCert. Optional when type isauto. Defaults tokubelet-ca.crtif not specified. spec.workloadAttestors.disableContainerSelectors-
Specifies whether to disable container selectors in the Kubernetes workload attestor. Set to
trueif usingholdApplicationUntilProxyStartsin Istio. The valid options aretrueandfalse. spec.workloadAttestors.useNewContainerLocator-
Specifies enabling the new container locator algorithm that has support for cgroups v2. The valid options are
trueandfalse.
Apply the configuration by running the following command:
$ oc apply -f SpireAgent.yaml
Verification
Verify that the daemon set of the SPIRE Agent is ready and available by running the following command:
$ oc get daemonset -l app.kubernetes.io/name=agent -n zero-trust-workload-identity-managerExample output
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE spire-agent 3 3 3 3 3 <none> 10mVerify that the status of SPIRE Agent pods is
Runningby running the following command:$ oc get po -l app.kubernetes.io/name=agent -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-agent-dp4jb 1/1 Running 0 12m spire-agent-nvwjm 1/1 Running 0 12m spire-agent-vtvlk 1/1 Running 0 12m
12.5.4. Deploying the SPIFFE Container Storage Interface driver Copy linkLink copied to clipboard!
Configure the Container Storage Interface (CSI) driver using the SpiffeCSIDriver CR. This configuration mounts SPIFFE sockets directly into workload pods, which allows your applications to access the SPIFFE Workload API securely.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have installed Zero Trust Workload Identity Manager in the cluster.
Procedure
Create the
SpiffeCSIDriverCR:Create a YAML file that defines the
SpiffeCSIDriverCR object, for example,SpiffeCSIDriver.yaml:Example
SpiffeCSIDriver.yamlapiVersion: operator.openshift.io/v1alpha1 kind: SpiffeCSIDriver metadata: name: cluster spec: agentSocketPath: "/run/spire/agent-sockets" pluginName: "csi.spiffe.io"where:
metadata.name-
Specifies that the name must be
cluster. spec.agentSocketPath-
Specifies the path to the directory containing the SPIRE agent’s Workload API socket. This directory is bind-mounted into workload containers by the CSI driver. The directory is shared between the SPIRE agent and CSI driver via a
hostPathvolume. Must be an absolute path with a maximum length of 256 characters. This value must matchSpireAgent.spec.socketPathfor workloads to access the socket. spec.pluginName-
Specifies the name of the CSI plugin. This sets the CSI driver name that is deployed to the cluster and used in
VolumeMountconfigurations. Must match the driver name referenced in the workload pods. Must be a valid domain name format (for example,csi.spiffe.io) with a maximum length of 127 characters.
Apply the configuration by running the following command:
$ oc apply -f SpiffeCSIDriver.yaml
Verification
Verify that the daemon set of the SPIFFE CSI driver is ready and available by running the following command:
$ oc get daemonset -l app.kubernetes.io/name=spiffe-csi-driver -n zero-trust-workload-identity-managerExample output
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE spire-spiffe-csi-driver 3 3 3 3 3 <none> 114sVerify that the status of SPIFFE Container Storage Interface (CSI) Driver pods is
Runningby running the following command:$ oc get po -l app.kubernetes.io/name=spiffe-csi-driver -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-spiffe-csi-driver-gpwcp 2/2 Running 0 2m37s spire-spiffe-csi-driver-rrbrd 2/2 Running 0 2m37s spire-spiffe-csi-driver-w6s6q 2/2 Running 0 2m37s
12.5.5. Deploying the SPIRE OpenID Connect Discovery Provider Copy linkLink copied to clipboard!
Deploy the SPIRE OpenID Connect (OIDC) Discovery Provider by configuring the SpireOIDCDiscoveryProvider CR. This allows you to define the trust domain and JSON web token (JWT) issuer for your cluster.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have installed Zero Trust Workload Identity Manager in the cluster.
Procedure
Create the
SpireOIDCDiscoveryProviderCR:Create a YAML file that defines the
SpireOIDCDiscoveryProviderCR, for example,SpireOIDCDiscoveryProvider.yaml:Example
SpireOIDCDiscoveryProviderYAMLaapiVersion: operator.openshift.io/v1alpha1 kind: SpireOIDCDiscoveryProvider metadata: name: cluster spec: logLevel: "info" logFormat: "text" csiDriverName: "csi.spiffe.io" jwtIssuer: "https://oidc-discovery.apps.cluster.example.com" replicaCount: 1 managedRoute: "true" externalSecretRef: ""where:
metadata.name-
Specifies that the value must be
cluster. spec.logLevel-
Specifies the logging level for the SPIRE Server. The valid options are
debug,info,warn, anderror. spec.logFormat-
Specifies the logging format for the SPIRE Server. The valid options are
textandjson. spec.csiDriverName-
Specifies the name of the CSI driver to use for mounting the Workload API socket. This must match the
SpiffeCSIDriver.spec.pluginNamevalue for the OIDC provider to access SPIFFE identities. Must be a valid DNS subdomain format (for example,csi.spiffe.io) with a maximum length of 127 characters. spec.jwtIssuer-
Specifies the JWT issuer URL. Must be a valid HTTPS or HTTP URL with a maximum length of 512 characters. This value must match the
SpireServer.spec.jwtIssuervalue. spec.replicaCount- Specifies the number of replicas for the OIDC Discovery Provider deployment. Must be between 1 and 5.
spec.managedRoute-
Specifies whether the Operator automatically creates an OpenShift route for the OIDC Discovery Provider endpoints. Set to
trueto have the Operator automatically create and maintain an OpenShift route for OIDC discovery endpoints (*.apps.). Set tofalsefor administrators to manually configure routes or ingress. spec.externalSecretRef- Specifies a reference to an externally managed secret that contains the TLS certificate for the OIDC Discovery Provider route host. Must be a valid Kubernetes secret reference name with a maximum length of 253 characters. This field is optional.
Apply the configuration by running the following command:
$ oc apply -f SpireOIDCDiscoveryProvider.yaml
Verification
Verify that the deployment of OIDC Discovery Provider is ready and available by running the following command:
$ oc get deployment -l app.kubernetes.io/name=spiffe-oidc-discovery-provider -n zero-trust-workload-identity-managerExample output
NAME READY UP-TO-DATE AVAILABLE AGE spire-spiffe-oidc-discovery-provider 1/1 1 1 2m58sVerify that the status of OIDC Discovery Provider pods is
Runningby running the following command:$ oc get po -l app.kubernetes.io/name=spiffe-oidc-discovery-provider -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-spiffe-oidc-discovery-provider-64586d599f-lcc94 2/2 Running 0 7m15s
12.5.6. Verify the health of the operands Copy linkLink copied to clipboard!
View the status fields to verify the operational health of managed components. This information helps you confirm that the SPIRE Server, SPIRE Agent, SPIFFE CSI driver, and the SPIRE OIDC discovery provider operands are ready and functioning correctly.
To verify the operands, run the following command:
oc get ZeroTrustWorkloadIdentityManager cluster -o yamlExample output
status: conditions: - lastTransitionTime: "2025-12-16T10:59:06Z" message: All components are ready reason: Ready status: "True" type: Ready - lastTransitionTime: "2025-12-16T10:59:06Z" message: All operand CRs are ready reason: Ready status: "True" type: OperandsAvailable operands: - kind: SpireServer message: Ready name: cluster ready: "true" - kind: SpireAgent message: Ready name: cluster ready: "true" - kind: SpiffeCSIDriver message: Ready name: cluster ready: "true" - kind: SpireOIDCDiscoveryProvider message: Ready name: cluster ready: "true" # ...
This status is reflected when all operands are healthy and stable.
The Operator adds the owner reference for the ZeroTrustWorkloadIdentityManager CR on the other operands' CRs. This causes the operands' resources to be deleted once the ZeroTrustWorkloadIdentityManager CRs are deleted.
12.5.7. Manually delete the custom security context constraints Copy linkLink copied to clipboard!
You can delete the spire-spiffe-csi-driver custom SCC when the SPIFFE CSI driver is ready and its pods are using the privileged security context constraint (SCC).
Prerequisites
- You have upgraded Zero Trust Workload Identity Manager to 1.1.0 from the OperatorHub catalog.
Procedure
Confirm the SPIFFE CSI driver is ready by running the following command:
$ oc get spiffecsidriver cluster -o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'The expected output includes
DaemonSetAvailable=TrueandReady=True.Confirm that the CSI
DaemonSetis available by running the following command:$ oc get ds spire-spiffe-csi-driver -n zero-trust-workload-identity-managerConfirm the CSI pods are running and are using the
privilegedSCC by running the following commands:$ oc get pods -n zero-trust-workload-identity-manager -l app.kubernetes.io/name=spiffe-csi-driver$ oc get pod -n zero-trust-workload-identity-manager -l app.kubernetes.io/name=spiffe-csi-driver \ -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.openshift\.io/scc}{"\n"}{end}'Every pod should show
privileged.After all checks have passed, delete the legacy custom SCC by running the following commands:
$ oc get scc spire-spiffe-csi-driver$ oc delete scc spire-spiffe-csi-driverIf the SCC is already absent, no action is needed.
12.6. Configuring the egress proxy for the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
Operator Lifecycle Manager (OLM) automatically configures managed Operators with proxy settings when you use a cluster-wide egress proxy. To support proxying HTTPS connections, you can inject certificate authority (CA) certificates into the Zero Trust Workload Identity Manager.
12.6.1. Injecting a custom CA certificate for the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
Inject certificate authority (CA) certificates into the Zero Trust Workload Identity Manager to support proxying HTTPS connections. This configuration helps ensure that the Identity Manager can communicate securely when you enable a cluster-wide proxy.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have enabled the cluster-wide proxy for OpenShift Container Platform.
- You have installed Zero Trust Workload Identity Manager 1.0.0 or later.
- You have deployed the SPIRE Server, SPIRE Agent, SPIFFE CSI Driver, and the SPIRE OIDC Discovery Provider operands in the cluster.
Procedure
Create a config map in the
zero-trust-workload-identity-managernamespace by running the following command:$ oc create configmap trusted-ca -n zero-trust-workload-identity-managerInject the CA bundle that is trusted by OpenShift Container Platform into the config map by running the following command:
$ oc label cm trusted-ca config.openshift.io/inject-trusted-cabundle=true -n zero-trust-workload-identity-managerUpdate the subscription for the Zero Trust Workload Identity Manager to use the config map by running the following command:
$ oc -n zero-trust-workload-identity-manager patch subscription openshift-zero-trust-workload-identity-manager --type='merge' -p '{"spec":{"config":{"env":[{"name":"TRUSTED_CA_BUNDLE_CONFIGMAP","value":"trusted-ca"}]}}}'
Verification
Verify that the operands have finished rolling out by running the following command:
$ oc rollout status deployment/zero-trust-workload-identity-manager-controller-manager -n zero-trust-workload-identity-manager && \ $ oc rollout status statefulset/spireserver -n zero-trust-workload-identity-manager && \ $ oc rollout status daemonset/spire-agent -n zero-trust-workload-identity-manager && \ $ oc rollout status deployment/spire-spiffe-oidc-discovery-provider -n zero-trust-workload-identity-managerExample output
deployment "zero-trust-workload-identity-manager-controller-manager" successfully rolled out statefulset "spire-server" successfully rolled out daemonset "spire-agent" successfully rolled out deployment "spire-spiffe-oidc-discovery-provider" successfully rolled outVerify that the CA bundle was mounted as a volume by running the following command:
$ oc get deployment zero-trust-workload-identity-manager -n zero-trust-workload-identity-manager -o=jsonpath={.spec.template.spec.'containers[0].volumeMounts'}$ oc get statefulset spire-server -n zero-trust-workload-identity-manager -o jsonpath='{.spec.template.spec.containers[*].volumeMounts[?(@.name=="trusted-ca-bundle")]}'$ oc get daemonset spire-agent -n zero-trust-workload-identity-manager -o jsonpath='{.spec.template.spec.containers[*].volumeMounts[?(@.name=="trusted-ca-bundle")]}'$ oc get daemonset spire-spiffe-csi-driver -n zero-trust-workload-identity-manager -o jsonpath='{.spec.template.spec.containers[*].volumeMounts[?(@.name=="trusted-ca-bundle")]}'Example output
[{{"mountPath":"/etc/pki/ca-trust/extracted/pem","name":"trusted-ca-bundle","readOnly":true}]Verify that the source of the CA bundle is the
trusted-caconfig map by running the following command:$ oc get deployment zero-trust-workload-identity-manager -n zero-trust-workload-identity-manager -o=jsonpath={.spec.template.spec.volumes}$ oc get statefulset spire-server -n zero-trust-workload-identity-manager -o=jsonpath='{.spec.template.spec.volumes}' | jq '.[] | select(.name=="trusted-ca-bundle")'$ oc get daemonset spire-agent -n zero-trust-workload-identity-manager -o=jsonpath='{.spec.template.spec.volumes}' | jq '.[] | select(.name=="trusted-ca-bundle")'$ oc get deployment spire-spiffe-oidc-discovery-provider -n zero-trust-workload-identity-manager -o=jsonpath='{.spec.template.spec.volumes}' | jq '.[] | select(.name=="trusted-ca-bundle")'Example output
{ "configMap": { "defaultMode": 420, "items": [ { "key": "ca-bundle.crt", "path": "tls-ca-bundle.pem" } ], "name": "trusted-ca" }, "name": "trusted-ca-bundle" }
12.7. Zero Trust Workload Identity Manager OIDC federation Copy linkLink copied to clipboard!
Ensure that your workloads can receive verifiable JSON Web Tokens (JWT-SVIDs) and allow external systems, such as cloud providers, to retrieve public keys from the discovery endpoint. Configure Zero Trust Workload Identity Manager to act as an OpenID Connect (OIDC) provider through the SPIRE server.
The following providers are verified to work with SPIRE OIDC federation:
- Azure Entra ID
- Vault
12.7.1. About the Entra ID OpenID Connect Copy linkLink copied to clipboard!
Integrate Entra ID OpenID Connect (OIDC) with SPIRE to provide workloads with automatic, short-lived cryptographic identities. This configuration allows you to securely authenticate services without maintaining static secrets.
12.7.1.1. Configuring the external certificate for the managed OIDC discovery provider route Copy linkLink copied to clipboard!
Configure the managed OIDC discovery provider route to use an externally managed TLS certificate. By referencing a TLS secret, you can secure the OIDC endpoint with your own certificate credentials.
Prerequisites
- You have installed Zero Trust Workload Identity Manager 0.2.0 or later.
- You have deployed the SPIRE Server, SPIRE Agent, SPIFFEE CSI Driver, and the SPIRE OIDC Discovery Provider operands in the cluster.
- You have installed the cert-manager Operator for Red Hat OpenShift. For more information, Installing the cert-manager Operator for Red Hat OpenShift.
-
You have created a
ClusterIssuerorIssuerconfigured with a publicly trusted CA service. For example, an Automated Certificate Management Environment (ACME) typeIssuerwith the "Let’s Encrypt ACME" service. For more information, see Configuring an ACME issuer
Procedure
Create a
Roleto provide the router service account permissions to read the referenced secret by running the following command:$ oc create role secret-reader \ --verb=get,list,watch \ --resource=secrets \ --resource-name=$TLS_SECRET_NAME \ -n zero-trust-workload-identity-managerCreate a
RoleBindingresource to bind the router service account with the newly created Role resource by running the following command:$ oc create rolebinding secret-reader-binding \ --role=secret-reader \ --serviceaccount=openshift-ingress:router \ -n zero-trust-workload-identity-managerConfigure the
SpireOIDCDIscoveryProviderCustom Resource (CR) object to reference the Secret generated in the earlier step by running the following command:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: externalSecretRef: ${TLS_SECRET_NAME} '
Verification
In the
SpireOIDCDiscoveryProviderCR, check if theManageRouteReadycondition is set toTrueby running the following command:$ oc wait --for=jsonpath='{.status.conditions[?(@.type=="ManagedRouteReady")].status}'=True SpireOIDCDiscoveryProvider/cluster --timeout=120sVerify that the OIDC endpoint can be accessed securely through HTTPS by running the following command:
$ curl https://$JWT_ISSUER_ENDPOINT/.well-known/openid-configuration { "issuer": "https://$JWT_ISSUER_ENDPOINT", "jwks_uri": "https://$JWT_ISSUER_ENDPOINT/keys", "authorization_endpoint": "", "response_types_supported": [ "id_token" ], "subject_types_supported": [], "id_token_signing_alg_values_supported": [ "RS256", "ES256", "ES384" ] }%
12.7.1.2. Disabling a managed route Copy linkLink copied to clipboard!
If you want to fully control the behavior of exposing the OIDC Discovery Provider service, you can disable the managed route based on your requirements.
Procedure
To manually configure the OIDC Discovery Provider, set
managedRoutetofalseby running the following command:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: managedRoute: "false"
12.7.1.3. Using Entra ID with Microsoft Azure Copy linkLink copied to clipboard!
Configure your Microsoft Azure environment to enable Entra ID integration with Azure. By defining variables and creating a resource group, you establish the infrastructure needed to securely manage workload identities.
Prerequisites
- You have configured the SPIRE OIDC Discovery Provider Route to serve the TLS certificates from a publicly trusted CA.
Procedure
Log in to Azure by running the following command:
$ az loginConfigure variables for your Azure subscription and tenant by running the following commands:
$ export SUBSCRIPTION_ID=$(az account list --query "[?isDefault].id" -o tsv)$ export TENANT_ID=$(az account list --query "[?isDefault].tenantId" -o tsv)$ export LOCATION=centraluswhere:
SUBSCRIPTION_ID- Specifies your unique subscription identifier.
TENANT_ID- Specifies the ID for your Azure Active Directory instance.
LOCATION- The Azure region where your resource is created.
Define resource variable names by running the following commands:
$ export NAME=ztwim$ export RESOURCE_GROUP="${NAME}-rg"$ export STORAGE_ACCOUNT="${NAME}storage"$ export STORAGE_CONTAINER="${NAME}storagecontainer"$ export USER_ASSIGNED_IDENTITY_NAME="${NAME}-identity"where:
NAME- Specifies A base name for all resources.
RESOURCE_GROUP- Specifies the name of the resource group.
STORAGE_ACCOUNT- Specifies the name for the storage account.
STORAGE_CONTAINER- Specifies the name for the storage container.
USER_ASSIGNED_IDENTITY_NAME- Specifies the name for a managed identity.
Create the resource group by running the following command:
$ az group create \ --name "${RESOURCE_GROUP}" \ --location "${LOCATION}"
12.7.1.4. Configuring Azure blob storage Copy linkLink copied to clipboard!
Create a new Microsoft Azure storage account and container to provide a dedicated location for your content. Configuring this storage ensures that the Zero Trust Workload Identity Manager can successfully store and retrieve blobs for your environment.
Procedure
Create a new storage account that is used to store content by running the following command:
$ az storage account create \ --name ${STORAGE_ACCOUNT} \ --resource-group ${RESOURCE_GROUP} \ --location ${LOCATION} \ --encryption-services blobObtain the storage ID for the newly created storage account by running the following command:
$ export STORAGE_ACCOUNT_ID=$(az storage account show -n ${STORAGE_ACCOUNT} -g ${RESOURCE_GROUP} --query id --out tsv)Create a storage container inside the newly created storage account to provide a location to support the storage of blobs by running the following command:
$ az storage container create \ --account-name ${STORAGE_ACCOUNT} \ --name ${STORAGE_CONTAINER} \ --auth-mode login
12.7.1.5. Configuring an Azure user managed identity Copy linkLink copied to clipboard!
Create a user-assigned managed identity in Azure to manage access control for your resources. You must also obtain the Client ID to associate roles with the service principal.
Procedure
Create a new User Managed Identity and then obtain the Client ID of the related Service Principal associated with the User Managed Identity by running the following command:
$ az identity create \ --name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP}$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)Retrieve the
CLIENT_IDof an Azure user-assigned managed identity and save it as an environment variable by running the following command:$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)Associate a role with the Service Principal associated with the User Managed Identity by running the following command:
$ az role assignment create \ --role "Storage Blob Data Contributor" \ --assignee "${IDENTITY_CLIENT_ID}" \ --scope ${STORAGE_ACCOUNT_ID}
12.7.1.6. Creating the demonstration application Copy linkLink copied to clipboard!
Create the demonstration application to verify that the entire system functions correctly. This process validates the configuration of your application secrets and namespaces.
Procedure
Set the application name and namespace by running the following commands:
$ export APP_NAME=workload-app$ export APP_NAMESPACE=demoCreate the namespace by running the following command:
$ oc create namespace $APP_NAMESPACECreate the application Secret by running the following command:
$ oc apply -f - << EOF apiVersion: v1 kind: Secret metadata: name: $APP_NAME namespace: $APP_NAMESPACE stringData: AAD_AUTHORITY: https://login.microsoftonline.com/ AZURE_AUDIENCE: "api://AzureADTokenExchange" AZURE_TENANT_ID: "${TENANT_ID}" AZURE_CLIENT_ID: "${IDENTITY_CLIENT_ID}" BLOB_STORE_ACCOUNT: "${STORAGE_ACCOUNT}" BLOB_STORE_CONTAINER: "${STORAGE_CONTAINER}" EOF
12.7.1.7. Deploying the workload application Copy linkLink copied to clipboard!
Deploy the workload application to your cluster to validate the Zero Trust Workload Identity Manager environment. This application confirms that the SPIFFE Workload API is functioning and can successfully retrieve JWT tokens.
Prerequisites
- The demonstration application has been created and deployed.
Procedure
To deploy the application, copy the entire command block provided and paste it directly into your terminal. Press Enter.
$ oc apply -f - << EOF apiVersion: v1 kind: ServiceAccount metadata: name: $APP_NAME namespace: $APP_NAMESPACE --- kind: Deployment apiVersion: apps/v1 metadata: name: $APP_NAME namespace: $APP_NAMESPACE spec: selector: matchLabels: app: $APP_NAME template: metadata: labels: app: $APP_NAME deployment: $APP_NAME spec: serviceAccountName: $APP_NAME containers: - name: $APP_NAME image: "registry.redhat.io/ubi9/python-311:latest" command: - /bin/bash - "-c" - | #!/bin/bash pip install spiffe azure-cli cat << EOF > /opt/app-root/src/get-spiffe-token.py #!/opt/app-root/bin/python from spiffe import JwtSource import argparse parser = argparse.ArgumentParser(description='Retrieve SPIFFE Token.') parser.add_argument("-a", "--audience", help="The audience to include in the token", required=True) args = parser.parse_args() with JwtSource() as source: jwt_svid = source.fetch_svid(audience={args.audience}) print(jwt_svid.token) EOF chmod +x /opt/app-root/src/get-spiffe-token.py while true; do sleep 10; done envFrom: - secretRef: name: $APP_NAME env: - name: SPIFFE_ENDPOINT_SOCKET value: unix:///run/spire/sockets/spire-agent.sock securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: false runAsNonRoot: true seccompProfile: type: RuntimeDefault ports: - containerPort: 8080 protocol: TCP volumeMounts: - name: spiffe-workload-api mountPath: /run/spire/sockets readOnly: true volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true EOF
Verification
Ensure that the
workload-apppod is running successfully by running the following command:$ oc get pods -n $APP_NAMESPACEExample output
NAME READY STATUS RESTARTS AGE workload-app-5f8b9d685b-abcde 1/1 Running 0 60sRetrieve the SPIFFE JWT Token (SVID-JWT):
Get the pod name dynamically by running the following command:
$ POD_NAME=$(oc get pods -n $APP_NAMESPACE -l app=$APP_NAME -o jsonpath='{.items[0].metadata.name}')Run the script inside the pod by running the following command:
$ oc exec -it $POD_NAME -n $APP_NAMESPACE -- \ /opt/app-root/src/get-spiffe-token.py -a "api://AzureADTokenExchange"
12.7.1.8. Configuring Azure with the SPIFFE identity federation Copy linkLink copied to clipboard!
Configure Microsoft Azure with SPIFFE identity federation to enable password-free, automated authentication for the demonstration application. This federates the User Managed Identity with the SPIFFE identity associated with your workload application.
Procedure
Federate the identities between the User Managed Identity and the SPIFFE identity associated with the workload application by running the following command:
$ az identity federated-credential create \ --name ${NAME} \ --identity-name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP} \ --issuer https://$JWT_ISSUER_ENDPOINT \ --subject spiffe://$APP_DOMAIN/ns/$APP_NAMESPACE/sa/$APP_NAME \ --audience api://AzureADTokenExchange
12.7.1.9. Verifying that the application workload can access the content in the Azure Blob Storage Copy linkLink copied to clipboard!
Verify that your application workload can connect to the Azure Blob Storage. By uploading a test file, you validate the authentication token and ensure that the workload has the correct permissions.
Prerequisites
- An Azure Blob Storage has been created.
Procedure
Retrieve a JWT token from the SPIFFE Workload API by running the following command:
$ oc rsh -n $APP_NAMESPACE deployment/$APP_NAMECreate and export an environment variable named
TOKENby running the following command:$ export TOKEN=$(/opt/app-root/src/get-spiffe-token.py --audience=$AZURE_AUDIENCE)Log in to Azure CLI included within the pod by running the following command:
$ az login --service-principal \ -t ${AZURE_TENANT_ID} \ -u ${AZURE_CLIENT_ID} \ --federated-token ${TOKEN}Create a new file with the application workload pod and upload the file to the Blob Storage by running the following command:
$ echo “Hello from OpenShift” > openshift-spire-federated-identities.txtUpload a file to the Azure Blog Storage by running the following command:
$ az storage blob upload \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --name openshift-spire-federated-identities.txt \ --file openshift-spire-federated-identities.txt \ --auth-mode login
Verification
Confirm the file uploaded successfully by listing the files contained by running the following command:
$ az storage blob list \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --auth-mode login \ -o table
12.7.2. About Vault OpenID Connect Copy linkLink copied to clipboard!
Use Vault OpenID Connect (OIDC) with SPIRE to securely authenticate workloads. Vault uses SPIRE as a trusted OIDC provider to validate workload identities. This configuration enables workloads to receive short-lived tokens to access secrets and perform actions within Vault.
12.7.2.1. Installing Vault Copy linkLink copied to clipboard!
Install HashiCorp Vault to serve as an OpenID Connect (OIDC) provider. This establishes the necessary infrastructure to manage workload identities securely in your Zero Trust Workload Identity Manager environment.
Prerequisites
- Configure a route. For more information, see Configuring routes
- Helm is installed.
- A command-line JSON processor for easily reading the output from the Vault API.
- A HashiCorp Helm repository is added.
Procedure
Create the
vault-helm-value.yamlfile.global: enabled: true openshift: true tlsDisable: true injector: enabled: false server: ui: enabled: true image: repository: docker.io/hashicorp/vault tag: "1.19.0" dataStorage: enabled: true size: 1Gi standalone: enabled: true config: | listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "file" { path = "/vault/data" } extraEnvironmentVars: {}-
The
openshiftfield optimizes the deployment for OpenShift-specific security contexts. -
The
tlsDisablefield disables TLS for Kubernetes objects created by the chart. -
The
datastorage.enabledfield creates a 1Gi persistent volume to store Vault data. -
The
standalone.enabledfield deploys a single Vault pod. -
The
tls_disabledfield tells the Vault server to not use TLS.
-
The
Run the
helm installcommand:$ helm install vault hashicorp/vault \ --create-namespace -n vault \ --values ./vault-helm-value.yamlExpose the Vault service by running the following command:
$ oc expose service vault -n vaultSet the
VAULT_ADDRenvironment variable to retrieve the hostname from the new route and then export it by running the following command:$ export VAULT_ADDR="http://$(oc get route vault -n vault -o jsonpath='{.spec.host}')"Notehttp://is prepended because TLS is disabled.
Verification
To ensure your Vault instance is running, run the following command:
$ curl -s $VAULT_ADDR/v1/sys/health | jqExample output
{ "initialized": true, "sealed": true, "standby": true, "performance_standby": false, "replication_performance_mode": "disabled", "replication_dr_mode": "disabled", "server_time_utc": 1663786574, "version": "1.19.0", "cluster_name": "vault-cluster-a1b2c3d4", "cluster_id": "5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b" }
12.7.2.2. Initializing and unsealing Vault Copy linkLink copied to clipboard!
To prepare a newly installed Vault server for operation, initialize and unseal it. This process loads the primary encryption key into memory so that Vault can decrypt data and protect other encryption keys.
The steps to initialize a Vault server are:
- Initialize and unseal Vault
- Enable the key-value (KV) secrets engine and store a test secret
- Configure JSON Web Token (JWT) authentication with SPIRE
- Deploy a demonstration application
- Authenticate and retrieve the secret
Prerequisites
- Ensure that Vault is running.
- Ensure that Vault is not initialized. You can only initialize a Vault server once.
Procedure
Open a remote shell into the
vaultpod by running the following command:$ oc rsh -n vault statefulset/vaultInitialize Vault to get your unseal key and root token by running the following command:
$ vault operator init -key-shares=1 -key-threshold=1 -format=jsonExport the unseal key and root token you received from the earlier command by running the following commands:
$ export UNSEAL_KEY=<Your-Unseal-Key>$ export ROOT_TOKEN=<Your-Root-Token>Unseal Vault using your unseal key by running the following command:
$ vault operator unseal -format=json $UNSEAL_KEY-
Exit the pod by entering
exit.
Verification
To verify that the Vault pod is ready, run the following command:
$ oc get pod -n vaultExample output
NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 65d
12.7.2.3. Enabling the key-value secrets engine and store a test secret Copy linkLink copied to clipboard!
Enable the key-value secrets engine to create a secure, centralized location for managing credentials. You can also store a test secret to verify that the engine is working.
Prerequisites
- Make sure that Vault is initialized and unsealed.
Procedure
Open another shell session in the
Vaultpod by running the following command:$ oc rsh -n vault statefulset/vaultExport your root token again within this new session and log in by running the following command:
$ export ROOT_TOKEN=<Your-Root-Token>$ vault login "${ROOT_TOKEN}"Enable the KV secrets engine at the
secret/path and create a test secret by running the following commands:$ export NAME=ztwim$ vault secrets enable -path=secret kv$ vault kv put secret/$NAME version=v0.1.0
Verification
To verify that the secret is stored correctly, run the following command:
$ vault kv get secret/$NAME
12.7.2.4. Configuring JSON Web Token authentication with SPIRE Copy linkLink copied to clipboard!
To help your applications securely log in to Vault using SPIFFE identities, configure JSON Web Token (JWT) authentication.
Prerequisites
- Make sure that Vault is initialized and unsealed.
- Ensure that a test secret is stored in the key-value secrets engine.
Procedure
On your local machine, retrieve the SPIRE Certificate Authority (CA) bundle and save it to a file by running the following command:
$ oc get cm -n zero-trust-workload-identity-manager spire-bundle -o jsonpath='{ .data.bundle\.crt }' > oidc_provider_ca.pemBack in the Vault pod shell, create a temporary file and paste the contents of
oidc_provider_ca.peminto it by running the following command:$ cat << EOF > /tmp/oidc_provider_ca.pem -----BEGIN CERTIFICATE----- <Paste-Your-Certificate-Content-Here> -----END CERTIFICATE----- EOF>Set up the necessary environment variables for the JWT configuration by running the following commands:
$ export APP_DOMAIN=<Your-App-Domain>$ export JWT_ISSUER_ENDPOINT="oidc-discovery.$APP_DOMAIN"$ export OIDC_URL="https://$JWT_ISSUER_ENDPOINT"$ export OIDC_CA_PEM="$(cat /tmp/oidc_provider_ca.pem)"Crate a new environment variable by running the following command:
$ export ROLE="${NAME}-role"Enable the JWT authentication method by running the following command:
$ vault auth enable jwtConfigure you ODIC authentication method by running the following command:
$ vault write auth/jwt/config \ oidc_discovery_url=$OIDC_URL \ oidc_discovery_ca_pem="$OIDC_CA_PEM" \ default_role=$ROLECreate a policy named
ztwim-policyby running the following command:$ export POLICY="${NAME}-policy"Grant read access to the secret you created earlier by running the following command:
$ vault policy write $POLICY -<<EOF path "secret/$NAME" { capabilities = ["read"] } EOFCreate the following environment variables by running the following commands:
$ export APP_NAME=client$ export APP_NAMESPACE=demo$ export AUDIENCE=$APP_NAMECreate a JWT role that binds the policy to workload with a specific SPIFFE ID by running the following command:
$ vault write auth/jwt/role/$ROLE -<<EOF { "role_type": "jwt", "user_claim": "sub", "bound_audiences": "$AUDIENCE", "bound_claims_type": "glob", "bound_claims": { "sub": "spiffe://$APP_DOMAIN/ns/$APP_NAMESPACE/sa/$APP_NAME" }, "token_ttl": "24h", "token_policies": "$POLICY" } EOF
12.7.2.5. Deploying a demonstration application Copy linkLink copied to clipboard!
Deploy a demonstration application to create a simple client that uses its SPIFFE identity to authenticate with Vault. By doing this you can verify that the client can successfully authenticate using the configured identity.
Procedure
On your local machine, set the environment variables for your application by running the following commands:
$ export APP_NAME=client$ export APP_NAMESPACE=demo$ export AUDIENCE=$APP_NAMEApply the Kubernetes manifest to create the namespace, service account, and deployment for the demo app by running the following command. This deployment mounts the SPIFFE CSI driver socket.
$ oc apply -f - <<EOF # ... (paste the full YAML from your provided code here) ... EOF>>
Verification
Verify that the client deployment is ready by running the following command:
$ oc get deploy -n $APP_NAMESPACEExample output
NAME READY UP-TO-DATE AVAILABLE AGE frontend-app 2/2 2 2 120d backend-api 3/3 3 3 120d
12.7.2.6. Authenticating and retrieving the secret Copy linkLink copied to clipboard!
Use the demonstration application to fetch a JWT token from the SPIFFE Workload API. Use the token to authenticate with Vault so that you can securely retrieve the secret and verify the workflow.
Procedure
Fetch a JWT-SVID by running the following command inside the running client pod:
$ oc -n $APP_NAMESPACE exec -it $(oc get pod -o=jsonpath='{.items[*].metadata.name}' -l app=$APP_NAME -n $APP_NAMESPACE) \ -- /opt/spire/bin/spire-agent api fetch jwt \ -socketPath /run/spire/sockets/spire-agent.sock \ -audience $AUDIENCECopy the token from the output and export it as an environment variable on your local machine by running the following command:
$ export IDENTITY_TOKEN=<Your-JWT-Token>Crate a new environment variable by running the following command:
$ export ROLE="${NAME}-role"Use
curlto send the JWT token to the Vault login endpoint to get a Vault client token by running the following command:$ VAULT_TOKEN=$(curl -s --request POST --data '{ "jwt": "'"${IDENTITY_TOKEN}"'", "role": "'"${ROLE}"'"}' "${VAULT_ADDR}"/v1/auth/jwt/login | jq -r '.auth.client_token')
Verification
Use the newly acquired Vault token to read the secret from the KV store by running the following command:
$ curl -s -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/secret/$NAME | jqYou should see the contents of the secret (
"version": "v0.1.0") in the output, confirming the entire workflow is successful
12.8. Zero Trust Workload Identity Manager SPIRE federation Copy linkLink copied to clipboard!
Configure SPIRE federation to enable workloads in different trust domains to securely authenticate each other across clusters, cloud providers, and organizational boundaries. By establishing trust relationships between separate SPIRE deployments, you can build a zero-trust architecture that spans multiple environments without compromising security or sharing secrets.
Federation works by securely sharing trust bundles between SPIRE servers through dedicated federation endpoints. Each SPIRE deployment maintains its own trust domain and cryptographic identity, while being able to verify identities from federated trust domains. This approach enables cross-cluster communication, multi-cloud deployments, and secure integration with external partners.
Setting up SPIRE federation involves the following high-level steps:
-
Choose an authentication profile: Select either
https_spiffeorhttps_web. - Configure the bundle endpoints: Each cluster exposes its trust bundle through a federation endpoint secured by the chosen authentication profile.
- Bootstrap the initial trust: Manually fetch and configure the initial trust bundle from each remote cluster.
-
Establish federation relationships: Create
ClusterFederatedTrustDomainresources to define which clusters trust each other. - Configure automatic synchronization: The SPIRE Controller Manager automatically keeps trust bundles synchronized after initial setup.
12.8.1. Understanding bundle endpoint profiles Copy linkLink copied to clipboard!
The bundle endpoint profile determines how your cluster exposes its trust bundle to other SPIRE deployments and how it authenticates remote clusters accessing the bundle. Choose the profile that best matches your security requirements and infrastructure.
The Zero Trust Workload Identity Manager supports two authentication profiles for federation:
- https_spiffe
- Uses SPIFFE-based TLS authentication. The SPIRE server presents its own SVID (SPIFFE Verifiable Identity Document) to authenticate itself to remote SPIRE servers. This profile provides strong cryptographic identity verification and is ideal for federation between SPIRE deployments.
- https_web
- Uses standard Web PKI (X.509 certificates from public or private certificate Authorities). This profile supports both automatic certificate management via ACME (Let’s Encrypt) and manual certificate management using tools like cert-manager.
The following table summarizes the key differences between the two profiles:
| Criteria | https_spiffe | https_web |
|---|---|---|
| Authentication method | SPIFFE SVID (TLS) | X.509 certificate from CA |
| Certificate management | Automatic (SPIRE-managed) | ACME (automatic) or manual |
| Trust model | SPIFFE trust domain | Web PKI / CA trust |
| Best for | Internal SPIRE-to-SPIRE federation | External federation, public endpoints |
| Security level | Very high (cryptographic identity) | High (CA-based trust) |
| Setup complexity | Medium (requires SPIFFE IDs) | Low (ACME) to Medium (manual certs) |
After enablement, federation cannot be disabled. The bundle endpoint profile is immutable once configured. Changing the profile or disabling federation requires reinstallation of the system. However, peer configurations (federatesWith) remain dynamic and can be added or removed at any time. Plan your profile selection carefully based on your long-term federation requirements.
12.8.2. Federation configuration examples Copy linkLink copied to clipboard!
The following examples demonstrate different SPIRE federation configurations. Use these as templates when setting up federation between your clusters.
- Example 1: Using ACME for automatic certificate management
The following example shows how to configure federation using Let’s Encrypt for automatic certificate provisioning and renewal:
apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_web refreshHint: 300 httpsWeb: acme: directoryUrl: https://acme-v02.api.letsencrypt.org/directory domainName: federation.apps.cluster1.example.com email: admin@example.com tosAccepted: "true" federatesWith: - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_web - trustDomain: cluster3.example.com bundleEndpointUrl: https://federation.apps.cluster3.example.com bundleEndpointProfile: https_web managedRoute: "true"-
The
profilefield useshttps_webprofile for Web PKI certificate-based authentication. -
The
directoryURLfield is used for the production directory. For testing, use staging URLhttps://acme-staging-v02.api.letsencrypt.org/directory
-
The
- Example 2: Using manual certificate management with cert-manager
The following example shows how to configure federation using externally managed certificates:
apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_web refreshHint: 300 httpsWeb: servingCert: fileSyncInterval: 86400 externalSecretRef: spire-server-federation-tls federatesWith: - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_web - trustDomain: cluster3.example.com bundleEndpointUrl: https://federation.apps.cluster3.example.com bundleEndpointProfile: https_web managedRoute: "true"-
The
fileSyncIntervalfield checks for certificate updates every 24 hours. -
The
externalSecretReffield is the name of the Kubernetes Secret containingtls.crtandtls.key
-
The
- Example 3: Using https_spiffe profile for SPIRE-to-SPIRE federation
The following example shows how to configure federation using SPIFFE-based TLS authentication:
apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_spiffe refreshHint: 300 federatesWith: - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_spiffe endpointSpiffeId: spiffe://cluster2.example.com/spire/server - trustDomain: cluster3.example.com bundleEndpointUrl: https://federation.apps.cluster3.example.com bundleEndpointProfile: https_spiffe endpointSpiffeId: spiffe://cluster3.example.com/spire/server managedRoute: "true"-
The
profilefield useshttps_spiffeprofile for SPIFFE-based TLS authentication. -
The
endpointSiffeIdfield contains the SPIFFE ID of the remote SPIRE server, required for identity validation.
-
The
- Example 4: Mixed federation with multiple authentication profiles
The following example shows a cluster federating with multiple remote clusters using different authentication profiles:
apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: internal-cluster.example.com federation: bundleEndpoint: profile: https_spiffe refreshHint: 300 federatesWith: # Internal cluster using SPIFFE TLS - trustDomain: dev-cluster.example.com bundleEndpointUrl: https://federation.apps.dev-cluster.example.com bundleEndpointProfile: https_spiffe endpointSpiffeId: spiffe://dev-cluster.example.com/spire/server # External partner using Web PKI - trustDomain: partner.example.com bundleEndpointUrl: https://federation.partner.example.com bundleEndpointProfile: https_web # Another external partner using Web PKI - trustDomain: vendor.example.com bundleEndpointUrl: https://spire-federation.vendor.example.com bundleEndpointProfile: https_web managedRoute: "true"-
The
profilefield cluster exposes its bundle usinghttps_spiffeprofile. -
The
bundleEndpointProfilefield cluster exposes its bundle usinghttps_spiffeprofile.
-
The
12.8.3. Configuring SPIRE federation with the https_spiffe profile Copy linkLink copied to clipboard!
The Zero Trust Workload Identity Manager includes SPIRE Federation support, allowing multiple independent SPIRE deployments to establish trust relationships. This procedure demonstrates how to configure federation using the https_spiffe profile, which uses SPIFFE-based TLS authentication between SPIRE servers.
Prerequisites
-
You have installed the OpenShift CLI (
oc). - You have installed the Zero Trust Workload Identity Manager on all clusters that will participate in the federation.
-
You have
cluster-adminprivileges on all participating clusters. - You have network connectivity between the clusters you intend to federate.
Procedure
Configure the
SpireServercustom resource on each cluster to enable federation with thehttps_spiffeprofile. Thehttps_spiffeprofile uses SPIFFE-based TLS authentication, where SPIRE servers authenticate to each other using their own SPIFFE Verifiable Identity Documents (SVIDs).apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_spiffe refreshHint: 300 managedRoute: "true"-
The
spec.trustDomainfield sets a unique trust domain for each cluster. -
The
spec.federation.bundleEnpoint.profilefield uses thehttps_spiffeprofile for SPIFFE-based TLS authentication. -
The
spec.federation.bundleEndpoint.refreshHintfield suggests intervals (in seconds) for remote servers to refresh the trust bundle. Range: 60-3600 seconds. -
The
spec.federation.managedRoutefield enables automatic route creation by the Operator.
-
The
Apply the configuration changes by running the following command:
$ oc apply -f spire-server.yamlCheck the status of the SPIRE Server by entering the following command. Wait for the
Readystatus to be returned.$ oc get spireserver cluster -wVerify that the federation route has been created:
$ oc get route -n zero-trust-workload-identity-manager | grep federationExample output
NAME HOST/PORT PATH SERVICES PORT TERMINATION spire-server-federation federation.apps.cluster1.example.com spire-server 8443 passthroughFetch the trust bundle from each remote cluster’s federation endpoint:
$ curl -k https://federation.apps.cluster2.example.com > cluster2-bundle.jsonNoteFor
https_spiffeprofile, you might need to use the-kflag if the certificate is not trusted by your system’s CA bundle:The response contains the trust bundle in JSON Web Key Set (JWKS) format:
Example trust bundle
{ "keys": [ { "use": "x509-svid", "kty": "RSA", "n": "...", "e": "AQAB", "x5c": ["..."] } ], "spiffe_sequence": 1, "refresh_hint": 300 }Create
ClusterFederatedTrustDomainresources for each remote trust domain.On Cluster 1, create a resource to federate with Cluster 2:
apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterFederatedTrustDomain metadata: name: federation-to-cluster2 spec: trustDomain: <CLUSTER2_APPS_DOMAIN> bundleEndpointURL: https://federation.<CLUSTER2_APPS_DOMAIN> bundleEndpointProfile: type: https_spiffe endpointSPIFFEID: spiffe://<CLUSTER2_APPS_DOMAIN>/spire/server className: zero-trust-workload-identity-manager-spire trustDomainBundle: | { "keys": [ { "use": "x509-svid", "kty": "RSA", "n": "...", "e": "AQAB", "x5c": ["..."] } ], "spiffe_sequence": 1 }where
<CLUSTER2_APPS_DOMAIN>- Specifies the trust domain of the external cluster you are federating with.
spec.bundleEndpointProfile.endpointSPIFFEID-
Specifies the SPIFFE ID of the remote SPIRE server. Required for
https_spiffeprofile to validate the remote server’s identity. spec.trustDomainBundle- Specifies the complete trust bundle JSON that you fetched in the previous step.
spec.className-
Specifies the name of a class to watch CRs for. Spire-controller-manager watches the resource only if
spec.classNameis set tozero-trust-workload-identity-manager-spire.
Apply the
ClusterFederatedTrustDomainresource by running the following command:$ oc apply -f clusterfederatedtrustdomain.yaml-
Repeat steps 5-7 on each cluster for every remote cluster it should federate with. For bidirectional federation, each cluster needs a
ClusterFederatedTrustDomainresource for every other cluster. Update the
SpireServerresource on each cluster to add thefederatesWithconfiguration:apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_spiffe refreshHint: 300 federatesWith: - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_spiffe endpointSpiffeId: spiffe://cluster2.example.com/spire/server - trustDomain: cluster3.example.com bundleEndpointUrl: https://federation.apps.cluster3.example.com bundleEndpointProfile: https_spiffe endpointSpiffeId: spiffe://cluster3.example.com/spire/server managedRoute: "true"-
The
spec.federation.federatesWithfield lists all remote trust domains this cluster should federate with.
-
The
Apply the updated configuration by running the following command:
$ oc apply -f spireserver.yaml
Verification
Verify that the
ClusterFederatedTrustDomainresources have been created by running the following command:$ oc get clusterfederatedtrustdomainsExample output
NAME TRUST DOMAIN ENDPOINT URL AGE cluster2-federation cluster2.example.com https://federation.apps.cluster2.example.com 5m cluster3-federation cluster3.example.com https://federation.apps.cluster3.example.com 5mCheck the status of a
ClusterFederatedTrustDomainto ensure bundle synchronization is working by running the following command:$ oc describe clusterfederatedtrustdomain cluster2-federationLook for successful status conditions indicating that the trust bundle has been synchronized.
Verify that the federation endpoint is accessible by running the following command:
$ curl https://federation.apps.cluster1.example.comYou should receive a JSON response containing the trust bundle.
Check the SPIRE Server logs to confirm federation is active by running the following command:
$ oc logs -n zero-trust-workload-identity-manager \ statefulset/spire-server -c spire-server --tail=50Look for log messages indicating successful bundle synchronization with federated trust domains.
12.8.4. Using SPIRE federation with Automatic Certificate Management Environment protocol Copy linkLink copied to clipboard!
Using SPIRE federation with Automatic Certificate Management Environment (ACME) protocol provides automatic certificate provisioning from Let’s Encrypt. ACME also enables automatic certificate renewal before expiration, eliminating manual certificate management overhead.
Prerequisites
- You have installed the Zero Trust Workload Identity Manager on all clusters that will participate in the federation.
-
You have installed the OpenShift CLI (
oc). -
You have
cluster-adminprivileges on all participating clusters. - Your federation endpoints must be publicly accessible for Let’s Encrypt HTTP-01 challenge validation.
- You have network connectivity between all federated clusters.
Procedure
Configure the
SpireServercustom resource on each cluster to enable federation with ACME certificate management.Create or update your
SpireServerresource with the federation configuration:apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_web refreshHint: 300 httpsWeb: acme: directoryUrl: https://acme-v02.api.letsencrypt.org/directory domainName: federation.apps.cluster1.example.com email: admin@example.com tosAccepted: "true" managedRoute: "true"-
The
spec.trustDomainfield sets a unique trust domain for each cluster (for example,cluster1.example.com,cluster2.example.com). -
The
spec.federation.bundleEndpoint.profilefield uses thehttps_webprofile for ACME-based certificate management. -
The
spec.federation.bundleEndpoint.httpsWeb.acme.directoryUrlfield contains the Let’s Encrypt production directory URL. For testing, use:https://acme-staging-v02.api.letsencrypt.org/directory. -
The
spec.federation.bundleEndpoint.httpsWeb.acme.domainNamefield is the domain name where your federation endpoint is accessible. This automatically sets tofederation.<cluster-apps-domain>ifmanagedRouteis set to "true". -
The
spec.federation.bundleEndpoint.httpsWeb.acme.emailfield is your email address for ACME account registration and certificate expiration notifications. -
The
spec.federation.bundleEndpoint.httpsWeb.acme.tosAcceptedfield accepts the Let’s Encrypt Terms of Service. -
The
spec.federation.managedRoutefield enables an automatic route creation by the operator for the federation bundle endpoint.
-
The
Apply the configuration to each cluster by running the following command:
$ oc apply -f spireserver.yamlCheck the status of the SPIRE Server by entering the following command. Wait for the
Readystatus to be returned before proceeding to the next step.$ oc get spireserver cluster -wExample output
NAME STATUS AGE cluster Ready 5mVerify that the federation route has been created by running the following command:
$ oc get route -n zero-trust-workload-identity-manager | grep federationExample output
NAME HOST/PORT PATH SERVICES PORT TERMINATION spire-server-federation federation.apps.cluster1.example.com spire-server 8443 passthroughOn each cluster, fetch the trust bundle from the federation endpoint by running the following command:
$ curl https://federation.apps.cluster1.example.com > cluster1-bundle.jsonThe response contains the trust bundle in JSON Web Key Set (JWKS) format:
Example trust bundle
{ "keys": [ { "use": "x509-svid", "kty": "RSA", "n": "...", "e": "AQAB", "x5c": ["..."] } ], "spiffe_sequence": 1, "refresh_hint": 300 }Create
ClusterFederatedTrustDomainresources to establish federation relationships.On Cluster 1, create resources to federate with Cluster 2 and Cluster 3:
apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterFederatedTrustDomain metadata: name: cluster2-federation spec: trustDomain: cluster2.example.com bundleEndpointURL: https://federation.apps.cluster2.example.com bundleEndpointProfile: type: https_web className: zero-trust-workload-identity-manager-spire trustDomainBundle: | { "keys": [...], "spiffe_sequence": 1 } --- apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterFederatedTrustDomain metadata: name: cluster3-federation spec: trustDomain: cluster3.example.com bundleEndpointURL: https://federation.apps.cluster3.example.com bundleEndpointProfile: type: https_web className: zero-trust-workload-identity-manager-spire trustDomainBundle: | { "keys": [...], "spiffe_sequence": 1 }-
The
spec.trustDomainBundlefield contains the complete trust bundle JSON that you fetched usingcurlin step 5. -
The
spec.classNamefield contains the name of a class to watch CRs for. Spire-controller-manager watches the resource only ifspec.classNameis set tozero-trust-workload-identity-manager-spire.
-
The
Apply the
ClusterFederatedTrustDomainresources by running the following command:$ oc apply -f cluster-federated-trust-domains.yaml-
Repeat steps 6 and 7 on each cluster to establish bidirectional federation. Each cluster needs
ClusterFederatedTrustDomainresources for every other cluster it federates with. Update the
SpireServerresource on each cluster to add thefederatesWithconfiguration:apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: # ... existing configuration ... federation: bundleEndpoint: # ... existing bundleEndpoint configuration ... federatesWith: - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_web - trustDomain: cluster3.example.com bundleEndpointUrl: https://federation.apps.cluster3.example.com bundleEndpointProfile: https_web managedRoute: "true"-
The
spec.federation.federatesWithfield lists all remote trust domains this cluster should federate with.
-
The
Apply the updated configuration by running the following command:
$ oc apply -f spireserver.yaml
Verification
Verify that the
ClusterFederatedTrustDomainresources have been created by running the following command:$ oc get clusterfederatedtrustdomainsExample output
NAME TRUST DOMAIN ENDPOINT URL AGE cluster2-federation cluster2.example.com https://federation.apps.cluster2.example.com 5m cluster3-federation cluster3.example.com https://federation.apps.cluster3.example.com 5mCheck the status of a
ClusterFederatedTrustDomainto ensure bundle synchronization is working by running the following command:$ oc describe clusterfederatedtrustdomain cluster2-federationLook for
Successfulstatus conditions indicating that the trust bundle has been synchronized.Verify that the federation endpoint is accessible and serving the trust bundle by running the following command:
$ curl https://federation.apps.cluster1.example.comYou should receive a JSON response containing the trust bundle.
Check the SPIRE Server logs to confirm federation is active by running the following command:
$ oc logs -n zero-trust-workload-identity-manager deployment/spire-server -c spire-server --tail=50Look for log messages indicating successful bundle synchronization with federated trust domains.
Verify that all SPIRE components are running by running the following command:
$ oc get pods -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-agent-abcde 1/1 Running 0 10m spire-server-0 2/2 Running 0 10m- Optional: Test cross-cluster workload authentication by deploying workloads with SPIFFE identities on different clusters and verifying they can authenticate to each other using the federated trust.
12.8.5. Using SPIRE federation with manual certificate management Copy linkLink copied to clipboard!
You can use SPIRE federation with custom certificate management using cert-manager or other certificate providers. This approach provides flexibility for organizations that require control over certificate issuance, support for internal certificate authorities (CAs), or integration with existing certificate management infrastructure.
Prerequisites
- You have installed the Zero Trust Workload Identity Manager on all clusters that will participate in the federation.
-
You have installed the OpenShift CLI (
oc). -
You have
cluster-adminprivileges on all participating clusters. - You have installed the cert-manager Operator for Red Hat OpenShift. For more information, see cert-manager Operator for Red Hat OpenShift.
- Your federation endpoints must be publicly accessible for certificate validation.
- You have network connectivity between all federated clusters.
Procedure
Install the cert-manager Operator on the cluster where you want to use externally managed certificates.
Create a namespace and install the operator:
apiVersion: v1 kind: Namespace metadata: name: cert-manager-operator --- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: openshift-cert-manager-operator namespace: cert-manager-operator spec: upgradeStrategy: Default --- apiVersion: operators.coreos.com/stable-v1 kind: Subscription metadata: name: openshift-cert-manager-operator namespace: cert-manager-operator spec: source: redhat-operators sourceNamespace: openshift-marketplace name: openshift-cert-manager-operator channel: stable-v1Apply the cert-manager installation by running the following command:
$ oc apply -f cert-manager-install.yamlCheck the status of the cert-manager Operator by entering the following command:
$ oc get pods -n cert-managerAll cert-manager pods should be in
Runningstatus.Create an Issuer for certificate provisioning.
For Let’s Encrypt with HTTP-01 challenge:
apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: letsencrypt-http01 namespace: zero-trust-workload-identity-manager spec: acme: server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-account-key solvers: - http01: ingress: ingressClassName: openshift-defaultAlternatively, for an internal CA:
apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: internal-ca namespace: zero-trust-workload-identity-manager spec: ca: secretName: internal-ca-key-pairApply the Issuer by running the following command:
$ oc apply -f issuer.yamlDetermine the federation endpoint domain name.
The federation route follows a predictable naming pattern if
managedRouteis set totrue. Get your cluster’s application domain by running the following command:$ CLUSTER_DOMAIN=$(oc get ingresses.config/cluster -o jsonpath='{.spec.domain}') $ FEDERATION_DOMAIN="federation.${CLUSTER_DOMAIN}" $ echo "Federation domain will be: $FEDERATION_DOMAIN"Example output
Federation domain will be: federation.apps.cluster1.example.comNoteThe federation route is created automatically if
managedRouteis set totruewhen you apply theSpireServerconfiguration in a later step. The route name isspire-server-federationand the hostname isfederation.<cluster-apps-domain>.Create a Certificate resource to request a TLS certificate.
Use the federation domain determined in the previous step:
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: spire-server-federation-tls namespace: zero-trust-workload-identity-manager spec: secretName: spire-server-federation-tls duration: 2160h renewBefore: 360h commonName: federation.apps.cluster1.example.com dnsNames: - federation.apps.cluster1.example.com usages: - server auth - digital signature - key encipherment issuerRef: kind: Issuer name: letsencrypt-http01-
The
secretNamefield must match theexternalSecretRefvalue in SpireServer. -
The
durationfield shows how long a certificate is valid. Certificates are valid for 90 days. -
The
renewBeforefield shows how many days a certificate must be renewed before it expires. Renew a certificate 15 days before expiration. -
The
commonNamefield must be replaced with your actual federation domain from the previous step. -
The
dnsNamesfield must match thecommonNameand the actual routehostnamethat was created. -
The
namefield must reference the Issuer that was created earlier.
-
The
Apply the Certificate resource by running the following command:
$ oc apply -f certificate.yamlMonitor the certificate issuance by running the following command:
$ oc get certificate spire-server-federation-tls \ -n zero-trust-workload-identity-manager -wExample output when ready
NAME READY SECRET AGE spire-server-federation-tls True spire-server-federation-tls 2mCreate RBAC permissions for the OpenShift Ingress Router to access the certificate secret.
Create a Role by running the following command:
$ oc create role secret-reader \ --verb=get,list,watch \ --resource=secrets \ --resource-name=spire-server-federation-tls \ -n zero-trust-workload-identity-managerCreate a
RoleBindingby running the following command:$ oc create rolebinding secret-reader-binding \ --role=secret-reader \ --serviceaccount=openshift-ingress:router \ -n zero-trust-workload-identity-managerConfigure the
SpireServercustom resource to use manual certificate management.Now that the certificate is ready, configure the SpireServer to reference it:
apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster1.example.com federation: bundleEndpoint: profile: https_web refreshHint: 300 httpsWeb: servingCert: fileSyncInterval: 86400 externalSecretRef: spire-server-federation-tls managedRoute: "true"-
The
profilefield must usehttps_webprofile for certificate-based authentication. -
The
fileSyncIntervalfield checks for certificate updates every 24 hours (86400 seconds). Range: 3600-7776000 seconds. -
The
externalSecretReffield is the name of the secret containing the TLS certificate and private key. Must match the certificate secret created in the previous steps.
-
The
Apply the configuration by running the following command:
$ oc apply -f spireserver.yamlWait for the SPIRE Server to be ready:
$ oc get spireserver cluster -n zero-trust-workload-identity-manager -wWait until the status shows
Ready.Verify that the federation route was created by running the following command:
$ oc get route spire-server-federation -n zero-trust-workload-identity-managerExample output
NAME HOST/PORT PATH SERVICES PORT TERMINATION spire-server-federation federation.apps.cluster1.example.com spire-server 8443 reencryptVerify that the route hostname matches the domain name used in your certificate.
Verify that the federation endpoint is accessible by running the following command:
$ curl https://$(oc get route spire-server-federation \ -n zero-trust-workload-identity-manager \ -o jsonpath='{.spec.host}')You should receive a JSON response containing the trust bundle.
Fetch the trust bundle from each federation endpoint that you want to federate with.
For each remote cluster, fetch its trust bundle by running the following commands:
$ curl https://federation.apps.cluster1.example.com > cluster1-bundle.json $ curl https://federation.apps.cluster2.example.com > cluster2-bundle.jsonThe trust bundle is in JSON Web Key Set (JWKS) format:
Example trust bundle
{ "keys": [ { "use": "x509-svid", "kty": "RSA", "n": "xGOzB...", "e": "AQAB", "x5c": ["MIIC..."] } ], "spiffe_sequence": 1, "refresh_hint": 300 }Create
ClusterFederatedTrustDomainresources for each remote trust domain you want to federate with:apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterFederatedTrustDomain metadata: name: cluster1-federation spec: trustDomain: cluster1.example.com bundleEndpointURL: https://federation.apps.cluster1.example.com bundleEndpointProfile: type: https_web className: zero-trust-workload-identity-manager-spire trustDomainBundle: | { "keys": [ { "use": "x509-svid", "kty": "RSA", "n": "xGOzB...", "e": "AQAB", "x5c": ["MIIC..."] } ], "spiffe_sequence": 1 } --- apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterFederatedTrustDomain metadata: name: cluster2-federation spec: trustDomain: cluster2.example.com bundleEndpointURL: https://federation.apps.cluster2.example.com bundleEndpointProfile: type: https_web className: zero-trust-workload-identity-manager-spire trustDomainBundle: | { "keys": [...], "spiffe_sequence": 1 }-
The
trustDomainBundlefield contains the complete trust bundle JSON that you fetched in the previous step. -
The
spec.classNamefield contains the name of a class to watch CRs for. Spire-controller-manager watches the resource only ifspec.classNameis set tozero-trust-workload-identity-manager-spire.
-
The
Apply the
ClusterFederatedTrustDomainresources by running the following command:$ oc apply -f clusterfederatedtrustdomains.yamlUpdate the
SpireServerresource to add thefederatesWithconfiguration:apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: trustDomain: cluster3.example.com federation: bundleEndpoint: profile: https_web refreshHint: 300 httpsWeb: servingCert: fileSyncInterval: 86400 externalSecretRef: spire-server-federation-tls federatesWith: - trustDomain: cluster1.example.com bundleEndpointUrl: https://federation.apps.cluster1.example.com bundleEndpointProfile: https_web - trustDomain: cluster2.example.com bundleEndpointUrl: https://federation.apps.cluster2.example.com bundleEndpointProfile: https_web managedRoute: "true"-
The
federatesWithfield lists all remote trust domains this cluster should federate with.
-
The
Apply the updated configuration by running the following command:
$ oc apply -f spireserver.yamlRepeat steps 1-15 on each cluster that participates in the federation, ensuring that:
- Each cluster has cert-manager installed and configured
-
Each cluster has its own certificate created and ready before applying the
SpireServerconfiguration - Each cluster has the RBAC for the ingress router configured
-
Each cluster has
ClusterFederatedTrustDomainresources for every other cluster it federates with -
Each cluster’s
SpireServerhas the completefederatesWithlist
Verification
Verify that the certificate has been issued successfully by running the following command:
$ oc get certificate spire-server-federation-tls \ -n zero-trust-workload-identity-managerExample output
NAME READY SECRET AGE spire-server-federation-tls True spire-server-federation-tls 5mCheck the certificate details and expiration by running the following command:
$ oc get secret spire-server-federation-tls \ -n zero-trust-workload-identity-manager \ -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -datesExample output
notBefore=Dec 16 10:00:00 2025 GMT notAfter=Mar 16 10:00:00 2026 GMTVerify that the RBAC permissions are configured correctly by running the following command:
$ oc get role,rolebinding -n zero-trust-workload-identity-manager \ | grep secret-readerExample output
role.rbac.authorization.k8s.io/secret-reader rolebinding.rbac.authorization.k8s.io/secret-reader-bindingVerify the RoleBinding references the correct ServiceAccount by running the following command:
$ oc describe rolebinding secret-reader-binding \ -n zero-trust-workload-identity-managerExample output
Name: secret-reader-binding Namespace: zero-trust-workload-identity-manager Role: Kind: Role Name: secret-reader Subjects: Kind Name Namespace ---- ---- --------- ServiceAccount router openshift-ingressVerify that the
ClusterFederatedTrustDomainresources have been created by running the following command:$ oc get clusterfederatedtrustdomainsExample output
NAME TRUST DOMAIN ENDPOINT URL AGE cluster1-federation cluster1.example.com https://federation.apps.cluster1.example.com 5m cluster2-federation cluster2.example.com https://federation.apps.cluster2.example.com 5mCheck the status of a
ClusterFederatedTrustDomainto ensure bundle synchronization is working by running the following command:$ oc describe clusterfederatedtrustdomain cluster1-federationLook for successful status conditions indicating that the trust bundle has been synchronized.
Verify that the federation endpoint is accessible and using the correct certificate by running the following command:
$ curl -v https://$(oc get route spire-server-federation \ -n zero-trust-workload-identity-manager \ -o jsonpath='{.spec.host}')In the output, verify that the certificate presented is issued by your configured CA (Let’s Encrypt or internal CA).
Check the SPIRE Server logs to confirm that by running the following command:
- Federation is active with remote trust domains
- Trust bundles are being synchronized
The bundle endpoint is serving correctly
$ oc logs -n zero-trust-workload-identity-manager \ statefulset/spire-server -c spire-server --tail=100Look for log messages indicating successful federation bundle synchronization.
Verify that all SPIRE components are running by running the following command:
$ oc get pods -n zero-trust-workload-identity-managerExample output
NAME READY STATUS RESTARTS AGE spire-agent-abc123 1/1 Running 0 10m spire-server-0 2/2 Running 0 10m- Optional: Test cross-cluster workload authentication by deploying workloads with SPIFFE identities on different clusters and verifying they can authenticate to each other using the federated trust.
12.8.6. Federation configuration field reference Copy linkLink copied to clipboard!
This reference provides detailed information about all configuration fields available for SPIRE federation in the SpireServer custom resource. Use this reference when customizing your federation setup.
- Top-level federation fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| object | Yes | N/A | Configuration for this cluster’s federation endpoint that exposes the trust bundle to remote clusters. |
|
| array | No |
| List of remote trust domains to federate with. |
|
| string | No |
|
Enable or disable automatic OpenShift Route creation. Set to |
- bundleEndpoint configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| string (enum) | Yes |
|
Authentication profile for the bundle endpoint. Valid values: |
|
| integer | No |
| Suggested interval (in seconds) for remote servers to refresh the trust bundle. Valid range: 60-3600. |
|
| object | Conditional | N/A |
Required when |
- httpsWeb configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| object | Conditional | N/A |
ACME configuration for automatic certificate management. Mutually exclusive with |
|
| object | Conditional | N/A |
Manual certificate configuration. Mutually exclusive with |
- ACME configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| string | Yes | N/A |
ACME directory URL. For Let’s Encrypt production: |
|
| string | Yes | N/A | Fully qualified domain name for the certificate. Typically the federation endpoint hostname. |
|
| string | Yes | N/A | Email address for ACME account registration and certificate expiration notifications. |
|
| string | No |
|
Accept the ACME provider’s Terms of Service. Must be |
- servingCert configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| integer | No |
| Interval (in seconds) to check for certificate updates. Valid range: 3600-7776000 (1 hour to 90 days). |
|
| string | Yes | N/A |
Name of the Kubernetes Secret containing the TLS certificate ( |
- federatesWith configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
| string | Yes | N/A |
Trust domain name of the remote SPIRE deployment (for example, |
|
| string | Yes | N/A |
HTTPS URL of the remote federation endpoint (for example, |
|
| string (enum) | Yes | N/A |
Authentication profile of the remote endpoint. Valid values: |
|
| string | Conditional | N/A |
SPIFFE ID of the remote SPIRE server (for example, |
- Field validation rules
The following validation rules are enforced by the operator:
-
Profile immutability: The
bundleEndpoint.profilefield cannot be changed after initial configuration. Changing it requires deleting and recreating theSpireServerresource (re-installation of the system). -
Mutual exclusivity: Within
httpsWeb, only one ofacmeorservingCertcan be specified. -
Conditional requirements: When
profileishttps_web, thehttpsWebobject must be present with eitheracmeorservingCertconfigured. -
SPIFFE ID requirement: When
bundleEndpointProfileishttps_spiffein thefederatesWithlist, theendpointSpiffeIdfield is required. -
Array limits: The
federatesWitharray supports a maximum of 50 entries. Numeric ranges:
-
refreshHint: 60-3600 seconds -
fileSyncInterval: 3600-7776000 seconds
-
-
Profile immutability: The
12.9. Using the SPIFFE Helper container image Copy linkLink copied to clipboard!
SPIFFE Helper writes Transport Layer Security (TLS) certificates to disk for applications that cannot use the SPIFFE Workload API. Use it to eliminate manual certificate management and reduce the risk of expired certificates.
12.9.1. SPIFFE Helper container image Copy linkLink copied to clipboard!
Use SPIFFE Helper with the Zero Trust Workload Identity Manager when your application cannot call the workload API directly but can read TLS certificates from a shared volume.
SPIFFE Helper is a utility that connects to the SPIFFE Workload API, fetches identity credentials, writes them to files on disk, and optionally notifies a workload when X.509 material is renewed.
The Zero Trust Workload Identity Manager provides a supported SPIFFE Helper container image based on the upstream SPIFFE Helper. The configuration file format, command-line flags, and workload API behavior are compatible with upstream.
SPIFFE Helper performs the following tasks:
- Connects to the SPIFFE Workload API, usually through the SPIFFE Runtime Environment Agent socket exposed by the SPIFFE CSI driver.
- Fetches X.509 SPIFFE Verifiable Identity Document (SVID)s, JSON Web Token (JWT) SVIDs, and JWT bundles from SPIFFE Runtime Environment.
-
Writes credentials to files under a configured directory such as
cert_dir. - Optionally notifies a workload when X.509 authentication credentials are renewed in daemon mode.
12.9.2. SPIFFE Helper modes Copy linkLink copied to clipboard!
SPIFFE Helper fetches credentials from the SPIFFE Workload API and writes them to disk for workloads that cannot call the API directly. On OpenShift Container Platform, a pod typically uses non-daemon mode in an init container for initial certificates and daemon mode in a sidecar to rotate them before expiring.
SPIFFE Helper runs in one of two modes:
-
Non-daemon mode (
-daemon-mode=falseordaemon_mode = false): Fetches credentials once, writes files, and exits. Use this mode in an init container to bootstrap TLS before the main application starts. In this mode,cmdandrenew_signalare ignored. -
Daemon mode (default): Stays running until the pod is terminated or an unrecoverable error occurs. Watches the Workload API and renews credentials before they expire. Supports
cmd,pid_file_name,renew_signal, and optional HTTP health checks. SPIFFE Helper does not background itself like a traditional Unix daemon.
On OpenShift Container Platform, a typical pod uses both modes:
-
An init container runs SPIFFE Helper in non-daemon mode to populate a shared
emptyDirvolume with initial TLS material. - A sidecar container runs SPIFFE Helper in daemon mode to rotate certificates before they expire.
- The main application container mounts the shared certificate directory and uses the files for TLS.
The SPIFFE workload API is exposed through the SPIFFE container storage interface (CSI), which mounts the Workload API socket into the pod at a path such as /spiffe-workload-api/spire-agent.sock.
The following deployment excerpt shows the init container and sidecar pattern on OpenShift Container Platform:
initContainers:
- name: spiffe-helper-init
image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>
args:
- '-config'
- /etc/spiffe-helper/helper.conf
- '-daemon-mode=false'
volumeMounts:
- name: spiffe-workload-api
readOnly: true
mountPath: /spiffe-workload-api
- name: postgresql-certs
mountPath: /opt/postgresql-certs
- name: spiffe-helper
mountPath: /etc/spiffe-helper
containers:
- name: spiffe-helper
image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>
args:
- '-config'
- /etc/spiffe-helper/helper.conf
volumeMounts:
- name: spiffe-workload-api
readOnly: true
mountPath: /spiffe-workload-api
- name: postgresql-certs
mountPath: /opt/postgresql-certs
- name: spiffe-helper
mountPath: /etc/spiffe-helper
volumes:
- name: spiffe-workload-api
csi:
driver: csi.spiffe.io
readOnly: true
- name: postgresql-certs
emptyDir:
medium: Memory
- name: spiffe-helper
configMap:
name: spiffe-helper
Replace <version> with the tag that matches your Zero Trust Workload Identity Manager installation.
12.9.3. SPIFFE Helper credential types Copy linkLink copied to clipboard!
SPIFFE Helper supports X.509 SPIFFE Verifiable Identity Document (SVID), JSON Web Token (JWT) bundle, and JWT SVID outputs configured in the SPIFFE Helper configuration file and written under cert_dir for workloads that cannot use the workload API directly.
At least one complete set must be specified:
-
X.509 SVID — Requires
svid_file_name,svid_key_file_name, andsvid_bundle_file_name. SPIFFE Helper writes Privacy-Enhanced Mail (PEM) certificate, PEM private key, and PEM trust bundle files undercert_dir. -
JWT bundle — Requires
jwt_bundle_file_name. SPIFFE Helper writes a JSON bundle file. -
JWT SVIDs — Requires one or more
jwt_svidsblocks withjwt_audience,jwt_svid_file_name, and related settings. SPIFFE Helper writes base64-encoded token files.
The cert_dir directory must exist before SPIFFE Helper starts.
The following helper.conf excerpt configures X.509 SVID output for a PostgreSQL server:
agent_address = "/spiffe-workload-api/spire-agent.sock"
cert_dir = "/opt/postgresql-certs"
svid_file_name = "svid.pem"
svid_key_file_name = "svid.key"
svid_bundle_file_name = "svid_bundle.pem"
The following helper.conf excerpt configures JWT SVID output:
agent_address = "/spiffe-workload-api/spire-agent.sock"
cert_dir = "/opt/jwt-certs"
jwt_svids = [{
jwt_audience = "your-audience"
jwt_svid_file_name = "jwt_svid.token"
}]
For JWT bundle output, set jwt_bundle_file_name instead of the X.509 or JWT SVID file names.
12.9.4. SPIFFE Helper architecture Copy linkLink copied to clipboard!
The SPIFFE Helper working pattern demonstrates how to apply the same certificate and identity flow to your own workloads.
The SPIFFE Helper architecture deploys PostgreSQL with mTLS across two namespaces, with SPIFFE Helper provisioning and renewing certificates, ClusterSPIFFEID defining identities, and the SPIFFE CSI driver connecting to the SPIFFE Workload API. Understanding this pattern helps you apply the same certificate and identity flow to your own workloads.
-
postgresql-spiffe— PostgreSQL server. SPIFFE Helper runs as an init container (non-daemon mode) to write initial certificates to a shared volume, then as a sidecar (daemon mode) to renew them. PostgreSQL reads TLS files from that volume andpg_hba.confrequires certificate authentication for remote connections. -
postgresql-spiffe-client— Client pod with the stock SPIFFE Helper sidecar. The client uses SPIFFE-issued certificates from the same shared volume pattern when connecting withpsql.
Each namespace defines a ClusterSPIFFEID so SPIFFE Runtime Environment issues X.509 SPIFFE Verifiable Identity Document (SVID)s with the correct DNS names: the server identity includes postgresql-spiffe.postgresql-spiffe.svc; the client identity includes postgresql_spiffe, which must match the PostgreSQL user created during database initialization.
SPIFFE Helper connects to the SPIFFE Workload API through the SPIFFE CSI driver, which mounts the agent socket at /spiffe-workload-api/spire-agent.sock.
12.9.5. Deploying PostgreSQL with SPIFFE Helper Copy linkLink copied to clipboard!
Deploy a PostgreSQL server and client that use SPIFFE Helper to fetch X.509 SPIFFE Verifiable Identity Document (SVID) from the SPIFFE Workload API and write them to disk for mutual Transport Layer Security (mTLS).
The manifests in this procedure use the Zero Trust Workload Identity Manager SPIFFE Helper image.
When certificates renew, the server must reload PostgreSQL TLS configuration. Containers in the same pod use separate process namespaces, so the SPIFFE Helper sidecar cannot signal the PostgreSQL container directly. Instead, SPIFFE Helper uses the managed-child pattern. This pattern in daemon mode it runs cmd (/usr/bin/psql) with cmd_args to execute SELECT pg_reload_conf(); as a child process inside the helper container whenever X.509 material is updated.
The stock SPIFFE Helper image does not include the psql client required for that reload command. Build a custom image that extends the Zero Trust Workload Identity Manager image and adds the PostgreSQL client package.
Prerequisites
-
You have access to the cluster with
cluster-adminprivileges. - You have installed the Zero Trust Workload Identity Manager.
-
The
ZeroTrustWorkloadIdentityManagercustom resource reportsReady.
Procedure
Create a
Containerfilewith the following contents:FROM registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version> USER 0 RUN dnf install -y postgresql && \ dnf clean all USER 65534Replace
<version>with the tag that matches your Zero Trust Workload Identity Manager installation.Build and push a custom SPIFFE Helper image for the PostgreSQL server by running the following command.
$ podman build -f Containerfile \ -t <registry>/spiffe-helper-postgresql:latest .$ podman push <registry>/spiffe-helper-postgresql:latestReplace
<registry>with your container registry.Create the PostgreSQL server resources by using the following example:
$ oc apply -f - << 'EOF' --- kind: Namespace apiVersion: v1 metadata: name: postgresql-spiffe --- kind: ServiceAccount apiVersion: v1 metadata: name: postgresql-spiffe namespace: postgresql-spiffe --- apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterSPIFFEID metadata: name: postgresql-spiffe spec: className: zero-trust-workload-identity-manager-spire dnsNameTemplates: - postgresql-spiffe.postgresql-spiffe.svc namespaceSelector: matchLabels: kubernetes.io/metadata.name: postgresql-spiffe podSelector: matchLabels: app: postgresql-spiffe spiffeIDTemplate: 'spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}' --- kind: ConfigMap apiVersion: v1 metadata: name: postgres-config namespace: postgresql-spiffe data: pg_hba.conf: | # TYPE DATABASE USER ADDRESS METHOD local all all trust host postgres postgres 127.0.0.1/32 trust hostnossl postgres postgres 127.0.0.1/32 trust hostnossl all all 0.0.0.0/0 reject hostssl all all 0.0.0.0/0 cert postgresql.conf: | listen_addresses '*' ssl = on ssl_cert_file = '/opt/postgresql-certs/svid.pem' ssl_key_file = '/opt/postgresql-certs/svid.key' ssl_ca_file = '/opt/postgresql-certs/svid_bundle.pem' --- kind: ConfigMap apiVersion: v1 metadata: name: postgresql-init-db namespace: postgresql-spiffe data: initdb.sh: | # Copy configuration files cp /opt/pg_hba/pg_hba.conf /var/lib/pgsql/data/userdata # Create postgresql resources psql -U postgres <<!!EOF CREATE DATABASE $SPIFFE_DATABASE; CREATE USER $SPIFFE_USER WITH encrypted password '$SPIFFE_PASSWORD'; GRANT ALL privileges ON database $SPIFFE_DATABASE TO $SPIFFE_USER; \c $SPIFFE_DATABASE; CREATE TABLE test_table ( id bigserial primary key, name VARCHAR(255) NOT NULL, text VARCHAR(255) NOT NULL ); GRANT ALL privileges ON table test_table TO $SPIFFE_USER; GRANT ALL privileges ON sequence test_table_id_seq TO $SPIFFE_USER; !!EOF --- kind: ConfigMap apiVersion: v1 metadata: name: spiffe-helper namespace: postgresql-spiffe data: helper.conf: | agent_address = "/spiffe-workload-api/spire-agent.sock" cmd = "/usr/bin/psql" cmd_args = "-U postgres -h 127.0.0.1 -c \"SELECT pg_reload_conf();\"" cert_dir = "/opt/postgresql-certs" renew_signal = "" svid_file_name = "svid.pem" svid_key_file_name = "svid.key" svid_bundle_file_name = "svid_bundle.pem" --- kind: Service apiVersion: v1 metadata: name: postgresql-spiffe namespace: postgresql-spiffe spec: ports: - protocol: TCP port: 5432 targetPort: 5432 type: ClusterIP selector: app: postgresql-spiffe --- kind: Deployment apiVersion: apps/v1 metadata: name: postgresql-spiffe namespace: postgresql-spiffe labels: app: postgresql-spiffe spec: replicas: 1 selector: matchLabels: app: postgresql-spiffe template: metadata: labels: app: postgresql-spiffe spec: serviceAccountName: postgresql-spiffe securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault initContainers: - name: spiffe-helper-init image: <registry>/spiffe-helper-postgresql:latest args: - '-config' - /etc/spiffe-helper/helper.conf - '-daemon-mode=false' volumeMounts: - name: spiffe-workload-api readOnly: true mountPath: /spiffe-workload-api - name: postgresql-certs mountPath: /opt/postgresql-certs - name: spiffe-helper mountPath: /etc/spiffe-helper securityContext: capabilities: drop: - ALL containers: - name: postgresql-spiffe image: registry.redhat.io/rhel9/postgresql-16:latest env: - name: POSTGRESQL_USER value: user - name: POSTGRESQL_PASSWORD value: wearenotusingthissoIdontworryaboutit - name: SPIFFE_USER value: postgresql_spiffe - name: SPIFFE_PASSWORD value: somealsonotusedpassword - name: SPIFFE_DATABASE value: testdb - name: POSTGRESQL_DATABASE value: sampledb ports: - name: postgresql containerPort: 5432 protocol: TCP volumeMounts: - name: postgresql-certs mountPath: /opt/postgresql-certs - name: pg-hba mountPath: /opt/pg_hba - name: postgresql-init-db mountPath: /opt/app-root/src/postgresql-init - name: postgres-config mountPath: /opt/app-root/src/postgresql-cfg securityContext: capabilities: drop: - ALL - name: spiffe-helper image: <registry>/spiffe-helper-postgresql:latest args: - '-config' - /etc/spiffe-helper/helper.conf volumeMounts: - name: spiffe-workload-api readOnly: true mountPath: /spiffe-workload-api - name: postgresql-certs mountPath: /opt/postgresql-certs - name: spiffe-helper mountPath: /etc/spiffe-helper securityContext: capabilities: drop: - ALL volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true - name: postgresql-certs emptyDir: medium: Memory - name: spiffe-helper configMap: name: spiffe-helper - name: postgresql-init-db configMap: name: postgresql-init-db - name: pg-hba configMap: name: postgres-config items: - key: pg_hba.conf path: pg_hba.conf - name: postgres-config configMap: name: postgres-config items: - key: postgresql.conf path: postgresql.conf EOFReplace both
<registry>occurrences in the deployment with your container registry.Confirm that the SPIFFE Helper configuration matches the PostgreSQL TLS settings by running the following command:
$ oc get configmap spiffe-helper -n postgresql-spiffe \ -o jsonpath='{.data.helper\.conf}{"\n"}'The
cert_dirandsvid_*file names must match thessl_*_filepaths inpostgresql.conf. The serverhelper.confmust include settings similar to the following example:agent_address = "/spiffe-workload-api/spire-agent.sock" cmd = "/usr/bin/psql" cmd_args = "-U postgres -h 127.0.0.1 -c \"SELECT pg_reload_conf();\"" cert_dir = "/opt/postgresql-certs" renew_signal = "" svid_file_name = "svid.pem" svid_key_file_name = "svid.key" svid_bundle_file_name = "svid_bundle.pem"Create the PostgreSQL client by using the following example. The client uses the stock SPIFFE Helper image from the Zero Trust Workload Identity Manager. Only the server deployment requires the custom image with the
psqlclient.$ oc apply -f - << 'EOF' --- kind: Namespace apiVersion: v1 metadata: name: postgresql-spiffe-client --- kind: ServiceAccount apiVersion: v1 metadata: name: postgresql-spiffe-client namespace: postgresql-spiffe-client --- kind: ConfigMap apiVersion: v1 metadata: name: spiffe-helper namespace: postgresql-spiffe-client data: helper.conf: | agent_address = "/spiffe-workload-api/spire-agent.sock" cmd = "" cmd_args = "" cert_dir = "/opt/postgresql-certs" renew_signal = "" svid_file_name = "svid.pem" svid_key_file_name = "svid.key" svid_bundle_file_name = "svid_bundle.pem" --- apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterSPIFFEID metadata: name: postgresql-spiffe-client spec: className: zero-trust-workload-identity-manager-spire dnsNameTemplates: - postgresql-spiffe namespaceSelector: matchLabels: kubernetes.io/metadata.name: postgresql-spiffe-client podSelector: matchLabels: app: postgresql-spiffe-client spiffeIDTemplate: 'spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}' --- kind: Deployment apiVersion: apps/v1 metadata: name: postgresql-spiffe-client namespace: postgresql-spiffe-client labels: app: postgresql-spiffe-client spec: replicas: 1 selector: matchLabels: app: postgresql-spiffe-client template: metadata: labels: app: postgresql-spiffe-client spec: serviceAccountName: postgresql-spiffe-client securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: postgresql-spiffe-client image: registry.redhat.io/rhel9/postgresql-16:latest command: - /bin/bash - '-c' - sleep infinity volumeMounts: - name: postgresql-certs mountPath: /opt/postgresql-certs securityContext: capabilities: drop: - ALL - name: spiffe-helper image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9: args: - '-config' - /etc/spiffe-helper/helper.conf volumeMounts: - name: spiffe-workload-api readOnly: true mountPath: /spiffe-workload-api - name: postgresql-certs mountPath: /opt/postgresql-certs - name: spiffe-helper mountPath: /etc/spiffe-helper securityContext: capabilities: drop: - ALL volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true - name: postgresql-certs emptyDir: medium: Memory - name: spiffe-helper configMap: name: spiffe-helper EOFReplace
<version>with the tag that matches your Zero Trust Workload Identity Manager installation.Confirm that pods are running in both namespaces by running the following commands:
$ oc get pods -n postgresql-spiffe$ oc get pods -n postgresql-spiffe-client
Verification
Inspect the server certificate by running the following command:
$ oc rsh -n postgresql-spiffe -c postgresql-spiffe \ deployment/postgresql-spiffe \ cat /opt/postgresql-certs/svid.pem | openssl x509 -noout -textThe server certificate should include the service hostname
postgresql-spiffe.postgresql-spiffe.svc.Inspect the client certificate by running the following command:
$ oc rsh -n postgresql-spiffe-client -c postgresql-spiffe-client \ deployment/postgresql-spiffe-client \ cat /opt/postgresql-certs/svid.pem | openssl x509 -noout -textThe certificate common name (CN) field should be
postgresql_spiffe.Connect to PostgreSQL from the client pod by running the following commands:
$ oc rsh -n postgresql-spiffe-client -c postgresql-spiffe-client \ deployment/postgresql-spiffe-client$ psql "host=postgresql-spiffe.postgresql-spiffe.svc port=5432 \ user=postgresql_spiffe dbname=testdb sslmode=verify-full \ sslcert=/opt/postgresql-certs/svid.pem \ sslkey=/opt/postgresql-certs/svid.key \ sslrootcert=/opt/postgresql-certs/svid_bundle.pem"If the deployment succeeded,
psqlconnects to the database.
12.9.6. SPIFFE Helper image reference Copy linkLink copied to clipboard!
Reference information for the SPIFFE Helper container image included with the Zero Trust Workload Identity Manager, including image location, command-line flags, configuration options, and volume requirements.
12.9.6.1. Image location Copy linkLink copied to clipboard!
The Zero Trust Workload Identity Manager provides a SPIFFE Helper image at the following location:
registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>
Replace <version> with the tag that matches your Zero Trust Workload Identity Manager installation. Some workloads require additional tools in the helper image.
12.9.6.2. Command-line flags Copy linkLink copied to clipboard!
| Flag | Description |
|---|---|
|
| Path to the SPIFFE Helper configuration file. Required. |
|
|
Controls operating mode. |
|
| Prints version information and exits. |
Examples of command-line flags
$ spiffe-helper -config /etc/spiffe-helper/helper.conf
$ spiffe-helper -config /etc/spiffe-helper/helper.conf -daemon-mode=false
12.9.6.3. Configuration file options Copy linkLink copied to clipboard!
SPIFFE Helper reads a configuration file. The following table describes the most common options for X.509 workloads on OpenShift Container Platform.
| Option | Description |
|---|---|
|
|
Path to the SPIFFE Runtime Environment Agent Workload API socket. On Linux, SPIFFE Helper connects with |
|
| Directory where SPIFFE Helper writes credential files. The directory must exist before SPIFFE Helper starts. |
|
|
When |
|
| File names for the X.509 SVID certificate, private key, and trust bundle. All three are required to enable X.509 output. |
|
| File name for a JWT bundle JSON file. |
|
| Block defining JWT SVID audiences and output file names. |
|
| Executable path for the managed-child pattern. On the first successful X.509 write, SPIFFE Helper starts this process. Ignored in non-daemon mode. |
|
|
Space-separated arguments for |
|
|
Path to a file containing one integer PID for the external-process pattern. Requires |
|
|
POSIX signal name sent on renewal, for example |
|
| Optional HTTP liveness and readiness endpoints. Available in daemon mode only. |
12.9.6.4. Volume and mount requirements Copy linkLink copied to clipboard!
When deploying SPIFFE Helper on OpenShift Container Platform, mount the following resources:
| Volume | Mount path | Access |
|---|---|---|
| SPIFFE Workload API (CSI) |
| Read-only |
|
SPIFFE Helper configuration ( |
Path passed to | Read-only |
|
Certificate directory ( |
Path matching | Read/write for the helper; read-only for the application container when possible |
CSI volume example
apiVersion: apps/v1
metadata:
name: postgresql-spiffe-client
namespace: postgresql-spiffe-client
labels:
app: postgresql-spiffe-client
# ...
volumes:
- name: spiffe-workload-api
csi:
driver: csi.spiffe.io
readOnly: true
Init and sidecar container example
apiVersion: apps/v1
metadata:
name: postgresql-spiffe
namespace: postgresql-spiffe
labels:
app: postgresql-spiffe
# ...
initContainers:
- name: spiffe-helper-init
image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>
args:
- '-config'
- /etc/spiffe-helper/helper.conf
- '-daemon-mode=false'
volumeMounts:
- name: spiffe-workload-api
readOnly: true
mountPath: /spiffe-workload-api
- name: postgresql-certs
mountPath: /opt/postgresql-certs
- name: spiffe-helper
mountPath: /etc/spiffe-helper
containers:
- name: spiffe-helper
image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>
args:
- '-config'
- /etc/spiffe-helper/helper.conf
volumeMounts:
- name: spiffe-workload-api
readOnly: true
mountPath: /spiffe-workload-api
- name: postgresql-certs
mountPath: /opt/postgresql-certs
- name: spiffe-helper
mountPath: /etc/spiffe-helper
SPIFFE Helper configuration example
agent_address = "/spiffe-workload-api/spire-agent.sock"
cert_dir = "/opt/postgresql-certs"
svid_file_name = "svid.pem"
svid_key_file_name = "svid.key"
svid_bundle_file_name = "svid_bundle.pem"
cmd = "/usr/bin/psql"
cmd_args = "-U postgres -h 127.0.0.1 -c \"SELECT pg_reload_conf();\""
renew_signal = ""
Replace <version> with the tag that matches your Zero Trust Workload Identity Manager installation.
12.9.6.5. Quick reference Copy linkLink copied to clipboard!
| Question | Answer |
|---|---|
|
What happens if both | SPIFFE Helper writes files, then runs managed-child logic, then PID-file logic. There is no priority between them. |
|
Is |
Yes. Validation fails if |
| Does SPIFFE Helper watch files for the application? | No. The application watches disk, polls, reads at connection time, or handles signals. |
| What happens in non-daemon mode? |
SPIFFE Helper fetches once, writes files, and exits. No watching, |
| What triggers workload notification? | Only X.509 updates in daemon mode. JWT-only refreshes write files only. |
| Can SPIFFE Helper signal another container in the same pod? |
Not by default. Containers use separate process namespaces unless |
12.9.7. Troubleshooting SPIFFE Helper deployments Copy linkLink copied to clipboard!
Resolve common issues when deploying the SPIFFE Helper image with Zero Trust Workload Identity Manager, and use the diagnostic oc commands to verify readiness, sidecars, and on-disk SVID certificates.
12.9.7.1. Common issues Copy linkLink copied to clipboard!
- Certificate output directory is empty
-
SPIFFE Helper cannot reach the Workload API, cannot write to
cert_dir, or the workload is not registered with SPIFFE Runtime Environment. Confirm theZeroTrustWorkloadIdentityManagercustom resource reportsReady, the CSI volume is mounted at the path inagent_address, and a matchingClusterSPIFFEIDexists for the pod namespace and labels. Verifycert_direxists before SPIFFE Helper starts; anemptyDirvolume satisfies this requirement. - Init container exits but TLS files are missing
-
The init container must run with
-daemon-mode=false. Check init container logs. If SPIFFE Helper reports configuration warnings about ignoredcmdorrenew_signal, that is expected in non-daemon mode. Ensure the certificate volume is shared with the main application container. - Sidecar runs but certificates are not renewed
-
Confirm the sidecar container does not set
-daemon-mode=false. Check sidecar logs for Workload API or write errors. If writes fail, SPIFFE Helper logs the error and does not runcmdor PID-file notification logic. - Application does not pick up renewed certificates
-
SPIFFE Helper overwrites files in
cert_dir; it does not notify your application unless you configurecmd,pid_file_name, or the application watches or polls the directory. For sidecar deployments with emptycmd, the application must reload TLS from disk. For PostgreSQL, configure the managed-child pattern withpsqlandpg_reload_conf()in the server sidecar. cmdorrenew_signalappears to have no effect-
In non-daemon mode, SPIFFE Helper ignores
cmdandrenew_signal. In daemon mode,cmdand PID-file logic run only after a successful X.509 write. JWT bundle or JWT SVID updates do not trigger them. Ifrenew_signalis empty whilecmdis set, the child starts on first write but later renewals send no signal. - Cannot signal the application container from the helper sidecar
-
Containers in the same pod use separate process namespaces by default.
pid_file_nameandcmdcannot signal another container unless you setshareProcessNamespace: trueor run the reload command as a child of SPIFFE Helper. The PostgreSQL example usescmdto runpsqllocally instead of signaling the database container. - Configuration validation fails for
pid_file_name -
renew_signalis required whenpid_file_nameis set.pid_file_nameis not valid in non-daemon mode. - Workload API socket is not accessible
-
Verify the CSI volume is mounted and the socket path matches
agent_address, typically/spiffe-workload-api/spire-agent.sock. Confirm the Zero Trust Workload Identity Manager operands are running and the pod service account matches theClusterSPIFFEIDselectors. - Permission denied when reading certificate files
-
Check file ownership and permissions on the shared volume. Consider setting
fsGroupin the podsecurityContextso the application container can read files written by SPIFFE Helper. Mount the certificate directory read-only in the application container when possible. - Image pull errors for the SPIFFE Helper image
-
Confirm your cluster can pull
registry.redhat.io/zero-trust-workload-identity-manager/spiffe-helper-rhel9:<version>. If you use a custom image, verify the registry credentials and image reference in the deployment manifest. - PostgreSQL mTLS connection fails after deployment
-
Confirm SPIFFE Helper wrote files to
/opt/postgresql-certs, the client certificate CN matches the PostgreSQL user, andpg_hba.confrequires certificate authentication. Compare server and client SVIDs withopenssl x509 -noout -text.
Use the following oc commands when investigating SPIFFE Helper deployments and workloads such as PostgreSQL.
12.9.7.2. Verify Zero Trust Workload Identity Manager readiness Copy linkLink copied to clipboard!
$ oc get ZeroTrustWorkloadIdentityManager cluster \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}{"\n"}'
$ oc get clusterspiffeid
12.9.7.3. Inspect SPIFFE Helper pods Copy linkLink copied to clipboard!
$ oc get pods -n postgresql-spiffe
$ oc get pods -n postgresql-spiffe-client
12.9.7.4. Review SPIFFE Helper logs Copy linkLink copied to clipboard!
$ oc logs -n postgresql-spiffe deployment/postgresql-spiffe -c spiffe-helper-init
$ oc logs -n postgresql-spiffe deployment/postgresql-spiffe -c spiffe-helper
$ oc logs -n postgresql-spiffe-client deployment/postgresql-spiffe-client -c spiffe-helper
12.9.7.5. Verify on-disk SVID certificates Copy linkLink copied to clipboard!
$ oc rsh -n postgresql-spiffe deployment/postgresql-spiffe -c postgresql-spiffe \
-- ls -la /opt/postgresql-certs
$ oc rsh -n postgresql-spiffe deployment/postgresql-spiffe -c postgresql-spiffe -- \
cat /opt/postgresql-certs/svid.pem | openssl x509 -noout -dates -subject
12.10. Integrating Red Hat OpenShift Service Mesh with Zero Trust Workload Identity Manager in a single-cluster Copy linkLink copied to clipboard!
Deploy and configure SPIFFE Runtime Environment as the certificate authority (CA) for Red Hat OpenShift Service Mesh workloads, replacing the Istio built-in CA with SPIFFE-compliant identities and automatically rotated short-lived certificates.
12.10.1. SPIRE integration with Red Hat OpenShift Service Mesh Copy linkLink copied to clipboard!
Red Hat OpenShift Service Mesh integrates with Zero Trust Workload Identity Manager so Envoy sidecars obtain mTLS certificates from Secure Production Identity Framework for Everyone (SPIFFE) instead of Istio’s built-in CA, enabling cryptographically verified workload identities.
SPIRE provides cryptographic workload identities based on the Secure Production Identity Framework for Everyone (SPIFFE) standard. This integration enables a zero-trust security model where workload identities are cryptographically verified rather than relying on network-based authentication.
12.10.1.1. Component overview Copy linkLink copied to clipboard!
The following table summarizes the main components in a single-cluster SPIFFE and Red Hat OpenShift Service Mesh integration and what each one does.
| Component | Purpose |
|---|---|
| Zero Trust Workload Identity Manager | Manages SPIRE deployment on OpenShift Container Platform |
| SPIRE Server | Certificate Authority; issues SVIDs |
| SPIRE Agent | Runs on each node; provides SDS API to workloads |
| SPIFFE CSI Driver | Mounts SPIRE socket into pods |
| ClusterSPIFFEID | Registers which pods get which identities |
| Red Hat OpenShift Service Mesh Operator | Manages Istio deployment |
| Istiod | Istio control plane |
| Envoy Sidecar | Proxy in each pod; uses SPIRE for certificates |
12.10.2. SPIRE integration architecture components Copy linkLink copied to clipboard!
Learn about the key components in the SPIRE integration architecture and how they work together to enable zero-trust workload identity and automated certificate management for secure mTLS connections in Red Hat OpenShift Service Mesh.
- Zero Trust Workload Identity Manager
- Manages the SPIRE deployment lifecycle on OpenShift Container Platform, including custom resources for SPIRE Server, SPIRE Agent, and related components.
- SPIRE Server
- Acts as the certificate authority that issues SPIFFE Verifiable Identity Documents (SVIDs) to authenticated workloads.
- SPIRE Agent
- Runs as a DaemonSet on each cluster node, providing the Envoy Secret Discovery Service (SDS) API to workloads on that node.
- SPIFFE CSI Driver
- Mounts the SPIRE Agent UNIX domain socket into pods, enabling secure communication between Envoy sidecars and the SPIRE Agent.
- Red Hat OpenShift Service Mesh
-
Manages the Istio deployment through the
servicemeshoperator3Operator. - Istiod
- The Istio control plane that configures Envoy proxies but delegates certificate issuance to SPIRE.
- Envoy sidecar
- The proxy injected into each workload pod that uses SPIRE-issued certificates for mTLS connections.
12.10.3. Deploying SPIRE operands for Red Hat OpenShift Service Mesh integration Copy linkLink copied to clipboard!
Deploy SPIRE operands by creating the ZeroTrustWorkloadIdentityManager custom resource (CR) and related SPIRE operand CRs together. A running SPIRE deployment is required before you configure Red Hat OpenShift Service Mesh to use SPIRE-issued certificates for workload mTLS.
Prerequisites
- You have installed Zero Trust Workload Identity Manager.
-
The OpenShift CLI (
oc) is configured with access to the cluster. -
You have permissions to create custom resources in the
zero-trust-workload-identity-managernamespace.
Procedure
Set the environment variables by running the following commands:
$ export TRUST_DOMAIN=ocp.one $ export ZTWIM_NS=zero-trust-workload-identity-manager $ export JWT_ISSUER="https://oidc-discovery.$(oc get ingresses.config/cluster -o jsonpath={.spec.domain})"Deploy all SPIRE operand CRs, including the
ZeroTrustWorkloadIdentityManagerCR:Create the
ZeroTrustWorkloadIdentityManagerCR:$ oc apply -f - <<EOF apiVersion: operator.openshift.io/v1alpha1 kind: ZeroTrustWorkloadIdentityManager metadata: name: cluster labels: app.kubernetes.io/name: zero-trust-workload-identity-manager app.kubernetes.io/managed-by: zero-trust-workload-identity-manager spec: trustDomain: ${TRUST_DOMAIN} clusterName: "" bundleConfigMap: "spire-bundle" EOFCreate the
SpireServerCR:$ cat <<EOF | oc apply -f - apiVersion: operator.openshift.io/v1alpha1 kind: SpireServer metadata: name: cluster spec: logLevel: "info" logFormat: "text" jwtIssuer: $JWT_ISSUER caValidity: "24h" defaultX509Validity: "1h" defaultJWTValidity: "5m" caKeytype: “rsa-2048” jwtKeyType: "rsa-2048" keyManager: “” caSubject: country: "US" organization: "RH" commonName: "SPIRE Server CA" persistence: size: "5Gi" accessMode: "ReadWriteOnce" datastore: databaseType: "sqlite3" connectionString: "/run/spire/data/datastore.sqlite3" tlsSecretName: "" maxOpenConns: 100 maxIdleConns: 10 connMaxLifetime: 0 disableMigration: "false" EOFWait for the SPIRE Server to become ready by running the following commands:
$ until oc get statefulset/spire-server -n "${ZTWIM_NS}" &> /dev/null; do sleep 3; done$ kubectl rollout status statefulset/spire-server -n "${ZTWIM_NS}" --timeout=300sCreate the
SpireAgentCR:$ cat <<EOF | oc apply -f - apiVersion: operator.openshift.io/v1alpha1 kind: SpireAgent metadata: name: cluster spec: socketPath: "/run/spire/agent-sockets" logLevel: "info" logFormat: "text" nodeAttestor: k8sPSATEnabled: "true" workloadAttestors: k8sEnabled: "true" workloadAttestorsVerification: type: "auto" hostCertBasePath: "/etc/kubernetes" hostCertFileName: "kubelet-ca.crt" disableContainerSelectors: "false" useNewContainerLocator: "true" EOFWait for the SPIRE Agent to become ready by running the following commands:
$ until oc get daemonset/spire-agent -n "${ZTWIM_NS}" &> /dev/null; do sleep 3; done$ kubectl rollout status daemonset/spire-agent -n "${ZTWIM_NS}" --timeout=300sDeploy the
SpiffeCSIDriverCR:$ cat <<EOF | oc apply -f - apiVersion: operator.openshift.io/v1alpha1 kind: SpiffeCSIDriver metadata: name: cluster spec: agentSocketPath: '/run/spire/agent-sockets' pluginName: "csi.spiffe.io" EOFWait for the SPIFFE CSI Driver to become ready by running the following commands:
$ until oc get daemonset/spire-spiffe-csi-driver -n "${ZTWIM_NS}" &> /dev/null; do sleep 3; done$ kubectl rollout status daemonset/spire-spiffe-csi-driver -n "${ZTWIM_NS}" --timeout=300sDeploy the
SpireOIDCDiscoveryProviderCR:$ export OIDC_DISCOVERY_CONFIG_MAP=spire-spiffe-oidc-discovery-provider$ cat <<EOF | oc apply -f - apiVersion: operator.openshift.io/v1alpha1 kind: SpireOIDCDiscoveryProvider metadata: name: cluster spec: logLevel: "info" logFormat: "text" csiDriverName: "csi.spiffe.io" jwtIssuer: $JWT_ISSUER replicaCount: 1 managedRoute: "true" EOFWait for the OIDC Discovery Provider to be created by running the following commands:
$ until oc get deployment spire-spiffe-oidc-discovery-provider -n "${ZTWIM_NS}" &> /dev/null; do sleep 3; done$ oc wait --for=condition=Available deployment/spire-spiffe-oidc-discovery-provider -n "${ZTWIM_NS}" --timeout=300s
Verification
Verify that Zero Trust Workload Identity Manager is installed:
Deploy the client workload and try to fetch a workload SVID:
$ cat <<EOF | oc apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: ztwim-client namespace: default labels: app: ztwim-client spec: selector: matchLabels: app: ztwim-client template: metadata: labels: app: ztwim-client spec: containers: - name: client image: ghcr.io/spiffe/spire-agent:1.5.1 command: ["/opt/spire/bin/spire-agent"] args: [ "api", "watch", "-socketPath", "/run/spire/sockets/spire-agent.sock" ] volumeMounts: - mountPath: /run/spire/sockets name: spiffe-workload-api readOnly: true volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true EOFWait for the client deployment to become ready by running the following command:
$ until oc get deployment ztwim-client -n default &> /dev/null; do sleep 3; done$ oc wait --for=condition=Available deployment/ztwim-client -n default --timeout=300s$ sleep 5
Verify that the x509 SVID is available by running the following command:
$ oc exec -it \ "$(oc get \ pods -o=jsonpath='{.items[0].metadata.name}' \ -l app=ztwim-client \ -n default \ )" -n default -- \ /opt/spire/bin/spire-agent \ api fetch -socketPath /run/spire/sockets/spire-agent.sockThe expected output is an SVID like the following example:
Received 1 svid after 29.636075ms SPIFFE ID: spiffe://ocp.one/ns/default/sa/default SVID Valid After: 2025-10-21 14:04:03 +0000 UTC SVID Valid Until: 2025-10-21 15:04:13 +0000 UTC CA #1 Valid After: 2025-10-21 07:38:03 +0000 UTC CA #1 Valid Until: 2025-10-22 07:38:13 +0000 UTCVerify that the JSON Web Token (JWT) SVID is available by running the following command:
$ oc exec -it \ "$(oc get \ pods -o=jsonpath='{.items[0].metadata.name}' \ -l app=ztwim-client \ -n default \ )" -n default -- \ /opt/spire/bin/spire-agent \ api fetch jwt -audience=sample-aud -socketPath /run/spire/sockets/spire-agent.sockThe expected output is a JWT SVID like the following example:
token(spiffe://ocp.one/ns/default/sa/default): eyJhbGciOiJSUzI1NiIsImtpZCI6Ij....IsIm bundle(spiffe://ocp.one): { "keys": [ { "kty": "RSA", "kid": "6k9PfhrAdfajT6jvLvR6bdomFvQxMeGf", "n": "wEYTV0ri4OOcdgEVgzN0...KhUEGf0NKxnuaeGQ", "e": "AQAB" } ] }Remove the client workload by running the following command:
$ oc delete deployment ztwim-client -n default
12.10.4. Deploying Red Hat OpenShift Service Mesh for SPIRE integration Copy linkLink copied to clipboard!
Deploy Red Hat OpenShift Service Mesh by creating the IstioCNI and Istio CRs with SPIRE integration settings so Envoy sidecars obtain SPIRE-issued certificates for workload mTLS after the SPIRE stack is running.
Prerequisites
- You have installed Zero Trust Workload Identity Manager.
-
The OpenShift CLI (
oc) is configured with access to the cluster. -
You have permissions to create namespaces and custom resources in the
istio-cniandistio-systemnamespaces. -
You have permissions to read secrets in the
zero-trust-workload-identity-managernamespace.
Procedure
Set the Istio environment variables by running the following commands:
$ export ZTWIM_NS=zero-trust-workload-identity-manager $ export TRUST_DOMAIN=ocp.one $ export JWT_ISSUER="https://oidc-discovery.$(oc get ingresses.config/cluster -o jsonpath={.spec.domain})" $ export OSSM_NS=istio-system $ export OSSM_CNI=istio-cni $ export VERIFY_NS=verify-ossm-ztwim $ export EXTRA_ROOT_CA="$(oc get secret oidc-serving-cert \ -n ${ZTWIM_NS} -o json | \ jq -r '.data."tls.crt"' | \ base64 -d | \ sed 's/^/ /')"Create the
IstioCNICR to deploy Istio CNI by running the following commands:$ oc new-project "${OSSM_CNI}" 2>/dev/null || oc project "${OSSM_CNI}"$ oc apply -f - <<EOF apiVersion: sailoperator.io/v1 kind: IstioCNI metadata: name: default spec: version: <version> namespace: ${OSSM_CNI} EOFwhere:
spec.version-
Replace
<version>with the Istio version supported by your Red Hat OpenShift Service Mesh Operator. You can find supported versions by runningoc get IstioCNI -o jsonpath='{.items[*].spec.version}'after the Operator is installed.
Wait for Istio CNI to become ready by running the following commands:
$ until oc get daemonset/istio-cni-node -n "${OSSM_CNI}" &> /dev/null; do sleep 3; done$ kubectl rollout status daemonset/istio-cni-node -n "${OSSM_CNI}" --timeout=300sThe
untilloop waits for the Red Hat OpenShift Service Mesh Operator to create theistio-cni-nodeDaemonSet. Theoc rollout statuscommand waits for the DaemonSet pods to become ready.Install the Istio CR with SPIRE integration by running the following commands:
$ oc new-project "${OSSM_NS}" 2>/dev/null$ cat <<EOF | oc apply -f - apiVersion: sailoperator.io/v1 kind: Istio metadata: name: default spec: namespace: istio-system updateStrategy: type: InPlace values: pilot: jwksResolverExtraRootCA: | ${EXTRA_ROOT_CA} env: PILOT_JWT_ENABLE_REMOTE_JWKS: "true" meshConfig: trustDomain: $TRUST_DOMAIN defaultConfig: proxyMetadata: WORKLOAD_IDENTITY_SOCKET_FILE: "spire-agent.sock" sidecarInjectorWebhook: templates: spire: | spec: initContainers: - name: istio-proxy volumeMounts: - name: workload-socket mountPath: /run/secrets/workload-spiffe-uds readOnly: true volumes: - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true spireGateway: | spec: containers: - name: istio-proxy volumeMounts: - name: workload-socket mountPath: /run/secrets/workload-spiffe-uds readOnly: true volumes: - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true EOFWait for all of the resources to become ready by running the following commands:
$ until oc get deployment istiod -n "${OSSM_NS}" &> /dev/null; do sleep 3; done$ oc wait --for=condition=Available deployment/istiod -n "${OSSM_NS}" --timeout=300s
Verification
Verify that Istio is integrated with SPIRE:
Create a test workload with the
spireinjection template by running the following commands:$ oc new-project "${VERIFY_NS}" 2>/dev/nullEnable the sidecar injection by running the following command:
$ oc label namespace "${VERIFY_NS}" istio-injection=enabledCreate the
httpbinworkload:$ cat <<EOF | oc apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: httpbin namespace: ${VERIFY_NS} spec: replicas: 1 selector: matchLabels: app: httpbin version: v1 template: metadata: annotations: inject.istio.io/templates: "sidecar,spire" spiffe.io/audience: "test-audience" labels: app: httpbin version: v1 spec: containers: - image: docker.io/mccutchen/go-httpbin:v2.15.0 imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 8080 EOFWait for all of the resources to become ready by running the following commands:
$ until oc get deployment httpbin -n "${VERIFY_NS}" &> /dev/null; do sleep 3; done$ oc wait --for=condition=Available deployment/httpbin -n "${VERIFY_NS}" --timeout=300sVerify the SPIRE workload identity by running the following command:
$ HTTPBIN_POD=$(oc get pod -l app=httpbin -n "${VERIFY_NS}" -o jsonpath="{.items[0].metadata.name}") $ istioctl proxy-config secret "$HTTPBIN_POD" \ -n "${VERIFY_NS}" -o json \ | jq -r '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' \ | base64 --decode > chain.pem openssl x509 -in chain.pem -text | grep SPIREExample output
Issuer: C=US, O=RH, CN=<APP_DOMAIN>/serialNumber=... Subject: C=US, O=SPIREIf you see
SPIREin bothIssuerandSubject, the integration is working. Envoy is getting its certificates from SPIRE, not from Istio’s built-in CA.Remove the namespace by running the following command:
$ oc delete namespace "${VERIFY_NS}"
12.10.5. Additional resources Copy linkLink copied to clipboard!
12.11. Enabling create-only mode for the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
To pause Operator reconciliation, enable create-only mode by setting an environment variable in the subscription object. By setting this value, you can perform manual configurations or debug the operator without the controller overwriting your changes.
The following scenarios are examples of when the create-only mode might be of use:
Manual Customization Required: You need to customize operator-managed resources (ConfigMaps, Deployments, DaemonSets, etc.) with specific configurations that differ from the operator’s defaults
Day 2 Operations: After initial deployment, you want to prevent the operator from overwriting their manual changes during subsequent reconciliation cycles
Configuration Drift Prevention: You want to maintain control over certain resource configurations while still benefiting from the operator’s lifecycle management
12.11.1. Pausing Operator reconciliation Copy linkLink copied to clipboard!
Pause reconciliation of the operands by enabling create-only mode. This setting prevents the Operator from automatically reverting your manual changes to the desired state. You can enable this mode by updating the Operator’s subscription object.
When create-only mode is disabled, the Operator overwrites the resources if any conflicts exist.
Prerequisites
- You have installed Zero Trust Workload Identity Manager on your machine.
- You have installed the SPIRE Servers, Agents, SPIFFE Container Storage Interface (CSI), and an OpenID Connect (OIDC) Discovery Provider and are in running status.
Procedure
To pause reconciling the operands resources managed by the Operator, add the environment variable
CREATE_ONLY_MODE:truein the subscription object by running the following command:$ oc -n $OPERATOR_NAMESPACE patch subscription openshift-zero-trust-workload-identity-manager --type='merge' -p '{"spec":{"config":{"env":[{"name":"CREATE_ONLY_MODE","value":"true"}]}}}'
Verification
Check the status of the
SpireServerresource to confirm that thecreate-onlymode is active. Thestatusmust betrueand thereasonmust beCreateOnlyModeEnabled.$ oc get SpireServer cluster -o yamlThe following is an example that confirms that the 'create-only' mode is active.
status:
conditions:
- lastTransitionTime: "2025-12-23T11:36:58Z"
message: All components are ready
reason: Ready
status: "True"
type: Ready
- lastTransitionTime: "2025-12-23T11:36:58Z"
message: All operand CRs are ready
reason: Ready
status: "True"
type: OperandsAvailable
- lastTransitionTime: "2025-12-23T11:36:58Z"
message: create-only mode enabled
reason: CreateOnlyModeEnabled
status: "True"
type: CreateOnlyMode
The Operator updates the upgradeable condition to false in the operatorCondition resource. You might not be able to upgrade the Operator when in create-only mode.
12.11.2. Resuming Operator reconciliation Copy linkLink copied to clipboard!
To resume Operator reconciliation after manual configuration or debugging, disable the create-only mode. This allows the controller to resume managing resources and applying the desired state. You can disable this mode by setting the environment variable in the subscription object.
Prerequisites
-
You have enabled
create-onlymode on the Zero Trust Workload Identity Manager. - You have completed your manual configuration or debugging tasks.
Procedure
To restart reconciling the Operator-managed resources, add the environment variable
CREATE_ONLY_MODE:falsein the subscription object by running the following command:$ oc -n $OPERATOR_NAMESPACE patch subscription openshift-zero-trust-workload-identity-manager --type='merge' -p '{"spec":{"config":{"env":[{"name":"CREATE_ONLY_MODE","value":"false"}]}}}'
Verification
Check the status of the
SpireServerresource to confirm thatcreate-onlymode is disabled by running the following command:$ oc get SpireServer cluster -o yamlExample output
status: conditions: - lastTransitionTime: "2025-12-23T11:40:00Z" message: create-only mode disabled reason: CreateOnlyModeDisabled status: "False" type: CreateOnlyMode
12.12. Monitoring Zero Trust Workload Identity Managergit Copy linkLink copied to clipboard!
Track the performance of the Zero Trust Workload Identity Manager by collecting metrics. Configure monitoring to collect metrics from the Security Production Identity Framework for Everyone (SPIRE) Server and SPIRE Agent components.
12.12.1. Enabling user workload monitoring Copy linkLink copied to clipboard!
Enable user workload monitoring to track metrics for your user-defined projects. Configuring this feature allows you to observe application performance and helps you maintain the health of your services.
Prerequisites
-
You have access to the cluster as a user with the
cluster-admincluster role.
Procedure
Create the
cluster-monitoring-config.yamlfile to define and configure theConfigMap:apiVersion: v1 kind: ConfigMap metadata: name: cluster-monitoring-config namespace: openshift-monitoring data: config.yaml: | enableUserWorkload: trueApply the
ConfigMapby running the following command:$ oc apply -f cluster-monitoring-config.yaml
Verification
Verify that the monitoring components for user workloads are running in the
openshift-user-workload-monitoringnamespace:$ oc -n openshift-user-workload-monitoring get podExample output
NAME READY STATUS RESTARTS AGE prometheus-operator-6cb6bd9588-dtzxq 2/2 Running 0 50s prometheus-user-workload-0 6/6 Running 0 48s prometheus-user-workload-1 6/6 Running 0 48s thanos-ruler-user-workload-0 4/4 Running 0 42s thanos-ruler-user-workload-1 4/4 Running 0 42s
The status of the pods such as prometheus-operator, prometheus-user-workload, and thanos-ruler-user-workload must be Running.
12.12.2. Configuring metrics collection for SPIRE Server by using a ServiceMonitor Copy linkLink copied to clipboard!
To collect custom metrics from the SPIRE Server, create a ServiceMonitor custom resource (CR). This configuration enables the Prometheus Operator to scrape metrics from the default endpoint, which helps you monitor your SPIRE deployment.
The SPIRE Server operand exposes metrics by default on port 9402 at the /metrics endpoint. You can configure metrics collection for the SPIRE Server by creating a ServiceMonitor custom resource (CR) that enables the Prometheus Operator to collect custom metrics.
Prerequisites
-
You have access to the cluster as a user with the
cluster-admincluster role. - You have installed the Zero Trust Workload Identity Manager.
- You have deployed the SPIRE Server operand in the cluster.
- You have enabled the user workload monitoring.
Procedure
Create the
ServiceMonitorCR:Create the YAML file that defines the
ServiceMonitorCR:Example
servicemonitor-spire-serverfileapiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: app.kubernetes.io/name: server app.kubernetes.io/instance: spire name: spire-server-metrics namespace: zero-trust-workload-identity-manager spec: endpoints: - port: metrics interval: 30s path: /metrics selector: matchLabels: app.kubernetes.io/name: server app.kubernetes.io/instance: spire namespaceSelector: matchNames: - zero-trust-workload-identity-managerCreate the
ServiceMonitorCR by running the following command:$ oc create -f servicemonitor-spire-server.yamlAfter the
ServiceMonitorCR is created, the user workload Prometheus instance begins metrics collection from the SPIRE Server. The collected metrics are labeled withjob="spire-server".
Verification
-
In the OpenShift Container Platform web console, navigate to Observe
Targets. In the Label filter field, enter the following label to filter the metrics targets:
$ service=zero-trust-workload-identity-manager-metrics-service-
Confirm that the Status column shows
Upfor thespire-server-metricsentry.
12.12.3. Configuring metrics collection for SPIRE Agent by using a Service Monitor Copy linkLink copied to clipboard!
Configure metrics collection for the SPIRE Agent by creating a ServiceMonitor custom resource (CR). This enables the Prometheus Operator to collect custom metrics that the SPIRE Agent exposes on the default port.
The SPIRE Agent operand exposes metrics by default on port 9402 at the /metrics endpoint. You can configure metrics collection for the SPIRE Agent by creating a ServiceMonitor custom resource (CR), which enables the Prometheus Operator to collect custom metrics.
Prerequisites
-
You have access to the cluster as a user with the
cluster-admincluster role. - You have installed the Zero Trust Workload Identity Manager.
- You have deployed the SPIRE Agent operand in the cluster.
- You have enabled the user workload monitoring.
Procedure
Create the
ServiceMonitorCR:Create the YAML file that defines the
ServiceMonitorCR:Example
servicemonitor-spire-agent.yamlfileapiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: app.kubernetes.io/name: agent app.kubernetes.io/instance: spire name: spire-agent-metrics namespace: zero-trust-workload-identity-manager spec: endpoints: - port: metrics interval: 30s path: /metrics selector: matchLabels: app.kubernetes.io/name: agent app.kubernetes.io/instance: spire namespaceSelector: matchNames: - zero-trust-workload-identity-managerCreate the
ServiceMonitorCR by running the following command:$ oc create -f servicemonitor-spire-agent.yamlAfter the
ServiceMonitorCR is created, the user workload Prometheus instance begins metrics collection from the SPIRE Agent. The collected metrics are labeled withjob="spire-agent".
Verification
-
In the OpenShift Container Platform web console, navigate to Observe
Targets. In the Label filter field, enter the following label to filter the metrics targets:
$ service=spire-agent-
Confirm that the Status column shows
Upfor thespire-agent-metricsentry.
12.12.4. Configuring metrics collection for the Operator by using a ServiceMonitor Copy linkLink copied to clipboard!
The Zero Trust Workload Identity Manager exposes metrics by default on port 8443 at the /metrics service endpoint. You can configure metrics collection for the Operator by creating a ServiceMonitor custom resource (CR) that enables the Prometheus Operator to collect custom metrics. For more information, see "Configuring user workload monitoring".
The SPIRE Server operand exposes metrics by default on port 9402 at the /metrics endpoint. You can configure metrics collection for the SPIRE Server by creating a ServiceMonitor custom resource (CR) that enables the Prometheus Operator to collect custom metrics.
Prerequisites
-
You have access to the cluster as a user with the
cluster-admincluster role. - You have installed the Zero Trust Workload Identity Manager.
- You have enabled the user workload monitoring.
Procedure
Configure the Operator to use HTTP or HTTPS protocols for the metrics server.
Update the subscription object for Zero Trust Workload Identity Manager to configure the HTTP protocol by running the following command:
$ oc -n zero-trust-workload-identity-manager patch subscription zero-trust-workload-identity-manager-subscription --type='merge' -p '{"spec":{"config":{"env":[{"name":"METRICS_BIND_ADDRESS","value":":8080"}, {"name": "METRICS_SECURE", "value": "false"}]}}}'Verify the Zero Trust Workload Identity Manager pod is redeployed and that the configured values for
METRICS_BIND_ADDRESSandMETRICS_SECUREis updated by running the following command:$ oc set env --list deployment/zero-trust-workload-identity-manager-controller-manager -n zero-trust-workload-identity-manager | grep -e METRICS_BIND_ADDRESS -e METRICS_SECURE -e containerExample output
deployments/zero-trust-workload-identity-manager-controller-manager, container manager METRICS_BIND_ADDRESS=:8080 METRICS_SECURE=false
Create the
Secretresource withkubernetes.io/service-account.nameannotation to inject the token required for authenticating with the metrics server.Create the
secret-zero-trust-workload-identity-manager.yamlYAML file:apiVersion: v1 kind: Secret metadata: labels: name: zero-trust-workload-identity-manager name: zero-trust-workload-identity-manager-metrics-auth namespace: zero-trust-workload-identity-manager annotations: kubernetes.io/service-account.name: zero-trust-workload-identity-manager-controller-manager type: kubernetes.io/service-account-tokenCreate the
Secretresource by running the following command:$ oc apply -f secret-zero-trust-workload-identity-manager.yaml
Create the
ClusterRoleBindingresource required for granting permissions to access the metrics.Create the
clusterrolebinding-zero-trust-workload-identity-manager.yamlYAML file:apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: name: zero-trust-workload-identity-manager name: zero-trust-workload-identity-manager-allow-metrics-access roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: zero-trust-workload-identity-manager-metrics-reader subjects: - kind: ServiceAccount name: zero-trust-workload-identity-manager-controller-manager namespace: zero-trust-workload-identity-managerCreate the
ClusterRoleBindingresource by running the following command:$ oc apply -f clusterrolebinding-zero-trust-workload-identity-manager.yaml
Create the following
ServiceMonitorCR if the metrics server is configured to usehttp.Create the
servicemonitor-zero-trust-workload-identity-manager-http.yamlYAML file:apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: name: zero-trust-workload-identity-manager name: zero-trust-workload-identity-manager-metrics-monitor namespace: zero-trust-workload-identity-manager spec: endpoints: - authorization: credentials: name: zero-trust-workload-identity-manager-metrics-auth key: token type: Bearer interval: 60s path: /metrics port: metrics-http scheme: http scrapeTimeout: 30s namespaceSelector: matchNames: - zero-trust-workload-identity-manager selector: matchLabels: name: zero-trust-workload-identity-managerCreate the
ServiceMonitorCR by running the following command:$ oc apply -f servicemonitor-zero-trust-workload-identity-manager-http.yaml
Create the following
ServiceMonitorCR if the metrics server is configured to usehttps.Create the
servicemonitor-zero-trust-workload-identity-manager-https.yamlYAML file:apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: name: zero-trust-workload-identity-manager name: zero-trust-workload-identity-manager-metrics-monitor namespace: zero-trust-workload-identity-manager spec: endpoints: - authorization: credentials: name: zero-trust-workload-identity-manager-metrics-auth key: token type: Bearer interval: 60s path: /metrics port: metrics-https scheme: https scrapeTimeout: 30s tlsConfig: ca: configMap: name: openshift-service-ca.crt key: service-ca.crt serverName: zero-trust-workload-identity-manager-metrics-service.zero-trust-workload-identity-manager.svc.cluster.local namespaceSelector: matchNames: - zero-trust-workload-identity-manager selector: matchLabels: name: zero-trust-workload-identity-managerCreate the
ServiceMonitorCR by running the following command:$ oc apply -f servicemonitor-zero-trust-workload-identity-manager-https.yamlAfter the
ServiceMonitorCR is created, the user workload Prometheus instance begins metrics collection from the SPIRE Server. The collected metrics are labeled withjob="zero-trust-workload-identity-manager-metrics-service".
Verification
-
In the OpenShift Container Platform web console, navigate to Observe
Targets. In the Label filter field, enter the following label to filter the metrics targets:
$ service=zero-trust-workload-identity-manager-metrics-service-
Confirm that the Status column shows
Upfor thezero-trust-workload-identity-managerentry.
12.12.5. Querying metrics for the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
Query SPIRE Agent and SPIRE Server metrics using the OpenShift Container Platform web console or the command line. This helps you monitor the performance of SPIRE components that match specific job labels.
Prerequisites
-
You have access to the cluster as a user with the
cluster-adminrole. - You have installed the Zero Trust Workload Identity Manager.
- You have deployed the SPIRE Server and SPIRE Agent operands in the cluster.
-
You have enabled monitoring and metrics collection by creating
ServiceMonitorobjects.
Procedure
-
In the OpenShift Container Platform web console, navigate to Observe
Metrics. In the query field, enter the following PromQL expression to query SPIRE Server metrics:
{job="spire-server"}In the query field, enter the following PromQL expression to query SPIRE Agent metrics.
{job="spire-agent"}
12.12.6. Zero Trust Workload Identity Manager monitoring available metrics Copy linkLink copied to clipboard!
Monitor the health and performance of Zero Trust Workload Identity Manager components by reviewing exposed metrics. This reference describes controller, certificate, and runtime metrics that help you maintain system health and troubleshoot errors.
The Zero Trust Workload Identity Manager exposes the following metrics:
- Controller runtime metrics
-
controller_runtime_active_workers: Number of currently used workers per controller -
controller_runtime_max_concurrent_reconciles: Maximum number of concurrent reconciles per controller -
controller_runtime_reconcile_errors_total: Total number of reconciliation errors per controller -
controller_runtime_reconcile_time_seconds: Length of time per reconciliation per controller -
controller_runtime_reconcile_total: Total number of reconciliations per controller
-
- Certificate watcher metrics
-
certwatcher_read_certificate_errors_total: Total number of certificate read errors -
certwatcher_read_certificate_total: Total number of certificates read
-
- Go runtime metrics
Standard Go runtime metrics including:
-
go_gc_duration_seconds: Garbage collection duration -
go_goroutines: Number of goroutines -
go_memstats_*: Memory statistics -
process_*: Process statistics
-
- Custom Operator metrics
The operator also exposes custom metrics related to:
- SPIRE Server status and health
- SPIRE Agent deployment status
- SPIFFE CSI Driver status
- OIDC Discovery Provider status
- Workload identity management operations
12.13. Uninstalling the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
To remove the Zero Trust Workload Identity Manager from OpenShift Container Platform, uninstall the Operator and delete its related resources. This process removes the component from your cluster.
12.13.1. Uninstalling the Zero Trust Workload Identity Manager Copy linkLink copied to clipboard!
To remove the Zero Trust Workload Identity Manager from your cluster, uninstall the Operator using the web console. This helps you clean up resources and delete the service from your environment.
Prerequisites
-
You have access to the cluster with
cluster-adminprivileges. - You have access to the OpenShift Container Platform web console.
- The Zero Trust Workload Identity Manager is installed.
Procedure
- Log in to the OpenShift Container Platform web console.
Uninstall the Zero Trust Workload Identity Manager.
-
Go to Ecosystem
Installed Operators. - Click the Options menu next to the Zero Trust Workload Identity Manager entry, and then click Uninstall Operator.
- In the confirmation dialog, click Uninstall.
-
Go to Ecosystem
Verification
Verify that the Zero Trust Workload Identity Manager Operator is uninstalled.
$ oc get csv -n openshift-zero-trust-workload-identityExample output
No resources found in openshift-zero-trust-workload-identity namespace.
12.13.2. Uninstalling Zero Trust Workload Identity Manager resources by using the CLI Copy linkLink copied to clipboard!
Remove Zero Trust Workload Identity Manager resources from your cluster using the CLI. This deletes the remaining operands and definitions to help ensure a clean environment after you uninstall the product.
Prerequisites
-
You have access to the cluster with
cluster-adminprivileges.
Procedure
Uninstall the operands by running each of the following commands:
Delete the
SpireOIDCDiscoveryProvidercluster by running the following command:$ oc delete SpireOIDCDiscoveryProvider clusterDelete the
SpiffeCSIDrivercluster by running the following command:$ oc delete SpiffeCSIDriver cluster -lDelete the
SpireAgentcluster by running the following command:$ oc delete SpireAgent clusterDelete the
SpireServercluster by running the following command:$ oc delete SpireServer clusterDelete the
ZeroTrustWorkloadIdentityManagercluster by running the following command:$ oc delete ZeroTrustWorkloadIdentityManager clusterDelete the persistent volume claim (PVC) by running the following command:
$ oc delete pvc -l=app.kubernetes.io/name=spire-serverDelete the service by running the following command:
$ oc delete service -l=app.kubernetes.io/name=zero-trust-workload-identity-manager -n zero-trust-workload-identity-managerDelete the namespace by running the following command:
$ oc delete ns zero-trust-workload-identity-managerDelete the cluster role by running the following command:
$ oc delete clusterrole -l=app.kubernetes.io/name=zero-trust-workload-identity-managerDelete the admission webhook configuration by running the following command:
$ oc delete validatingwebhookconfigurations -l=app.kubernetes.io/name=zero-trust-workload-identity-manager
Delete the custom resource definitions (CRDs) by running each of the following commands:
Delete the SPIRE Server CRD by running the following command:
$ oc delete crd spireservers.operator.openshift.ioDelete the SPIRE Agent CRD by running the following command:
$ oc delete crd spireagents.operator.openshift.ioDelete the SPIFFEE CSI Drivers CRD by running the following command:
$ oc delete crd spiffecsidrivers.operator.openshift.ioDelete the SPIRE OIDC Discovery Provider CRD by running the following command:
$ oc delete crd spireoidcdiscoveryproviders.operator.openshift.ioDelete the SPIRE and SPIFFE cluster federated trust domains CRD by running the following command:
$ oc delete crd clusterfederatedtrustdomains.spire.spiffe.ioDelete the cluster SPIFFE IDs CRD by running the following command:
$ oc delete crd clusterspiffeids.spire.spiffe.ioDelete the SPIRE and SPIFFE cluster static entries CRD by running the following command:
$ oc delete crd clusterstaticentries.spire.spiffe.ioDelete the Zero Trust Workload Identity Manager CRD by running the following command:
$ oc delete crd zerotrustworkloadidentitymanagers.operator.openshift.io
Verification
To verify that the resources have been deleted, replace each oc delete command with oc get, and then run the command. If no resources are returned, the deletion was successful.