Demo Walkthrough
This walkthrough shows the complete Ratatouille flow from a user’s perspective: enroll a machine, push a signed policy, watch continuous attestation, then trigger a failure and recover by pushing an updated policy.
No deep Keylime knowledge required. That’s the point.
The Setup
Section titled “The Setup”The demo uses two machines. The Core server runs the Ratatouille backend, Keylime verifier, and Keylime registrar. The Agent machine is the machine being attested and runs the Keylime Rust agent. The Ratatouille UI is accessible from the Core server.
Phase 1: Enroll a Machine and Generate a Baseline
Section titled “Phase 1: Enroll a Machine and Generate a Baseline”1. Create a Policy Group in the UI
Section titled “1. Create a Policy Group in the UI”Navigate to the Ratatouille dashboard → New Group → give it a name.
A baseline enrollment token is generated: esp_b_...
2. Run the install script on the agent machine
Section titled “2. Run the install script on the agent machine”sudo ./install.sh \ --token "esp_b_..." \ --registrar "<core-server-ip>" \ --core-url "http://<core-server-ip>:8001" \ --baselineThe script installs the Keylime Rust agent, configures it via systemd environment variables, and starts it. The agent performs the TPM2 activate-credential ceremony:
- Agent generates an AIK (Attestation Identity Key) and presents its EK certificate
- The registrar issues an encrypted challenge that only the TPM can decrypt
- Agent decrypts it using the TPM, confirming hardware identity
The agent then sends its full IMA measurement log to Ratatouille Core.
3. Ratatouille generates the baseline policy
Section titled “3. Ratatouille generates the baseline policy”Core calls Keylime’s create_policy.py against the IMA log, building a runtime policy
from TPM-measured values (not re-hashed from disk). The policy is stored as Baseline::<group_name>.
The agent now appears in the UI with status: provisioning. It has an identity and a baseline,
but no active signed policy has been pushed yet.
Phase 2: Connect GitHub and Push a Signed Policy
Section titled “Phase 2: Connect GitHub and Push a Signed Policy”4. Connect a GitHub repo
Section titled “4. Connect a GitHub repo”Click Connect GitHub → install the Ratatouille GitHub App → it links your repo to the Policy Group.
Your repo structure:
runtime/ runtime_policy.json artifact.sigstore.json5. Sign the policy with cosign
Section titled “5. Sign the policy with cosign”cosign sign-blob runtime_policy.json \ --bundle artifact.sigstore.json \ --identity-token $(gcloud auth print-identity-token)Cosign opens a browser to complete OIDC authentication. The bundle written to
artifact.sigstore.json contains the signature, certificate chain, and Rekor log entry.
6. Push to main
Section titled “6. Push to main”git add runtime/git commit -m "policy: baseline v1"git pushRatatouille’s GitHub webhook fires:
- HMAC-SHA256 signature verified on the raw payload body
- Policy and bundle fetched from the commit SHA via GitHub API
- Sigstore bundle verified (invalid signature or untrusted signer → rejected)
- Policy stored in DB,
active_policy_idupdated on the group keylime_tenant -c addruns for every enrolled agent
The agent flips to active in the UI.
Phase 3: Watch Continuous Attestation
Section titled “Phase 3: Watch Continuous Attestation”The Keylime verifier polls the agent every ~10 seconds:
INFO keylime.tpm Checking IMA measurement list on agent: d432fbb3-d2f1-4a97-9ef7-75bd81c00000INFO keylime.tpm Checking IMA measurement list on agent: d432fbb3-d2f1-4a97-9ef7-75bd81c00000INFO keylime.tpm Checking IMA measurement list on agent: d432fbb3-d2f1-4a97-9ef7-75bd81c00000Each cycle:
- Verifier requests a fresh TPM quote (nonce-based, replay-resistant)
- Agent returns signed PCR values + incremental IMA log entries
- Verifier checks PCR[10] extends with each new IMA entry
- Every new IMA entry is looked up against the runtime policy hash set
- All entries match → attestation passes
Phase 4: Trigger an Attestation Failure
Section titled “Phase 4: Trigger an Attestation Failure”On the attested machine:
# Option A: compile a new binarygcc /tmp/evil.c -o /tmp/evil && /tmp/evil
# Option B: copy an existing binary to a new path (new IMA entry, different path)cp /bin/ls /tmp/evil_ls && /tmp/evil_lsWithin ~10 seconds:
WARNING keylime.ima Hashes for file /tmp/evil_ls don't match b94d27b9... not in policyERROR keylime.ima IMA ERRORS: Some entries couldn't be validated. ImaNg failures: 1WARNING keylime.verifier Agent d432fbb3... failed, stopping pollingThe agent flips to failed in the UI.
Phase 5: Recover by Updating the Policy
Section titled “Phase 5: Recover by Updating the Policy”If the execution was intentional (a legitimate software update), the recovery path is: approve the new binary via policy GitOps.
# Get the hash of the new binary from the IMA logsudo grep /tmp/evil_ls /sys/kernel/security/ima/ascii_runtime_measurements# → sha256:b94d27b9934d3e08...
# Add it to runtime_policy.json under the "/tmp/evil_ls" key# Sign the updated policycosign sign-blob runtime_policy.json \ --bundle artifact.sigstore.json \ --identity-token $(...)
git add runtime/ && git commit -m "policy: allow /tmp/evil_ls" && git pushRatatouille detects the push, re-runs the Sigstore verification, and pushes the updated policy
to the verifier. Attestation resumes with the new policy. Agent flips back to active.
What you just saw
Section titled “What you just saw”Enrollment established TPM hardware identity via the activate-credential ceremony. Baseline generation measured every binary running at that moment via IMA and captured the result as the approved policy. The policy push verified the Sigstore signature, checked the Rekor log, and fanned the policy out to the agent. During continuous polling, the verifier checked the TPM quote and IMA log every 10 seconds against that policy. When a new ELF binary appeared, attestation failed within one poll cycle. Recovery was a signed Git push, and the updated policy was verified and enforced automatically.
This is the full cryptographic chain, from TPM hardware through to the relying party, operating in real time.