Commit 2bb7dc04 authored by Kirill Smelkov's avatar Kirill Smelkov

trun: Spawn user test with sole regular uid/gid in /etc/{passwd,group} database

Even though libc.opentty stopped to insist on chown(group=tty) for
/dev/pts/*, openssh still wants to do it and fails, preventing sshd from
working. Fix it by spawning test workload with sole current user and
group being present in the password database.

We anyway don't have IDs for users/groups different from current uid/gid
mapped from current namespace, and the kernel, when seeing a file owned
by those, maps them to "nobody/nogroup" for existing files, and rejects
chown to those original IDs obtained from parent's namespace
/etc/{passwd,group} as EINVAL. For the same reason we don't try to mount
our own /dev/pts instance, because we have only current uid/gid mapped
to parent namespace and gid=5 maps to nogroup in parent. With existing
/dev/pts mount entries are only listed as having nogroup, and from
outside they _are_ owned by parent's tty group. If we would mount
/dev/pts anew, parent won't see our /dev/pts/* at all which moves us
a bit more far from desired behaviour.

Still keep root and nobody/nogroup as @jerome reports that without those
users Go tests fail on Debian9:

    nexedi/slapos!1095 (comment 147177)
    nexedi/slapos!1095 (comment 147201)

See added comment about all this for more details.

This patch fixes sshd to work under tryn.py(*)

No test because libc.openpty works both with and without this patch, and
it would need too spawn whole sshd under test to verify this.

(*) the diff for sshd 1) running successfully under regular user, and 2)
    previously failing under trun.py without this patch is below:

    diff --git a/regular-nopam.txt b/trun-nopam.txt
    index 378ccb6..5b96c08 100644
    --- a/regular-nopam.txt
    +++ b/trun-nopam.txt
    @@ -1,4 +1,4 @@
    -(neo) (z-dev) (g.env) kirr@deca:~/tmp/trashme/sshd$ /sbin/sshd -d -p 2222 -h `pwd`/ssh_host_rsa_key -o UsePAM=no
    +kirr@deca:~/tmp/trashme/sshd$ /sbin/sshd -d -p 2222 -h `pwd`/ssh_host_rsa_key -o UsePAM=no
     debug1: sshd version OpenSSH_8.4, OpenSSL 1.1.1k  25 Mar 2021
     debug1: private host key #0: ssh-rsa SHA256:y+ujVDqqFBXTclDM2NLy4GME7wReutLcUYOWAeriXdc
     debug1: setgroups() failed: Operation not permitted
    @@ -91,35 +91,13 @@ debug1: session_input_channel_req: session 0 req pty-req
     debug1: Allocating pty.
     debug1: session_new: session 0
     debug1: SELinux support disabled
    -Attempt to write login records by non-root user (aborting)
    -debug1: session_pty_req: session 0 alloc /dev/pts/2
    -debug1: server_input_channel_req: channel 0 request env reply 0
    -debug1: session_by_channel: session 0 channel 0
    -debug1: session_input_channel_req: session 0 req env
    -debug1: server_input_channel_req: channel 0 request shell reply 1
    -debug1: session_by_channel: session 0 channel 0
    -debug1: session_input_channel_req: session 0 req shell
    -Starting session: shell on pts/2 for kirr from 127.0.0.1 port 44106 id 0
    -debug1: Setting controlling tty using TIOCSCTTY.
    -
    -debug1: Received SIGCHLD.
    -debug1: session_by_pid: pid 693948
    -debug1: session_exit_message: session 0 channel 0 pid 693948
    -debug1: session_exit_message: release channel 0
    -debug1: session_by_tty: session 0 tty /dev/pts/2
    -debug1: session_pty_cleanup2: session 0 release /dev/pts/2
    -Attempt to write login records by non-root user (aborting)
    -debug1: session_by_channel: session 0 channel 0
    -debug1: session_close_by_channel: channel 0 child 0
    -Close session: user kirr from 127.0.0.1 port 44106 id 0
    -debug1: channel 0: free: server-session, nchannels 1
    -Received disconnect from 127.0.0.1 port 44106:11: disconnected by user
    -Disconnected from user kirr 127.0.0.1 port 44106
    +chown(/dev/pts/2, 1000, 5) failed: Invalid argument
     debug1: do_cleanup
     debug1: temporarily_use_uid: 1000/1000 (e=1000/1000)
     debug1: restore_uid: (unprivileged)
    +debug1: session_pty_cleanup2: session 0 release /dev/pts/2
    +Attempt to write login records by non-root user (aborting)
    +debug1: audit_event: unhandled event 12
     debug1: do_cleanup
     debug1: temporarily_use_uid: 1000/1000 (e=1000/1000)
     debug1: restore_uid: (unprivileged)
    -debug1: audit_event: unhandled event 12

    (see nexedi/slapos!1095 (comment 147018))

/helped-by @jerome
parent 4e686c9a
Pipeline #18715 running with stage
in 0 seconds
......@@ -30,7 +30,7 @@ If user namespaces are not available, the test is still run but without the chec
from __future__ import print_function, absolute_import
import errno, os, sys, stat, difflib
import errno, os, sys, stat, difflib, pwd, grp, shutil
from subprocess import check_call as xrun, CalledProcessError
from os.path import join, devnull
from golang import func, defer
......@@ -124,6 +124,45 @@ def run_in_userns(f):
print("# leaked mount: %s" % d[2:])
defer(_)
# pretend we don't have tty or any other special group to avoid issues with e.g. pseudo-terminals.
#
# POSIX requires /dev/pts/* slaves to be chown'ed to tty group (gid=5) on
# grantpt. However we do not have that gid mapped and chown fails. Glibc
# stopped insisting on such chown and delegates proper setup to the kernel
# expecting /dev/pts to be mounted with gid=5,mode=0620 options:
# https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=77356912e836
#
# However e.g. openssh still wants to do the chown(group=tty):
# https://github.com/openssh/openssh-portable/blob/V_8_8_P1-120-ga2188579/sshpty.c#L165-L205
#
# Avoid that by adjusting the system view so that there is only one sole
# single regular user and group with uid/gid of current user. We anyway
# mapped only single uid/gid to parent namespace.
#
# Still include root and nobody/nogroup as an exception since several software
# expect to have zero ID and those names in the user/group database - see e.g.
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1095#note_147177
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1095#note_147201
xetc = "/tmp/xetc"
os.mkdir(xetc)
ustr = lambda u: "%s:%s:%d:%d:%s:%s:%s\n" % (u.pw_name, u.pw_passwd, u.pw_uid, u.pw_gid, u.pw_gecos, u.pw_dir, u.pw_shell)
gstr = lambda g: "%s:%s:%d:%s\n" % (g.gr_name, g.gr_passwd, g.gr_gid, ','.join(g.gr_mem))
writefile("%s/passwd" % xetc,
ustr(pwd.getpwuid(os.getuid())) +
ustr(pwd.getpwnam("root")) +
ustr(pwd.getpwnam("nobody")))
writefile("%s/group" % xetc,
gstr(grp.getgrgid(os.getgid())) +
gstr(grp.getgrnam("root")) +
gstr(grp.getgrnam("nogroup")))
xrun(["mount", "--bind", xetc+"/passwd", "/etc/passwd"])
xrun(["mount", "--bind", xetc+"/group", "/etc/group"])
def _():
xrun(["umount", "-n", "/etc/passwd"])
xrun(["umount", "-n", "/etc/group"])
shutil.rmtree(xetc)
defer(_)
# run the test
f()
......@@ -143,6 +182,11 @@ def readfile(path): # -> str
with open(path, "r") as f:
return f.read()
# writefile creates file @path and fills it with data.
def writefile(path, data):
with open(path, "w") as f:
f.write(data)
if __name__ == '__main__':
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment