What is the issue?
On FreeBSD, we don't use the login command when running a command without a shell/TTY:
|
if !ia.isShell || !ia.hasTTY { |
|
// We can only use login command if a shell was requested with a TTY. If |
|
// there is no TTY, login exits immediately, which breaks things likes |
|
// mosh and VSCode. |
|
return nil |
|
} |
Additionally, FreeBSD (and Darwin) exhibit non-POSIX-compliant behaviour in their getgroups() and setgroups() system calls: the first entry in the list is returned and used to set the process's egid, as if it were returned by getegid and changed by setegid.
As a result, our existing behaviour for dropping privileges has a bug:
|
if err := setGroups(groupIDs); err != nil { |
|
return err |
|
} |
|
if egid := os.Getegid(); egid != ia.gid { |
|
if err := syscall.Setgid(int(ia.gid)); err != nil { |
|
logf(err.Error()) |
|
os.Exit(1) |
|
} |
|
} |
|
if euid != ia.uid { |
|
// Switch users if required before starting the desired process. |
|
if err := syscall.Setuid(int(ia.uid)); err != nil { |
|
logf(err.Error()) |
|
os.Exit(1) |
|
} |
|
} |
For example, if we call our incubator with --uid=1002 --gid=1002 --groups=1002, it will:
- Set the new supplemental groups to
[]int{1002} with setGroups, which changes the egid to 1002
- Read the current now-changed egid, which is 1002, and compare against the expected egid of 1002; nothing changes, so it does not call
setgid.
- Check the current UID and call
setuid(1002).
As a result, the egid has changed to 1002, but the gid remains 0 (assuming Tailscale is running as root).
This doesn't affect other OSes because:
- On Darwin, we always use the
login command, which resets groups for us
- On non-FreeBSD platforms, the
setgroups command doesn't change the egid, which means that the comparison against egid succeeds and we call setgid to reset the GID.
See also:
Steps to reproduce
This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a Tailscale SSH shell, is:
andrew@freebsd:~ $ id -p
uid andrew
groups andrew
However, when run via "ssh andrew@freebsd id -p", the output would be:
$ ssh andrew@freebsd id -p
login root
uid andrew
rgid wheel
groups andrew
Are there any recent changes that introduced the issue?
n/a
OS
Other
OS version
FreeBSD
Tailscale version
1.38.1
Other software
No response
Bug report
No response
What is the issue?
On FreeBSD, we don't use the
logincommand when running a command without a shell/TTY:tailscale/ssh/tailssh/incubator.go
Lines 762 to 767 in 654b5a0
Additionally, FreeBSD (and Darwin) exhibit non-POSIX-compliant behaviour in their
getgroups()andsetgroups()system calls: the first entry in the list is returned and used to set the process's egid, as if it were returned bygetegidand changed bysetegid.As a result, our existing behaviour for dropping privileges has a bug:
tailscale/ssh/tailssh/incubator.go
Lines 247 to 262 in 654b5a0
For example, if we call our incubator with
--uid=1002 --gid=1002 --groups=1002, it will:[]int{1002}withsetGroups, which changes the egid to 1002setgid.setuid(1002).As a result, the egid has changed to 1002, but the gid remains 0 (assuming Tailscale is running as root).
This doesn't affect other OSes because:
logincommand, which resets groups for ussetgroupscommand doesn't change the egid, which means that the comparison against egid succeeds and we callsetgidto reset the GID.See also:
Steps to reproduce
This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a Tailscale SSH shell, is:
However, when run via "ssh andrew@freebsd id -p", the output would be:
Are there any recent changes that introduced the issue?
n/a
OS
Other
OS version
FreeBSD
Tailscale version
1.38.1
Other software
No response
Bug report
No response