← Week 3: Integration + failure modes + fit analysis

Day 17: SPIRE + ACM PCA Integration

Phase 5 · September 11, 2026

← Week 3: Integration + failure modes + fit analysis

Agenda (2–3 hours)

  • Read (30 min): SPIRE server UpstreamAuthority plugin docs (aws_pca plugin)
  • Study (60 min): How SPIRE uses ACM PCA as its upstream CA
  • Build (60 min): SPIRE server configuration with ACM PCA upstream
← Week 3: Integration + failure modes + fit analysis

Connecting Phase 4 and Phase 5

In Phase 4, SPIRE's CA was self-signed (memory KeyManager, development setup).

Phase 4 (development):
  SPIRE server → self-signed CA → SVIDs
  (No HSM, no ACM PCA, trust domain = "example.org")

Phase 5 (production):
  ACM PCA Subordinate CA
    └── SPIRE Intermediate CA (signed by ACM PCA)
            └── Workload SVIDs (1-hour TTL, signed by SPIRE)

The SPIRE UpstreamAuthority "aws_pca" plugin connects these.

← Week 3: Integration + failure modes + fit analysis

SPIRE UpstreamAuthority: aws_pca

# /etc/spire/server.conf (production version)

server {
  trust_domain  = "leo.amazon.com"
  ca_ttl        = "168h"     # SPIRE intermediate CA validity: 7 days
  default_svid_ttl = "1h"    # Leaf SVID validity: 1 hour
}

plugins {
  DataStore "sql" {
    plugin_data {
      database_type = "postgres"
      connection_string = "postgresql://..."
    }
  }

  UpstreamAuthority "aws_pca" {
    plugin_data {
      region              = "us-east-1"
      certificate_authority_arn = "arn:aws:acm-pca:us-east-1:ACCT:certificate-authority/..."
      signing_algorithm   = "SHA256WITHECDSA"
      ca_signing_template_arn = "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
    }
  }

  KeyManager "aws_kms" {
    plugin_data {
      region   = "us-east-1"
      key_policy_file = "/etc/spire/kms-key-policy.json"
    }
  }
}
← Week 3: Integration + failure modes + fit analysis

What the UpstreamAuthority Plugin Does

When SPIRE server starts:

  1. Generates a new SPIRE intermediate CA key (in KMS, if using aws_kms KeyManager)
  2. Creates a CSR for spiffe://leo.amazon.com
  3. Calls ACM PCA IssueCertificate with:
    • Template: SubordinateCACertificate_PathLen0/V1
    • CSR: SPIRE's intermediate CA CSR
    • Validity: ca_ttl (168 hours)
  4. Stores the signed intermediate CA cert
  5. Issues leaf SVIDs signed by this intermediate (not directly by ACM PCA)

When ca_ttl expires:

  1. SPIRE server auto-rotates: generates new key, calls ACM PCA again
  2. Ongoing — no manual intervention needed
← Week 3: Integration + failure modes + fit analysis

The Full Certificate Chain

ACM PCA Root CA
└── ACM PCA Subordinate CA (provisioning PKI)
    └── SPIRE Intermediate CA (signed by ACM PCA, 168h TTL)
        └── Workload SVID: spiffe://leo.amazon.com/ns/prod/svc/leo-agent (1h TTL)

The SVID chain has 4 levels:

  1. Root (ACM PCA, offline or AWS-managed)
  2. Subordinate (ACM PCA, online)
  3. SPIRE intermediate (ephemeral, 7-day TTL — SPIRE manages this)
  4. Workload SVID (1-hour TTL — SPIRE manages this)

Levels 3 and 4 are entirely automated. ACM PCA only issues level 3.
ACM PCA sees approximately 1 IssueCertificate call / 7 days per SPIRE deployment.

← Week 3: Integration + failure modes + fit analysis

IAM Role for SPIRE Server

SPIRE server needs permission to call ACM PCA:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm-pca:IssueCertificate",
        "acm-pca:GetCertificate"
      ],
      "Resource": "arn:aws:acm-pca:us-east-1:ACCT:certificate-authority/SPIRE-CA-ID",
      "Condition": {
        "StringEquals": {
          "acm-pca:TemplateArn":
            "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["kms:Sign", "kms:GetPublicKey", "kms:DescribeKey"],
      "Resource": "arn:aws:kms:us-east-1:ACCT:key/SPIRE-KMS-KEY-ID"
    }
  ]
}
← Week 3: Integration + failure modes + fit analysis

Testing the Integration

# 1. Start SPIRE server with ACM PCA config
spire-server run -config /etc/spire/server.conf &

# 2. Check server logs for UpstreamAuthority init
# Expected: "SVID obtained upstream authority" log line

# 3. Verify the intermediate CA cert in the trust bundle
spire-server bundle show

# 4. Start an agent and register a workload
spire-server agent list
spire-server entry create \
  -parentID spiffe://leo.amazon.com/spire/agent/aws_iid/... \
  -spiffeID spiffe://leo.amazon.com/ns/prod/svc/test-workload \
  -selector unix:uid:1000

# 5. Fetch an SVID and inspect its chain
spire-agent fetch x509 -write /tmp/svid/

# 6. Verify: the SVID's issuer should be the SPIRE intermediate CA
#    The SPIRE intermediate CA's issuer should be the ACM PCA subordinate CA
openssl verify -CAfile /tmp/bundle.pem /tmp/svid/svid.0.pem
← Week 3: Integration + failure modes + fit analysis

Challenge Assignment

Update the SPIRE Docker Compose config from Phase 4 (Day 12):

  1. Replace the self-signed KeyManager "memory" with UpstreamAuthority "aws_pca"
    (Use a real ACM PCA CA if available, or document the config changes needed)

  2. Add KeyManager "aws_kms" (or keep disk for local testing — document the choice)

  3. Update acm-pca-design.md §6 (preview) with the SPIRE integration:

    ### 6.1 SPIRE + ACM PCA Integration
    - SPIRE server role: UpstreamAuthority "aws_pca"
    - Certificate chain: Root → Sub → SPIRE intermediate → SVID
    - Rotation frequency: SPIRE intermediate auto-rotates every 168h
    - ACM PCA calls per month: ~4 per SPIRE server (one per ca_ttl)
    
  4. Answer: what happens if SPIRE server cannot reach ACM PCA at startup?

← Week 3: Integration + failure modes + fit analysis

Resources

  • SPIRE aws_pca plugin: github.com/spiffe/spire/blob/main/doc/plugin_server_upstreamauthority_aws_pca.md
  • SPIRE aws_kms plugin: github.com/spiffe/spire/blob/main/doc/plugin_server_keymanager_aws_kms.md
  • ACM PCA subordinate template: SubordinateCACertificate_PathLen0/V1
  • Phase 4, Day 11: SPIRE CA options overview
  • Phase 4, Day 12: Docker Compose config (baseline to update)