Anti-Rollback Protection
Anti-rollback protection prevents an attacker or misconfiguration from replaying an older bundle to restore previously-revoked certificates to “good” status. This is a critical security property — without it, an adversary with access to historical bundles could silently undo revocations.
The Threat
Consider a certificate revoked in epoch 40. If an attacker can replace the current bundle (epoch 42) with a bundle from epoch 38 (before the revocation), the edge would start serving “good” responses for the revoked certificate. Anti-rollback makes this impossible.
flowchart LR
subgraph Epoch Chain
E38[Epoch 38<br/>cert: good] --> E39[Epoch 39] --> E40[Epoch 40<br/>cert: revoked] --> E41[Epoch 41] --> E42[Epoch 42<br/>cert: revoked]
end
E38 -.->|"Rollback attempt<br/>REJECTED"| Edge[Edge Node]
E42 -->|"Current bundle<br/>ACCEPTED"| Edge
Epoch Chain
Each bundle carries a monotonically increasing epoch number, scoped per CA. The signer increments the epoch on every bundle production — whether scheduled or triggered by an urgent revocation.
The epoch chain forms a simple sequence:
epoch N → epoch N+1 → epoch N+2 → ...
Every bundle’s epoch is recorded in its CBOR manifest and covered by the CMS seal, so it cannot be altered without breaking the signature.
High-Water Marks
The state_db directory persists the highest epoch seen for each CA. On every bundle load, the edge enforces:
new_epoch ≥ stored_high_water_mark
If the new bundle’s epoch is less than the stored high-water mark, the bundle is rejected as a rollback.
Example
| Event | Stored HWM | Incoming Epoch | Result |
|---|---|---|---|
| Load epoch 40 | 39 | 40 | Accepted — HWM updated to 40 |
| Load epoch 41 | 40 | 41 | Accepted — HWM updated to 41 |
| Load epoch 38 | 41 | 38 | Rejected — rollback detected |
| Load epoch 41 (different digest) | 41 | 41 | Rejected — fork detected |
Fork Detection
If two bundles arrive with the same epoch but different content digests, this is a fork — it means two signers produced bundles independently for the same CA, or a single signer’s state was cloned.
Fork detection catches:
- Misconfigured duplicate signers: Two signer instances both believe they are authoritative for the same CA
- State cloning: A signer’s state directory was copied, producing a second lineage
- Compromise: An attacker with the signing key producing alternative bundles
Fork is always a critical security event requiring immediate investigation.
Rejection Reasons
Bundle load failures are categorized into four reasons:
| Reason | Condition | Severity |
|---|---|---|
| rollback | New epoch < stored high-water mark | Critical — possible replay attack |
| fork | Same epoch, different content digest | Critical — duplicate signer or compromise |
| digest | Bundle content does not match manifest digest | High — corruption or tampering |
| seal | CMS signature verification failed | High — wrong key, tampering, or corruption |
The first two (rollback and fork) are security events. The latter two (digest and seal) typically indicate data corruption during transfer, though tampering should not be ruled out.
state_db Persistence
The state_db directory is where epoch high-water marks live. It must persist across process restarts, container recreations, and node replacements.
[storage]
state_db = "/var/lib/hoike/state"
If state_db is lost, the node loses all high-water marks and becomes vulnerable to rollback attacks until it loads a current-epoch bundle. This is why state_db is a separate path from bundle_dir:
bundle_dircan be ephemeral — bundles are replaceable (re-pull from signer or re-import)state_dbis persistent state — mount it on durable storage, back it up, and include it in disaster recovery plans
In containerized environments, state_db should be on a persistent volume, not an ephemeral container filesystem.
Critical Alerts
Two metrics form the foundation of hoike operational monitoring:
bundle_next_update_seconds
What: Gauge showing seconds until the current bundle’s nextUpdate timestamp.
Why it matters: When this reaches zero, the edge is serving responses past their validity window. Relying parties that check freshness will reject them.
Alert thresholds:
| Level | Threshold | Meaning |
|---|---|---|
| Warning | < 4h remaining | Signer may be down; investigate |
| Critical | < 1h remaining | Responses will expire soon; immediate action required |
The warning threshold should be comfortably above batch_interval (default 1h). With a 24h validity and 1h batch interval, alerting at 4h gives you three missed batches before going critical.
bundle_load_failures (by reason)
What: Counter of failed bundle loads, labeled by rejection reason (rollback, fork, digest, seal).
Why it matters: Any non-zero increment for rollback or fork is a critical security alert requiring immediate investigation.
Alert rules:
| Reason | Alert level | Action |
|---|---|---|
rollback | Critical | Possible replay attack. Investigate bundle distribution path immediately. |
fork | Critical | Duplicate signer or compromise. Identify and shut down the rogue signer. |
digest | Warning | Likely transfer corruption. Re-transfer the bundle. |
seal | Warning | Wrong signing key or corruption. Verify signer configuration. |
Recovery Procedures
Rollback Detected
- Investigate the source. Why was an old bundle offered? Common causes:
- Stale bundle cached in a CDN or reverse proxy
- Misconfigured bundle distribution pipeline pointing at an old directory
- An attacker replaying a captured bundle
- Fix the distribution path. Purge stale caches, correct directory pointers.
- Produce a new bundle from the authoritative signer. The new epoch will be above the high-water mark and will load successfully.
Fork Detected
- Identify the duplicate signer. Check which hosts are running in signer mode for the affected CA.
- Shut down the unauthorized signer. Only one signer should be authoritative per CA at any time.
- Produce a new bundle from the authoritative signer with the next epoch.
- Audit the fork window. Determine whether any responses from the forked lineage were served, and whether they differed in revocation status.
Digest or Seal Failure
- Check for media corruption. Re-download or re-transfer the bundle.
- Verify with
ahu verifyon a trusted workstation to confirm the bundle is intact at the source. - If the source bundle also fails verification, investigate the signer — the signing key may have changed, or the signer may be compromised.