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 host
4
5import (
6 "context"
7 "fmt"
8 "github.com/carlosframework/platform/internal/cgroup"
9 "golang.org/x/sys/unix"
10 "net"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "strconv"
15 "strings"
16 "time"
17)
18
19func filesSeedEdgePID(ctx context.Context) (int, error) {
20 ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
21 defer cancel()
22 b, err := exec.CommandContext(ctx, "systemctl", "show", "-p", "MainPID", "--value", "carlos-edge.service").Output()
23 if err != nil {
24 return 0, err
25 }
26 pid, err := strconv.Atoi(strings.TrimSpace(string(b)))
27 if err != nil || pid <= 0 {
28 return 0, ErrClaimStopIdentity
29 }
30 return pid, nil
31}
32
33// Root repeats both the service identity and the kernel process identity.
34// Capability supplies no path or PID from the caller and authorizes no write.
35func verifyFilesSeedDelegation(ctx context.Context, peer StopPeer) error {
36 if os.Geteuid() != 0 || peer.UID == 0 || peer.PID <= 0 {
37 return ErrClaimStopIdentity
38 }
39 check := func() error {
40 pid, err := filesSeedEdgePID(ctx)
41 if err != nil {
42 return err
43 }
44 if pid != peer.PID {
45 return ErrClaimStopIdentity
46 }
47 id, err := (productionStopControllers{}).VerifiedController(ctx, pid)
48 if err != nil {
49 return err
50 }
51 if id != peer.ControllerID {
52 return ErrClaimStopIdentity
53 }
54 return nil
55 }
56 if err := check(); err != nil {
57 return err
58 }
59 root, err := agentUnitCgroup(ctx)
60 if err != nil {
61 return err
62 }
63 rel, err := filepath.Rel("/sys/fs/cgroup", root)
64 if err != nil || !filepath.IsLocal(rel) || rel == "." {
65 return ErrClaimStopIdentity
66 }
67 first, err := inspectClaimContainment(rel)
68 if err != nil {
69 return err
70 }
71 fd, err := unix.Open(root, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0)
72 if err != nil {
73 return err
74 }
75 defer unix.Close(fd)
76 var st unix.Stat_t
77 if err := unix.Fstat(fd, &st); err != nil {
78 return err
79 }
80 if st.Uid != peer.UID || uint64(st.Dev) != first.Device || st.Ino != first.Inode {
81 return ErrClaimStopIdentity
82 }
83 controls, err := os.ReadFile(filepath.Join(root, "cgroup.subtree_control"))
84 if err != nil {
85 return err
86 }
87 enabled := map[string]bool{}
88 for _, c := range strings.Fields(string(controls)) {
89 enabled[c] = true
90 }
91 for _, c := range []string{"cpu", "memory", "io", "pids"} {
92 if !enabled[c] {
93 return ErrClaimStopIdentity
94 }
95 }
96 groups, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", peer.PID))
97 if err != nil {
98 return err
99 }
100 if strings.TrimSpace(string(groups)) != "0::/"+filepath.ToSlash(filepath.Join(rel, "supervise")) {
101 return ErrClaimStopIdentity
102 }
103 second, err := inspectClaimContainment(rel)
104 if err != nil {
105 return err
106 }
107 if first != second {
108 return ErrClaimStopIdentity
109 }
110 if err := check(); err != nil {
111 return err
112 }
113 return ctx.Err()
114}
115
116func filesSeedManager(ctx context.Context) (*cgroup.Manager, error) {
117 root, err := agentUnitCgroup(ctx)
118 if err != nil {
119 return nil, errFilesSeedDelegation
120 }
121 body, err := os.ReadFile("/proc/self/cgroup")
122 if err != nil {
123 return nil, err
124 }
125 rel, err := filepath.Rel("/sys/fs/cgroup", root)
126 if err != nil || !filepath.IsLocal(rel) || rel == "." {
127 return nil, errFilesSeedDelegation
128 }
129 if strings.TrimSpace(string(body)) != "0::/"+filepath.ToSlash(rel) {
130 return nil, errFilesSeedDelegation
131 }
132 return cgroup.SetupAtRetaining(root, func(string, ...any) {})
133}
134
135// The connection supplies the PID. pidfd pins that process while two complete
136// observations detect exit, reuse, cgroup movement and an edge restart.
137func authenticateFilesSeedPeer(ctx context.Context, conn *net.UnixConn, uid int, server bool) error {
138 raw, err := conn.SyscallConn()
139 if err != nil {
140 return err
141 }
142 var peer *unix.Ucred
143 var peerErr error
144 if err := raw.Control(func(fd uintptr) { peer, peerErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) }); err != nil {
145 return err
146 }
147 if peerErr != nil {
148 return peerErr
149 }
150 if peer == nil || peer.Pid <= 1 || peer.Uid != uint32(uid) || uid == 0 {
151 return ErrClaimStopIdentity
152 }
153 fd, err := unix.PidfdOpen(int(peer.Pid), 0)
154 if err != nil {
155 return err
156 }
157 defer unix.Close(fd)
158 first, err := filesSeedPeerObservation(ctx, int(peer.Pid), server)
159 if err != nil {
160 return err
161 }
162 second, err := filesSeedPeerObservation(ctx, int(peer.Pid), server)
163 if err != nil {
164 return err
165 }
166 if first != second {
167 return ErrClaimStopIdentity
168 }
169 if err := unix.PidfdSendSignal(fd, 0, nil, 0); err != nil {
170 return err
171 }
172 return ctx.Err()
173}
174
175func filesSeedPeerObservation(ctx context.Context, pid int, server bool) (string, error) {
176 edge, err := filesSeedEdgePID(ctx)
177 if err != nil {
178 return "", err
179 }
180 if server && edge != pid || !server && edge != os.Getpid() {
181 return "", ErrClaimStopIdentity
182 }
183 controller, err := (productionStopControllers{}).VerifiedController(ctx, edge)
184 if err != nil {
185 return "", err
186 }
187 root, err := agentUnitCgroup(ctx)
188 if err != nil {
189 return "", err
190 }
191 rel, err := filepath.Rel("/sys/fs/cgroup", root)
192 if err != nil || !filepath.IsLocal(rel) || rel == "." {
193 return "", ErrClaimStopIdentity
194 }
195 raw, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
196 if err != nil {
197 return "", err
198 }
199 end := strings.LastIndexByte(string(raw), ')')
200 if end < 0 {
201 return "", ErrClaimStopIdentity
202 }
203 fields := strings.Fields(string(raw)[end+1:])
204 if len(fields) < 20 || fields[0] == "Z" {
205 return "", ErrClaimStopIdentity
206 }
207 groups, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
208 if err != nil {
209 return "", err
210 }
211 group := strings.TrimSpace(string(groups))
212 if !strings.HasPrefix(group, "0::/") || strings.Contains(group, "\n") {
213 return "", ErrClaimStopIdentity
214 }
215 member := strings.TrimPrefix(group, "0::")
216 unit := "/" + filepath.ToSlash(rel)
217 if server {
218 if member != unit+"/supervise" {
219 return "", ErrClaimStopIdentity
220 }
221 } else if member == unit || strings.HasPrefix(member, unit+"/") {
222 return "", ErrClaimStopIdentity
223 }
224 return fmt.Sprintf("%d:%s:%s:%s:%s", edge, controller, root, fields[19], group), nil
225}
226