Skip to main content

Using the SPIFFE Helper container image

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.

SPIFFE Helper container image​

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:

  1. Connects to the SPIFFE Workload API, usually through the SPIFFE Runtime Environment Agent socket exposed by the SPIFFE CSI driver.
  2. Fetches X.509 SPIFFE Verifiable Identity Document (SVID)s, JSON Web Token (JWT) SVIDs, and JWT bundles from SPIFFE Runtime Environment.
  3. Writes credentials to files under a configured directory such as cert_dir.
  4. Optionally notifies a workload when X.509 authentication credentials are renewed in daemon mode.

SPIFFE Helper modes​

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=false or daemon_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, cmd and renew_signal are 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 emptyDir volume 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.

SPIFFE Helper credential types​

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, and svid_bundle_file_name. SPIFFE Helper writes Privacy-Enhanced Mail (PEM) certificate, PEM private key, and PEM trust bundle files under cert_dir.
  • JWT bundle -- Requires jwt_bundle_file_name. SPIFFE Helper writes a JSON bundle file.
  • JWT SVIDs -- Requires one or more jwt_svids blocks with jwt_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.

Deploying PostgreSQL with SPIFFE Helper​

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-admin privileges.
  • You have installed the Zero Trust Workload Identity Manager.
  • The ZeroTrustWorkloadIdentityManager custom resource reports Ready.

Procedure

  1. Create a Containerfile with 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 65534

    Replace <version> with the tag that matches your Zero Trust Workload Identity Manager installation.

  2. 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:latest

    Replace <registry> with your container registry.

  3. 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
    EOF

    Replace both <registry> occurrences in the deployment with your container registry.

  4. 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_dir and svid_* file names must match the ssl_*_file paths in postgresql.conf. The server helper.conf must 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"
  5. 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 psql client.

    $ 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
    EOF

    Replace <version> with the tag that matches your Zero Trust Workload Identity Manager installation.

  6. 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

  1. 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 -text

    The server certificate should include the service hostname postgresql-spiffe.postgresql-spiffe.svc.

  2. 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 -text

    The certificate common name (CN) field should be postgresql_spiffe.

  3. 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, psql connects to the database.

SPIFFE Helper image reference​

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.

Image location​

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.

Command-line flags​

FlagDescription
-config <path>Path to the SPIFFE Helper configuration file. Required.
-daemon-mode <boolean>Controls operating mode. true (default) runs continuously and renews credentials. false fetches once and exits.
-versionPrints 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

Configuration file options​

SPIFFE Helper reads a configuration file. The following table describes the most common options for X.509 workloads on OpenShift Container Platform.

OptionDescription
agent_addressPath to the SPIFFE Runtime Environment Agent Workload API socket. On Linux, SPIFFE Helper connects with unix://<path>. With the SPIFFE CSI driver, use /spiffe-workload-api/spire-agent.sock. You can also set the SPIFFE_ENDPOINT_SOCKET environment variable.
cert_dirDirectory where SPIFFE Helper writes credential files. The directory must exist before SPIFFE Helper starts.
daemon_modeWhen true (default), runs continuously. When false, fetches once and exits. Can also be controlled with the -daemon-mode flag.
svid_file_name, svid_key_file_name, svid_bundle_file_nameFile names for the X.509 SVID certificate, private key, and trust bundle. All three are required to enable X.509 output.
jwt_bundle_file_nameFile name for a JWT bundle JSON file.
jwt_svidsBlock defining JWT SVID audiences and output file names.
cmdExecutable path for the managed-child pattern. On the first successful X.509 write, SPIFFE Helper starts this process. Ignored in non-daemon mode.
cmd_argsSpace-separated arguments for cmd. Use quoted strings for arguments that contain spaces. Not parsed by a shell unless you start a shell explicitly, for example /bin/sh -c "...".
pid_file_namePath to a file containing one integer PID for the external-process pattern. Requires renew_signal. Invalid in non-daemon mode.
renew_signalPOSIX signal name sent on renewal, for example SIGUSR1 or SIGHUP. Required when pid_file_name is set. Optional with cmd; when empty and cmd is set, later renewals do not signal the child.
health_checksOptional HTTP liveness and readiness endpoints. Available in daemon mode only.

Volume and mount requirements​

When deploying SPIFFE Helper on OpenShift Container Platform, mount the following resources:

VolumeMount pathAccess
SPIFFE Workload API (CSI)/spiffe-workload-api (or path matching agent_address)Read-only
SPIFFE Helper configuration (ConfigMap)Path passed to -config, for example /etc/spiffe-helper/helper.confRead-only
Certificate directory (emptyDir or persistent volume)Path matching cert_dir, for example /opt/postgresql-certs or /certsRead/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.

Quick reference​

QuestionAnswer
What happens if both cmd and pid_file_name are set?SPIFFE Helper writes files, then runs managed-child logic, then PID-file logic. There is no priority between them.
Is renew_signal required for pid_file_name?Yes. Validation fails if pid_file_name is set without renew_signal.
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, cmd, or signals.
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 shareProcessNamespace: true is set.

Troubleshooting SPIFFE Helper deployments​

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.

Common issues​

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 the ZeroTrustWorkloadIdentityManager custom resource reports Ready, the CSI volume is mounted at the path in agent_address, and a matching ClusterSPIFFEID exists for the pod namespace and labels. Verify cert_dir exists before SPIFFE Helper starts; an emptyDir volume 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 ignored cmd or renew_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 run cmd or 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 configure cmd, pid_file_name, or the application watches or polls the directory. For sidecar deployments with empty cmd, the application must reload TLS from disk. For PostgreSQL, configure the managed-child pattern with psql and pg_reload_conf() in the server sidecar.
cmd or renew_signal appears to have no effect
In non-daemon mode, SPIFFE Helper ignores cmd and renew_signal. In daemon mode, cmd and PID-file logic run only after a successful X.509 write. JWT bundle or JWT SVID updates do not trigger them. If renew_signal is empty while cmd is 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_name and cmd cannot signal another container unless you set shareProcessNamespace: true or run the reload command as a child of SPIFFE Helper. The PostgreSQL example uses cmd to run psql locally instead of signaling the database container.
Configuration validation fails for pid_file_name
renew_signal is required when pid_file_name is set. pid_file_name is 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 the ClusterSPIFFEID selectors.
Permission denied when reading certificate files
Check file ownership and permissions on the shared volume. Consider setting fsGroup in the pod securityContext so 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, and pg_hba.conf requires certificate authentication. Compare server and client SVIDs with openssl x509 -noout -text.

Use the following oc commands when investigating SPIFFE Helper deployments and workloads such as PostgreSQL.

Verify Zero Trust Workload Identity Manager readiness​

$ oc get ZeroTrustWorkloadIdentityManager cluster \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}{"\n"}'
$ oc get clusterspiffeid

Inspect SPIFFE Helper pods​

$ oc get pods -n postgresql-spiffe
$ oc get pods -n postgresql-spiffe-client

Review SPIFFE Helper logs​

$ 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

Verify on-disk SVID certificates​

$ 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