diff --git a/client/cmd/root.go b/client/cmd/root.go index 0a0aa4197..b5a30def4 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -77,6 +77,8 @@ var ( updateSettingsDisabled bool captureEnabled bool networksDisabled bool + socketOwner string + strictSocketDisabled bool rootCmd = &cobra.Command{ Use: "netbird", diff --git a/client/cmd/service.go b/client/cmd/service.go index 56d8a8726..8d702eb46 100644 --- a/client/cmd/service.go +++ b/client/cmd/service.go @@ -57,6 +57,9 @@ func init() { installCmd.Flags().StringSliceVar(&serviceEnvVars, "service-env", nil, serviceEnvDesc) reconfigureCmd.Flags().StringSliceVar(&serviceEnvVars, "service-env", nil, serviceEnvDesc) + serviceCmd.PersistentFlags().StringVar(&socketOwner, "socket-owner", "", "Socket owner TODO") + serviceCmd.PersistentFlags().BoolVar(&strictSocketDisabled, "disable-strict-socket", false, "Restrict socket permissions TODO") + rootCmd.AddCommand(serviceCmd) } diff --git a/client/cmd/service_controller.go b/client/cmd/service_controller.go index 8de147946..efc6735b0 100644 --- a/client/cmd/service_controller.go +++ b/client/cmd/service_controller.go @@ -4,9 +4,13 @@ package cmd import ( "context" + "errors" "fmt" "net" "os" + "os/exec" + "os/user" + "strconv" "strings" "time" @@ -55,7 +59,32 @@ func (p *program) Start(svc service.Service) error { defer listen.Close() if split[0] == "unix" { - if err := os.Chmod(split[1], 0666); err != nil { + socketPerm := os.FileMode(0666) + if socketOwner != "" && !strictSocketDisabled { + socketPerm = 0660 + gid, err := addGroup("netbird") + if err != nil { + log.Errorf("failed setting up group (%d): %v", gid, err) + } + user, err := user.Lookup(socketOwner) + if err != nil { + log.Errorf("lookup user %q: %v", socketOwner, err) + return + } + uid, err := strconv.ParseInt(user.Uid, 10, 64) + if err != nil { + log.Errorf("falied to convert uid (%d) to int: %v", uid, err) + return + } + if err = os.Chown(split[1], int(uid), int(gid)); err != nil { + log.Errorf("failed setting daemon group (%d) on socket: %v", gid, split[1]) + return + } + } + if socketOwner == "" && !strictSocketDisabled { + // TODO: handle TOFU + } + if err := os.Chmod(split[1], socketPerm); err != nil { log.Errorf("failed setting daemon permissions: %v", split[1]) return } @@ -79,6 +108,36 @@ func (p *program) Start(svc service.Service) error { return nil } +// addGroup creates a system group if it doesn't already exist and returns the gid. +// Must run as root. +func addGroup(name string) (int, error) { + if group, err := user.LookupGroup(name); err == nil { + gid, err := strconv.ParseInt(group.Gid, 10, 64) + return int(gid), err + } else if _, ok := err.(user.UnknownGroupError); !ok { + return -1, fmt.Errorf("lookup group %q: %w", name, err) + } + + groupadd, err := exec.LookPath("groupadd") + if err != nil { + // Fallback for Alpine/BusyBox systems. + if groupadd, err = exec.LookPath("addgroup"); err != nil { + return -1, errors.New("neither groupadd nor addgroup found") + } + } + + // Use --system for a service/daemon group (no login, low GID). + out, err := exec.Command(groupadd, "--system", name).CombinedOutput() + if err != nil { + return -1, fmt.Errorf("create group %q: %w: %s", name, err, out) + } + if group, err := user.LookupGroup(name); err == nil { + gid, err := strconv.ParseInt(group.Gid, 10, 64) + return int(gid), err + } + return -1, fmt.Errorf("lookup group %q: %w", name, err) +} + func (p *program) Stop(srv service.Service) error { p.serverInstanceMu.Lock() if p.serverInstance != nil { diff --git a/client/cmd/service_installer.go b/client/cmd/service_installer.go index 2d45fa063..11acbd518 100644 --- a/client/cmd/service_installer.go +++ b/client/cmd/service_installer.go @@ -67,6 +67,10 @@ func buildServiceArguments() []string { args = append(args, "--disable-networks") } + if socketOwner != "" { + args = append(args, "--socket-owner", socketOwner) + } + return args } @@ -127,6 +131,8 @@ var installCmd = &cobra.Command{ return err } + cmd.Printf("SUDO_UID: %s\n", os.Getenv("SUDO_UID")) + if err := loadAndApplyServiceParams(cmd); err != nil { cmd.PrintErrf("Warning: failed to load saved service params: %v\n", err) }