From ac74dfa5cd8ba3153a90a84381dad9500162c746 Mon Sep 17 00:00:00 2001 From: Gesa Stupperich Date: Thu, 5 Mar 2026 15:58:18 +0000 Subject: [PATCH] util/osuser: extend id command fallback for group IDs to freebsd Users on FreeBSD run into a similar problem as has been reported for Linux #11682 and fixed in #11682: because the tailscaled binaries that we distribute are static and don't link cgo tailscaled fails to fetch group IDs that are returned via NSS when spawning an ssh child process. This change extends the fallback on the 'id' command that was put in place as part of #11682 to FreeBSD. More precisely, we try to fetch the group IDs with the 'id' command first, and only if that fails do we fall back on the logic in the os/user package. Updates #14025 Signed-off-by: Gesa Stupperich --- util/osuser/group_ids.go | 17 ++++++++++++++--- util/osuser/group_ids_test.go | 4 +++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/util/osuser/group_ids.go b/util/osuser/group_ids.go index 2a1f147d8..34d15c926 100644 --- a/util/osuser/group_ids.go +++ b/util/osuser/group_ids.go @@ -23,7 +23,7 @@ func GetGroupIds(user *user.User) ([]string, error) { return nil, nil } - if runtime.GOOS != "linux" { + if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" { return user.GroupIds() } @@ -46,13 +46,24 @@ func getGroupIdsWithId(usernameOrUID string) ([]string, error) { defer cancel() cmd := exec.CommandContext(ctx, "id", "-Gz", usernameOrUID) - out, err := cmd.Output() + if runtime.GOOS == "freebsd" { + cmd = exec.CommandContext(ctx, "id", "-G", usernameOrUID) + } + + out, err := cmd.CombinedOutput() if err != nil { return nil, fmt.Errorf("running 'id' command: %w", err) } + return parseGroupIds(out), nil } func parseGroupIds(cmdOutput []byte) []string { - return strings.Split(strings.Trim(string(cmdOutput), "\n\x00"), "\x00") + s := strings.TrimSpace(string(cmdOutput)) + // Parse NUL-delimited output. + if strings.ContainsRune(s, '\x00') { + return strings.Split(strings.Trim(s, "\x00"), "\x00") + } + // Parse whitespace-delimited output. + return strings.Fields(s) } diff --git a/util/osuser/group_ids_test.go b/util/osuser/group_ids_test.go index 79e189ed8..fee86029b 100644 --- a/util/osuser/group_ids_test.go +++ b/util/osuser/group_ids_test.go @@ -15,7 +15,9 @@ func TestParseGroupIds(t *testing.T) { }{ {"5000\x005001\n", []string{"5000", "5001"}}, {"5000\n", []string{"5000"}}, - {"\n", []string{""}}, + {"\n", []string{}}, + {"5000 5001 5002\n", []string{"5000", "5001", "5002"}}, + {"5000\t5001\n", []string{"5000", "5001"}}, } for _, test := range tests { actual := parseGroupIds([]byte(test.in))