Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Extend zero trust workload identity manager to virtual machines with Red Hat OpenShift Virtualization

Bridging SPIRE Identity for VMs in Red Hat OpenShift

August 12, 2026
Raushan Kumar Singh
Related topics:
Virtualization
Related products:
Red Hat OpenShift Virtualization

    Containers on Red Hat OpenShift can get automatic cryptographic identities through zero trust workload identity manager, but workloads running inside a virtual machine (VM) cannot. In this tutorial, I demonstrate how I bridged that gap using a virtual socket (VSOCK) and a dedicated in-VM SPIRE agent to give every workload — whether it's running as a container or as an application inside a VM — a short-lived, automatically rotating SPIFFE identity.

    Why an application in a VM doesn't get a SPIRE identity

    If you're running Red Hat OpenShift Virtualization alongside containerized workloads, you've likely encountered this kind of friction. Your containers enjoy automatic cryptographic identities through the zero trust workload identity manager. An application running inside a VM is stuck with static credentials, manual rotation, and an inconsistent security posture. Zero trust workload identity manager has no way to reach inside the guest OS to issue it an identity.

    Zero trust workload identity manager works seamlessly for containers because the SPIRE agent runs as a DaemonSet on each node and the container storage interface (CSI) driver plug-in (spiffe-csi-driver) mounts its Unix socket to every pod. The SPIRE agent performs node attestation, then communicates with the SPIRE server to fetch cryptographic identities for workloads on its node. It issues these identities once workload attestation succeeds. But a VM is a complete computer running inside a computer with its own kernel, its own filesystem, its own process table.

    Even though the VM runs inside a virt-launcher pod, the VM guest OS cannot see the mounted socket. The mount exists in the pod's filesystem. As a result, the virt-launcher pod itself can receive a cryptographic identity, but the workloads running inside the VM cannot.

    Although spiffe-csi-driver mounts the SPIRE agent socket into pods, a VM guest OS cannot access it because it has a separate kernel and filesystem.
    Figure 1: Although spiffe-csi-driver mounts the SPIRE agent socket into pods, a VM guest OS cannot access it because it has a separate kernel and filesystem.

    You can't copy a Unix domain socket into the VM, either. A socket is kernel state (buffers, queues) connected to a running process, not a portable file. It only exists in the kernel that created it.

    So how do we give VM workloads the same zero-trust identity model that containers already enjoy?

    Solution: VSOCK + a dedicated in-VM SPIRE agent

    The basic idea is straightforward: Run a dedicated SPIRE agent inside each VM and connect it to the SPIRE server on the cluster using VSOCK, the secure communication channel built into the Linux kernel specifically for VM-to-host communication.

    Why VSOCK instead of TCP?

    1. Network isolation: VSOCK is invisible to IP routing, port scanning, or any network-based attack.
    2. VM isolation: Each VM gets its own context ID (CID), so VMs can't snoop on each other.
    3. No exposure: If we used TCP over the pod network, then the connection would be visible to anything on the cluster's virtual network, which provides opportunity for interception or spoofing.
    A worker node contains the virt-launcher pod with the VM, a vsock-socat-bridge pod, and the SPIRE agent DaemonSet. The VM communicates with the SPIRE server over a VSOCK-to-TCP bridge.
    Figure 2: A worker node contains the virt-launcher pod with the VM, a vsock-socat-bridge pod, and the SPIRE agent DaemonSet. The VM communicates with the SPIRE server over a VSOCK-to-TCP bridge.

    Two socat instances form a transparent tunnel. The SPIRE agent inside the VM sends TCP traffic to localhost:8081, and socat relays it over VSOCK to the host node. The second socat instance forwards it to the SPIRE server. Both the agent and server speak plain TCP, completely unaware that VSOCK sits in the middle.

    Why per-VM agents?

    A single SPIRE agent on the host can't distinguish between applications running inside a VM. From the host's perspective, the entire VM is just one QEMU process. To give each app (Redis, Postgres, and so on) its own identity, we need an agent inside the VM that can inspect the VM's /proc filesystem and see the individual processes.

    This follows the same model that zero trust workload identity manager uses for Kubernetes nodes: Each node gets an agent, and the agent serves workloads on that node. We treat each VM like a node.

    Deployment schema

    Each application inside the VM gets its own unique cryptographic identity (an SVID) that rotates automatically. In this demonstration, time to live (TTL) is kept very short (120s for Redis, 180s for PostgreSQL) so rotation can be observed in real time.

    A full deployment schema, showing the end-to-end path from VM workloads through VSOCK to the SPIRE server in an OpenShift cluster.
    Figure 3: A full deployment schema, showing the end-to-end path from VM workloads through VSOCK to the SPIRE server in an OpenShift cluster.

    Prerequisites

    Before starting, you need:

    • Red Hat OpenShift cluster with the zero trust workload identity manager operator installed
    • Red Hat OpenShift Virtualization operator installed with a running VM (I used RHEL 9)
    • The oc and virtctl commands

    Set these environment variables for your session:

    export KUBECONFIG="/path/to/your/kubeconfig"
    export APP_DOMAIN="apps.$(oc get dns cluster -o jsonpath='{ .spec.baseDomain }')"
    export VM_NAME="your-vm-name"
    export VM_NAMESPACE="openshift-cnv"
    export SPIRE_NAMESPACE="zero-trust-workload-identity-manager"
    export SPIRE_SERVER_POD="spire-server-0"

    Step 1: Enable VSOCK on the cluster and VM

    VSOCK must be enabled at two levels: The cluster feature gate and the individual VM spec. To enable the VSOCK feature gate at the cluster level:

    oc annotate hyperconverged kubevirt-hyperconverged \
    -n openshift-cnv \  'kubevirt.kubevirt.io/jsonpatch=[{"op":"add","path":"/spec/configuration/developerConfiguration/featureGates/-","value":"VSOCK"}]' \--overwrite

    Enable VSOCK on your specific VM and restart it:

    oc patch vm ${VM_NAME} -n ${VM_NAMESPACE} \
    --type=merge -p \ '{"spec":{"template":{"spec":{"domain":{"devices":{"autoattachVSOCK":true}}}}}}'
    
    oc virt stop ${VM_NAME} -n ${VM_NAMESPACE}
    oc virt start ${VM_NAME} -n ${VM_NAMESPACE}

    After the restart, verify that VSOCK is available inside the VM by checking for /dev/vsock:

    ls -l /dev/vsock

    As output, you see the character device. If it's missing, confirm the feature gate annotation was applied correctly and that the VM was restarted.

    Step 2: Deploy the host-side VSOCK bridge

    This pod runs on the same node as your VM and forwards VSOCK connections to the SPIRE server over TCP:

    NODE=$(oc get vmi ${VM_NAME} -n ${VM_NAMESPACE} -o jsonpath='{.status.nodeName}')
    SPIRE_POD_IP=$(oc get pod ${SPIRE_SERVER_POD} -n ${SPIRE_NAMESPACE} -o jsonpath='{.status.podIP}')
    
    cat <<EOF | oc apply -f -           
    apiVersion: v1
    kind: Pod
    metadata:
      name: vsock-socat-bridge
      namespace: ${VM_NAMESPACE}
    spec:
      nodeName: $NODE
      hostNetwork: true
      containers:
      - name: socat
        image: alpine/socat:latest
        command: ["socat", "-d", "-d", "VSOCK-LISTEN:8081,fork,reuseaddr", "TCP:${SPIRE_POD_IP}:8081"]
        securityContext:
          privileged: true
      restartPolicy: Always
    EOF

    Verify that the bridge is listening by reviewing its logs for something like listening on AF=40 cid:4294967295 port:8081.

    Step 3: Set up the VM-side bridge and SPIRE agent

    SSH to the VM and install socat, then start the TCP-to-VSOCK relay:

    sudo dnf install socatsudo socat TCP-LISTEN:8081,fork,reuseaddr VSOCK-CONNECT:2:8081 &

    Verify that the full VSOCK path works with this Python test:

    python3 -c "import sockets = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)s.settimeout(5)s.connect((2, 8081))print('VSOCK connection successful!')s.close()"

    Now install the SPIRE agent binary:

    curl -L \
    https://github.com/spiffe/spire/releases/download/v1.13.3/spire-1.13.3-linux-amd64-musl.tar.gz \
    -o /tmp/spire.tar.gzcd /tmp && \
    tar xzf spire.tar.gzsudo cp spire-1.13.3/bin/spire-agent /usr/local/bin/

    Get the trust bundle from the SPIRE server (on your workstation):

    oc exec -n ${SPIRE_NAMESPACE} ${SPIRE_SERVER_POD} -- ./spire-server bundle show > bundle.pem

    Copy the bundle to the VM at /opt/spire/bundle.pem, then create the agent configuration at /opt/spire/conf/agent/agent.conf:

    agent {
    	data_dir = "/var/lib/spire/agent"
    	log_level = "DEBUG"
    	server_address = "127.0.0.1"
    	server_port = "8081"
    	socket_path = "/run/spire/sockets/agent.sock"
    	trust_domain = "apps.your-cluster.example.com" #Must match the trust domain configured during zero trust workload identity manager  installation
    	trust_bundle_path = "/opt/spire/bundle.pem"
    }
    plugins {
    	NodeAttestor "join_token" {
    		plugin_data {}
    	}
    	KeyManager "disk" {
    		plugin_data {
    			directory = "/var/lib/spire/agent"
    		}
    	}
    	WorkloadAttestor "unix" {
    		plugin_data {}
    	}
    }

    Two configuration details are critical here. First, server_address points to localhost because socat handles the VSOCK relay transparently. Second, the Unix WorkloadAttestor lets the agent identify processes by UID, which is how we give each application its own identity.

    Step 4: Attest the agent and register workloads

    Generate a join token on your workstation:

    oc exec -n ${SPIRE_NAMESPACE} ${SPIRE_SERVER_POD} -- \
    ./spire-server token generate \
    -spiffeID "spiffe://${APP_DOMAIN}/vm/${VM_NAME}" \
    -ttl 600000

    Start the agent inside the VM with that token:

    sudo mkdir -p /run/spire/sockets /var/lib/spire/agent
    sudo /usr/local/bin/spire-agent run \
     -config /opt/spire/conf/agent/agent.conf \
     -joinToken <your-token> > /tmp/spire-agent.log 2>&1 &

    Watch the logs with tail -f /tmp/spire-agent.log. The agent is connected and ready when you see Node attestation was successful and Starting Workload and SDS APIs.

    Now register your workloads. On your workstation:

    AGENT_ID="spiffe://${APP_DOMAIN}/spire/agent/join_token/<your-token-uuid>"
    oc exec -n ${SPIRE_NAMESPACE} ${SPIRE_SERVER_POD} -- \
     ./spire-server entry create \
     -parentID "$AGENT_ID" \
     -spiffeID "spiffe://${APP_DOMAIN}/vm/${VM_NAME}/redis" \ 
     -selector unix:uid:994 \
     -x509SVIDTTL 120
    oc exec -n ${SPIRE_NAMESPACE} ${SPIRE_SERVER_POD} -- \
     ./spire-server entry create \
     -parentID "$AGENT_ID" \
     -spiffeID "spiffe://${APP_DOMAIN}/vm/${VM_NAME}/postgres" \
     -selector unix:uid:26 \
     -x509SVIDTTL 180

    Each registration entry tells the SPIRE Server: "any process running as this UID, attested by this VM's agent, gets this SPIFFE ID." The agent verifies the caller's UID via SO_PEERCRED on the Unix socket, which cannot be spoofed by userspace.

    Step 5: Verify identity issuance and rotation

    Back in the VM, fetch SVIDs as each application user:

    sudo -u redis /usr/local/bin/spire-agent api fetch x509 \
     -socketPath /run/spire/sockets/agent.sock

    Output:

    Received 1 svid after 6.123148ms
    SPIFFE ID:      spiffe://apps.gcp26feb.gcp.devcluster.openshift.com/vm/rhel9-magenta-gull-92/redis
    SVID Valid After:  2026-02-27 08:55:07 +0000 UTC
    SVID Valid Until:  2026-02-27 09:55:17 +0000 UTC

    Each application gets its own unique X.509 certificate with the SPIFFE ID embedded in the subject alternative name (SAN) field. You can confirm this with the `openssl` command:

    openssl x509 -in /tmp/redis-svid/svid.0.pem -noout -text | grep -A1 "Subject Alternative Name"

    The output:

    X509v3 Subject Alternative Name:
    URI:spiffe://apps.gcp26feb.gcp.devcluster.openshift.com/vm/rhel9-magenta-gull-92/redis

    With the short TTLs I configured (120s for Redis, 180s for PostgreSQL), you can watch automatic rotation happen in real time. The agent renews SVIDs at roughly 50% of their TTL without any application intervention. Wait 70 seconds, re-fetch, and compare to see a completely new certificate with different serial numbers and validity dates.

    What this proves

    This proof-of-concetp validates four key things:

    1. VSOCK provides an isolated channel between VMs and the SPIRE server that never touches the cluster network.
    2. A per-VM SPIRE agent distinguishes individual applications inside the VM using Unix UID-based attestation.
    3. Multiple workloads get unique, short-lived identities that rotate automatically without application changes.
    4. The same trust domain spans containers and VMs, enabling unified zero-trust policies across your entire platform.

    Moving toward production

    For a production deployment, I'd replace several PoC-specific choices:

    VM attestation

    • Proof of concept: join_token (one-time use)
    • Production: x509pop attestation or custom KubeVirt attestor plug-in (supports re-attestation)

    Registration

    • Proof of concept: Manual command-line entries
    • Production: Explore the possibility of automating with spire-controller-manager

    Host bridge

    • Proof of concept: Manual pod deployment
    • Production: Managed by zero trust workload identity manager operator, auto-deployed per node

    VM bridge

    • Proof of concept: Manual socat
    • Production: systemd service with cloud-init

    Installation of per-VM SPIRE Agent

    • Proof of concept: Manual
    • Production: Explore the possibility of automating it with Red Hat Ansible Automation Platform

    SPIRE Server Service Discovery

    • Proof of concept: Connects directly to SPIRE server's pod IP
    • Production: Connect using Kubernetes Service for SPIRE server

    Get started

    If you're running mixed container and VM workloads on OpenShift and want to extend zero-trust workload identity to your VMs:

    1. Install the zero trust workload identity manager operator from OperatorHub.
    2. Follow the steps in this tutorial to bridge SPIRE into your VMs with VSOCK.
    3. Explore the SPIFFE/SPIRE documentation for deeper understanding of workload identity standards.
    4. Check out the OpenShift Virtualization documentation for more on running VMs alongside containers.

    Zero-trust shouldn't stop at the VM boundary. With VSOCK and a dedicated in-VM SPIRE Agent, it doesn't have to.

    Related Posts

    • Troubleshoot Red Hat OpenShift Virtualization localnet with the netobserv command

    • Deploy hosted control planes with OpenShift Virtualization: Distributed hosting

    • Deploy hosted control planes with OpenShift Virtualization

    • Get raw device mapping (RDM) disks with OpenShift Virtualization

    • Right-sizing recommendations for OpenShift Virtualization

    Recent Posts

    • Extend zero trust workload identity manager to virtual machines with Red Hat OpenShift Virtualization

    • Just-in-time access to HashiCorp Vault using the Red Hat Ansible Automation Platform OIDC provider

    • MiDojo: Improve AI agent security with real-world red-teaming

    • Harden local container base images in Podman Desktop

    • Upgrade OpenShift AI faster using an AI coding assistant

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility
    Ask AI