carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux && rootrunner
2
3package host
4
5import (
6 "context"
7 "crypto/ed25519"
8 "crypto/sha256"
9 "encoding/base64"
10 "encoding/hex"
11 "encoding/json"
12 "github.com/carlosframework/platform/internal/release"
13 "github.com/carlosframework/platform/internal/rootparticipation"
14 "net/http"
15 "net/http/httptest"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "runtime"
20 "sync/atomic"
21 "syscall"
22 "testing"
23 "time"
24
25 "github.com/carlosframework/platform/internal/moves"
26 "github.com/carlosframework/platform/internal/rootstate"
27)
28
29func TestPlan14RootFloorCrashOrderingRootRunner(t *testing.T) {
30 for _, after := range []bool{false, true} {
31 for fail := 1; fail <= 5; fail++ {
32 dir := generationRootRunnerDir(t)
33 id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}
34 f, err := openRootMovesFloors(dir, id, true)
35 if err != nil {
36 t.Fatal(err)
37 }
38 a := moves.Floor{Min: 1, MinRelease: "v1.0.0"}
39 b := moves.Floor{Min: 2, MinRelease: "v2.0.0"}
40 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "root observation", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
41 t.Fatal(err)
42 }
43 n := 0
44 broken := f
45 broken.private = &floorFaultStore{Store: f.private, n: &n, fail: fail, after: after}
46 broken.public = &floorFaultStore{Store: f.public, n: &n, fail: fail, after: after}
47 if err := broken.refresh(t.Context(), &b, nil, nil); err == nil {
48 t.Fatal("injected write succeeded")
49 }
50 f.close()
51 f, err = openRootMovesFloors(dir, id, false)
52 if err != nil {
53 t.Fatal(err)
54 }
55 if p, e := readRootFloorProjection(dir); e == nil && p.Floor != nil && *p.Floor == b {
56 t.Fatal("failed write acknowledged B")
57 }
58 if err := f.refresh(t.Context(), &b, nil, nil); err != nil {
59 t.Fatal("retry", err)
60 }
61 if p, e := readRootFloorProjection(dir); e != nil || p.Floor == nil || *p.Floor != b {
62 t.Fatal("durable retry", p, e)
63 }
64 f.close()
65 // Root-owned leaf under a service-owned ancestor must not open.
66 if err := os.Chown(rootFloorDir(dir), 65534, 65534); err != nil {
67 t.Fatal(err)
68 }
69 if _, err := rootstate.Open(filepath.Join(rootFloorDir(dir), "private"), false, false); err == nil {
70 t.Fatal("hostile parent accepted")
71 }
72 if err := os.Chown(rootFloorDir(dir), 0, 0); err != nil {
73 t.Fatal(err)
74 }
75 }
76 }
77}
78
79func TestPlan14FloorRiseWithoutPlatformUpdateRootRunner(t *testing.T) {
80 for _, prev := range []string{"none", "v2.0.0", "v1.0.0"} {
81 t.Run(prev, func(t *testing.T) {
82 opt := generationRootRunnerDir(t)
83 dd := filepath.Join(opt, "data")
84 if err := os.Mkdir(dd, 0755); err != nil {
85 t.Fatal(err)
86 }
87 t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "deploy")
88 pub, key, err := ed25519.GenerateKey(nil)
89 if err != nil {
90 t.Fatal(err)
91 }
92 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub))
93 rootFloorRunnerBinary(t, opt, systemBinaryPath(opt), "v2.0.0", key)
94 if prev != "none" {
95 rootFloorRunnerBinary(t, opt, systemPrevPath(opt), prev, key)
96 }
97 id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}
98 f, err := openRootMovesFloors(opt, id, true)
99 if err != nil {
100 t.Fatal(err)
101 }
102 defer f.close()
103 a := moves.Floor{Min: 1, MinRelease: "v1.0.0"}
104 b := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
105 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
106 t.Fatal(err)
107 }
108 store := release.NewDirStore(t.TempDir())
109 raw, _ := json.Marshal(b)
110 if _, err := store.Put(t.Context(), moves.FloorKey, raw, ""); err != nil {
111 t.Fatal(err)
112 }
113 if _, err := syncLocalFloor(t.Context(), store, dd); err != nil {
114 t.Fatal(err)
115 }
116 if ad := withMovesFields(edgesFile{Box: "box"}, dd, opt); ad.MovesFloorMinRelease == b.MinRelease {
117 t.Fatal("agent mirror acknowledged B")
118 }
119 gets := 0
120 j := rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), idle: func(context.Context) error { return nil }, binary: func(path string) (rootFloorBinary, error) { return readRootFloorBinary(opt, path) }, legacy: func() ([]*moves.Floor, error) { return rootFloorLegacy(dd) }, remote: func(ctx context.Context) (*moves.Floor, error) { gets++; return moves.ReadFloor(ctx, store) }}
121 if err := j.refresh(t.Context()); err != nil {
122 t.Fatal(err)
123 }
124 ad := withMovesFields(edgesFile{Box: "box"}, dd, opt)
125 if gets != 1 || ad.MovesFloorState != "accepted" || ad.MovesFloorMinRelease != b.MinRelease || ad.PrevRelease != prev {
126 t.Fatal("root projection", gets, ad)
127 }
128 if (ad.PrevAllowedFor == b.MinRelease) != (prev == "v2.0.0") {
129 t.Fatal("prev verdict", ad)
130 }
131 mirror := filepath.Join(dd, ".moves")
132 if err := os.Rename(mirror, mirror+"-moved"); err != nil {
133 t.Fatal(err)
134 }
135 if err := os.Mkdir(mirror, 0700); err != nil {
136 t.Fatal(err)
137 }
138 renamed := withMovesFields(edgesFile{Box: "box"}, dd, opt)
139 if renamed.MovesFloorMinRelease != ad.MovesFloorMinRelease || renamed.PrevRelease != ad.PrevRelease {
140 t.Fatal("mirror rename changed acceptance")
141 }
142 if err := os.RemoveAll(filepath.Join(dd, ".moves")); err != nil {
143 t.Fatal(err)
144 }
145 again := withMovesFields(edgesFile{Box: "box"}, dd, opt)
146 if again.MovesFloorMinRelease != ad.MovesFloorMinRelease || again.PrevRelease != ad.PrevRelease {
147 t.Fatal("mirror deletion changed acceptance")
148 }
149 if err := os.WriteFile(systemBinaryPath(opt), []byte("forged"), 0755); err != nil {
150 t.Fatal(err)
151 }
152 if j.refresh(t.Context()) == nil {
153 t.Fatal("unsigned binary accepted")
154 }
155 if withMovesFields(edgesFile{Box: "box"}, dd, opt).MovesFloorState == "accepted" {
156 t.Fatal("unsigned current acknowledged")
157 }
158 })
159 }
160}
161
162func rootFloorRunnerBinary(t *testing.T, opt, path, version string, key ed25519.PrivateKey) {
163 t.Helper()
164 raw := []byte("#!/bin/sh\n# " + version + "\nexit 0\n")
165 if err := os.WriteFile(path, raw, 0755); err != nil {
166 t.Fatal(err)
167 }
168 identities, err := rootstate.Open(filepath.Join(opt, "system", "guard", "identities"), true, true)
169 if err != nil {
170 t.Fatal(err)
171 }
172 defer identities.Close()
173 sum := sha256.Sum256(raw)
174 platform := runtime.GOOS + "-" + runtime.GOARCH
175 p := release.SystemPointer{Schema: 1, Component: "router", Channel: "stable", Spread: "0s", PromotedAt: time.Now(), Current: release.SystemRelease{Version: version, ReleaseTag: version, MovesProtocol: 1, Artifacts: map[string]release.SystemArtifact{platform: {Key: release.SystemArtifactKey(version, platform), SHA256: hex.EncodeToString(sum[:])}}}}
176 wire, err := release.SignRootEnvelope(release.RootSigner(key), p)
177 if err != nil {
178 t.Fatal(err)
179 }
180 if _, err := release.VerifySystemIdentity(key.Public().(ed25519.PublicKey), wire); err != nil {
181 t.Fatal(err)
182 }
183 if err := identities.Write(version+".json", wire); err != nil {
184 t.Fatal(err)
185 }
186}
187
188func TestPlan14RootFloorAgentProjectionPayload(t *testing.T) {
189 opt := os.Getenv("PLAN14_P3_AGENT_OPT")
190 if opt == "" || os.Geteuid() == 0 {
191 t.Fatal("service-UID payload only")
192 }
193 ad := withMovesFields(edgesFile{Box: "box"}, filepath.Join(opt, "data"), opt)
194 if ad.MovesFloorState != "accepted" || ad.MovesFloorMinRelease != "v2.0.0" {
195 t.Fatal("service projection refused", ad)
196 }
197 // The same UID must be unable to change either projection or private state.
198 for _, path := range []string{filepath.Join(rootFloorDir(opt), rootFloorProjectionFile), filepath.Join(rootFloorDir(opt), "private", rootFloorAccepted)} {
199 if err := os.WriteFile(path, []byte("forged"), 0644); err == nil {
200 t.Fatal("service changed root floor")
201 }
202 }
203}
204
205func TestPlan14RootFloorAdoptRefreshRootRunner(t *testing.T) {
206 // This fixture owns its whole namespace; refuse any existing root census.
207 if err := os.Mkdir("/var/lib/carlos-claims", 0700); err != nil {
208 t.Fatal(err)
209 }
210 t.Cleanup(func() {
211 if err := os.RemoveAll("/var/lib/carlos-claims"); err != nil {
212 t.Error(err)
213 }
214 })
215 census, err := rootstate.Open(rootparticipation.Namespace, true, false)
216 if err != nil {
217 t.Fatal(err)
218 }
219 raw, _ := json.Marshal(rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true})
220 if err := census.Write("census.json", raw); err != nil {
221 t.Fatal(err)
222 }
223 census.Close()
224 opt := generationRootRunnerDir(t)
225 dd := filepath.Join(opt, "data")
226 if err := os.Mkdir(dd, 0755); err != nil {
227 t.Fatal(err)
228 }
229 storeDir := filepath.Join(opt, "deployment")
230 if err := os.Mkdir(storeDir, 0755); err != nil {
231 t.Fatal(err)
232 }
233 t.Setenv("CARLOS_OPT_DIR", opt)
234 t.Setenv("CARLOS_DATA_DIR", dd)
235 t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "deploy")
236 t.Setenv("CARLOS_DEPLOYMENT_DIR", storeDir)
237 pub, key, err := ed25519.GenerateKey(nil)
238 if err != nil {
239 t.Fatal(err)
240 }
241 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub))
242 rootFloorRunnerBinary(t, opt, systemBinaryPath(opt), "v2.0.0", key)
243 id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}
244 f, err := openRootMovesFloors(opt, id, true)
245 if err != nil {
246 t.Fatal(err)
247 }
248 defer f.close()
249 a := moves.Floor{Min: 1, MinRelease: "v1.0.0"}
250 b := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
251 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
252 t.Fatal(err)
253 }
254 store := release.NewDirStore(storeDir)
255 raw, _ = json.Marshal(b)
256 if _, err := store.Put(t.Context(), moves.FloorKey, raw, ""); err != nil {
257 t.Fatal(err)
258 }
259 if _, err := syncLocalFloor(t.Context(), store, dd); err != nil {
260 t.Fatal(err)
261 }
262 // The real provider reads only this loopback S3 fixture; it makes no AWS call.
263 var gets atomic.Int32
264 endpoint := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
265 if r.Method != "GET" || r.URL.Path != "/deploy/"+moves.FloorKey {
266 http.NotFound(w, r)
267 return
268 }
269 gets.Add(1)
270 w.Header().Set("ETag", "fixture")
271 w.Write(raw)
272 }))
273 defer endpoint.Close()
274 t.Setenv("AWS_REGION", "eu-west-1")
275 t.Setenv("AWS_DEFAULT_REGION", "eu-west-1")
276 t.Setenv("AWS_ACCESS_KEY_ID", "fixture")
277 t.Setenv("AWS_SECRET_ACCESS_KEY", "fixture")
278 t.Setenv("AWS_SESSION_TOKEN", "")
279 t.Setenv("AWS_EC2_METADATA_DISABLED", "true")
280 t.Setenv("AWS_ENDPOINT_URL_S3", endpoint.URL)
281 t.Setenv("CARLOS_S3_ENDPOINT", endpoint.URL)
282 t.Setenv("AWS_PROFILE", "")
283 t.Setenv("AWS_DEFAULT_PROFILE", "")
284 if err := withClaimRootRelease(t.Context(), true, func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(opt, "release"), true, false) }, func() error { return rootJudgePrev(nil) }); err != nil {
285 t.Fatal(err)
286 }
287 if gets.Load() == 0 {
288 t.Fatal("no authoritative GET before no-prev return")
289 }
290 ad := withMovesFields(edgesFile{Box: "box"}, dd, opt)
291 if ad.MovesFloorState != "accepted" || ad.MovesFloorMinRelease != b.MinRelease || ad.PrevRelease != "none" {
292 t.Fatal("actual adopt refresh", ad)
293 }
294 rawBinary, err := os.ReadFile(os.Args[0])
295 if err != nil {
296 t.Fatal(err)
297 }
298 agentBinary := filepath.Join(opt, "agent.test")
299 if err := os.WriteFile(agentBinary, rawBinary, 0755); err != nil {
300 t.Fatal(err)
301 }
302 cmd := exec.CommandContext(t.Context(), agentBinary, "-test.run=^TestPlan14RootFloorAgentProjectionPayload$", "-test.v")
303 cmd.Dir = opt
304 cmd.Env = append(os.Environ(), "PLAN14_P3_AGENT_OPT="+opt)
305 cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534}}
306 if out, err := cmd.CombinedOutput(); err != nil {
307 t.Fatalf("service UID: %v\n%s", err, out)
308 }
309 // Direct apply/recovery adapters cannot use erased legacy files to go back.
310 if err := os.RemoveAll(filepath.Join(dd, ".moves")); err != nil {
311 t.Fatal(err)
312 }
313 app := systemApplier{optDir: opt, dataDir: dd, rootMoves: rootMovesFloorIdentity}
314 if app.floorAllowsForward("v1.0.0") == nil {
315 t.Fatal("legacy deletion allowed downgrade")
316 }
317 if err := app.floorAllowsForward("v2.0.0"); err != nil {
318 t.Fatal("same floor refused", err)
319 }
320}
321
322func TestPlan14RootFloorSignedTargetRootRunner(t *testing.T) {
323 opt := generationRootRunnerDir(t)
324 pub, key, err := ed25519.GenerateKey(nil)
325 if err != nil {
326 t.Fatal(err)
327 }
328 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub))
329 id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}
330 f, err := openRootMovesFloors(opt, id, true)
331 if err != nil {
332 t.Fatal(err)
333 }
334 defer f.close()
335 b := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
336 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &b, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
337 t.Fatal(err)
338 }
339 store := release.NewDirStore(t.TempDir())
340 platform := runtime.GOOS + "-" + runtime.GOARCH
341 bytes := []byte("new signed target without earlier root history")
342 sum := sha256.Sum256(bytes)
343 p, err := release.PromoteSystem(t.Context(), store, release.RootSigner(key), "router", "stable", release.SystemRelease{Version: "v3.0.0", ReleaseTag: "v3.0.0", MovesProtocol: 1, Artifacts: map[string]release.SystemArtifact{platform: {Key: release.SystemArtifactKey("v3.0.0", platform), SHA256: hex.EncodeToString(sum[:])}}}, release.SystemPromoteOptions{})
344 if err != nil {
345 t.Fatal(err)
346 }
347 staged := filepath.Join(opt, "staging", "carlos")
348 if err := os.Mkdir(filepath.Dir(staged), 0700); err != nil {
349 t.Fatal(err)
350 }
351 if err := os.WriteFile(staged, bytes, 0755); err != nil {
352 t.Fatal(err)
353 }
354 calls := 0
355 a := systemApplier{optDir: opt, channel: "stable", rootMoves: func() (rootFloorIdentity, bool, error) { return id, true, nil }, openStore: func(context.Context) (release.Store, ed25519.PublicKey, error) { return store, pub, nil }, ops: sysOps{run: func(context.Context, time.Duration, string, ...string) (string, error) { calls++; return "ran", nil }}}
356 if _, err := a.verifyPointer(t.Context()); err != nil {
357 t.Fatal(err)
358 }
359 if _, err := a.guardRun(t.Context(), time.Second, staged, "version"); err != nil || calls != 1 {
360 t.Fatal("signed target with unknown history refused", err, calls)
361 }
362 for _, storeName := range []struct {
363 store rootstate.Store
364 name string
365 }{{f.private, rootFloorAccepted}, {f.private, rootFloorDeny}, {f.public, rootFloorSelector}, {f.public, rootFloorProjectionFile}} {
366 if err := storeName.store.Remove(storeName.name); err != nil {
367 t.Fatal(err)
368 }
369 }
370 if _, err := a.guardRun(t.Context(), time.Second, staged, "version"); err != nil || calls != 2 {
371 t.Fatal("unknown floor blocked signed version", err, calls)
372 }
373 changed := *p
374 changed.Current.Version = "other"
375 if rememberRootFloorSystemIdentity(t.Context(), store, pub, opt, &changed) == nil {
376 t.Fatal("pointer substitution accepted")
377 }
378 if err := os.WriteFile(staged, []byte("substitution"), 0755); err != nil {
379 t.Fatal(err)
380 }
381 if _, err := a.guardRun(t.Context(), time.Second, staged, "version"); err == nil || calls != 2 {
382 t.Fatal("unsigned target executed", err, calls)
383 }
384}
385