Problem
An AWS Nitro Enclave's clock (kvm-clock) is set once at boot from the Nitro Hypervisor and never re-synchronized afterward; it drifts ~1s/day proportional to load (AWS-confirmed). There is no time synchronization in the codebase, so the drift silently corrupts every wall-clock decision the runtime makes:
- Freshness anchor rollback records —
time.Now().Unix() + S3 Object-Lock RetainUntilDate (runtime/anchor.go)
- PCR0 lineage audit timestamps + Object-Lock retain-until (
runtime/lineage.go)
- Self-signed TLS cert
NotBefore/NotAfter (runtime/runtime.go)
- K/V TTL expiry, stream XADD IDs, migration cooldown (
runtime/kvstore.go, runtime/resp.go, runtime/migrate.go)
Fix
Sync CLOCK_REALTIME to the hypervisor's PTP hardware clock (/dev/ptp0, the built-in ptp_kvm driver) for the enclave's lifetime:
(*Runtime).StartClockSync() (runtime/runtime.go) opens /dev/ptp0, hard-steps CLOCK_REALTIME to it once at boot, then spawns the slew loop. Wired into runtime/cmd/runtime/main.go before any outbound TLS / listeners.
runClockSync() (runtime/clocksync.go) reads the PTP + system clocks every 5s and, when drift exceeds 5ms, slews gradually via unix.ClockAdjtime (ADJ_OFFSET_SINGLESHOT) — never a backward jump, safe for the runtime's near-monotonic timestamp consumers. Exits on the lifecycle stop channel.
- Uses already-vendored
golang.org/x/sys/unix (ClockGettime/ClockSettime/ClockAdjtime) — no new dependency, no vendorHash change. Runs as root → CAP_SYS_TIME present.
- Graceful no-op if
/dev/ptp0 is absent (logs clock sync disabled: /dev/ptp0 unavailable and returns).
Why this is host-safe
The enclave's time comes from the Nitro Hypervisor (/dev/ptp0/kvm-clock), which sits outside the parent EC2 instance's trust boundary. A malicious host cannot forge or rewind the clock — setting the parent OS clock has zero effect, and it can't substitute a fake PTP device (the enclave kernel reads it via a KVM hypercall the host isn't in the path of). Across restarts the new enclave re-reads the hypervisor's current time. The host's only residual is delay/DoS, already in the threat model.
Feasibility (verified)
The EIF's kernel is aws/aws-nitro-enclaves-cli v1.2.3 (pinned via monzo/aws-nitro-util). Its bzImage.config has CONFIG_PTP_1588_CLOCK_KVM=y (built-in), CONFIG_PTP_1588_CLOCK=y, CONFIG_KVM_GUEST=y, and CONFIG_DEVTMPFS_MOUNT=y — so /dev/ptp0 is auto-created at boot on real Nitro. No rootfs/kernel/mknod work needed.
Problem
An AWS Nitro Enclave's clock (
kvm-clock) is set once at boot from the Nitro Hypervisor and never re-synchronized afterward; it drifts ~1s/day proportional to load (AWS-confirmed). There is no time synchronization in the codebase, so the drift silently corrupts every wall-clock decision the runtime makes:time.Now().Unix()+ S3 Object-LockRetainUntilDate(runtime/anchor.go)runtime/lineage.go)NotBefore/NotAfter(runtime/runtime.go)runtime/kvstore.go,runtime/resp.go,runtime/migrate.go)Fix
Sync
CLOCK_REALTIMEto the hypervisor's PTP hardware clock (/dev/ptp0, the built-inptp_kvmdriver) for the enclave's lifetime:(*Runtime).StartClockSync()(runtime/runtime.go) opens/dev/ptp0, hard-stepsCLOCK_REALTIMEto it once at boot, then spawns the slew loop. Wired intoruntime/cmd/runtime/main.gobefore any outbound TLS / listeners.runClockSync()(runtime/clocksync.go) reads the PTP + system clocks every 5s and, when drift exceeds 5ms, slews gradually viaunix.ClockAdjtime(ADJ_OFFSET_SINGLESHOT) — never a backward jump, safe for the runtime's near-monotonic timestamp consumers. Exits on the lifecyclestopchannel.golang.org/x/sys/unix(ClockGettime/ClockSettime/ClockAdjtime) — no new dependency, no vendorHash change. Runs as root →CAP_SYS_TIMEpresent./dev/ptp0is absent (logsclock sync disabled: /dev/ptp0 unavailableand returns).Why this is host-safe
The enclave's time comes from the Nitro Hypervisor (
/dev/ptp0/kvm-clock), which sits outside the parent EC2 instance's trust boundary. A malicious host cannot forge or rewind the clock — setting the parent OS clock has zero effect, and it can't substitute a fake PTP device (the enclave kernel reads it via a KVM hypercall the host isn't in the path of). Across restarts the new enclave re-reads the hypervisor's current time. The host's only residual is delay/DoS, already in the threat model.Feasibility (verified)
The EIF's kernel is
aws/aws-nitro-enclaves-cliv1.2.3 (pinned viamonzo/aws-nitro-util). ItsbzImage.confighasCONFIG_PTP_1588_CLOCK_KVM=y(built-in),CONFIG_PTP_1588_CLOCK=y,CONFIG_KVM_GUEST=y, andCONFIG_DEVTMPFS_MOUNT=y— so/dev/ptp0is auto-created at boot on real Nitro. No rootfs/kernel/mknod work needed.