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 "os"
6 "testing"
7
8 "github.com/carlosframework/platform/internal/hostbox"
9 "github.com/carlosframework/platform/internal/hostclaim"
10 "github.com/carlosframework/platform/internal/hostguard"
11 "github.com/carlosframework/platform/internal/provision"
12 "github.com/carlosframework/platform/internal/release"
13 "github.com/carlosframework/platform/internal/rootparticipation"
14 "github.com/carlosframework/platform/internal/rootstate"
15)
16
17func TestPlan14GenerationEmptyInstallationRequiresCreation(t *testing.T) {
18 plan14GenerationCreation(t, false)
19}
20func TestPlan14GenerationLocalRecoveryCreation(t *testing.T) {
21 plan14GenerationCreation(t, true)
22}
23func plan14GenerationCreation(t *testing.T, local bool) {
24 for _, attack := range []string{"none", "missing", "erased", "corrupt", "consumed", "wrong-installation", "wrong-host", "visible-sync", "changed-during-kernel"} {
25 t.Run(attack, func(t *testing.T) {
26 f, disk, rec := generationLaunchFixture(t)
27 rec.Schema = 1
28 plan14PutRecord(t, f.root.generations.store, rec)
29 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
30 if err != nil {
31 t.Fatal(err)
32 }
33 views[0].Installation.Empty = true
34 in := views[0].Installation
35 snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: in.Box, Enrollment: in.Enrollment, Migration: in.Migration, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: generationPayloadFixture(views)}
36 historyDisk := &generationDisk{rollbackDisk: &rollbackDisk{dir: t.TempDir()}}
37 history := &release.RootHistory{Store: historyDisk}
38 binding := release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}
39 create := func() {
40 t.Helper()
41 if err := history.RecordCreation(binding, rootHistoryInstallation(in)); err != nil {
42 t.Fatal(err)
43 }
44 }
45 create()
46 active := false
47 fresh := func(in hostguard.RootInstallation) error {
48 ok, err := history.FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in))
49 if err != nil {
50 return err
51 }
52 if !ok {
53 return os.ErrPermission
54 }
55 return nil
56 }
57 v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) {
58 if local {
59 return generationLocalObligations(ctx, snapshot, fresh)
60 }
61 return generationRequiredInventory(ctx, f.root.generations.store, snapshot, fresh)
62 }, kernel: func(context.Context) (string, string, error) {
63 if active && attack == "changed-during-kernel" {
64 if err := history.ConsumeCreation(binding); err != nil {
65 t.Fatal(err)
66 }
67 }
68 return "boot", generationInventoryDigest("kernel"), nil
69 }}
70 source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile}
71 ledger := &release.RootGenerations{Store: disk}
72 accept := func() {
73 t.Helper()
74 pins, err := source.StagedPins(t.Context(), f.root.generations.optDir)
75 if err != nil || len(pins) != 0 {
76 t.Fatalf("new installation: %v %v", pins, err)
77 }
78 if c, err := ledger.Completeness(); err != nil || !c.Complete {
79 t.Fatalf("creation not certified: %+v %v", c, err)
80 }
81 }
82 accept()
83 if attack == "none" {
84 return
85 }
86 before, _ := ledger.Completeness()
87 names, err := historyDisk.Names()
88 if err != nil || len(names) != 1 {
89 t.Fatalf("history names: %v %v", names, err)
90 }
91 raw, err := historyDisk.Read(names[0])
92 if err != nil {
93 t.Fatal(err)
94 }
95 switch attack {
96 case "visible-sync":
97 historyDisk.fail = 2
98 case "missing":
99 fresh = nil
100 case "erased":
101 if err := historyDisk.Remove(names[0]); err != nil {
102 t.Fatal(err)
103 }
104 case "corrupt":
105 if err := historyDisk.Write(names[0], []byte("{")); err != nil {
106 t.Fatal(err)
107 }
108 case "consumed":
109 if err := history.ConsumeCreation(binding); err != nil {
110 t.Fatal(err)
111 }
112 case "wrong-installation", "wrong-host":
113 if err := historyDisk.Remove(names[0]); err != nil {
114 t.Fatal(err)
115 }
116 other, identity := binding, rootHistoryInstallation(in)
117 if attack == "wrong-host" {
118 other.Host = "other.example"
119 } else {
120 identity = "other-installation"
121 }
122 if err := history.RecordCreation(other, identity); err != nil {
123 t.Fatal(err)
124 }
125 }
126 active = true
127 if pins, err := source.StagedPins(t.Context(), f.root.generations.optDir); err == nil || pins != nil {
128 t.Fatalf("unknown creation certified: %v %v", pins, err)
129 }
130 if c, err := ledger.Completeness(); err != nil || c.Complete || c.Revision <= before.Revision {
131 t.Fatalf("creation refusal retained certificate: %+v %v", c, err)
132 }
133 if attack == "visible-sync" {
134 if pins, err := source.StagedPins(t.Context(), f.root.generations.optDir); err == nil || pins != nil {
135 t.Fatalf("visible unsynced creation accepted on retry: %v %v", pins, err)
136 }
137 }
138 if err := historyDisk.Write(names[0], raw); err != nil {
139 t.Fatal(err)
140 }
141 active = false
142 if attack != "missing" {
143 accept()
144 }
145 })
146 }
147}
148
149func TestPlan14GenerationCreationFromRegistration(t *testing.T) {
150 f, root, q := plan14InstallRegistration(t, hostclaim.KindServing, "")
151 history := &release.RootHistory{Store: &rollbackDisk{dir: t.TempDir()}}
152 root.history = func(ctx context.Context, fn func(*release.RootHistory) error) error { return fn(history) }
153 if _, err := root.commitRootInventory(t.Context(), q); err != nil {
154 t.Fatal(err)
155 }
156 views, err := root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context())
157 if err != nil {
158 t.Fatal(err)
159 }
160 payloads, err := f.journal.GenerationPayloads(t.Context())
161 if err != nil {
162 t.Fatal(err)
163 }
164 if len(views) != 1 || !views[0].Installation.Empty || len(payloads) != 1 || len(payloads[0].Inventory.Entries) != 0 {
165 t.Fatal("first registration did not persist an empty installation")
166 }
167 in := views[0].Installation
168 v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) {
169 return generationRequiredInventory(ctx, f.claims, generationInventory{Census: rootparticipation.Census{Schema: 1, Box: in.Box, Enrollment: in.Enrollment, Migration: in.Migration, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: payloads}, func(in hostguard.RootInstallation) error {
170 fresh, err := history.FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in))
171 if err != nil {
172 return err
173 }
174 if !fresh {
175 return os.ErrPermission
176 }
177 return nil
178 })
179 }, kernel: func(context.Context) (string, string, error) { return "boot", generationInventoryDigest("kernel"), nil }}
180 disk := &rollbackDisk{dir: t.TempDir()}
181 source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile}
182 if pins, err := source.StagedPins(t.Context(), root.Roots["opt"]); err != nil || len(pins) != 0 {
183 t.Fatalf("registered creation refused: %v %v", pins, err)
184 }
185 if c, err := (&release.RootGenerations{Store: disk}).Completeness(); err != nil || !c.Complete {
186 t.Fatalf("registered creation incomplete: %+v %v", c, err)
187 }
188}
189
190func TestPlan14GenerationUnwokenSibling(t *testing.T) {
191 for _, attack := range []string{"none", "no-proof", "consumed", "state-exists", "changed-during-scan"} {
192 t.Run(attack, func(t *testing.T) {
193 f, _, _ := generationLaunchFixture(t)
194 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
195 if err != nil {
196 t.Fatal(err)
197 }
198 views[0].Installation.Empty = false
199 views[0].Installation.Artifacts.StateDirectory = true
200 in := views[0].Installation
201 t.Setenv("CARLOS_DATA_DIR", t.TempDir())
202 snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: in.Box, Enrollment: in.Enrollment, Migration: in.Migration, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: generationPayloadFixture(views)}
203 history := &release.RootHistory{Store: &rollbackDisk{dir: t.TempDir()}}
204 binding := release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}
205 if err := history.RecordCreation(binding, rootHistoryInstallation(in)); err != nil {
206 t.Fatal(err)
207 }
208 if attack == "consumed" {
209 if err := history.ConsumeCreation(binding); err != nil {
210 t.Fatal(err)
211 }
212 }
213 if attack == "state-exists" {
214 if err := os.MkdirAll(provision.InstanceDir(dataDir(), in.Host), 0700); err != nil {
215 t.Fatal(err)
216 }
217 }
218 fresh := func(in hostguard.RootInstallation) error {
219 ok, err := history.FreshInstallation(binding, rootHistoryInstallation(in))
220 if err != nil {
221 return err
222 }
223 if !ok {
224 return os.ErrPermission
225 }
226 return nil
227 }
228 if attack == "no-proof" {
229 fresh = nil
230 }
231 v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) {
232 return generationLocalObligations(ctx, snapshot, fresh)
233 }, kernel: func(context.Context) (string, string, error) {
234 if attack == "changed-during-scan" {
235 if err := history.ConsumeCreation(binding); err != nil {
236 t.Fatal(err)
237 }
238 }
239 return "boot", generationInventoryDigest("kernel"), nil
240 }}
241 _, err = v.reconcile(t.Context(), nil)
242 if (err == nil) != (attack == "none") {
243 t.Fatalf("unwoken installation: %v", err)
244 }
245 })
246 }
247}
248
249func TestPlan14GenerationFreshPrelaunchHandover(t *testing.T) {
250 if ok, err := generationUnlaunched(hostguard.FenceView{Installation: hostguard.RootInstallation{Empty: true}}, generationInventory{}, func(hostguard.RootInstallation) error {
251 t.Fatal("empty installation requested new proof")
252 return os.ErrPermission
253 }); err != nil || ok {
254 t.Fatal(ok, err)
255 }
256
257 view := hostguard.FenceView{Host: "h.example", Installation: hostguard.RootInstallation{Host: "h.example", Incarnation: "inc", Stamp: 2}, Retired: []hostguard.Captured{{Incarnation: "inc", Stamp: 1}}}
258 fresh := func(in hostguard.RootInstallation) error {
259 if in.Host == "h.example" && in.Incarnation == "inc" && in.Stamp == 1 {
260 return nil
261 }
262 return os.ErrPermission
263 }
264 if ok, err := generationUnlaunched(view, generationInventory{}, fresh); err != nil || !ok {
265 t.Fatal(ok, err)
266 }
267 view.Retired[0].Incarnation = "other"
268 if ok, err := generationUnlaunched(view, generationInventory{}, fresh); err == nil || ok {
269 t.Fatal(ok, err)
270 }
271 view.Retired[0].Incarnation = "inc"
272 inv := generationInventory{Launches: []hostbox.LaunchInventory{{Target: hostguard.FenceTarget{Host: view.Host}}}}
273 if ok, err := generationUnlaunched(view, inv, fresh); err != nil || ok {
274 t.Fatal("launched installation treated as fresh", ok, err)
275 }
276 if ok, err := generationUnlaunched(view, generationInventory{}, func(hostguard.RootInstallation) error { return os.ErrInvalid }); err == nil || ok {
277 t.Fatal("unreadable creation accepted", ok, err)
278 }
279}
280