edge: door ACL tripwire — cert material never transits the fleet door
Permanent guard for the multi-region ingress design's central promise: control/certs/**, control/edge-mesh-key and the housekeeper's own lease object are absent from aclRead's allowlist, so no fleet box can read a private key, ACME account key, challenge token or the housekeeper's coordination lock through the store door however it authenticates. Runs as an authenticated fleet client across GET/PUT/DELETE plus the list/dirs verbs, and folds seeded and unseeded keys into the same byte-identical-refusal check TestStoreDoorRefusalsAreByteIdentical uses, so an existence oracle on cert material fails the same way any other shape mismatch would. Verified as a real tripwire by locally widening aclRead to admit control/certs/ (watched the test fail), then reverting before commit — the revert is confirmed clean in this commit's diff.
1 file changed,
+94
−0
internal/edge/storedoor_test.go+94 −0
diff --git a/internal/edge/storedoor_test.go b/internal/edge/storedoor_test.go| index 3a0df9c7a..0d3f8d50c 100644 |
| --- a/internal/edge/storedoor_test.go |
| +++ b/internal/edge/storedoor_test.go |
| @@ -512,6 +512,100 @@ func TestStoreDoorRefusalsAreByteIdentical(t *testing.T) { |
| } |
| } |
| +// TestDoorNeverServesCertMaterial is the permanent tripwire for the |
| +// multi-region ingress design's central promise (bucket-writer-audit.md): |
| +// control/certs/**, control/edge-mesh-key and the housekeeper's own lease |
| +// object are NOT in aclRead's allowlist (compare the doc comment above |
| +// aclRead — control/serial, bootstrap/carlos, domains/, accounts/<sqid>/, |
| +// apps/<sqid>/, fleets/<fleet>/, and nothing else), so no fleet box can ever |
| +// read a private key, an ACME account key, a challenge token or the |
| +// housekeeper's coordination lock through this door, however it authenticates. |
| +// |
| +// It runs as an AUTHENTICATED fleet client (own sqid/fleet, real bearer |
| +// token) — an unauthenticated 404 would prove nothing about the ACL — across |
| +// all three verbs the door exposes on a single object (GET/PUT/DELETE) plus |
| +// both listing verbs, and it proves the refusal is byte-identical whether the |
| +// key exists in the backing store or not: two of the seven keys below are |
| +// seeded with plausible cert bytes before the loop runs, the rest are left |
| +// absent, and every response — seeded or not — is folded into the same |
| +// byte-identical comparison TestStoreDoorRefusalsAreByteIdentical uses, so an |
| +// existence oracle on cert material would fail exactly the same way a status |
| +// mismatch would. |
| +func TestDoorNeverServesCertMaterial(t *testing.T) { |
| + d, store := newTestDoor(t) |
| + |
| + // The real Task 4/5 key shapes (certsync.go's certKey naming: bare host, |
| + // the autocert account key, an HTTP-01 token response, a tls-alpn-01 |
| + // token cert, a wildcard account key) plus the two other control/ |
| + // entries the multi-region design reserves for the housekeeper alone |
| + // (bucket-writer-audit.md, "Not yet built" table): the mesh pairing key |
| + // and the housekeeper's own election lock. |
| + certKeys := []string{ |
| + "control/certs/example.com", |
| + "control/certs/acme_account+key", |
| + "control/certs/wildcard-account.key", |
| + "control/certs/tok+http-01", |
| + "control/certs/example.com+token", |
| + "control/edge-mesh-key", |
| + "control/housekeeper-lease/lock.json", |
| + } |
| + // Seed two of the seven with plausible-looking secret bytes: if the ACL |
| + // ever let a Get through, these are the ones that would leak real |
| + // material instead of a "not found" — and mixing seeded with unseeded |
| + // keys through the identical-shape check below is what proves the door |
| + // isn't just refusing absent keys, it's refusing to distinguish "exists" |
| + // from "doesn't" at all for this whole prefix. |
| + seedObject(t, store, "control/certs/example.com", "-----BEGIN CERTIFICATE-----\nfake\n-----END CERTIFICATE-----") |
| + seedObject(t, store, "control/edge-mesh-key", "fake-mesh-shared-secret") |
| + |
| + type shape struct { |
| + code int |
| + header string |
| + body string |
| + } |
| + var want *shape |
| + var wantName string |
| + check := func(name string, w *httptest.ResponseRecorder) { |
| + t.Helper() |
| + if w.Code != http.StatusNotFound { |
| + t.Errorf("%s: status = %d, want 404 — cert material must never transit the door", name, w.Code) |
| + return |
| + } |
| + if strings.Contains(w.Body.String(), "BEGIN CERTIFICATE") || strings.Contains(w.Body.String(), "fake-mesh-shared-secret") { |
| + t.Errorf("%s: response body carries seeded secret bytes", name) |
| + } |
| + got := shape{w.Code, fmt.Sprintf("%v", w.Header()), w.Body.String()} |
| + if want == nil { |
| + g := got |
| + want, wantName = &g, name |
| + return |
| + } |
| + if got != *want { |
| + t.Errorf("%q answers %+v but %q answers %+v — cert refusal is distinguishable from an ordinary refusal (existence oracle)", name, got, wantName, *want) |
| + } |
| + } |
| + |
| + for _, key := range certKeys { |
| + check("GET "+key, doorDo(d, "GET", "/store/o/"+key, "", nil)) |
| + check("DELETE "+key, doorDo(d, "DELETE", "/store/o/"+key, "", nil)) |
| + check("PUT "+key, doorDo(d, "PUT", "/store/o/"+key, "x", map[string]string{"If-None-Match": "*"})) |
| + } |
| + // The listing verbs: a fleet box must never enumerate the cert prefix |
| + // either, whether asking for objects (list) or child directories (dirs). |
| + check("list prefix=control/certs/", doorDo(d, "GET", "/store/list?prefix=control/certs/", "", nil)) |
| + check("dirs prefix=control/certs/", doorDo(d, "GET", "/store/dirs?prefix=control/certs/", "", nil)) |
| + |
| + // The seeded keys must still be untouched in the backing store — a |
| + // refused DELETE that quietly deleted anyway would be worse than a |
| + // refused DELETE that didn't. |
| + if _, _, err := store.Get(context.Background(), "control/certs/example.com"); err != nil { |
| + t.Errorf("the refused DELETE removed control/certs/example.com anyway: %v", err) |
| + } |
| + if _, _, err := store.Get(context.Background(), "control/edge-mesh-key"); err != nil { |
| + t.Errorf("the refused DELETE removed control/edge-mesh-key anyway: %v", err) |
| + } |
| +} |
| + |
| // TestStoreDoorListScopeStripsOtherTenants: a listing is the easiest place to |
| // leak a neighbour, so every listing route is checked against a store that |
| // holds another account's and another fleet's objects. |