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 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/hostbox"
11 "github.com/carlosframework/platform/internal/hostclaim"
12)
13
14func TestPlan14FirstInstallWithVendorUnitsRootRunner(t *testing.T) {
15 servingRootRunner(t)
16 uid, _ := generationRunnerRegistryUser(t)
17 units, err := os.MkdirTemp("/var/lib", "plan14-vendor-census-")
18 lifecycleCheck(t, "units", err)
19 defer os.RemoveAll(units)
20 lifecycleCheck(t, "public units ancestry", os.Chmod(units, 0755))
21 f, root, q := plan14InstallRegistration(t, hostclaim.KindServing, "")
22 root.Roots["units"] = units
23 selected := hostbox.RecordSelection{Host: q.Target.Host, Key: q.RecordKey, ETag: q.RecordETag, Incarnation: q.Target.Incarnation, Family: "instance"}
24 check := func() error { return root.Reconcile.Census.CheckLocal(t.Context(), f.read(), selected) }
25 lifecycleCheck(t, "empty census", check())
26 target := "/lib/systemd/system/systemd-journald.service"
27 _, err = os.Stat(target)
28 lifecycleCheck(t, "vendor unit", err)
29 wants := filepath.Join(units, "multi-user.target.wants")
30 lifecycleCheck(t, "wants", os.Mkdir(wants, 0755))
31 link := filepath.Join(wants, "systemd-journald.service")
32 lifecycleCheck(t, "vendor link", os.Symlink(target, link))
33 lifecycleCheck(t, "template wants", os.Mkdir(filepath.Join(units, "sshd@.service.wants"), 0755))
34 lifecycleCheck(t, "template wants link", os.Symlink(target, filepath.Join(units, "sshd@.service.wants", "sshd-keygen.service")))
35 lifecycleCheck(t, "template link", os.Symlink("/lib/systemd/system/getty@.service", filepath.Join(wants, "getty@tty1.service")))
36 lifecycleCheck(t, "first installation with OS unit", check())
37 lifecycleCheck(t, "migration with OS unit", hostbox.CheckMigrationRoots(t.Context(), root.Roots, root.Reconcile.Census.SharedPaths, nil))
38 for _, attack := range []string{"service owner", "foreign target", "host alias", "carlos alias", "regular file", "writable ancestor"} {
39 t.Run(attack, func(t *testing.T) {
40 bad := filepath.Join(wants, "unexpected.service")
41 switch attack {
42 case "service owner":
43 lifecycleCheck(t, "owner", os.Lchown(link, uid, -1))
44 defer os.Lchown(link, 0, 0)
45 case "writable ancestor":
46 lifecycleCheck(t, "mode", os.Chmod(wants, 0777))
47 defer os.Chmod(wants, 0755)
48 case "foreign target":
49 lifecycleCheck(t, "link", os.Symlink("/etc/passwd", bad))
50 defer os.Remove(bad)
51 case "host alias":
52 bad = filepath.Join(wants, q.Target.Host+".service")
53 lifecycleCheck(t, "link", os.Symlink(target, bad))
54 defer os.Remove(bad)
55 case "carlos alias":
56 bad = filepath.Join(wants, "carlos-instance@other.service")
57 lifecycleCheck(t, "link", os.Symlink(target, bad))
58 defer os.Remove(bad)
59 case "regular file":
60 lifecycleCheck(t, "file", os.WriteFile(bad, []byte("[Service]\nExecStart=/bin/true\n"), 0644))
61 defer os.Remove(bad)
62 }
63 if err := check(); err == nil {
64 t.Fatal("unsafe unit inventory accepted")
65 }
66 })
67 }
68 _, err = root.commitRootInventory(t.Context(), q)
69 lifecycleCheck(t, "first production registration", err)
70}
71