//go:build linux package host import ( "context" "fmt" "github.com/carlosframework/platform/internal/cgroup" "golang.org/x/sys/unix" "net" "os" "os/exec" "path/filepath" "strconv" "strings" "time" ) func filesSeedEdgePID(ctx context.Context) (int, error) { ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() b, err := exec.CommandContext(ctx, "systemctl", "show", "-p", "MainPID", "--value", "carlos-edge.service").Output() if err != nil { return 0, err } pid, err := strconv.Atoi(strings.TrimSpace(string(b))) if err != nil || pid <= 0 { return 0, ErrClaimStopIdentity } return pid, nil } // Root repeats both the service identity and the kernel process identity. // Capability supplies no path or PID from the caller and authorizes no write. func verifyFilesSeedDelegation(ctx context.Context, peer StopPeer) error { if os.Geteuid() != 0 || peer.UID == 0 || peer.PID <= 0 { return ErrClaimStopIdentity } check := func() error { pid, err := filesSeedEdgePID(ctx) if err != nil { return err } if pid != peer.PID { return ErrClaimStopIdentity } id, err := (productionStopControllers{}).VerifiedController(ctx, pid) if err != nil { return err } if id != peer.ControllerID { return ErrClaimStopIdentity } return nil } if err := check(); err != nil { return err } root, err := agentUnitCgroup(ctx) if err != nil { return err } rel, err := filepath.Rel("/sys/fs/cgroup", root) if err != nil || !filepath.IsLocal(rel) || rel == "." { return ErrClaimStopIdentity } first, err := inspectClaimContainment(rel) if err != nil { return err } fd, err := unix.Open(root, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0) if err != nil { return err } defer unix.Close(fd) var st unix.Stat_t if err := unix.Fstat(fd, &st); err != nil { return err } if st.Uid != peer.UID || uint64(st.Dev) != first.Device || st.Ino != first.Inode { return ErrClaimStopIdentity } controls, err := os.ReadFile(filepath.Join(root, "cgroup.subtree_control")) if err != nil { return err } enabled := map[string]bool{} for _, c := range strings.Fields(string(controls)) { enabled[c] = true } for _, c := range []string{"cpu", "memory", "io", "pids"} { if !enabled[c] { return ErrClaimStopIdentity } } groups, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", peer.PID)) if err != nil { return err } if strings.TrimSpace(string(groups)) != "0::/"+filepath.ToSlash(filepath.Join(rel, "supervise")) { return ErrClaimStopIdentity } second, err := inspectClaimContainment(rel) if err != nil { return err } if first != second { return ErrClaimStopIdentity } if err := check(); err != nil { return err } return ctx.Err() } func filesSeedManager(ctx context.Context) (*cgroup.Manager, error) { root, err := agentUnitCgroup(ctx) if err != nil { return nil, errFilesSeedDelegation } body, err := os.ReadFile("/proc/self/cgroup") if err != nil { return nil, err } rel, err := filepath.Rel("/sys/fs/cgroup", root) if err != nil || !filepath.IsLocal(rel) || rel == "." { return nil, errFilesSeedDelegation } if strings.TrimSpace(string(body)) != "0::/"+filepath.ToSlash(rel) { return nil, errFilesSeedDelegation } return cgroup.SetupAtRetaining(root, func(string, ...any) {}) } // The connection supplies the PID. pidfd pins that process while two complete // observations detect exit, reuse, cgroup movement and an edge restart. func authenticateFilesSeedPeer(ctx context.Context, conn *net.UnixConn, uid int, server bool) error { raw, err := conn.SyscallConn() if err != nil { return err } var peer *unix.Ucred var peerErr error if err := raw.Control(func(fd uintptr) { peer, peerErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) }); err != nil { return err } if peerErr != nil { return peerErr } if peer == nil || peer.Pid <= 1 || peer.Uid != uint32(uid) || uid == 0 { return ErrClaimStopIdentity } fd, err := unix.PidfdOpen(int(peer.Pid), 0) if err != nil { return err } defer unix.Close(fd) first, err := filesSeedPeerObservation(ctx, int(peer.Pid), server) if err != nil { return err } second, err := filesSeedPeerObservation(ctx, int(peer.Pid), server) if err != nil { return err } if first != second { return ErrClaimStopIdentity } if err := unix.PidfdSendSignal(fd, 0, nil, 0); err != nil { return err } return ctx.Err() } func filesSeedPeerObservation(ctx context.Context, pid int, server bool) (string, error) { edge, err := filesSeedEdgePID(ctx) if err != nil { return "", err } if server && edge != pid || !server && edge != os.Getpid() { return "", ErrClaimStopIdentity } controller, err := (productionStopControllers{}).VerifiedController(ctx, edge) if err != nil { return "", err } root, err := agentUnitCgroup(ctx) if err != nil { return "", err } rel, err := filepath.Rel("/sys/fs/cgroup", root) if err != nil || !filepath.IsLocal(rel) || rel == "." { return "", ErrClaimStopIdentity } raw, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid)) if err != nil { return "", err } end := strings.LastIndexByte(string(raw), ')') if end < 0 { return "", ErrClaimStopIdentity } fields := strings.Fields(string(raw)[end+1:]) if len(fields) < 20 || fields[0] == "Z" { return "", ErrClaimStopIdentity } groups, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) if err != nil { return "", err } group := strings.TrimSpace(string(groups)) if !strings.HasPrefix(group, "0::/") || strings.Contains(group, "\n") { return "", ErrClaimStopIdentity } member := strings.TrimPrefix(group, "0::") unit := "/" + filepath.ToSlash(rel) if server { if member != unit+"/supervise" { return "", ErrClaimStopIdentity } } else if member == unit || strings.HasPrefix(member, unit+"/") { return "", ErrClaimStopIdentity } return fmt.Sprintf("%d:%s:%s:%s:%s", edge, controller, root, fields[19], group), nil }