When trying to enter a user namespace and still communicating with the rest of the Linux system via D-Bus then this won't work:
$ unshare -U -r
$ root # dbus-monitor --system
Failed to open connection to system bus: Did not receive a reply. Possible causes include: \
the remote application did not send a reply, the message bus security policy blocked the reply, \
the reply timeout expired, or the network connection was broken.
The reason for this is a bit intricate. It is not found on kernel permission level but in the D-Bus userspace logic. It is explained in this mailing list post:
https://lists.linuxcontainers.org/pipermail/lxc-users/2017-March/012934.html
Basically the D-Bus client from within the user namespace explicitly sends its preceived uid (i.e. 0 in this case) to the D-Bus daemon. This is then rejected by the D-Bus daemon, probably because it doesn't match what the kernel reports via the SO_PEERCREDS
socket option.
There is no easy workaround but an LD_PRELOAD library seems to help:
$ cat <<EOF >geteuid_preload.c
#include <unistd.h>
// simply report the real host UID unconditionally
// you need to replace UID 1000 by whatever UID you user has
// in the host user namespace
uid_t geteuid() {
return 1000;
}
EOF
$ gcc geteuid_preload.c -shared -o geteuid_preload.so
By using this library we can make the D-Bus protocol authentication work again:
$ unshare -U -r
# in the user namespace we appear to have uid 0
$ root # id -u
0
$ root # export LD_PRELOAD=$PWD/geteuid_preload.so
# now it seems we have uid 1000 as outside the user namespace
$ root # id -u
1000
$ root # dbus-monitor --system
signal time=1637663883.226976 sender=org.freedesktop.DBus -> destination=:1.27 serial=2 \
path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
Note that this means that programs that should think they're actually root
in the user namespace could get confused again, because geteuid()
not longer returns zero. For some use cases it works out, though.