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
2
3package instancectl
4
5import (
6 "context"
7 "errors"
8 "net"
9 "os"
10 "path/filepath"
11 "testing"
12 "time"
13)
14
15func claimFilesystemUnits(t *testing.T) (ClaimUnits, string) {
16 t.Helper()
17 root, err := os.MkdirTemp("", "cfs")
18 if err != nil {
19 t.Fatal(err)
20 }
21 t.Cleanup(func() { os.RemoveAll(root) })
22 return ClaimUnits{ExeRoot: filepath.Join(root, "exe"), UnitRoot: filepath.Join(root, "units"), DataRoot: filepath.Join(root, "data"), Systemctl: func(context.Context, ...string) (string, error) { return "", nil }}, "safe.example.com"
23}
24
25func TestClaimFilesystemRootDirRefusesParentSymlinkBeforeCreate(t *testing.T) {
26 root := t.TempDir()
27 victim := filepath.Join(root, "victim")
28 if err := os.Mkdir(victim, 0700); err != nil {
29 t.Fatal(err)
30 }
31 link := filepath.Join(root, "link")
32 if err := os.Symlink(victim, link); err != nil {
33 t.Fatal(err)
34 }
35 if err := claimRootDir(filepath.Join(link, "created")); err == nil {
36 t.Fatal("accepted symlink parent")
37 }
38 if _, err := os.Lstat(filepath.Join(victim, "created")); !errors.Is(err, os.ErrNotExist) {
39 t.Fatalf("changed victim before refusal: %v", err)
40 }
41}
42
43func TestClaimFilesystemClearRefusesSymlinkParents(t *testing.T) {
44 for _, target := range []string{"source", "dropin"} {
45 t.Run(target, func(t *testing.T) {
46 u, host := claimFilesystemUnits(t)
47 source, dropin, err := u.launchPaths(host)
48 if err != nil {
49 t.Fatal(err)
50 }
51 victim := t.TempDir()
52 for _, name := range []string{"launch.json", "permit.json", "exec.sock", "60-claim-launch.conf"} {
53 if err := os.WriteFile(filepath.Join(victim, name), []byte("victim"), 0600); err != nil {
54 t.Fatal(err)
55 }
56 }
57 linked, ordinary := source, dropin
58 if target == "dropin" {
59 linked, ordinary = dropin, source
60 }
61 if err := os.MkdirAll(filepath.Dir(linked), 0700); err != nil {
62 t.Fatal(err)
63 }
64 if err := os.MkdirAll(ordinary, 0700); err != nil {
65 t.Fatal(err)
66 }
67 if err := os.Symlink(victim, linked); err != nil {
68 t.Fatal(err)
69 }
70 if err := u.clearLaunch(context.Background(), host); err == nil {
71 t.Error("accepted symlink parent")
72 }
73 for _, name := range []string{"launch.json", "permit.json", "exec.sock", "60-claim-launch.conf"} {
74 raw, err := os.ReadFile(filepath.Join(victim, name))
75 if err != nil || string(raw) != "victim" {
76 t.Errorf("victim %s changed: %q, %v", name, raw, err)
77 }
78 }
79 })
80 }
81}
82
83func TestClaimFilesystemFreezeReplacesHardlinksWithoutChangingVictim(t *testing.T) {
84 u, host := claimFilesystemUnits(t)
85 source, dropin, err := u.launchPaths(host)
86 if err != nil {
87 t.Fatal(err)
88 }
89 for _, dir := range []string{source, dropin} {
90 if err := os.MkdirAll(dir, 0700); err != nil {
91 t.Fatal(err)
92 }
93 }
94 victim := filepath.Join(t.TempDir(), "victim")
95 if err := os.WriteFile(victim, []byte("victim"), 0600); err != nil {
96 t.Fatal(err)
97 }
98 for _, path := range []string{filepath.Join(source, "launch.json"), filepath.Join(dropin, "60-claim-launch.conf")} {
99 if err := os.Link(victim, path); err != nil {
100 t.Fatal(err)
101 }
102 }
103 if err := u.freezeLaunch(context.Background(), host, Launch{Schema: LaunchSchema, Host: host}); err != nil {
104 t.Fatal(err)
105 }
106 raw, err := os.ReadFile(victim)
107 if err != nil || string(raw) != "victim" {
108 t.Fatalf("victim changed: %q, %v", raw, err)
109 }
110 st, err := os.Stat(victim)
111 if err != nil {
112 t.Fatal(err)
113 }
114 if st.Mode().Perm() != 0600 {
115 t.Fatalf("victim mode changed: %o", st.Mode().Perm())
116 }
117}
118
119func TestClaimFilesystemExecGateCanonicalSocketRetained(t *testing.T) {
120 u, host := claimFilesystemUnits(t)
121 if err := u.freezeLaunch(context.Background(), host, Launch{Schema: LaunchSchema, Host: host}); err != nil {
122 t.Fatal(err)
123 }
124 gate, err := u.prepareExecGate(context.Background(), host, ClaimExecPermit{Reservation: "r", Inputs: "i", BootID: "b", SafeUntil: 1})
125 if err != nil {
126 t.Fatal(err)
127 }
128 defer gate.close()
129 conn, err := net.DialTimeout("unix", gate.path, time.Second)
130 if err != nil {
131 t.Fatal(err)
132 }
133 conn.Close()
134 before, err := os.Lstat(gate.path)
135 if err != nil {
136 t.Fatal(err)
137 }
138 if before.Mode()&os.ModeSocket == 0 || before.Mode().Perm() != 0666 {
139 t.Fatalf("socket mode = %v", before.Mode())
140 }
141 gate.close()
142 after, err := os.Lstat(gate.path)
143 if err != nil || !os.SameFile(before, after) {
144 t.Fatalf("socket inode removed or replaced: %v", err)
145 }
146}
147