carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "crypto/ed25519"
6 "errors"
7 "os"
8 "path/filepath"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/registry"
13)
14
15func heldAuthRoute(host string) registry.Route {
16 return registry.Route{Host: host, Account: "bab", App: "km", HoldOp: "op-1"}
17}
18
19// nr is a fresh, unarmed ratchet: today's admission.
20func nr(t *testing.T) *DigestRatchet { return NewDigestRatchet(t.TempDir(), nil, false) }
21
22// Astra I1 #2: a pointer the box's ratchet refuses is not held-stage evidence.
23func TestHeldStageAppliesTheDigestRatchet(t *testing.T) {
24 s := NewDirStore(t.TempDir())
25 ctx := context.Background()
26 priv, pub := testKeys(t)
27 opt := t.TempDir()
28 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("v1"))
29 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v1"); err != nil { // a legacy, digestless pointer
30 t.Fatal(err)
31 }
32 if err := s.Delete(ctx, PromotionRecordKey("bab", "km", "v1")); err != nil { // so the pointer is the only evidence
33 t.Fatal(err)
34 }
35 armed := NewDigestRatchet(t.TempDir(), nil, false)
36 if err := armed.Admit("bab", &ChannelPointer{App: "km", Channel: "canary/x", Version: "v0", Digest: ManifestDigest([]byte("m"))}); err != nil {
37 t.Fatal(err) // this box has seen canary/x with digests
38 }
39 if _, err := StageHeld(ctx, s, pub, armed, opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrHeldNotAuthorized) {
40 t.Fatalf("a ratcheted channel's legacy pointer authorized a held stage: %v", err)
41 }
42 required := NewDigestRatchet(t.TempDir(), nil, true)
43 if _, err := StageHeld(ctx, s, pub, required, opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrHeldNotAuthorized) {
44 t.Fatalf("CARLOS_REQUIRE_POINTER_DIGEST did not apply to a held stage: %v", err)
45 }
46 if _, err := StageHeld(ctx, s, pub, nil, opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrHeldNotAuthorized) {
47 t.Fatalf("a nil ratchet admitted pointer evidence: %v", err)
48 }
49}
50
51func TestHeldStageRefusesABuildNothingAuthorizes(t *testing.T) {
52 s := NewDirStore(t.TempDir())
53 ctx := context.Background()
54 _, pub := testKeys(t)
55 opt := t.TempDir()
56 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("shipped, never promoted"))
57 if _, err := StageHeld(ctx, s, pub, nr(t), opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrHeldNotAuthorized) {
58 t.Fatalf("StageHeld = %v; want ErrHeldNotAuthorized", err)
59 }
60 if _, err := os.Stat(VersionPath(opt, "bab", "v1", "km")); err == nil {
61 t.Fatal("an unauthorized build was staged")
62 }
63}
64
65func TestHeldStageAcceptsARecordedBuildOutOfHistory(t *testing.T) {
66 s := NewDirStore(t.TempDir())
67 ctx := context.Background()
68 priv, pub := testKeys(t)
69 opt := t.TempDir()
70 for i := 0; i < historyMax+2; i++ {
71 v := "v" + string(rune('a'+i))
72 shipRelease(t, s, "bab", "km", v, adoptPlatform, []byte(v))
73 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", v); err != nil {
74 t.Fatal(err)
75 }
76 }
77 if _, err := StageHeld(ctx, s, pub, nr(t), opt, heldAuthRoute("a.example.com"), "va"); err != nil {
78 t.Fatalf("a recorded build out of the pointer's history: %v", err)
79 }
80}
81
82func TestHeldStageAcceptsAPointerNamedBuildWithoutARecord(t *testing.T) {
83 s := NewDirStore(t.TempDir())
84 ctx := context.Background()
85 priv, pub := testKeys(t)
86 opt := t.TempDir()
87 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("v1"))
88 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v1"); err != nil {
89 t.Fatal(err)
90 }
91 if err := s.Delete(ctx, PromotionRecordKey("bab", "km", "v1")); err != nil { // promoted before records
92 t.Fatal(err)
93 }
94 if _, err := StageHeld(ctx, s, pub, nr(t), opt, heldAuthRoute("a.example.com"), "v1"); err != nil {
95 t.Fatalf("a build the signed pointer names: %v", err)
96 }
97}
98
99func TestHeldStageBindsTheRecordDigest(t *testing.T) {
100 s := NewDirStore(t.TempDir())
101 ctx := context.Background()
102 priv, pub := testKeys(t)
103 opt := t.TempDir()
104 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("promoted"))
105 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v1"); err != nil {
106 t.Fatal(err)
107 }
108 saved, _, _ := s.Get(ctx, PromotionRecordKey("bab", "km", "v1"))
109 for i := 0; i <= historyMax; i++ { // push v1 out of the pointer, so the record is the only evidence
110 v := "w" + string(rune('a'+i))
111 shipRelease(t, s, "bab", "km", v, adoptPlatform, []byte(v))
112 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", v); err != nil {
113 t.Fatal(err)
114 }
115 }
116 if err := DeleteVersion(ctx, s, "bab", "km", "v1"); err != nil {
117 t.Fatal(err)
118 }
119 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("nobody promoted these"))
120 if _, err := s.Put(ctx, PromotionRecordKey("bab", "km", "v1"), saved, ""); err != nil {
121 t.Fatal(err)
122 }
123 if _, err := StageHeld(ctx, s, pub, nr(t), opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrManifestDigest) {
124 t.Fatalf("a reused name under a replayed record = %v; want ErrManifestDigest", err)
125 }
126 if _, err := os.Stat(VersionPath(opt, "bab", "v1", "km")); err == nil {
127 t.Fatal("the reused name's bytes were staged")
128 }
129}
130
131// Under a promotion record the held stage binds the record's digest (spec
132// §4.6): a copy staged while the canonical manifest cannot be read is
133// refused at staging, never published and never overwritten, where pointer
134// trust would have used it as staged and left it to D3's bind.
135func TestHeldStageRecordRefusesAStaleCopyItCannotCheck(t *testing.T) {
136 ctx, s, opt := heldFixture(t)
137 stale := []byte("not the shipped bytes")
138 down := stageStaleCopyKept(t, ctx, s, opt, "bab", "console", "v1", stale)
139 _, err := MaterializeHeld(ctx, down, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), openGuard)
140 if err == nil || errors.Is(err, ErrStageBlocked) {
141 t.Fatalf("MaterializeHeld = %v; want the record-bound stage to refuse the copy", err)
142 }
143 gone(t, LiveLink(opt, heldTestHost))
144 if b, rerr := os.ReadFile(VersionPath(opt, "bab", "v1", "console")); rerr != nil || string(b) != string(stale) {
145 t.Fatalf("staged v1 = %q, %v; want it untouched", b, rerr)
146 }
147}
148
149// §4.1 refusals are final: the root relay removes a request for a build
150// nothing authorizes, or whose manifest does not hash to the digest its
151// record signs, writes no ack and never repoints live/<host>.
152func TestServeHeldStageRequestsDropsUnauthorizedBuilds(t *testing.T) {
153 for name, setup := range map[string]func(t *testing.T, ctx context.Context, s Store) string{
154 "nothing authorizes it": func(t *testing.T, _ context.Context, s Store) string {
155 shipRelease(t, s, "bab", "console", "v3", adoptPlatform, []byte("shipped, never promoted"))
156 return "v3"
157 },
158 "its manifest is not the recorded one": func(t *testing.T, ctx context.Context, s Store) string {
159 raw, etag, err := s.Get(ctx, ManifestKey("bab", "console", "v1"))
160 if err != nil {
161 t.Fatal(err)
162 }
163 if _, err := s.Put(ctx, ManifestKey("bab", "console", "v1"), append(raw, '\n'), etag); err != nil {
164 t.Fatal(err)
165 }
166 return "v1"
167 },
168 } {
169 t.Run(name, func(t *testing.T) {
170 ctx, s, opt := heldFixture(t)
171 req := goodHeldRequest()
172 req.Version = setup(t, ctx, s)
173 dd := t.TempDir()
174 path := writeHeldRequest(t, dd, req.Host+".json", req)
175 if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(nil)); err != nil {
176 t.Fatal(err)
177 }
178 gone(t, path)
179 gone(t, LiveLink(opt, heldTestHost))
180 gone(t, filepath.Join(HeldStagedDir(dd), heldTestHost+".json"))
181 if _, err := os.Stat(VersionPath(opt, "bab", req.Version, "console")); err == nil {
182 t.Fatalf("%s was staged", req.Version)
183 }
184 })
185 }
186}
187
188// pointerOnly ships v1 of bab/km, promotes it to each channel in turn (a
189// channel listed in digested is promoted after the digest marker is written,
190// so its pointer signs v1's digest), and deletes v1's promotion record, so
191// the pointers are the only evidence.
192func pointerOnly(t *testing.T, s Store, priv ed25519.PrivateKey, legacy, digested []string) {
193 t.Helper()
194 ctx := context.Background()
195 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("v1"))
196 for _, c := range legacy {
197 if err := Promote(ctx, s, priv, "bab", "km", c, "v1"); err != nil {
198 t.Fatal(err)
199 }
200 }
201 if len(digested) > 0 {
202 if err := WriteDigestMarker(ctx, s, RootSigner(priv), "bab", "test"); err != nil {
203 t.Fatal(err)
204 }
205 }
206 for _, c := range digested {
207 if err := Promote(ctx, s, priv, "bab", "km", c, "v1"); err != nil {
208 t.Fatal(err)
209 }
210 }
211 if err := s.Delete(ctx, PromotionRecordKey("bab", "km", "v1")); err != nil {
212 t.Fatal(err)
213 }
214}
215
216// Fix round 1, I1: a held stage that cannot read the pointer that may name
217// its build cannot tell, so it is not ErrHeldNotAuthorized (a final refusal)
218// but an error the root relay keeps the request for.
219func TestHeldStageRetriesAPointerItCannotRead(t *testing.T) {
220 base := NewDirStore(t.TempDir())
221 ctx := context.Background()
222 priv, pub := testKeys(t)
223 opt := t.TempDir()
224 pointerOnly(t, base, priv, []string{"canary/x"}, nil)
225 s := failingGetStore{Store: base, key: ChannelKey("bab", "km", "canary/x")}
226 _, err := StageHeld(ctx, s, pub, nr(t), opt, heldAuthRoute("a.example.com"), "v1")
227 if err == nil || errors.Is(err, ErrHeldNotAuthorized) || !errors.Is(err, errTransient) {
228 t.Fatalf("StageHeld over an unreadable pointer = %v; want the read error, not ErrHeldNotAuthorized", err)
229 }
230 if _, err := os.Stat(VersionPath(opt, "bab", "v1", "km")); err == nil {
231 t.Fatal("a build nothing could vouch for was staged")
232 }
233}
234
235// The root relay keeps a request whose authority it could not read.
236func TestServeHeldStageRequestsKeepsARequestItCannotAuthorizeYet(t *testing.T) {
237 ctx, base, opt := heldFixture(t)
238 if err := base.Delete(ctx, PromotionRecordKey("bab", "console", "v1")); err != nil {
239 t.Fatal(err) // canary/x's pointer is v1's only evidence
240 }
241 s := failingGetStore{Store: base, key: ChannelKey("bab", "console", "canary/x")}
242 req := goodHeldRequest()
243 dd := t.TempDir()
244 path := writeHeldRequest(t, dd, req.Host+".json", req)
245 if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(nil)); err != nil {
246 t.Fatal(err)
247 }
248 if _, err := os.Stat(path); err != nil {
249 t.Fatalf("the request was dropped on a transient read error: %v", err)
250 }
251 gone(t, LiveLink(opt, heldTestHost))
252 gone(t, filepath.Join(HeldStagedDir(dd), heldTestHost+".json"))
253}
254
255// A ratchet that cannot tell (it could not persist the mark for a pointer
256// with digests, or could not read the mark for one without) is retried, not
257// taken for a refusal.
258func TestHeldStageRetriesARatchetThatCannotTell(t *testing.T) {
259 t.Run("the mark could not be persisted", func(t *testing.T) {
260 if os.Geteuid() == 0 {
261 t.Skip("root can write a 0555 directory")
262 }
263 s := NewDirStore(t.TempDir())
264 priv, pub := testKeys(t)
265 pointerOnly(t, s, priv, nil, []string{"canary/x"})
266 ro := filepath.Join(t.TempDir(), "ro")
267 if err := os.MkdirAll(ro, 0o555); err != nil {
268 t.Fatal(err)
269 }
270 _, err := HeldAuthorized(context.Background(), s, pub, NewDigestRatchet(ro, nil, false), "bab", "km", "v1")
271 if errors.Is(err, ErrHeldNotAuthorized) || !errors.Is(err, ErrRatchetUnpersisted) {
272 t.Fatalf("HeldAuthorized = %v; want ErrRatchetUnpersisted, not ErrHeldNotAuthorized", err)
273 }
274 })
275 t.Run("the mark could not be read", func(t *testing.T) {
276 s := NewDirStore(t.TempDir())
277 priv, pub := testKeys(t)
278 pointerOnly(t, s, priv, []string{"canary/x"}, nil)
279 dir := t.TempDir()
280 if err := os.MkdirAll(markPath(dir, "bab", "km", "canary/x"), 0o755); err != nil {
281 t.Fatal(err) // a directory where the mark belongs: Admit cannot tell
282 }
283 _, err := HeldAuthorized(context.Background(), s, pub, NewDigestRatchet(dir, nil, false), "bab", "km", "v1")
284 if err == nil || errors.Is(err, ErrHeldNotAuthorized) {
285 t.Fatalf("HeldAuthorized = %v; want a retryable error, not ErrHeldNotAuthorized", err)
286 }
287 })
288}
289
290// Among pointers that name the build, the one that signs its digest wins,
291// whichever channel lists first: binding to "" when a signed digest exists
292// would throw away the stronger evidence.
293func TestHeldAuthorizedPrefersASignedDigest(t *testing.T) {
294 for name, chans := range map[string][2][]string{
295 "the digestless channel lists first": {{"a"}, {"b"}},
296 "the digested channel lists first": {{"b"}, {"a"}},
297 } {
298 t.Run(name, func(t *testing.T) {
299 s := NewDirStore(t.TempDir())
300 ctx := context.Background()
301 priv, pub := testKeys(t)
302 pointerOnly(t, s, priv, chans[0], chans[1])
303 raw, _, err := s.Get(ctx, ManifestKey("bab", "km", "v1"))
304 if err != nil {
305 t.Fatal(err)
306 }
307 want, err := HeldAuthorized(ctx, s, pub, nr(t), "bab", "km", "v1")
308 if err != nil || want != ManifestDigest(raw) {
309 t.Fatalf("HeldAuthorized = %q, %v; want the signed digest %q", want, err, ManifestDigest(raw))
310 }
311 })
312 }
313}
314
315// Every verified pointer a held stage reads goes through the ratchet (spec
316// §4.6, "once verified"; ruling R15): one that does not name the build is not
317// evidence, but its digests still mark its channel. Its ratchet outcome never
318// changes the answer, so a mark that cannot be written does not hold up a
319// build another pointer authorizes.
320func TestHeldAuthorizedAdmitsOnlyNamingPointers(t *testing.T) {
321 s := NewDirStore(t.TempDir())
322 ctx := context.Background()
323 priv, pub := testKeys(t)
324 shipRelease(t, s, "bab", "km", "v0", adoptPlatform, []byte("v0"))
325 pointerOnly(t, s, priv, []string{"b"}, nil)
326 if err := WriteDigestMarker(ctx, s, RootSigner(priv), "bab", "test"); err != nil {
327 t.Fatal(err)
328 }
329 if err := Promote(ctx, s, priv, "bab", "km", "a", "v0"); err != nil {
330 t.Fatal(err) // channel a carries digests and does not name v1
331 }
332 dir := t.TempDir()
333 if want, err := HeldAuthorized(ctx, s, pub, NewDigestRatchet(dir, nil, false), "bab", "km", "v1"); err != nil || want != "" {
334 t.Fatalf("HeldAuthorized = %q, %v; want channel b's pointer trust", want, err)
335 }
336 if _, err := os.Stat(markPath(dir, "bab", "km", "a")); err != nil {
337 t.Fatalf("channel a's verified digests left no mark: %v", err)
338 }
339 if os.Geteuid() != 0 {
340 ro := filepath.Join(t.TempDir(), "ro")
341 if err := os.MkdirAll(ro, 0o555); err != nil {
342 t.Fatal(err)
343 }
344 if want, err := HeldAuthorized(ctx, s, pub, NewDigestRatchet(ro, nil, false), "bab", "km", "v1"); err != nil || want != "" {
345 t.Fatalf("HeldAuthorized with channel a's mark unwritable = %q, %v; want channel b's pointer trust", want, err)
346 }
347 }
348}
349
350// heldNothingDownloaded fails the test if the held stage read any of
351// version's release objects or left anything in the version store.
352func heldNothingDownloaded(t *testing.T, rec *heldRecordingStore, opt, version string) {
353 t.Helper()
354 rel := ReleasesPrefix("bab", "km") + version + "/"
355 rec.mu.Lock()
356 defer rec.mu.Unlock()
357 for _, c := range rec.calls {
358 if strings.HasPrefix(c, "get "+rel) {
359 t.Fatalf("the held stage downloaded %s", strings.TrimPrefix(c, "get "))
360 }
361 }
362 if _, err := os.Stat(VersionPath(opt, "bab", version, "km")); err == nil {
363 t.Fatalf("%s was staged", version)
364 }
365}
366
367// heldSubstitute replaces version's shipped bytes under the same name.
368func heldSubstitute(t *testing.T, s Store, version string) {
369 t.Helper()
370 if err := DeleteVersion(context.Background(), s, "bab", "km", version); err != nil {
371 t.Fatal(err)
372 }
373 shipRelease(t, s, "bab", "km", version, adoptPlatform, []byte("nobody promoted these"))
374}
375
376// Astra final A1, ruling R16: a pointer whose head signs a digest, naming a
377// legacy history entry that signs none, is no evidence for that entry ("never
378// a rollback target once its channel has any digest", spec §4.6). With no
379// record, its replaced bytes are refused with the flag off and on, and
380// nothing is downloaded.
381func TestHeldStageRefusesADigestlessEntryOnADigestedChannel(t *testing.T) {
382 for name, require := range map[string]bool{"flag off": false, "flag on": true} {
383 t.Run(name, func(t *testing.T) {
384 s := NewDirStore(t.TempDir())
385 ctx := context.Background()
386 priv, pub := testKeys(t)
387 opt := t.TempDir()
388 pointerOnly(t, s, priv, []string{"canary/x"}, nil) // v1, digestless
389 if err := WriteDigestMarker(ctx, s, RootSigner(priv), "bab", "test"); err != nil {
390 t.Fatal(err)
391 }
392 shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("v2"))
393 if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v2"); err != nil {
394 t.Fatal(err)
395 }
396 p, err := Channel(ctx, s, pub, "bab", "km", "canary/x")
397 if err != nil {
398 t.Fatal(err)
399 }
400 if p.Version != "v2" || p.Digest == "" || len(p.History) != 1 || p.History[0] != "v1" || p.DigestOf("v1") != "" {
401 t.Fatalf("pointer = %+v; want a digested head v2 over a digestless history v1", p)
402 }
403 heldSubstitute(t, s, "v1")
404 rec := &heldRecordingStore{Store: s}
405 if _, err := StageHeld(ctx, rec, pub, NewDigestRatchet(t.TempDir(), nil, require), opt, heldAuthRoute("a.example.com"), "v1"); !errors.Is(err, ErrHeldNotAuthorized) {
406 t.Fatalf("StageHeld of a digestless entry on a digested channel = %v; want ErrHeldNotAuthorized", err)
407 }
408 heldNothingDownloaded(t, rec, opt, "v1")
409 })
410 }
411}
412
413// Astra final A2, ruling R15: a held stage remembers every verified pointer
414// with digests it reads, naming the build or not. Pass 1 stages v1 on channel
415// b's word while channel a's pointer signs digests for v2; pass 2, on the
416// same ratchet, is a saved digestless pointer of channel a replayed over
417// substituted v2 bytes, and is refused.
418func TestHeldStageRemembersEveryVerifiedPointer(t *testing.T) {
419 s := NewDirStore(t.TempDir())
420 ctx := context.Background()
421 priv, pub := testKeys(t)
422 opt := t.TempDir()
423 shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("v1"))
424 shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("v2"))
425 _, v2raw, err := GetManifestRaw(ctx, s, "bab", "km", "v2")
426 if err != nil {
427 t.Fatal(err)
428 }
429 sign := func(p *ChannelPointer) []byte {
430 t.Helper()
431 w, err := signPointer(RootSigner(priv), p)
432 if err != nil {
433 t.Fatal(err)
434 }
435 return w
436 }
437 saved := sign(&ChannelPointer{Account: "bab", App: "km", Channel: "a", Version: "v2"}) // signed before digests
438 put := func(channel string, w []byte) {
439 t.Helper()
440 _, etag, err := s.Get(ctx, ChannelKey("bab", "km", channel))
441 if err != nil && !errors.Is(err, os.ErrNotExist) {
442 t.Fatal(err)
443 }
444 if _, err := s.Put(ctx, ChannelKey("bab", "km", channel), w, etag); err != nil {
445 t.Fatal(err)
446 }
447 }
448 put("a", sign(&ChannelPointer{Account: "bab", App: "km", Channel: "a", Version: "v2", Digest: ManifestDigest(v2raw)}))
449 put("b", sign(&ChannelPointer{Account: "bab", App: "km", Channel: "b", Version: "v1"}))
450 ratchet := NewDigestRatchet(t.TempDir(), nil, false)
451 if _, err := StageHeld(ctx, s, pub, ratchet, opt, heldAuthRoute("a.example.com"), "v1"); err != nil {
452 t.Fatalf("pass 1, v1 on channel b's word: %v", err)
453 }
454 put("a", saved)
455 heldSubstitute(t, s, "v2")
456 rec := &heldRecordingStore{Store: s}
457 if _, err := StageHeld(ctx, rec, pub, ratchet, opt, heldAuthRoute("a.example.com"), "v2"); !errors.Is(err, ErrHeldNotAuthorized) {
458 t.Fatalf("pass 2, a replayed digestless pointer over substituted v2 = %v; want ErrHeldNotAuthorized", err)
459 }
460 heldNothingDownloaded(t, rec, opt, "v2")
461}
462