SPIRE
Overview
SPIRE, the SPIFFE Runtime Environment, is the graduated Cloud Native Computing Foundation project that gives every workload a cryptographically verifiable identity. Instead of distributing shared secrets, API keys or long-lived certificates to your services, SPIRE attests what each workload actually is. It issues each one a short-lived identity document, called an SVID, that other services can verify.
This SecureAnyCloud image is a complete, self-contained identity system. Both halves of SPIRE run on one VM: the SPIRE server, which is the certificate authority for your trust domain and holds the registration entries, and a local SPIRE agent, which attests to the server and serves the SPIFFE Workload API over a unix socket. Both services start on boot, so by the time you connect over SSH the system is already issuing identities.
The product is headless, with no web interface. You operate SPIRE through the spire-server and spire-agent command line tools, a pair of unix sockets, and plain health endpoints.
Every VM generates its own trust domain and its own certificate authority on first boot. The signing keys are created locally on your machine. No certificate authority material is shared between instances, and none ships in the image, so identities issued on your VM are valid only inside your own trust domain.
What you get:
- SPIRE 1.15.2 (server and agent), Apache 2.0 licensed
- A per-VM trust domain and self-signed trust domain certificate authority, generated on first boot
- A demonstration workload identity and registration entry
- An identity round trip self test at
/usr/local/sbin/spire-identity-roundtrip.sh - A dedicated 16 GB Azure data disk mounted at
/var/lib/spire - Platform: Microsoft Azure, with default user azureuser
- Server node API on 127.0.0.1:8081 (loopback only)
- Server health on 127.0.0.1:8080 and agent health on 127.0.0.1:8082
- Workload API socket at /run/spire/agent/public/api.sock
- Server admin socket at /run/spire/server/private/api.sock
- Data disk at /var/lib/spire
- 24/7 SecureAnyCloud support
Prerequisites
Before you deploy this image you need:
- A Microsoft Azure subscription where you can create virtual machines
- Permissions to create VMs, virtual networks, network security groups and public IP addresses
- An SSH key pair for access to the VM (the Azure CLI can generate one)
- A virtual network and subnet in the target region, with a network security group allowing inbound port 22 from your management network
- The Azure CLI installed locally if you plan to deploy from the command line
Recommended VM size: Standard_B2s or larger.
Launch the VM from the Azure Marketplace
Sign in to the Azure portal, open Create a resource, and search the Marketplace for SPIRE by SecureAnyCloud. Select the SecureAnyCloud listing and choose Create.
On the Basics tab, pick your subscription and resource group, a region, and a VM size of Standard_B2s or larger. Under Administrator account, select SSH public key, set the username to azureuser, and supply your public key. Under Inbound port rules, allow SSH (22) only.
Complete the Networking tab with your virtual network and subnet, then select Review + create and Create.
First boot initialisation generates this VM's own trust domain and certificate authority and registers the demonstration workload. It takes a short moment after the VM state becomes Running.
Choosing your own trust domain
To choose the trust domain name yourself rather than letting the VM derive one, write a single line containing it to /etc/sac/spire-trust-domain before first boot. For example, use cloud-init user data:
#cloud-config write_files: - path: /etc/sac/spire-trust-domain content: | example.orgIf you do not supply one, the VM derives a unique trust domain from its hostname, so two SecureAnyCloud VMs never share a trust domain by accident.
Launch the VM from the Azure CLI
The following block launches a VM from the SecureAnyCloud SPIRE image into an existing resource group. Replace the values in angle brackets with your own. The image reference comes from the Marketplace listing's Usage information.
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:spire-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-resource-group> \
--name spire \
--image sac1647283583153:spire-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku StandardWhen the VM reaches the Running state, note its public IP address from the portal or with az vm list-ip-addresses.
Connect to Your VM
Connect over SSH as azureuser using your key pair.
ssh azureuser@<vm-ip>Confirm the SPIRE Services and Versions
Check that both halves of SPIRE are installed and running.
spire-server --version spire-agent --version sudo systemctl is-active spire-server.service spire-agent.serviceThe output looks like this:
1.15.2 1.15.2 active activeCheck the Health Endpoints
Both the server and the agent expose plain HTTP health endpoints on the loopback address. They are the quickest way to confirm that the identity system is up.
curl -fsS http://127.0.0.1:8080/ready echo
curl -fsS http://127.0.0.1:8082/ready echoThe output looks like this:
{"catalog.datastore":{},"server":{},"server.ca":{},"server.ca.rotator":{}} {"agent":{}}Confirm that SPIRE listens on the loopback address only. The server's node API on 8081 is deliberately not reachable from outside the VM in the shipped image.
sudo ss -tln | grep -E ':(8080|8081|8082) 'The output looks like this:
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* LISTEN 0 4096 127.0.0.1:8081 0.0.0.0:* LISTEN 0 4096 127.0.0.1:8082 0.0.0.0:*Read Your VM's Identity Details
First boot writes a root-only summary of this VM's identity configuration. It records your trust domain, the identity paths in use and the fingerprint of your certificate authority. It contains no private key material.
sudo cat /root/spire-credentials.txtThe file records values like these. Your trust domain and fingerprint are unique to your VM.
spire.trust.domain=<your-vm>.spire.sac.internal spire.agent.id=spiffe://<your-vm>.spire.sac.internal/agent/sac-local spire.workload.demo.id=spiffe://<your-vm>.spire.sac.internal/workload/demo spire.workload.demo.user=spire-workload spire.ca.fingerprint.sha256=CC:55:70:68:24:2A:50:EF:... spire.server.socket=/run/spire/server/private/api.sock spire.agent.socket=/run/spire/agent/public/api.sock spire.server.bind=127.0.0.1:8081The single-use join token that bootstrapped the local agent was generated on this VM, consumed immediately, and deleted. It is not recorded in this file.
Prove a Real Identity Round Trip
A running process and an open socket prove nothing about an identity system. The image ships a self test that proves the whole path end to end. It is the fastest way to confirm that your deployment works.
The self test pulls the trust bundle directly from the server. It has the demonstration workload fetch an SVID over the Workload API into a fresh directory, and checks that the SVID carries your trust domain. It verifies the certificate chain against the server-supplied bundle and confirms that an unrelated certificate authority is rejected. Finally, it reconfirms the positive case.
sudo /usr/local/sbin/spire-identity-roundtrip.shThe output looks like this:
OK trust_domain=<your-vm>.spire.sac.internal workload=spiffe://<your-vm>.spire.sac.internal/workload/demo ca_sha256=CC:55:70:68:24:2A:50:EF:...Any other output begins with FAIL and names the step that did not hold.
Inspect Attested Agents and Registration Entries
The server tracks which agents have attested and which workloads may receive which identities.
sudo spire-server agent list -socketPath /run/spire/server/private/api.sockThe output looks like this:
Found 1 attested agent: SPIFFE ID : spiffe://<your-vm>.spire.sac.internal/spire/agent/join_token/fc29a008-8ea7-4bcd-b417-a1904a16b416 Attestation type : join_token Expiration time : 2026-07-19 17:03:13 +0000 UTC Serial number : 167376281661483824124114967622690597522 Can re-attest : false Agent version : 1.15.2Now list the registration entries.
sudo spire-server entry show -socketPath /run/spire/server/private/api.sockTwo entries exist on a fresh VM. The first is the delegated agent identity, .../agent/sac-local, which is the parent your own workload entries hang off. The second is the demonstration workload, selected by the unix user id it runs as.
Entry ID : 9641806b-147c-490c-8fc6-ad1d259dfef8 SPIFFE ID : spiffe://<your-vm>.spire.sac.internal/workload/demo Parent ID : spiffe://<your-vm>.spire.sac.internal/agent/sac-local Revision : 0 X509-SVID TTL : default JWT-SVID TTL : default Selector : unix:uid:997Fetch and Inspect an SVID
Fetch an identity document as the demonstration workload and inspect what SPIRE issued. The workload identity is granted on the basis of the unix user the process runs as, so run the fetch with sudo -u spire-workload.
The SPIFFE identity is carried in the certificate's URI Subject Alternative Name, which makes it verifiable by any SPIFFE-aware peer.
WD=$(mktemp -d); chmod 777 "$WD"
sudo -u spire-workload spire-agent api fetch x509 \
-socketPath /run/spire/agent/public/api.sock -write "$WD/" openssl x509 -in "$WD/svid.0.pem" -noout -text | grep -A1 "Subject Alternative Name" openssl x509 -in "$WD/svid.0.pem" -noout -issuer -dates openssl verify -CAfile "$WD/bundle.0.pem" "$WD/svid.0.pem" rm -rf "$WD"The output looks like this:
Received 1 svid after 1.839615ms SPIFFE ID: spiffe://<your-vm>.spire.sac.internal/workload/demo SVID Valid After: 2026-07-19 16:04:08 +0000 UTC SVID Valid Until: 2026-07-19 17:04:18 +0000 UTC CA #1 Valid After: 2026-07-19 16:03:02 +0000 UTC CA #1 Valid Until: 2026-07-26 16:03:12 +0000 UTC Writing SVID #0 to file /tmp/tmp.uj6oEt8zMe/svid.0.pem. Writing key #0 to file /tmp/tmp.uj6oEt8zMe/svid.0.key. Writing bundle #0 to file /tmp/tmp.uj6oEt8zMe/bundle.0.pem. X509v3 Subject Alternative Name: URI:spiffe://<your-vm>.spire.sac.internal/workload/demo issuer=C = GB, O = sac, CN = sac SPIRE CA (<your-vm>.spire.sac.internal), serialNumber = 97931985402370782961606615520983568838 notBefore=Jul 19 16:04:08 2026 GMT notAfter=Jul 19 17:04:18 2026 GMT /tmp/tmp.uj6oEt8zMe/svid.0.pem: OKNote the one-hour lifetime. Short-lived identities are the point of SPIFFE. The agent renews them automatically well before expiry, so there is no long-lived credential to steal or rotate by hand.
Register Your Own Workload
Register one of your own services by selecting on the unix user id it runs as. The trust domain is read from the credentials file, so you can copy the command as is.
The block is safe to rerun. SPIRE rejects a duplicate registration with AlreadyExists, so the block checks for the entry before creating it.
TD=$(sudo grep '^spire.trust.domain=' /root/spire-credentials.txt | cut -d= -f2-) SOCK=/run/spire/server/private/api.sock MYID="spiffe://${TD}/workload/my-service"
EXISTING=$(sudo spire-server entry show -socketPath "$SOCK" -spiffeID "$MYID" 2>/dev/null || true) case "$EXISTING" in *"Entry ID"*) echo "Entry already exists for $MYID" printf '%s\n' "$EXISTING" ;; *)
sudo spire-server entry create -socketPath "$SOCK" \
-parentID "spiffe://${TD}/agent/sac-local" \
-spiffeID "$MYID" \
-selector "unix:uid:$(id -u www-data)" ;; esacThe output looks like this:
Entry ID : 3f9d1a0e-e37e-4192-862b-36822ee9675f SPIFFE ID : spiffe://<your-vm>.spire.sac.internal/workload/my-service Parent ID : spiffe://<your-vm>.spire.sac.internal/agent/sac-local Revision : 0 X509-SVID TTL : default JWT-SVID TTL : default Selector : unix:uid:33Any process running as that user can now fetch its identity from the Workload API socket, with no secret to distribute.
Selectors are not limited to the unix user id. unix:gid, unix:path and other selectors let you describe a workload as precisely as you need.
Where the Identity Material Lives
The trust domain certificate authority signing keys, the registration datastore and the agent's own node identity all live on a dedicated Azure data disk mounted at /var/lib/spire. The disk is separate from the operating system disk, so you can resize and snapshot it independently.
df -h /var/lib/spire
sudo ls -l /var/lib/spire/server /var/lib/spire/agentThe output looks like this:
Filesystem Size Used Avail Use% Mounted on /dev/sdc 16G 304K 15G 1% /var/lib/spire /var/lib/spire/agent: -rw------- 1 root root 2944 Jul 19 16:04 agent-data.json -rw-r--r-- 1 root root 1062 Jul 19 16:03 bootstrap-bundle.crt -rw------- 1 root root 222 Jul 19 16:03 keys.json /var/lib/spire/server: -rw-r----- 1 spire spire 143360 Jul 19 16:03 datastore.sqlite3 -rw------- 1 spire spire 425 Jul 19 16:03 keys.jsonserver/keys.json holds your trust domain's certificate authority signing keys. It is readable only by the spire service user, and it was generated on this VM. Back up this disk if you need your trust domain to survive the VM.
Open the Server Port for Agents on Other Hosts
The shipped image runs a single-node identity system. The server listens on 127.0.0.1:8081 only, so no unauthenticated identity API is exposed to the network. To attest SPIRE agents running on other hosts, bind the server to a routable address and open the port to those hosts only.
Edit /etc/spire/server/server.conf and change bind_address from 127.0.0.1 to 0.0.0.0. Then restart the server:
server { bind_address = "0.0.0.0" bind_port = "8081" ... }Then allow inbound 8081 from the hosts that need it, never from the whole internet:
az network nsg rule create \
--resource-group <your-resource-group> \
--nsg-name <your-nsg> \
--name allow-spire-server \
--priority 1010 \
--protocol Tcp --destination-port-ranges 8081 \
--source-address-prefixes <your-workload-cidr> \
--access AllowEach remote agent needs its own join token, generated on the server, plus a copy of your trust bundle:
TD=$(sudo grep '^spire.trust.domain=' /root/spire-credentials.txt | cut -d= -f2-) sudo spire-server token generate \
-socketPath /run/spire/server/private/api.sock \
-spiffeID "spiffe://${TD}/agent/remote-host-1" -ttl 600
sudo spire-server bundle show -socketPath /run/spire/server/private/api.sockJoin tokens are single-use and short-lived. For production fleets, consider a node attestor suited to your platform rather than issuing tokens by hand.
The Azure managed identity node attestors require Azure identity configuration that you set up yourself. They are deliberately not enabled in this image, so that it works standalone out of the box.
Server Components
- SPIRE Server 1.15.2: /opt/spire/bin/spire-server
- SPIRE Agent 1.15.2: /opt/spire/bin/spire-agent
- Server configuration: /etc/spire/server/server.conf
- Agent configuration: /etc/spire/agent/agent.conf
- Identity round trip self test: /usr/local/sbin/spire-identity-roundtrip.sh
Filesystem Layout
- / (29 GB): root filesystem
- /boot/efi (105 MB): UEFI boot partition (Gen2 Hyper-V)
- /var/lib/spire (16 GB): dedicated data disk holding the certificate authority keys, datastore and agent identity
- /mnt (size varies): Azure temporary resource disk
Key directories:
- /opt/spire/bin: SPIRE server and agent binaries
- /etc/spire: server and agent configuration
- /var/lib/spire/server: certificate authority signing keys and the registration datastore
- /var/lib/spire/agent: the agent's node identity and bootstrap trust bundle
- /run/spire/server/private: server admin socket (mode 0700)
- /run/spire/agent/public: Workload API socket
Managing the Services
sudo systemctl status spire-server.service
sudo systemctl status spire-agent.serviceUse sudo systemctl restart spire-server.service and sudo systemctl restart spire-agent.service to restart the services. Use journalctl -u spire-server.service -n 50 or journalctl -u spire-agent.service -n 50 to read their logs.
The agent persists its node identity, so it reattests from stored state across a restart without needing a new join token.
Scripts and Log Files
- /usr/local/sbin/spire-firstboot.sh: first boot initialisation, which generates the per-VM trust domain and certificate authority
- /usr/local/sbin/spire-identity-roundtrip.sh: identity round trip self test
- /usr/local/sbin/spire-write-config.sh: renders the server and agent configuration for a given trust domain
- /usr/local/sbin/spire-agent-start.sh: agent start wrapper that presents the single-use join token only when needed
- /var/lib/sac/data/spire-firstboot.done: first boot sentinel
- /stage/scripts/initial_boot_update.sh: standard SecureAnyCloud first boot update helper
On Startup
On the first boot of a new VM, spire-firstboot.service generates this VM's trust domain and a brand new certificate authority, starts the server, exports the trust bundle, issues a single-use join token to the local agent, registers the demonstration workload and writes /root/spire-credentials.txt. It then creates the sentinel file /var/lib/sac/data/spire-firstboot.done, so that it never runs again.
The standard SecureAnyCloud initial_boot_update.sh helper also runs on first boot from a root crontab entry. To disable it, edit the root crontab with sudo crontab -e and remove the @reboot line.
Troubleshooting
The round trip self test reports FAIL
Run it again and read the named step. FAIL the demo workload could not fetch an X.509 SVID usually means that the agent is not running or has not attested. Check sudo systemctl status spire-agent.service and journalctl -u spire-agent.service -n 50.
A workload gets no identity
The Workload API only issues an identity to a process that matches a registration entry's selectors. Confirm that the entry exists with sudo spire-server entry show -socketPath /run/spire/server/private/api.sock. Then confirm that the selector matches the user the process runs as, with id -u <user>.
The agent will not attest after a restart
The agent stores its node identity in /var/lib/spire/agent/agent-data.json. If that file is removed the agent needs a fresh join token, because join tokens are single use. Generate one with sudo spire-server token generate and restart the agent.
First boot did not complete
Check sudo systemctl status spire-firstboot.service and journalctl -u spire-firstboot.service. The sentinel at /var/lib/sac/data/spire-firstboot.done is only created on success.
The server will not start after a configuration change
SPIRE validates its configuration at startup. Read journalctl -u spire-server.service -n 50 for the parse error.
Changing the trust domain of an existing server invalidates every identity already issued, because the certificate authority is scoped to the trust domain.
Security Recommendations
- Keep the server on loopback unless you need remote agents. The shipped default exposes no identity API to the network.
- Restrict port 8081 to your workload subnet if you open it, never to the whole internet.
- Protect the data disk.
/var/lib/spire/server/keys.jsonholds your trust domain's certificate authority signing keys. Anyone with those keys can mint identities in your trust domain. - Keep SVID lifetimes short. The default is one hour, which limits the value of a stolen certificate. The agent renews automatically.
- Prefer precise selectors. A broad selector grants an identity to more processes than you intended.
- Restrict SSH to your management network with a network security group rule.
- Keep the VM patched with
sudo apt-get update && sudo apt-get upgrade.
Support
For assistance, contact SecureAnyCloud support:
- Email: sales@secureanycloud.com
- Response Time: 24/7 with a one hour average response time for critical issues
- Website: https://secureanycloud.com
Deploy on Azure
Launch SPIRE on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com