fix(truststore): give each profile its own CA trust entry
What does this MR do and why?
On Linux every profile installed its dev CA as caproni-dev-ca.crt, so trusting profile B overwrote profile A's anchor and only the most recently trusted profile verified from the host. Untrusting either removed whichever CA happened to be installed.
The NSS step had the same shape through the fixed nickname Caproni Dev CA: trusting both profiles left two entries under one nickname, and untrusting removed the wrong one.
Both the anchor filename and the NSS nickname derive from the name passed to Install and Uninstall, which was the bare pki.CommonName. Passing a label that carries the profile fixes both stores in one change.
References
Closes #307
Differences
Before: every profile wrote caproni-dev-ca.crt and used the nickname Caproni Dev CA.
After: profile caproni-a-7f3c1d writes caproni-dev-ca-caproni-a-7f3c1d.crt under the nickname Caproni Dev CA (caproni-a-7f3c1d). Trusting B leaves A trusted, and untrusting A removes only A.
The issue's reproduction table becomes:
Step anchor file A trusted B trusted
ca trust in a/ caproni-dev-ca-caproni-a-*.crt yes no
ca trust in b/ both files present yes yes
ca untrust in a/ caproni-dev-ca-caproni-b-*.crt no yesEvery profile's CA still shares the subject CN=Caproni Dev CA, so the label is the only thing telling them apart.
How to set up and validate locally
The package builds only on Linux and macOS, so from a Linux host:
-
go test ./internal/truststore/ ./cmd/ca/ -
Confirm the tests catch the collision. In
TrustLabel, change the final return toreturn pki.CommonName. Two tests fail:TestTrustLabel/profile_qualifies_the_label, expectingCaproni Dev CA (caproni-a-7f3c1d)and gettingCaproni Dev CATestTrustLabel_DoesNotCollideWithTheLegacyAnchor, withShould not be: "caproni-dev-ca.crt"
TestTrustLabel_DistinguishesProfilesis the direct statement of the bug: two profiles must not resolve to the same filename or the same nickname. -
The full reproduction in the issue, with two profiles under
cluster.name.strategy: directory-hash.
Two things I did not do, and why
Per-profile subject. The issue notes that every profile's CA carries CN=Caproni Dev CA, and that a hashed CApath lookup may only try the first root with a given subject. That is a real second problem, but changing the subject only helps newly generated CAs, existing ones must keep theirs or the certificates they issued stop verifying, and it is independent of the filename collision. Worth its own issue.
Legacy anchor cleanup. An anchor installed by an earlier version keeps the name caproni-dev-ca.crt and is not removed by this. A user carrying one runs caproni ca untrust before upgrading, or removes the file by hand. Removing it automatically would mean one profile's untrust deleting a file another profile may still be relying on. Happy to add it if you would rather.
MR acceptance checklist
- Each profile gets its own anchor filename and NSS nickname
-
untrustremoves only that profile's entry - Tests added, verified to fail without the fix
- An empty profile name still yields the bare common name, which cannot collide with a profiled one
- Conforms to the code review guidelines