Why is a new group not active after SSH login via tmux?
Solution 1:
as the
ssh
connection was reusing some process with the old group settings
After you mentioned SSH connection sharing, it's quite obvious this is the culprit. To understand the problem let's see how a process in Linux carries information about its owner.
Spawning new processes
All processes stem from the one with PID 1 (init
or upstart
or systemd
, whatever) that belongs to the user root
. A process can duplicate itself, see man 2 fork
; or it can replace its own image with another one, see man 3 exec
. A common way for program A to spawn program B is to fork, so there are temporarily two instances of A, and then exec to B (the new A becomes B). Now there is A (the parent process) and B (the child process).
Each process carries information about its owner and group(s), see man 2 getuid
, man 2 getgid
, man 2 getgroups
. When a child process is spawned, it usually inherits this information.
In order to create a process that belongs to a user other than root
, at some point a privileged process must change its owner. It uses setuid(2)
, setgid(2)
, setgroups(2)
and similar to achieve this. Usually the desired UID is given and groups (including supplementary groups) are derived from it. This is the moment a new group gets active.
After a non-privileged process gets spawned, its children (if any) just inherit the information without querying the OS for the current set of groups the user belongs to. At this moment the new group cannot get active. There are exceptions: programs like su
, sg
or sudo
with setuid flag and owned by root
start as privileged. After they make sure the user is allowed to perform the action, the situation is identical to the already discussed one, where the new group gets active.
So su - myuser
notices the new group because it changes users behind the scenes. Retrieving the current set of groups the user belongs to is a part of this process.
On the other hand id
shows what it inherited from the shell. This information is old; it's from the time its privileged ancestor manipulated its user ID.
Note id myuser
would notice the new group. Sole id
retrieves information associated to itself (with getgroups(2)
and so) while id myuser
queries the OS for the current information regarding the chosen user.
The conclusion is: your shell (and its children) will notice the new group if it's a descendant of some privileged process, so the transition between root
and your user occurred after the group was added. (Formal note: transition from root
to root
is no different; what matters is the act of setting the ownership anew).
In practice
/dev/tty2
or so (without SSH)
I can log in via a standard text console (/dev/tty2
or so). The relevant part of the process tree is:
systemd───login───bash───pstree
^^^^^^^^^^^^^^^ these are owned by root
^^^^^^^^^^^^^ these are owned by my user
After I add my user to a new group and log in in another tty:
systemd─┬─login───bash
└─login───bash───pstree
^^^^^^^^^^^^^^^ these are owned by root
^^^^^^^^^^^^^ these are owned by my user
Because the second bash
was spawned from a privileged login
after I made the change, it will be aware of the new group. In this basic case the "log out and log in again" approach works.
GUI (still without SSH)
Consider an example part of the process tree:
systemd───plasmashell───konsole───bash
^^^^^^^ this is owned by root
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ these are owned by my user
After adding my user to a new group, a new shell (new tab) in konsole
won't see the change. Nor will xterm
, if I spawn it from my KDE Plasma (plasmashell
). But if I log in via /dev/tty2
, properly set and export the DISPLAY
variable, then spawn xterm
from there, the shell within xterm
will see the change. In both cases xterm
is displayed on the same KDE desktop, but the first one originates (indirectly) from a privileged process that switched users long ago, before the change; the latter originates (indirectly) from a privileged process that switched users a minute ago, after the change.
SSH
In my Debian sshd
(SSH daemon) forks and forks again, the relevant part of the process tree is like this:
systemd───sshd───sshd───sshd───bash
^^^^^^^^^^^^^^^^^^^^^ these are owned by root
^^^^^^^^^^^ these are owned by my user
After I connect for the second time, without connection sharing:
systemd───sshd─┬─sshd───sshd───bash
└─sshd===sshd───bash───pstree
^^^^^^^^^^^^^^^^^^^^^ these are owned by root
^^^^^^^^^^^^^^^^^^^^ these are owned by my user
Suppose I added my user to a new group in the meantime. The "link" I denoted ===
means some privileged sshd
spawned a non-privileged one after the change. The latter and its descendants are aware of the new group.
With connection sharing this is different:
systemd───sshd───sshd───sshd─┬─bash
└─bash───pstree
^^^^^^^^^^^^^^^^^^^^^ these are owned by root
^^^^^^^^^^^^^^^^^^^^ these are owned by my user
In this case the new bash
got spawned from the old sshd
owned by my user. This sshd
carries old information about my groups, the new bash
inherited it.
I believe the same happens in your case.
tmux
The problem is not limited to sshd
. Take tmux
. In general case of using tmux
, after a user logs in, tmux
can be started directly as a login shell or indirectly from a (non-tmux
) login shell (manually or like from .bashrc
; with or without exec
). The tool connects as a client to tmux
server owned by the user. If there's no server for this particular user yet, it will be started. It's the server who starts a "final" shell. A process tree may look like this:
systemd─┬─login───bash───tmux: client
└─tmux: server─┬─3*[bash]
└─bash───pstree
After I add my user to a new group, a new bash
spawned from login
does see the group. A new tmux
server and its children would see it. But the already running tmux
server and its children don't; this is true even for a child (shell) spawned after I add my user to the new group.
To be clear: tmux
client and server have nothing to do with SSH client and server; they both run on the same machine. tmux
will only matter if it runs where the change was made. You mentioned tmux
on the SSH's client side only, and the change was made on the SSH's server side; so in your case tmux
is irrelevant.
Suppose I have tmux
where the change was made. To make new shells inside tmux
notice the new group, I need to exit (not detach, really exit) everything already running inside tmux
, so my tmux
server exits as well, so it can be spawned anew. Killing the server should work, but this has obvious disadvantages.
Starting a separate tmux
server is technically possible. If I don't need to attach to already running session, this is the way to go. See -L
and -S
in man 1 tmux
. If tmux
runs automatically after I log in, I may need to circumvent this in order to pass -L
, e.g.:
ssh -t user@host tmux -L foo