Skip to main content

X.509 Proof of Possession

The X.509 Proof of Possession (X.509 PoP) method attests agents that have been provisioned with an X.509 certificate through an out-of-band mechanism. The Trust Domain Server verifies that the certificate chains to a trusted CA, then issues a cryptographic challenge to confirm the agent holds the corresponding private key.

Use this method in environments where you already operate a PKI and can provision host certificates, or when you need to carry structured identity (via URI SANs) from your existing certificate infrastructure into SPIFFE IDs.

Attributes available for SVID issuance

The following attributes are produced from the agent's leaf certificate and its verified chain. All attributes have the origin x509_pop.

AttributeDescription
x509_pop.subject.common_nameCommon Name from the leaf certificate's Subject. Only emitted when non-empty.
x509_pop.serial_numberLeaf certificate serial number as a lowercase hex string.
x509_pop.ca.<serialNumber>One attribute per CA in the verified chain (excluding the leaf). Key suffix is the CA's serial number in hex; value is the SHA1 fingerprint of that CA.
x509_pop.san.<type>One attribute per URI SAN matching x509pop://{trust_domain}/{type}/{value}. Key suffix is {type}; value is {value}.

URI SAN Format

URI SANs in the leaf certificate that match the following pattern are parsed into x509_pop.san.* attributes:

x509pop://{trust_domain}/{type}/{value}

For example, a certificate for trust domain example.com with the URI SAN x509pop://example.com/environment/production produces:

x509_pop.san.environment = "production"

URI SANs that do not match the trust domain prefix are silently ignored.

Where Attributes Are Available

Agent attributes are available in SPIFFE ID path templates, X.509 SVID customization templates, and JWT SVID additional claims — alongside workload attributes from the same request.

/agents/{{x509_pop.subject.common_name}}/{{x509_pop.serial_number}}
# e.g. /agents/my-host.example.com/3a7f2c1b

How to Deploy

X.509 PoP spans two configuration surfaces. Deploy them in this order to avoid downtime: server-side Managed Config first, then agent configuration.

Step 1 — Update cluster configuration

Create or update the AgentAttestation Managed Config policy with your PEM-encoded CA certificate(s) inline in the caCerts field:

section: AgentAttestation
schema: v1
spec:
policies:
- name: x509pop_policy
requiredAttestors:
- type: x509pop
config:
caCerts: |
-----BEGIN CERTIFICATE-----
<base64-encoded CA certificate>
-----END CERTIFICATE-----

Apply it using spirlctl:

spirlctl config set cluster --id <cluster-id> attestation-policy.yaml

Or using Terraform:

resource "spirl_cluster_config" "agent_attestation" {
cluster_id = spirl_cluster.my_cluster.id
sections = {
AgentAttestation = <<-YAML
section: AgentAttestation
schema: v1
spec:
policies:
- name: x509pop_policy
requiredAttestors:
- type: x509pop
config:
caCerts: |
-----BEGIN CERTIFICATE-----
<base64-encoded CA certificate>
-----END CERTIFICATE-----
YAML
}
}

Once a configuration document passes validation and is stored, the Defakto control plane syncs it to your Trust Domain Servers automatically. No server or agent restart is required.

Verify by watching server logs for:

"Metadata updated"
"Updating configuration snapshot"
"Updating configuration cache"

Server Configuration Reference

FieldRequiredDefaultDescription
caCertsYesPEM-encoded trusted root CA certificates. One or more concatenated. The agent's certificate chain must verify against these roots.
maxIntermediatesNo4Maximum number of intermediate certificates in the agent's chain.
maxRSAKeySizeNo8192Maximum RSA key size in bits. Non-RSA keys are not subject to this limit.

Step 2 — Configure the Agent

The agent needs paths to its certificate and private key. These are node-local filesystem paths and must be set as static bootstrap configuration.

agent:
auth:
clusterId: c-xxxxxx
attestors:
- type: x509pop
config:
certificatePath: "/etc/spirl/agent.crt"
privateKeyPath: "/etc/spirl/agent.key"
# intermediatesPath: "/etc/spirl/intermediates.pem" # optional

Agent Configuration Reference

FieldRequiredDescription
privateKeyPathYesPath to the PEM-encoded private key (PKCS#1 or PKCS#8). Must match the certificate's public key.
certificatePathYesPath to the PEM-encoded X.509 leaf certificate. Must include digitalSignature in its Key Usage extension.
intermediatesPathNoPath to a PEM file of intermediate certificates forming the chain to the trusted root.

Step 3 — Verify

Server logs — look for these in order:

  1. "Login started with multi-attestation support" — confirms the agent offered providedMethods: ["x509pop"]
  2. "Authorization received and verified" — includes agentAttestationAttributes with the extracted certificate identity:
    {
    "msg": "Authorization received and verified",
    "agentAttestationAttributes": [
    "x509pop:x509_pop.subject.common_name=\"my-host.example.com\"",
    "x509pop:x509_pop.serial_number=\"3a7f2c1b\""
    ]
    }
  3. "Connected to agent" — session is fully established

Agent logs — enable debug logging to see "Sending Login" with attestors: ["x509pop"]. At the default log level, "Connected to server" confirms the session is live.

Metrics — confirm proofs are succeeding:

spirl_attestation_signer.proof{attestor_type="x509pop",outcome="success"}
spirl_attestation_agent.proof{attestor_type="x509pop",outcome="success"}

Alert on outcome="failed" to detect certificate validation or key-matching failures.

Common errors:

ErrorLikely cause
no cluster policy authorizes the provided attestorsAgent's x509pop method doesn't match any policy in the cluster's AgentAttestation config
Attestor rejected proof, policy failedCertificate chain didn't verify against caCerts — check attestorType: x509pop in logs
Authorization failed: challenge response failed validationPrivate key at privateKeyPath does not match the certificate

Security Considerations

X.509 PoP uses a cryptographic challenge-response protocol:

  1. The agent presents its certificate chain.
  2. The Trust Domain Server validates the chain against the configured trust bundle.
  3. The server issues a random nonce challenge.
  4. The agent signs the nonce with its private key.
  5. The server verifies the signature.

Merely possessing a copy of the certificate is insufficient — the private key must be present on the attesting host.

Both RSA (RSA-PSS with SHA-256) and ECDSA (with SHA-256) key types are supported.

Resource exhaustion protection:

  • maxIntermediates (default 4) limits chain depth to prevent CPU/memory exhaustion from deeply nested chains.
  • maxRSAKeySize (default 8192 bits) prevents signature-verification exhaustion from abnormally large RSA keys.

Troubleshooting

Error in Trust Domain Server logsLikely cause
attestation policy not found or no matching policyManaged Config not applied
caCerts is requiredcaCerts field is present but empty
could not parse any PEM certificatescaCerts contains invalid PEM
certificate chain verification failedAgent cert is not signed by a trusted CA in caCerts
failed to verify signaturePrivate key at privateKeyPath does not match the certificate
certificate does not include digitalSignature key usageAgent certificate was issued without the required Key Usage extension