Skip to content

ADR-0010: Datum does not manage its own trust anchors

Status

Accepted

Context

An agent verifying that desired state is genuine needs a set of trusted signing keys. An agent reading a repository needs a credential. An agent detecting a downgrade needs a record of the revision it last applied. All three are files on the host.

Managing them with Datum is the obvious thing to want. They are configuration, they differ per host, and a fleet that can distribute its own signer list can rotate a key without rebuilding machines. Every argument that applies to managing /etc/ssh/sshd_config appears to apply here.

The problem is that a control cannot be allowed to authorise its own replacement.

If allowed-signers were an ordinary File resource, one malicious commit would replace it with an attacker's key. That commit would be verified against the old, trusted key and would pass. Every commit after it would verify against the new key. Signature verification would have validated the change that disabled signature verification, and nothing in the plan would look unusual, because replacing a file is what Datum does.

The same reasoning applies to the recorded revision. A resource that could write the state directory could reset the baseline used for downgrade protection, after which any older revision becomes acceptable. It applies to the credential, where a resource could redirect a host at a different remote. It applies to agent.yaml, which contains the host's identity and the trust settings themselves, so a resource managing it could set require: none fleet-wide.

Decision

Datum rejects a resource whose target is one of its own trust anchors.

Path Protects
/etc/datum/agent.yaml Host identity, source, and the trust settings
/etc/datum/allowed-signers The keys that can authorise desired state
/etc/datum/credentials/ Repository credentials
/var/lib/datum/ The recorded revision used for downgrade protection

The refusal happens when the manifest is validated, so it fails before the host is touched and the error names both the file and the control it protects.

The exclusion is a specific list, not a general rule about /etc. A broad exclusion would be unpredictable, would grow by accident, and would stop Datum managing configuration it has no reason to avoid.

These files are provisioned and rotated by whatever builds machines, which is outside Datum.

Consequences

A compromised repository cannot escalate into a compromised trust configuration. The worst a malicious commit achieves is whatever that commit's own content does on the hosts it matches, which is already the accepted trust assumption, rather than permanently disabling the control that would have caught the next one.

Rotating a signer key is a task for the provisioning system, and on a large fleet that is genuinely worse than a commit would have been. This is the real cost of the decision and it is accepted, because the alternative is a control that can be switched off by the thing it exists to constrain.

Bootstrapping is pushed to provisioning, including the initial recorded revision. That turns out to be the right place for it anyway, since a host that chooses its own first revision cannot detect a downgrade on its first pass.

The list has to be kept accurate. A future control that relies on a file on the host has to be added to it, and forgetting is the mistake that reopens the hole. Anything the agent reads in order to decide whether to trust something belongs on the list, which is the test to apply when the question comes up again.

The convenience argument will come back, most likely as a request for a narrow exception allowing allowed-signers to be managed so that key rotation can be automated. The answer is that an exception for the signer list is an exception for the entire control, because a fleet that can rewrite its own signer list has the security properties of a fleet with no signer list at all.