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 unix
2
3package host
4
5import (
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "strconv"
13
14 "github.com/carlosframework/platform/internal/registry"
15)
16
17const registryChildCommand = "__registry-child"
18
19func openRegistryChild(path string, uid, gid int) (*registry.Registry, error) {
20 return openRegistryChildMode(path, uid, gid, false)
21}
22
23func openRegistryChildReadOnly(path string, uid, gid int) (*registry.Registry, error) {
24 return openRegistryChildMode(path, uid, gid, true)
25}
26
27func openRegistryChildMode(path string, uid, gid int, readOnly bool) (*registry.Registry, error) {
28 if uid <= 0 || gid < 0 {
29 return nil, errors.New("registry: invalid service credentials")
30 }
31 path, err := filepath.Abs(path)
32 if err != nil {
33 return nil, err
34 }
35 return registry.OpenRemoteReconnect(path, func() (io.WriteCloser, io.ReadCloser, func() error, error) {
36 cmd, err := registryChildExec(path, uid, gid)
37 if err != nil {
38 return nil, nil, nil, err
39 }
40 if readOnly {
41 cmd.Args = append(cmd.Args, "read-only")
42 }
43 return registryChildPipes(cmd, uid, gid)
44 })
45}
46
47func startRegistryChild(cmd *exec.Cmd, path string, uid, gid int) (*registry.Registry, error) {
48 input, output, wait, err := registryChildPipes(cmd, uid, gid)
49 if err != nil {
50 return nil, err
51 }
52 return registry.OpenRemote(path, input, output, wait)
53}
54
55func registryChildPipes(cmd *exec.Cmd, uid, gid int) (io.WriteCloser, io.ReadCloser, func() error, error) {
56 if uid <= 0 || gid < 0 {
57 return nil, nil, nil, errors.New("registry: invalid service credentials")
58 }
59 if err := registryChildCloseExec(); err != nil {
60 return nil, nil, nil, err
61 }
62 cmd.Env = []string{}
63 if value, ok := os.LookupEnv("CARLOS_HOST_CLAIM"); ok {
64 cmd.Env = append(cmd.Env, "CARLOS_HOST_CLAIM="+value)
65 }
66 cmd.Dir = "/"
67 cmd.SysProcAttr = nil
68 cmd.Stderr = os.Stderr
69 input, err := cmd.StdinPipe()
70 if err != nil {
71 return nil, nil, nil, err
72 }
73 output, err := cmd.StdoutPipe()
74 if err != nil {
75 input.Close()
76 return nil, nil, nil, err
77 }
78 if err = registryChildStart(cmd); err != nil {
79 input.Close()
80 output.Close()
81 return nil, nil, nil, err
82 }
83 return input, output, cmd.Wait, nil
84}
85
86// RunRegistryChild is the private re-exec entry point, before CLI dispatch.
87func RunRegistryChild(args []string) error {
88 if err := registryChildHarden(); err != nil {
89 return err
90 }
91 closeRegistryExecutable()
92 readOnly := len(args) == 4 && args[3] == "read-only"
93 if len(args) != 3 && !readOnly {
94 return errors.New("registry: invalid child arguments")
95 }
96 uid, err := strconv.Atoi(args[1])
97 if err != nil {
98 return err
99 }
100 gid, err := strconv.Atoi(args[2])
101 if err != nil {
102 return err
103 }
104 groups, err := os.Getgroups()
105 if err != nil {
106 return err
107 }
108 if uid <= 0 || os.Getuid() != uid || os.Geteuid() != uid || os.Getgid() != gid || os.Getegid() != gid || len(groups) != 0 {
109 return fmt.Errorf("registry: child credentials were not dropped")
110 }
111 if readOnly {
112 return registry.ServeRemoteReadOnly(args[0], os.Stdin, os.Stdout)
113 }
114 return registry.ServeRemote(args[0], os.Stdin, os.Stdout)
115}
116