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