mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-14 18:59:08 +02:00
103 lines
3.2 KiB
Go
103 lines
3.2 KiB
Go
//go:build !windows && !ios && !android
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/getent"
|
|
)
|
|
|
|
// Socket modes. openSocketMode is the historical one: any local account may
|
|
// connect, and what it may then do is decided from its kernel-attested
|
|
// identity. restrictedSocketMode is what --allow-group installs, where the
|
|
// kernel refuses the connect() outright for an account outside the group.
|
|
const (
|
|
openSocketMode os.FileMode = 0666
|
|
restrictedSocketMode os.FileMode = 0660
|
|
)
|
|
|
|
// resolveAllowGroup resolves one --allow-group value to a "gid:<id>"
|
|
// principal. A numeric value, with or without the prefix, is the GID itself;
|
|
// anything else is a group name resolved through NSS, so groups that only
|
|
// LDAP, SSSD or winbind know about work as well as ones in /etc/group.
|
|
func resolveAllowGroup(value string) (string, error) {
|
|
if kind, rest, ok := cutKind(value); ok {
|
|
if kind != allowGroupKindGID {
|
|
return "", fmt.Errorf("unsupported principal kind %q, use a group name or %s:<id>", kind, allowGroupKindGID)
|
|
}
|
|
return gidPrincipal(rest)
|
|
}
|
|
|
|
if _, err := parseGID(value); err == nil {
|
|
return gidPrincipal(value)
|
|
}
|
|
|
|
group, err := getent.LookupGroupName(value)
|
|
if err != nil {
|
|
return "", fmt.Errorf("look up group: %w", err)
|
|
}
|
|
return gidPrincipal(group.Gid)
|
|
}
|
|
|
|
// checkAllowGroupSet rejects more than one principal: a Unix socket carries a
|
|
// single owning group, so a second one could not be enforced and must not be
|
|
// accepted as though it were.
|
|
func checkAllowGroupSet(principals []string) error {
|
|
if len(principals) > 1 {
|
|
return fmt.Errorf("--allow-group takes a single group on this platform, got %d: %v", len(principals), principals)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// applySocketAccess sets the access the socket grants to other accounts: the
|
|
// allowed group at 0660, or every local account at 0666 when no group is
|
|
// configured.
|
|
//
|
|
// The owner is left untouched so a daemon running as an ordinary user, as in a
|
|
// rootless container, keeps access to the socket it created. The group is set
|
|
// before the mode is widened, so the window between the two is one where the
|
|
// group has no access rather than one where it has access it should not.
|
|
func applySocketAccess(path string, principals []string) error {
|
|
if len(principals) == 0 {
|
|
if err := os.Chmod(path, openSocketMode); err != nil {
|
|
return fmt.Errorf("set mode %#o: %w", openSocketMode, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
value, ok := principalValue(principals[0], allowGroupKindGID)
|
|
if !ok {
|
|
return fmt.Errorf("not a %s principal: %q", allowGroupKindGID, principals[0])
|
|
}
|
|
gid, err := parseGID(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.Chown(path, -1, gid); err != nil {
|
|
return fmt.Errorf("set group to gid %d: %w", gid, err)
|
|
}
|
|
if err := os.Chmod(path, restrictedSocketMode); err != nil {
|
|
return fmt.Errorf("set mode %#o: %w", restrictedSocketMode, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func gidPrincipal(gid string) (string, error) {
|
|
if _, err := parseGID(gid); err != nil {
|
|
return "", err
|
|
}
|
|
return allowGroupKindGID + ":" + gid, nil
|
|
}
|
|
|
|
func parseGID(value string) (int, error) {
|
|
gid, err := strconv.ParseUint(value, 10, 32)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("parse gid %q: %w", value, err)
|
|
}
|
|
return int(gid), nil
|
|
}
|