Skip to content

Instantly share code, notes, and snippets.

@mohashari
Created July 14, 2026 01:03
Show Gist options
  • Select an option

  • Save mohashari/320608d8955990760ea2b15cd3678c1c to your computer and use it in GitHub Desktop.

Select an option

Save mohashari/320608d8955990760ea2b15cd3678c1c to your computer and use it in GitHub Desktop.
Automating Ephemeral SPIFFE/SPIRE ID Attestation for AWS Fargate Tasks via KMS and OIDC — code snippets
resource "aws_kms_key" "spiffe_attestation" {
description = "Asymmetric KMS Key for SPIFFE/SPIRE Fargate Attestation"
customer_master_key_spec = "RSA_2048"
key_usage = "SIGN_VERIFY"
deletion_window_in_days = 7
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::112233445566:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow Fargate Tasks to Sign Claims"
Effect = "Allow"
Principal = {
AWS = [
"arn:aws:iam::112233445566:role/payment-service-fargate-task-role",
"arn:aws:iam::112233445566:role/auth-service-fargate-task-role"
]
}
Action = "kms:Sign"
Resource = "*"
Condition = {
StringEquals = {
"kms:SigningAlgorithm" = ["RSA_SIGN_PSS_2048_SHA_256"]
}
}
}
]
})
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSPIREServerToVerifyAndDescribe",
"Effect": "Allow",
"Action": [
"kms:GetPublicKey",
"kms:Verify"
],
"Resource": "arn:aws:kms:us-west-2:112233445566:key/a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"
},
{
"Sid": "AllowSPIREServerToDescribeECSTasks",
"Effect": "Allow",
"Action": [
"ecs:DescribeTasks"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"ecs:cluster": "arn:aws:ecs:us-west-2:112233445566:cluster/production-core"
}
}
}
]
}
{
"iss": "arn:aws:ecs:us-west-2:112233445566:task/production-core/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"sub": "arn:aws:iam::112233445566:role/payment-service-fargate-task-role",
"aud": "spire-server.production.internal",
"exp": 1783929600,
"iat": 1783926000,
"ecs": {
"cluster": "arn:aws:ecs:us-west-2:112233445566:cluster/production-core",
"task_definition": "arn:aws:ecs:us-west-2:112233445566:task-definition/payment-service:42"
},
"public_key_fingerprint": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
package main
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"time"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
)
type Assertion struct {
Issuer string `json:"iss"`
Subject string `json:"sub"`
Audience string `json:"aud"`
ExpiresAt int64 `json:"exp"`
IssuedAt int64 `json:"iat"`
ECSMetadata map[string]string `json:"ecs"`
PublicKeyFingerprint string `json:"public_key_fingerprint"`
}
func SignAssertion(ctx context.Context, keyID string, pubKeyPEM []byte) ([]byte, []byte, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to load AWS SDK config: %w", err)
}
kmsClient := kms.NewFromConfig(cfg)
// Hash the public key to embed in the assertion
hasher := sha256.New()
hasher.Write(pubKeyPEM)
pubKeyHash := hex.EncodeToString(hasher.Sum(nil))
assertion := Assertion{
Issuer: "arn:aws:ecs:us-west-2:112233445566:task/production-core/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
Subject: "arn:aws:iam::112233445566:role/payment-service-fargate-task-role",
Audience: "spire-server.production.internal",
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(5 * time.Minute).Unix(),
ECSMetadata: map[string]string{
"cluster": "arn:aws:ecs:us-west-2:112233445566:cluster/production-core",
"task": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
},
PublicKeyFingerprint: pubKeyHash,
}
payloadBytes, err := json.Marshal(assertion)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal assertion: %w", err)
}
payloadHash := sha256.Sum256(payloadBytes)
// Request KMS to sign the hashed assertion
signOutput, err := kmsClient.Sign(ctx, &kms.SignInput{
KeyId: &keyID,
Message: payloadHash[:],
MessageType: types.MessageTypeDigest,
SigningAlgorithm: types.SigningAlgorithmSpecRsaSignPss2048Sha256,
})
if err != nil {
return nil, nil, fmt.Errorf("failed to call KMS Sign: %w", err)
}
return payloadBytes, signOutput.Signature, nil
}
package attester
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
)
type Verifier struct {
CachedKMSPublicKey crypto.PublicKey // Loaded via kms:GetPublicKey
}
func (v *Verifier) VerifyFargateAssertion(payload []byte, signature []byte, expectedRole string) error {
var assertion Assertion
if err := json.Unmarshal(payload, &assertion); err != nil {
return fmt.Errorf("failed to decode assertion JSON: %w", err)
}
// 1. Validate role membership and signature parameters
if assertion.Subject != expectedRole {
return fmt.Errorf("role mismatch: got %s, expected %s", assertion.Subject, expectedRole)
}
// 2. Local signature verification using the cached KMS public key
rsaPubKey, ok := v.CachedKMSPublicKey.(*rsa.PublicKey)
if !ok {
return errors.New("cached KMS public key is not an RSA key")
}
payloadHash := sha256.Sum256(payload)
// Verify using RSA-PSS
err := rsa.VerifyPSS(rsaPubKey, crypto.SHA256, payloadHash[:], signature, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
})
if err != nil {
return fmt.Errorf("signature verification failed: %w", err)
}
return nil
}
NodeAttester "aws_kms_oidc" {
plugin_data {
kms_key_arn = "arn:aws:kms:us-west-2:112233445566:key/a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"
oidc_provider_url = "https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"
allowed_task_roles = [
"arn:aws:iam::112233445566:role/payment-service-fargate-task-role",
"arn:aws:iam::112233445566:role/auth-service-fargate-task-role"
]
ecs_metadata_validation = true
kms_pubkey_cache_ttl = "1h"
}
}
spiffe_id_template: "spiffe://example.com/ns/{{ .ecs.cluster | cluster_name }}/sa/{{ .sub | role_name }}/task/{{ .ecs.task }}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment