//go:build unix package host import ( "errors" "fmt" "io" "os" "os/exec" "path/filepath" "strconv" "github.com/carlosframework/platform/internal/registry" ) const registryChildCommand = "__registry-child" func openRegistryChild(path string, uid, gid int) (*registry.Registry, error) { return openRegistryChildMode(path, uid, gid, false) } func openRegistryChildReadOnly(path string, uid, gid int) (*registry.Registry, error) { return openRegistryChildMode(path, uid, gid, true) } func openRegistryChildMode(path string, uid, gid int, readOnly bool) (*registry.Registry, error) { if uid <= 0 || gid < 0 { return nil, errors.New("registry: invalid service credentials") } path, err := filepath.Abs(path) if err != nil { return nil, err } return registry.OpenRemoteReconnect(path, func() (io.WriteCloser, io.ReadCloser, func() error, error) { cmd, err := registryChildExec(path, uid, gid) if err != nil { return nil, nil, nil, err } if readOnly { cmd.Args = append(cmd.Args, "read-only") } return registryChildPipes(cmd, uid, gid) }) } func startRegistryChild(cmd *exec.Cmd, path string, uid, gid int) (*registry.Registry, error) { input, output, wait, err := registryChildPipes(cmd, uid, gid) if err != nil { return nil, err } return registry.OpenRemote(path, input, output, wait) } func registryChildPipes(cmd *exec.Cmd, uid, gid int) (io.WriteCloser, io.ReadCloser, func() error, error) { if uid <= 0 || gid < 0 { return nil, nil, nil, errors.New("registry: invalid service credentials") } if err := registryChildCloseExec(); err != nil { return nil, nil, nil, err } cmd.Env = []string{} if value, ok := os.LookupEnv("CARLOS_HOST_CLAIM"); ok { cmd.Env = append(cmd.Env, "CARLOS_HOST_CLAIM="+value) } cmd.Dir = "/" cmd.SysProcAttr = nil cmd.Stderr = os.Stderr input, err := cmd.StdinPipe() if err != nil { return nil, nil, nil, err } output, err := cmd.StdoutPipe() if err != nil { input.Close() return nil, nil, nil, err } if err = registryChildStart(cmd); err != nil { input.Close() output.Close() return nil, nil, nil, err } return input, output, cmd.Wait, nil } // RunRegistryChild is the private re-exec entry point, before CLI dispatch. func RunRegistryChild(args []string) error { if err := registryChildHarden(); err != nil { return err } closeRegistryExecutable() readOnly := len(args) == 4 && args[3] == "read-only" if len(args) != 3 && !readOnly { return errors.New("registry: invalid child arguments") } uid, err := strconv.Atoi(args[1]) if err != nil { return err } gid, err := strconv.Atoi(args[2]) if err != nil { return err } groups, err := os.Getgroups() if err != nil { return err } if uid <= 0 || os.Getuid() != uid || os.Geteuid() != uid || os.Getgid() != gid || os.Getegid() != gid || len(groups) != 0 { return fmt.Errorf("registry: child credentials were not dropped") } if readOnly { return registry.ServeRemoteReadOnly(args[0], os.Stdin, os.Stdout) } return registry.ServeRemote(args[0], os.Stdin, os.Stdout) }