Enterprise security used to draw a clean line: humans log in; machines use secrets. But that line is blurring, and interactions with databases are often where you’ll encounter that ambiguity.
Historically, authentication has followed a familiar path: usernames and passwords are stored in configuration or Kubernetes Secrets; long-lived tokens and API keys are often kept in Secrets as well, on the assumption they are safer than passwords.
In practice, however, Secrets bring many of the same problems. They must be rotated, distributed, and revoked manually; they are frequently shared across replicas or teams; and if a Secret is exposed, an attacker has a credential that remains valid until someone notices.
For applications like databases, the situation is even more complex. The same data store must serve humans (analysts and DBAs connecting through corporate identity systems like OIDC and OAuth) and machines (microservices, pipelines, and AI agents that connect programmatically). The risks of misconfiguration, identity theft, and security breaches grow quickly.
In this blog post, we’ll delve into this problem in more detail, and then walk through how SPIFFE (Secure Production Identity Framework for Everyone) and SPIRE (SPIFFE Runtime Environment), delivered on Red Hat OpenShift via the zero trust workload identity manager, give workloads cryptographic identity without long-lived passwords or tokens.
We’ll start with the ideal situation—2 SPIFFE-enabled applications talking to one another—then provide a roadmap for enterprises to actually adopt the solution, using application-to-database access as a practical first step.
We’ll finish with 2 concrete authentication patterns, a discussion of the lessons learned as we built them, and a demo that you can run yourself.
Why identity matters in the enterprise
Humans, workloads, and security principals
In a previous blog post, we described how authentication and authorization work in Kubernetes and OpenShift for microservices architectures. If you’ve worked with those types of platforms for a while, you’ll know that a lot of the complexity around security starts with a simple question: who is calling what?
A key consideration in that blog post is the difference between human users and non-human users like service accounts.
In secured enterprise systems, a human might need to prove their identity for a number of reasons: to access web applications interactively, to use command-line interface (CLI) tools, or to connect to administrative consoles. In most enterprises, that path is already well understood. People authenticate through OpenID Connect (OIDC), SAML, or LDAP, often federated via an identity provider (IdP) such as Microsoft Entra ID or CyberArk. Once you’re authenticated, authorization might be configured in the platform itself (via OpenShift role-based access control, or RBAC, for example), or may be specified further downstream in an application or a data store (such as database roles or row-level policies). That human identity, an entity that a system can verify and then use to grant permission to access resources, is what we call a security principal, or simply a principal.
But workloads must also be identifiable. A microservice, a batch job, a data pipeline, an AI agent, or an integration service is still a caller. When a containerized application makes a request to an API or opens a database connection, that caller is a principal too. It needs to be identifiable, authorized, and auditable, in the same way a person logging into a web console would be.
However, as outlined in Table 1, the mechanisms for authenticating each type of caller are different and that’s where a lot of the confusion (and risk) arises.
Table 1. Authenticating humans vs. authenticating workloads
Identity type | Typical credential | Typical use |
Human | OIDC/OAuth token, session | Console, ad hoc queries, break-glass |
Workload | Cryptographic identity (certificate or token) | Service-to-service, app-to-database |
Even though the underlying question is the same—which principal is this caller?—the answer is usually provided by different mechanisms, as illustrated in Figure 1.
Figure 1. Authentication methods for humans and workloads
Databases make the split visible
Most systems primarily serve one kind of caller: Admin consoles are designed for humans, for instance, while internal APIs are designed for workloads. However, systems like databases routinely serve both humans and workloads, often through the same database instance and sometimes through the same tables.
To make matters even more complicated, enterprise business applications often require user impersonation or delegation capabilities. For example, a person may start an action in a UI that instructs a backend system to call another system, and the backend system will, for purposes of authorization and audit, use the identity of that person to make the system call.
In this scenario, a workload must use a human principal’s identity. But if it does so using the human’s own token, or using a long-lived token shared by all users (as illustrated in Figure 2), that introduces the risk that a human token can be leaked and used for other purposes or that the user who makes use of the service token cannot be identified.
Figure 2. A potentially insecure authentication chain
AI raises the stakes for workload identity
This distinction matters even more as AI workloads (AI agents like OpenClaw or Claude, for example) and automation become more sophisticated. AI agents can query data, call APIs, and chain actions in ways that look a lot like what a human operator would do—except that there may be nobody at the keyboard approving each step.
From a platform perspective, an AI agent is still a workload. It is still a principal, and it needs an identity of its own.
Connecting a SPIFFE-enabled application to a database
Let’s start by looking at a scenario where we use SPIFFE and SPIRE to connect an application workload (Application 1) to an existing PostgreSQL database, but without using long-lived passwords. We’ll use some generic terms in this example, but go into more details about how SPIFFE and SPIRE work later in this blog.We have chosen PostgreSQL as the example but the same pattern applies to other data stores that support client certificate authentication.
- The application is SPIFFE-enabled and receives cryptographic identifiers (called SVIDs) from the zero trust workload identity manager running on Red Hat OpenShift.
- The database is not yet SPIFFE-enabled.
- The database does support client certificate authentication against a configured Certificate Authority (CA). This should be a CA that is also used as an authority by our zero trust infrastructure.
- The same identity fabric that will eventually connect Application 1 to Application 2 using SPIFFE can authenticate Application 1 to its database today.
Figure 3 illustrates how this pattern works.
Figure 3. Using SPIFFE to connect to a database
Application 1 carries a SPIFFE identity. PostgreSQL continues to enforce local authorization through roles and grants.
There are a number of benefits to such an approach:
- Long-lived Secrets and tokens are removed
- Auditable workload identities are now collected in connection logs
- The value of the SPIRE investment is demonstrated on a security boundary that matters, without waiting for every downstream service to become SPIFFE-aware.
Once this configuration is in place, the same SPIFFE-enabled application can call other SPIFFE-enabled services using the same credential machinery.
Applications like databases tend to have many-to-one connections: many client applications connect to a single database server. This approach allows an enterprise to upgrade those client systems one-by-one and finally approach the database identity as the last migration step.
At the database boundary: Two technical options
In this scenario, when Application 1 connects to PostgreSQL, it needs to know what the database expects for authentication. SPIFFE and SPIRE support 2 main patterns, outlined in Table 2.
Table 2. Two options for our example application
Option | Mechanism | Who authenticates whom |
A: mTLS and X.509-SVID | Application 1 presents an X.509-SVID from SPIRE; PostgreSQL verifies it against the SPIRE CA | Mutual: Application 1 verifies PostgreSQL’s server certificate; PostgreSQL verifies Application 1’s client certificate. Both sides authenticate with certificates. |
B: TLS + JWT / OIDC | Application 1 connects over TLS (verifies PostgreSQL’s server certificate) and presents a JSON Web Token (JWT) for client authentication | Asymmetric: Application 1 authenticates PostgreSQL via TLS (server cert). PostgreSQL authenticates Application 1 via a bearer token, typically a JWT-SVID exchanged through EntraID (or similar) into an OIDC access token that PostgreSQL validates. There is no client certificate on Application 1. |
In the next 2 sections, we’ll walk through each option.
Scenario A: mTLS with X.509-SVID
In this scenario, mutual certificate authentication is established between Application 1 and PostgreSQL, with the client certificate issued by SPIRE as an X.509 SVID.
Access to the database follows these steps:
- Trust setup: PostgreSQL trusts the SPIRE/zero trust workload identity manager CA for client certificates. Application 1 trusts PostgreSQL’s server certificate (which could be issued by the Service CA in Red Hat OpenShift, for example).
- Issue: Application 1 requests an X.509 SVID from SPIRE via the in-pod Workload API.
- Connect: The application opens an mTLS session and presents its X.509 SVID (client certificate).
- Authenticate: PostgreSQL verifies the client certificate chain. Application 1 verifies the server certificate. Both principals are authenticated.
- Authorize: PostgreSQL applies local authorization through database roles and grants independently of SPIRE.
These steps are illustrated in Figure 4.
Figure 4. Database access in Scenario A
Why would you choose this pattern?
- Many organizations already use X.509 mTLS for database authentication. Using X.509 here allows them to continue using this model but without static certificate credentials.
- No database password is stored in a Kubernetes Secret.
- Certificates are short-lived (expiring after one hour, for example) and are rotated automatically by SPIRE.
- Connection logs reflect a cryptographic workload identity, not a shared credential that might be used by many client workloads.
- Authorization stays in PostgreSQL, as SPIRE proves who has connected; database domain roles such as a
pp_readonly,app_readwrite,andapp_adminthen define what they may do.
A simple example of this scenario is available in GitHub with a directory for manifests, deployment steps, and a Flask client with a web UI for live testing.
Scenario B: TLS and JWT/OIDC federation
In this scenario, the application connects to PostgreSQL over TLS and authenticates itself with a JSON Web Token (JWT), rather than presenting a client certificate. PostgreSQL (or an OAuth-aware layer in front of it) validates the token; SPIRE remains the source of workload identity upstream.
Access to the database follows these steps:
- Trust setup: Application 1 obtains and verifies the PostgreSQL server’s TLS certificate (which could be issued by the Service CA in Red Hat OpenShift, for example). PostgreSQL (or the IdP it relies on) is configured to accept and validate OAuth access tokens for client authentication.
- Issue: Application 1 requests a JWT SVID from SPIRE via the in-pod Workload API.
- Exchange: Application 1 presents the JWT SVID to EntraID (or a similar identity management platform). EntraID validates the token against SPIRE’s OIDC discovery provider and issues an OAuth access token that PostgreSQL understands. In the case of EntraID, this uses Microsoft Entra Workload Identity Federation. The application presents its SPIFFE JWT SVID as a client assertion and receives an Entra OAuth access token via the OAuth client credentials flow.
- Connect: Application 1 opens a TLS session to PostgreSQL, verifying the server certificate. No client certificate is presented.
- Authenticate: Application 1 sends the OAuth access token as part of the database connection. PostgreSQL validates the token (issuer, signature, expiry, audience) and establishes the session identity.
- Authorize: PostgreSQL applies database domain authorization based on the SPIFFE identity encoded in the JWT.
These steps are illustrated in Figure 5.
Figure 5. Database access in Scenario B
Why would you choose this pattern?
- Your organization mandates EntraID (or something similar) as the gatekeeper for resource access, including databases.
- PostgreSQL is already configured for OAuth/OIDC client authentication.
- Security policy requires workload access to federate through the corporate IdP, even for machine callers.
Table 3 shows how this scenario differs from Scenario A.
Table 3. Scenario A vs. Scenario B
Scenario A (mTLS) | Scenario B (TLS + JWT) | |
Application 1 to PostgreSQL | X.509 SVID (client certificate) | OIDC access token (bearer JWT) |
PostgreSQL to Application 1 | Server certificate | Server certificate |
Transport | mTLS | TLS (server auth only) |
IdP involvement | None required | Entra ID (or similar) in the token exchange path |
SPIRE's role | Issues X.509 SVID directly used at the database | Issues JWT-SVID; database sees the downstream OIDC token |
An example of Scenario B is also available on GitHub.
In summary, both scenarios rest on the same SPIFFE foundation, and SPIRE proves the identity of Application 1 in each case. The difference is the credential PostgreSQL accepts at the connection boundary: a certificate in Scenario A, or a token in Scenario B.
SPIFFE, SPIRE, and token exchange
How does this actually work?
How do you give a workload its own identity, and use that to access other workloads? SPIFFE assures and SPIRE provides workload identity, and token exchange is used to access other workloads. If you’re interested in more details of how the token exchange operates, please read this article from the Red Hat Emerging Technologies team on token exchange for more details.
What SPIFFE and SPIRE address
Workloads need the same kind of assurance we expect for human principals: verifiable identity, controlled access, and credentials that do not live forever in a Secret. SPIFFE and SPIRE address that gap with an open, workload-focused identity model.
SPIFFE is a standard that defines what a workload identity looks like. Each workload receives a SPIFFE ID, which is a stable, portable identifier that looks like this:
spiffe://trust-domain/ns/my-app/sa/my-serviceTo prove that identity at runtime, SPIFFE defines short-lived credentials called SVIDs (SPIFFE Verifiable Identity Documents), either as X.509 certificates or JSON Web Tokens (JWTs).
SPIRE is a runtime implementation of the SPIFFE standard that makes it operational. It issues, rotates, and delivers SVIDs to workloads based on attestation. It gives assurance that a running process really is the workload it claims to be, derived from signals such as namespace, service account, physical hardware, virtual machine instance, and pod metadata.
For example, SPIRE could use SPIFFE attestations to inspect a pod running on an AWS node, and validate that a particular AWS tag has been attached to the node. If that pod were launched in the same namespace, but on another AWS node in the cluster, it won't be issued a validated SVID, because the node doesn’t have the relevant AWS tag.
On OpenShift, the zero trust workload identity manager packages SPIRE for the Kubernetes platform. The key components of the zero trust workload identity manager are:
- SPIRE server: Issues identities within a trust domain
- SPIRE agent: Runs on each node and attests pods
- SPIFFE CSI driver: Mounts the Workload API endpoint into pods as a Unix socket
- OIDC discovery provider: Publishes metadata so external systems can verify SPIFFE-issued JWTs
From inside a pod, a workload can call the Workload API and request a short-lived SVID. This means your workloads do not require manual certificate requests; you don’t need to copy database passwords into Deployment definitions. Your workload identity is bound to the running workload process, and that identity can be cycled and renewed on a schedule that you define.
A word on authentication vs. authorization
SPIFFE and SPIRE provide authentication: they prove which workload is calling, identified by the SPIFFE ID encoded in the SVID. They do not provide authorization: that is, they do not define what that workload is allowed to do once identity is established.
When 2 SPIFFE-enabled applications (Application 1 and Application 2) communicate, Mutual Transport Layer Security (mTLS) or JWT validation only answers the question “Is this really Application 1?”, not “May Application 2 call this endpoint?”
Application 2, or a component in front of it—for example, a policy engine such as Open Policy Agent (OPA), a service mesh authorization policy, or an API gateway—must map the verified SPIFFE ID to permitted actions. The same principle applies at a database interface: SPIFFE proves the workload’s identity; the database’s roles and grants decide what that identity may read or write.
SPIFFE replaces long-lived secrets for proving identity, but doesn’t remove the need for an authorization provider.
The SPIFFE target state
We’ve seen an example of how to get started with using SPIFFE identity to connect to a database. But how do we take the next steps to enforcing workload identity everywhere?
The target: Application 1 talking to Application 2
In a fully zero trust deployment, illustrated in Figure 6, Application 1 connects to Application 2 using SPIFFE identities. Each side presents an SVID.
mTLS mutual authentication establishes both parties to the communication. Authorization decisions rest on who the caller is—that is, the SPIFFE ID in the mTLS certificate or JWT—not a shared API key baked into a ConfigMap.
Figure 6. Zero trust workload-to-workload communication
Both applications use the Workload API injected by the SPIFFE CSI driver. SPIRE attests each pod and issues credentials with a bounded lifetime.
Strong mutual authentication is only half the story: Application 2 still needs an authorization mechanism, like application logic, a policy engine, or mesh rules to decide what Application 1’s SPIFFE identity is permitted to do.
Starting from a non-SPIFFE world
Unless you’re a new enterprise building a platform from scratch, you are unlikely to switch everything to SPIFFE identity in one big change. It’s much more likely that your enterprise will need to integrate SPIFFE into an existing ecosystem.
Typical starting points look like this:
- Applications authenticate to a database with a username and password stored in a Secret repository.
- Service-to-service calls rely on long-lived API keys, basic authentication, or network ACLs.
- Certificates may exist, but they are long-lived, manually rotated, and owned by a central PKI team.
- Identity management tools, like EntraID, cover humans well; workloads remain second-class security citizens.
A big-bang SPIFFE rollout across every application is rarely feasible. A better approach is progressive adoption, illustrated in Figure 7.
Figure 7. A progressive rollout of SPIFFE across an enterprise’s applications
SPIFFE Helper for non-SPIFFE-enabled applications
SPIFFE defines a SPIFFE Helper function that writes credentials (for both X.509 and JWTs) to disk for applications that cannot be modified to use the SPIFFE Workload API.
The 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 SVIDs, JWT SVIDs, and JWT bundles from the 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.
Red Hat OpenShift documentation includes a specific example of configuration for PostgreSQL with the SPIFFE Helper.
In this blog post, we focused on a scenario where we are able to make changes to the configuration and operation of a client application to show you the scope of what is required to enable SPIFFE in a client workload. However, for applications where these sorts of changes are not possible, we recommend investigating the SPIFFE Helper as an alternative pathway.
Lessons learned
Implementing SPIFFE for workload-to-database access surfaced some practical considerations worth sharing. Some of the things to keep in mind while implementing such changes include:
Category | Considerations |
Trust and certificates | Dual trust stores (OpenShift Service CA for the server, SPIRE CA for clients), SPIFFE SAN URIs instead of Common Name, short-lived SVID rotation, and how PostgreSQL verifies client certificates |
Identity vs authorization | SPIRE proves which workload is connected; PostgreSQL roles and grants still decide what they may do. Mapping SPIFFE identity to a database user/role is explicit and not automatic |
Platform and adoption | OpenShift specifics (SCCs, mounting the SPIRE CA), asymmetric adoption (SPIFFE-enabled client, non-SPIFFE database), and picking one primary auth method (mTLS or JWT) per connection |
Further details about these considerations can be found in the demo repository.
Choosing where to start: Learnings from the pilot
There is no single correct first integration; the choice will depend on ownership, change appetite, and where long-lived secrets are causing the most pain.
From our discussions with Red Hat customers, a successful initial pilot typically includes workloads already on Red Hat OpenShift where SPIRE attestation can be applied without retrofitting hosts outside the cluster. The approaches in these pilots have generated the following benefits:
- A clear increase in security of Kubernetes Secrets. Replacing a Secret or API key delivers an obvious, demonstrable security improvement.
- A clear decrease in operational burden. SPIFFE and SPIRE automatically handle the management and lifecycle of secrets and certificates.
- Authorization continues to be managed in the same way. Database roles, application ACLs, or mesh policy define authorization, once SPIFFE attests identity.
Table 4 outlines a couple of examples that pilots have used to demonstrate these benefits.
Table 4. Benefits shown in integration pilots
Integration | Why teams often try it early |
Application to database | The data store supports certificate or token authentication natively (PostgreSQL and EDB Postgres are common examples); it does not need to support SPIRE, and it only trusts its CA or IdP |
Application to application (within the same cluster) | Both workloads can receive SVIDs; moves you toward the full SPIFFE-to-SPIFFE model with symmetric identity |
Conclusion
For many critical applications, workload identity is no longer optional. As AI-driven automation behaves more like human operators, the credentials we attach to running processes need the same rigor we demand for user login—cryptographic proof, short lifetimes, and a clear audit trail—all without compromising security by storing long-lived passwords or tokens across platforms.
SPIFFE and SPIRE, delivered through Red Hat OpenShift's zero trust workload identity manager, provide that foundation. SPIFFE-to-SPIFFE workload communication is the destination. Application-to-PostgreSQL certificate authentication is a practical, low-friction first step on the journey. For more information on zero trust workload identity manager please visit the product page and documentation, and maybe try out the Operator for yourself and give us feedback!
The authors would like to thank Shane Boulden and Anjali Telang for their thorough feedback and review.
Red Hat Product Security
About the authors
Derek Waters is an experienced technology professional with over 25 years of experience in software development, solution architecture and team leadership. He has worked on IT projects as diverse as mine clearance simulations, TV ratings prediction, pathology lab information management and highly secure information management systems. He brings an entire career of scars from doing things the wrong way to his role as a Solution Architect at Red Hat helping people do them the right way instead.
James Blair is a Specialist Architect at Red Hat. James is a vivid open source advocate and hands-on engineer. He has a deep technical background in automation, DevSecOps and cloud-native platform engineering across several sectors and is passionate about growing cloud-native in Aotearoa, New Zealand.
Simon Delord is a Solution Architect at Red Hat. He works with enterprises on their container/Kubernetes practices and driving business value from open source technology. The majority of his time is spent on introducing OpenShift Container Platform (OCP) to teams and helping break down silos to create cultures of collaboration.Prior to Red Hat, Simon worked with many Telco carriers and vendors in Europe and APAC specializing in networking, data-centres and hybrid cloud architectures.Simon is also a regular speaker at public conferences and has co-authored multiple RFCs in the IETF and other standard bodies.
More like this
Substituting IP address evaluation with hardware-rooted sovereign zero trust
Zero trust workload identity manager version 1.1 generally available on Red Hat OpenShift
Keep exploring
- Security approaches for hybrid cloud environments
Whitepaper - A layered approach to container and Kubernetes securityWhitepaper
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Virtualization
The future of enterprise virtualization for your workloads on-premise or across clouds