carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "path/filepath"
6 "testing"
7
8 "github.com/carlosframework/platform/internal/hostbox"
9 "github.com/carlosframework/platform/internal/hostguard"
10 "github.com/carlosframework/platform/internal/release"
11 "github.com/carlosframework/platform/internal/rootparticipation"
12 "github.com/carlosframework/platform/internal/rootstate"
13)
14
15func generationPayloadFixture(views []hostguard.FenceView) []hostbox.GenerationPayload {
16 var out []hostbox.GenerationPayload
17 for _, v := range views {
18 in := v.Installation
19 stamps := append([]hostguard.Captured{{Incarnation: in.Incarnation, Stamp: in.Stamp}}, v.Retired...)
20 for _, stamp := range stamps {
21 p := hostbox.GenerationPayload{Revision: in.InventoryRevision, Inventory: hostbox.StopInventory{Complete: true, Host: in.Host, Incarnation: stamp.Incarnation, InstallationStamp: stamp.Stamp, Box: in.Box, Enrollment: in.Enrollment, DirectoryRevision: "1"}}
22 if !in.Empty {
23 p.Inventory.Entries = []hostbox.StopInventoryEntry{{Kind: "route", ArtifactID: "route", ManagedRoot: "registry", RelativePath: in.Host, RowIdentity: "row", Ownership: "owner", Materialization: "materialized"}}
24 }
25 out = append(out, p)
26 }
27 }
28 return out
29}
30
31func TestPlan14GenerationRegistryPayloadUnfinished(t *testing.T) {
32 for _, kind := range []string{"route", "timer"} {
33 for _, phase := range []string{"materialized", "intended", "predecessor", "changed-during-kernel"} {
34 t.Run(kind+"/"+phase, func(t *testing.T) {
35 f, disk, _ := generationLaunchFixture(t)
36 if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil {
37 t.Fatal(err)
38 }
39 r := readGenerationLaunch(t, disk)
40 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
41 if err != nil {
42 t.Fatal(err)
43 }
44 launch, err := f.journal.Read(t.Context(), f.target)
45 if err != nil {
46 t.Fatal(err)
47 }
48 if phase == "predecessor" {
49 views[0].Retired = []hostguard.Captured{{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp}}
50 views[0].Installation.Stamp++
51 views[0].Installation.Empty = true
52 }
53 payloads := generationPayloadFixture(views)
54 index := 0
55 if phase == "predecessor" {
56 index = 1
57 }
58 entry := hostbox.StopInventoryEntry{Kind: kind, ArtifactID: "registry-payload", ManagedRoot: "registry", RelativePath: f.target.Host, RowIdentity: "row", Ownership: "owner", Materialization: "materialized"}
59 if kind == "timer" {
60 entry.RelativePath += "/backup"
61 }
62 payloads[index].Inventory.Entries = []hostbox.StopInventoryEntry{entry}
63 journal, err := hostbox.OpenLaunchJournal(filepath.Join(t.TempDir(), "payload.db"))
64 if err != nil {
65 t.Fatal(err)
66 }
67 defer journal.Close()
68 service, err := hostbox.NewJournalService(journal)
69 if err != nil {
70 t.Fatal(err)
71 }
72 write := func() {
73 t.Helper()
74 for _, p := range payloads {
75 if err := service.Inventory.WriteStopInventory(t.Context(), p.Inventory); err != nil {
76 t.Fatal(err)
77 }
78 }
79 current, err := journal.GenerationPayloads(t.Context())
80 if err != nil {
81 t.Fatal(err)
82 }
83 for _, p := range current {
84 if p.Inventory.InstallationStamp == views[0].Installation.Stamp {
85 views[0].Installation.InventoryRevision = p.Revision
86 }
87 }
88 }
89 write()
90 attack := false
91 v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) {
92 p, err := journal.GenerationPayloads(ctx)
93 return generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: p, Launches: []hostbox.LaunchInventory{launch}}, err
94 }, kernel: func(context.Context) (string, string, error) {
95 if attack && phase == "changed-during-kernel" {
96 payloads[index].Inventory.Entries[0].Materialization = "intended"
97 write()
98 }
99 return "boot", r.Binding.Digest, nil
100 }}
101 source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile}
102 ledger := &release.RootGenerations{Store: disk}
103 accept := func() {
104 t.Helper()
105 pins, err := source.StagedPins(t.Context(), f.root.generations.optDir)
106 if err != nil {
107 t.Fatal(err)
108 }
109 if pinned, err := pins.Refers(r.Binding.Path); err != nil || !pinned {
110 t.Fatalf("exact pin lost: %v", err)
111 }
112 if c, err := ledger.Completeness(); err != nil || !c.Complete {
113 t.Fatalf("complete registry payload: %+v %v", c, err)
114 }
115 }
116 accept()
117 if phase == "materialized" {
118 return
119 }
120 initial, _ := ledger.Completeness()
121 attack = true
122 if phase != "changed-during-kernel" {
123 payloads[index].Inventory.Entries[0].Materialization = "intended"
124 write()
125 }
126 if pins, err := source.StagedPins(t.Context(), f.root.generations.optDir); err == nil || pins != nil {
127 t.Fatalf("unfinished %s payload certified: pins=%v err=%v", kind, pins, err)
128 }
129 if c, err := ledger.Completeness(); err != nil || c.Complete || c.Revision <= initial.Revision {
130 t.Fatalf("unfinished payload retained certificate: %+v %v", c, err)
131 }
132 current, err := ledger.Read(r.Binding)
133 if err != nil || current.Closed || current.Binding != r.Binding {
134 t.Fatal("refusal changed the reservation")
135 }
136 attack = false
137 payloads[index].Inventory.Entries[0].Materialization = "materialized"
138 write()
139 accept()
140 })
141 }
142 }
143}
144
145func TestPlan14GenerationPayloadCompleteness(t *testing.T) {
146 for _, attack := range []string{"none", "missing", "wrong-revision", "wrong-host", "wrong-incarnation", "wrong-stamp", "wrong-box", "wrong-enrollment", "incomplete", "empty", "duplicate", "extra-predecessor", "changed-payload", "changed-revision"} {
147 t.Run(attack, func(t *testing.T) {
148 f, disk, _ := generationLaunchFixture(t)
149 if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil {
150 t.Fatal(err)
151 }
152 r := readGenerationLaunch(t, disk)
153 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
154 if err != nil {
155 t.Fatal(err)
156 }
157 launch, err := f.journal.Read(t.Context(), f.target)
158 if err != nil {
159 t.Fatal(err)
160 }
161 snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Launches: []hostbox.LaunchInventory{launch}, Payloads: generationPayloadFixture(views)}
162 p := &snapshot.Payloads[0]
163 switch attack {
164 case "missing":
165 snapshot.Payloads = nil
166 case "wrong-revision":
167 p.Revision++
168 case "wrong-host":
169 p.Inventory.Host = "other.example"
170 case "wrong-incarnation":
171 p.Inventory.Incarnation = "other"
172 case "wrong-stamp":
173 p.Inventory.InstallationStamp++
174 case "wrong-box":
175 p.Inventory.Box = "other"
176 case "wrong-enrollment":
177 p.Inventory.Enrollment = "other"
178 case "incomplete":
179 p.Inventory.Complete = false
180 case "empty":
181 p.Inventory.Entries = nil
182 case "duplicate":
183 snapshot.Payloads = append(snapshot.Payloads, *p)
184 case "extra-predecessor":
185 extra := *p
186 extra.Inventory.InstallationStamp += 5
187 snapshot.Payloads = append(snapshot.Payloads, extra)
188 }
189 reads := 0
190 verifier := generationReconciler{read: func(context.Context) (generationInventory, error) {
191 reads++
192 if reads > 1 {
193 if attack == "changed-payload" {
194 snapshot.Payloads[0].Inventory.DirectoryRevision = "2"
195 }
196 if attack == "changed-revision" {
197 snapshot.Payloads[0].Revision++
198 }
199 }
200 return snapshot, nil
201 }, kernel: func(context.Context) (string, string, error) { return "boot", r.Binding.Digest, nil }}
202 e, err := verifier.reconcile(t.Context(), []release.GenerationReservation{r})
203 if (err == nil) != (attack == "none") {
204 t.Fatalf("payload %s: evidence=%+v err=%v", attack, e, err)
205 }
206 if attack == "none" {
207 snapshot.Payloads[0].Inventory.DirectoryRevision = "2"
208 next, err := verifier.reconcile(t.Context(), []release.GenerationReservation{r})
209 if err != nil || next.Installations == e.Installations {
210 t.Fatalf("changed retained payload reused evidence: %+v %v", next, err)
211 }
212 }
213 })
214 }
215}
216
217func TestPlan14GenerationPayloadRetainedPredecessor(t *testing.T) {
218 for _, attack := range []string{"none", "missing-old", "foreign-old", "extra-incarnation", "wrong-empty", "fresh-empty"} {
219 t.Run(attack, func(t *testing.T) {
220 v := hostguard.FenceView{Host: "tenant.example", Installation: hostguard.RootInstallation{Host: "tenant.example", Incarnation: "inc", Stamp: 5, Box: "box", Enrollment: "en", InventoryRevision: 7, Empty: true}, Retired: []hostguard.Captured{{Incarnation: "inc", Stamp: 2}}}
221 payloads := generationPayloadFixture([]hostguard.FenceView{v})
222 payloads[1].Revision = 3
223 payloads[1].Inventory.Entries = []hostbox.StopInventoryEntry{{Kind: "path", ArtifactID: "old-state", ManagedRoot: "data", RelativePath: "instances/tenant.example/state", Ownership: "owner", Materialization: "materialized", Device: 1, Inode: 2, Type: 0100000}}
224 switch attack {
225 case "missing-old":
226 payloads = payloads[:1]
227 case "foreign-old":
228 payloads[1].Inventory.Enrollment = "other"
229 case "extra-incarnation":
230 extra := payloads[1]
231 extra.Inventory.Incarnation = "other"
232 payloads = append(payloads, extra)
233 case "wrong-empty":
234 v.Installation.Empty = false
235 case "fresh-empty":
236 v.Retired = nil
237 payloads = payloads[:1]
238 }
239 err := generationPayloadBindings(payloads, map[string]hostguard.FenceView{v.Host: v})
240 if (err == nil) != (attack == "none" || attack == "fresh-empty") {
241 t.Fatalf("%s: %v", attack, err)
242 }
243 })
244 }
245}
246