← Week 2: SPIRE Internals and Attestation

Day 9: Node Attestation — AWS IID, k8s PSAT, TPM

Phase 4 · August 13, 2026

← Week 2: SPIRE Internals and Attestation

Agenda (2–3 hours)

  • Read (60 min): SPIRE node attestation docs; AWS IID plugin README; PSAT plugin README
  • Study (45 min): Understand what each attestor sends and what the server verifies
  • Challenge (45 min): Trace through the AWS IID attestation flow step by step
← Week 2: SPIRE Internals and Attestation

What Node Attestation Solves

Node attestation answers: "Is this agent running on a legitimate compute node
that belongs to my infrastructure?"

Without it, anyone could start a SPIRE agent and claim to be any node.
Node attestation uses external trust anchors — things only AWS/k8s/hardware can sign —
to prove the agent is where it claims to be.

The key insight: you're not trusting the agent's self-assertion.
You're trusting a signed document from an external authority (AWS, k8s API server, TPM).

← Week 2: SPIRE Internals and Attestation

AWS IID Node Attestation

IID = Instance Identity Document

AWS provides each EC2 instance with a signed JSON document at:

http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/dynamic/instance-identity/signature

The document contains:

{
  "instanceId": "i-0abc123def456",
  "accountId": "123456789012",
  "region": "us-east-1",
  "imageId": "ami-0abcdef1234567890",
  "instanceType": "t3.micro",
  "privateIp": "10.0.1.42"
}

The signature is an RSA-2048 signature by AWS's regional key.
The SPIRE server verifies it using the AWS public key for that region.

← Week 2: SPIRE Internals and Attestation

AWS IID Attestation Flow

1. SPIRE agent starts on EC2 instance
2. Agent fetches IID document + signature from metadata endpoint
3. Agent sends both to SPIRE server (node attestation call)
4. SPIRE server:
   a. Verifies IID signature using AWS public cert for us-east-1
   b. Checks instanceId not previously seen (prevents replay)
   c. Optionally: verifies accountId, region, instanceType match allow-list
   d. Issues an agent SVID:
      spiffe://leo.amazon.com/spire/agent/aws_iid/us-east-1/i-0abc123def456
5. Agent uses this SVID for all future server communication

The agent's SPIFFE ID encodes the instance ID — so registration entries can
scope workloads to specific instances if needed.

← Week 2: SPIRE Internals and Attestation

Kubernetes PSAT Node Attestation

PSAT = Projected Service Account Token

In k8s, every pod gets a projected service account token — a JWT signed by
the k8s API server.

# k8s mounts this automatically in pods at:
# /var/run/secrets/kubernetes.io/serviceaccount/token
{
  "iss": "https://kubernetes.default.svc",
  "sub": "system:serviceaccount:spire:spire-agent",
  "aud": ["spire-server"],
  "exp": 1754700000
}

The SPIRE server verifies it using the k8s OIDC discovery endpoint.

Agent SVID issued:

spiffe://leo.amazon.com/spire/agent/k8s_psat/my-cluster/node-name
← Week 2: SPIRE Internals and Attestation

TPM-Based Node Attestation

For on-premises or hardware-sensitive environments:

The agent's node attestor uses the TPM 2.0 to prove the node identity:

  1. Server sends a nonce
  2. Agent creates a TPM quote (signed by the TPM's attestation key, includes nonce)
  3. Server verifies the quote against the TPM's endorsement key certificate
  4. If valid: the agent is running on the expected hardware

This is relevant for edge devices — but Amazon Leo's satellite terminals
are probably too constrained for SPIRE agents. Edge node attestation is still
an open research area.

← Week 2: SPIRE Internals and Attestation

Lambda: The Gap

Lambda has no persistent agent. Each invocation is a fresh ephemeral process.

Node attestation options for Lambda:

  1. AWS IAM OIDC — Lambda's IAM role has an OIDC token you can exchange
    for a SPIFFE-like identity via federation with SPIRE
  2. Custom attestor — write a Lambda-specific node attestor plugin using
    the Lambda execution role credentials
  3. Don't use SPIRE for Lambda — use IAM for Lambda auth, SPIFFE for
    long-running services

The honest answer: SPIRE is designed for persistent workloads.
For Lambda, the IAM-native approach is usually better unless you're
bridging to a non-AWS service that requires SPIFFE identity.

← Week 2: SPIRE Internals and Attestation

Challenge Assignment

Trace the AWS IID attestation flow and answer:

  1. What prevents an attacker from replaying a stolen IID document to get an agent SVID?
    (Look at SPIRE server's IID attestor config: skip_block_device and the instance ID deduplication mechanism)

  2. What happens if an EC2 instance is terminated and a new one starts with the
    same instance ID? (Can instance IDs be reused? What does SPIRE do?)

  3. If your Lambda function can't run a SPIRE agent, but needs to call a
    service that validates SPIFFE X.509-SVIDs — what's the cleanest architecture?
    Sketch it in spiffe-analysis.md.

  4. In the k8s PSAT case, the SPIRE server verifies the token against the k8s
    API server. What happens if the k8s API server is unreachable? Does SPIRE
    server fail open or closed?

← Week 2: SPIRE Internals and Attestation

Resources

  • SPIRE AWS IID attestor: github.com/spiffe/spire/blob/main/doc/plugin_server_nodeattestor_aws_iid.md
  • SPIRE k8s PSAT attestor: github.com/spiffe/spire/blob/main/doc/plugin_server_nodeattestor_k8s_psat.md
  • AWS IID documentation: docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
  • SPIRE TPM attestor: github.com/spiffe/spire-tpm-plugin