Minimal infrastructure for running attested workloads with verifiable supply chain, designed for web and mobile clients that need to verify they're talking to genuine, unmodified code.
- Minimal β fewest possible moving parts, all off-the-shelf
- Verifiable β every build is signed, logged, and reproducible
- Attested β clients verify they're talking to real TEE hardware running expected code
- Stateful β encrypted persistent storage, keys released only after attestation
- Updatable β ship new code without exposing or losing data
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BUILD (CI) β
β β
β Source β Nix Build β Container Image β Cosign Sign β
β β β
β βΌ β
β Rekor Transparency Log β
β (digest + signature + measurements) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DEPLOY (k3s) β
β β
β Kyverno admission β verify Cosign signature against Rekor β
β β β
β βΌ β
β Kata Containers β Confidential VM (AMD SEV-SNP) β
β β β
β βΌ β
β dm-verity root FS β hardware attestation quote generated β
β β β
β βΌ β
β KBS releases LUKS key β encrypted volume mounted β
β β β
β βΌ β
β Workload running with attested identity β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VERIFY (Client) β
β β
β Client β Noise handshake β server embeds attestation quote β
β β verify quote signature (AMD/Intel root CA) β
β β compare measurements to transparency log β
β β encrypted session with forward secrecy β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Component | Tool | Purpose |
|---|---|---|
| Build system | Nix | Reproducible, deterministic builds |
| Container signing | Cosign (keyless via Fulcio) | Sign container images with OIDC identity |
| Transparency log | Rekor | Append-only log of signatures + measurements |
| Orchestrator | k3s | Lightweight Kubernetes |
| Admission | Kyverno | Enforce signature policy before pod admission |
| Runtime isolation | Kata Containers | VM-per-pod with TEE support |
| TEE | AMD SEV-SNP | Hardware-level memory encryption + attestation |
| Filesystem integrity | dm-verity | Immutable root filesystem with hash tree |
| Key management | KBS (Trustee) | Release secrets only to attested workloads |
| Storage encryption | LUKS CSI | Encrypted persistent volumes |
| Client protocol | Noise Protocol (Pipes) | Encrypted channels with embedded attestation |
secure-perimeter/
βββ README.md # This file
βββ ARCHITECTURE.md # Detailed architecture doc
βββ nix/ # Nix build system
β βββ flake.nix # Nix flake for reproducible builds
β βββ flake.lock
β βββ default.nix
βββ images/ # Container image definitions
β βββ workload/
β βββ Dockerfile
β βββ dm-verity.conf
βββ deploy/ # k3s deployment manifests
β βββ kustomization.yaml
β βββ namespace.yaml
β βββ kyverno-policy.yaml # Signature verification policy
β βββ kata-runtime-class.yaml # RuntimeClass for confidential VMs
β βββ workload.yaml # Pod/Deployment spec
β βββ pvc-encrypted.yaml # LUKS-encrypted PVC
βββ attestation/ # Attestation infrastructure
β βββ kbs/ # Key Broker Service config
β β βββ kbs-config.yaml
β β βββ reference-values.yaml # Expected measurements
β βββ transparency/
β βββ publish.sh # Script to publish measurements to Rekor
βββ server/ # Server-side workload code
β βββ src/
β β βββ main.ts # Entry point
β β βββ noise.ts # Noise Protocol server with attestation
β β βββ attestation.ts # Read TEE quote, embed in handshake
β β βββ storage.ts # Encrypted state management
β βββ package.json
β βββ tsconfig.json
βββ client/ # Client verification libraries
β βββ web/
β β βββ src/
β β β βββ index.ts # Web client entry
β β β βββ noise-client.ts # Noise Protocol client
β β β βββ verify.ts # Attestation quote verification
β β β βββ transparency.ts # Query Rekor for measurements
β β βββ package.json
β β βββ tsconfig.json
β βββ mobile/
β βββ ios/
β β βββ SecurePerimeter/
β β βββ NoiseClient.swift
β β βββ AttestationVerifier.swift
β βββ android/
β βββ src/
β βββ SecurePerimeter.kt
βββ ci/ # CI/CD pipelines
β βββ .github/
β βββ workflows/
β βββ build-sign.yml # Build + Cosign + Rekor
β βββ deploy.yml # Deploy to k3s
βββ scripts/ # Helper scripts
βββ setup-k3s.sh # k3s + kata + kyverno setup
βββ setup-kbs.sh # KBS deployment
βββ verify-local.sh # Local attestation verification test
- Nix builds container image (deterministic hash)
- GitHub Actions signs with Cosign (keyless via Fulcio OIDC)
- Signature + image digest + expected measurements published to Rekor
- SLSA provenance attached to the build
- Image pushed to registry with Cosign signature
- k3s admission (Kyverno) verifies signature against Rekor before allowing pod
- Kata creates confidential VM (AMD SEV-SNP)
- dm-verity locks root filesystem
- Hardware generates attestation quote (CPU-signed)
- Workload sends quote to KBS β KBS validates β releases LUKS key
- Encrypted volume mounted, workload starts
- Client initiates Noise Pipes handshake
- Server embeds attestation quote in handshake response
- Client verifies:
- Quote signature against AMD/Intel root certificate
- Measurements match known-good values from Rekor
- Public key is bound to the quote
- Forward-secret encrypted session established
- New code merged β CI builds new image
- Cosign signs β Rekor logs new measurements
- Deploy new version to k3s
- KBS reference values updated for new measurements
- New pods attest with new measurements β get LUKS keys
- Old pods drain β encrypted state accessible to new code (same LUKS key)
- Clients verify new measurements from transparency log
Encrypted persistent volumes using LUKS:
- Keys managed by KBS (Trustee)
- Keys released ONLY after TEE attestation succeeds
- Key rotation supported (re-encrypt with new key)
- Data survives code updates (KBS releases key to new valid measurements)
- Host/orchestrator never sees plaintext keys
@stablelib/noiseornoise-protocolfor Noise handshake- Custom attestation quote parser (AMD SEV-SNP report format)
- Rekor client for fetching expected measurements
- AMD/Intel root CA certificates bundled in app
NoiseSocketsfor Noise Protocol- Custom
AttestationVerifierfor TEE quote validation - Keychain for storing verified session keys
noise-javafor Noise Protocol- Custom attestation verification
- Android Keystore for session keys
Critical invariant: no shared encryption keys between users.
Each pod gets its own encryption key hierarchy. Users' data is cryptographically isolated even if pods share the same physical volume or the same TEE.
KBS Master Secret (released after attestation)
|
+-> HKDF(master, "pod:" + pod_id) -> Pod Root Key
| |
| +-> HKDF(pod_root, "user:" + user_id) -> User Data Key
| | +-> Encrypts user's state (AES-256-GCM)
| |
| +-> HKDF(pod_root, "session:" + session_id) -> Session Key
| +-> Ephemeral, forward-secret per-connection
|
+-> Each pod attestation gets a UNIQUE master secret from KBS
(KBS tracks which pod_id received which key)
- Pod boots -> TEE generates attestation quote with unique pod identity
- Pod attests to KBS -> KBS validates, generates unique master secret for this pod
- User connects -> after Noise handshake, user provides user_id
- Key derivation -> HKDF derives user-specific data key from pod master + user_id
- Storage -> each user's data encrypted with their own derived key
- Key never shared -> different pods get different masters; different users get different derived keys
// Key derivation (HKDF-SHA256)
const podRootKey = hkdf(masterSecret, "pod:" + podId);
const userDataKey = hkdf(podRootKey, "user:" + userId);
const encrypted = aes256gcm.encrypt(userDataKey, plaintext);- Pod isolation: Each pod gets a unique master from KBS
- User isolation: HKDF ensures user keys are cryptographically independent
- Forward secrecy: Session keys are ephemeral
- Key rotation: KBS can issue new masters; pods re-derive user keys
- No shared state: Even if two pods serve the same user, they derive independently
# 1. Setup k3s with Kata + Kyverno
./scripts/setup-k3s.sh
# 2. Setup KBS
./scripts/setup-kbs.sh
# 3. Build and sign
nix build .#workload
cosign sign --yes ghcr.io/yourorg/workload@sha256:...
# 4. Deploy
kubectl apply -k deploy/
# 5. Verify from client
cd client/web && npm run verify