Library code should not call freeze(), this is something that should
only be done by "application code", so moving it into shared/ is appropriate.
The fallback to call _exit() is dropped: let's trust that the infinite loop
is infinite.
When mount points are stacked, bind_remount_recursive_with_mountinfo()
uses the existing mount options of the "lower" level mount (ie: the
first one that was mounted on a mount point). But the actual mount
point in use is the "top" one (ie: the last one that was mounted on a
mount point), so in practice if the mount options are different between
the layers, the bottom options are used by mistake on the top mount,
which is not what we want. This is because libmount returns the "bottom"
one first.
If the hashmap returns EEXIST, which means the same key (path) with different
value (options) is already present, update the hashmap instead of discarding
the result. This way, the last/top mount options are always used when
mounts are stacked on a mount point.
This was found to cause problems as LXC version 4.x stacks two /sys mounts,
the bottom one read-write and the top one read-only. systemd accidentally
remounts the top-one read-write, breaking various expectations since a
read-only /sys is the way we decide whether we are running in a container
or not (in this particular case, networkd tests are broken as networkd
expects to be able to modify network settings with a writable /sys).
Future versions of LXC will no longer do this double-stacking, but we
need to support running inside older versions too.
This was triggered by https://github.com/systemd/systemd/commit/6720e356c137
as that causes a recursive remount of '/', which processes '/sys' as one
of the submounts, from make_nosuid(). But it's likely that other combinations
of options could trigger this as well.
Before:
root@systemd-debug:/# systemd-run -t --wait --property ProtectSystem=yes findmnt
Running as unit: run-u9.service
Press ^] three times within 1s to disconnect TTY.
TARGET SOURCE FSTYPE OPTIONS
/ /dev/sda2[/var/lib/lxc/systemd-debug/rootfs]
│ ext4 ro,nosuid,relatime,errors=remount-ro,stripe=
├─/dev none tmpfs rw,nosuid,relatime,size=492k,mode=755
│ ├─/dev/.lxc/proc proc proc rw,nosuid,relatime
│ ├─/dev/.lxc/sys sys sysfs rw,nosuid,relatime
│ ├─/dev/console devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/ptmx devpts[/ptmx] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/tty1 devpts[/0] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/tty2 devpts[/1] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/tty3 devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/tty4 devpts[/3] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptm
│ ├─/dev/shm tmpfs tmpfs rw,nosuid,nodev
│ ├─/dev/hugepages hugetlbfs hugetlbfs rw,nosuid,relatime,pagesize=2M
│ └─/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
│ ├─/proc/sys proc[/sys] proc ro,nosuid,nodev,noexec,relatime
│ │ ├─/proc/sys/net proc[/sys/net] proc rw,nosuid,nodev,noexec,relatime
│ │ └─/proc/sys/kernel/random/boot_id
│ │ none[/.lxc-boot-id] tmpfs ro,nosuid,nodev,noexec,relatime,size=492k,mo
│ └─/proc/sysrq-trigger proc[/sysrq-trigger] proc ro,nosuid,nodev,noexec,relatime
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
│ └─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/devices/virtual/net sysfs sysfs rw,relatime
│ │ └─/sys/devices/virtual/net
│ │ sysfs[/devices/virtual/net] sysfs rw,nosuid,relatime
│ ├─/sys/fs/fuse/connections fusectl fusectl rw,nosuid,nodev,noexec,relatime
│ └─/sys/fs/cgroup cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,m
├─/run tmpfs tmpfs ro,nosuid,nodev,size=4912348k,nr_inodes=8192
│ ├─/run/credentials tmpfs[/systemd/inaccessible/dir] tmpfs ro,nosuid,nodev,noexec,size=4912348k,nr_inod
│ └─/run/systemd/incoming tmpfs[/systemd/propagate/run-u9.service]
│ tmpfs ro,nosuid,nodev,size=4912348k,nr_inodes=8192
├─/tmp tmpfs tmpfs rw,nosuid,nodev,size=12280872k,nr_inodes=409
│ └─/tmp tmpfs[/systemd-private-b730df90da424397a3f246cb15dcdbb1-run-u9.service-K6EUwf/tmp]
│ tmpfs rw,nosuid,nodev,size=12280872k,nr_inodes=409
└─/var/tmp /dev/sda2[/var/lib/lxc/systemd-debug/rootfs/var/tmp/systemd-private-b730df90da424397a3f246cb15dcdbb1-run-u9.service-vEHyRi/tmp]
ext4 rw,nosuid,relatime,errors=remount-ro,stripe=
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 14.249s
CPU time consumed: 37ms
After:
root@systemd-debug:/# systemd-run -t --wait --property ProtectSystem=yes findmnt
Running as unit: run-u3.service
Press ^] three times within 1s to disconnect TTY.
TARGET SOURCE FSTYPE OPTIONS
/ /dev/sda2[/var/lib/lxc/systemd-debug/rootfs]
│ ext4 rw,relatime,errors=remount-ro,stripe=32699
├─/dev none tmpfs rw,relatime,size=492k,mode=755
│ ├─/dev/.lxc/proc proc proc rw,relatime
│ ├─/dev/.lxc/sys sys sysfs rw,relatime
│ ├─/dev/console devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/ptmx devpts[/ptmx] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/tty1 devpts[/0] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/tty2 devpts[/1] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/tty3 devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/tty4 devpts[/3] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode
│ ├─/dev/shm tmpfs tmpfs rw,nosuid,nodev
│ ├─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,pagesize=2M
│ └─/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
│ ├─/proc/sys proc[/sys] proc ro,nosuid,nodev,noexec,relatime
│ │ ├─/proc/sys/net proc[/sys/net] proc rw,nosuid,nodev,noexec,relatime
│ │ └─/proc/sys/kernel/random/boot_id
│ │ none[/.lxc-boot-id] tmpfs ro,nosuid,nodev,noexec,relatime,size=492k,mode=75
│ └─/proc/sysrq-trigger proc[/sysrq-trigger] proc ro,nosuid,nodev,noexec,relatime
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
│ └─/sys sysfs sysfs ro,nosuid,nodev,noexec,relatime
│ ├─/sys/devices/virtual/net sysfs sysfs rw,relatime
│ │ └─/sys/devices/virtual/net
│ │ sysfs[/devices/virtual/net] sysfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/fuse/connections fusectl fusectl rw,nosuid,nodev,noexec,relatime
│ └─/sys/fs/cgroup cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory
├─/run tmpfs tmpfs rw,nosuid,nodev,size=4912348k,nr_inodes=819200,mo
│ ├─/run/credentials tmpfs[/systemd/inaccessible/dir]
│ │ tmpfs ro,nosuid,nodev,noexec,size=4912348k,nr_inodes=81
│ └─/run/systemd/incoming tmpfs[/systemd/propagate/run-u3.service]
│ tmpfs ro,nosuid,nodev,size=4912348k,nr_inodes=819200,mo
├─/tmp tmpfs tmpfs rw,nosuid,nodev,size=12280872k,nr_inodes=409600
├─/boot /dev/sda2[/var/lib/lxc/systemd-debug/rootfs/boot]
│ ext4 ro,relatime,errors=remount-ro,stripe=32699
└─/usr /dev/sda2[/var/lib/lxc/systemd-debug/rootfs/usr]
ext4 ro,relatime,errors=remount-ro,stripe=32699
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 14ms
CPU time consumed: 5ms
Host (LXC):
root@systemd-debug:/# findmnt
TARGET SOURCE FSTYPE OPTIONS
/ /dev/sda2[/var/lib/lxc/systemd-debug/rootfs]
│ ext4 rw,relatime,errors=remount-ro,stripe=32699
├─/run tmpfs tmpfs rw,nosuid,nodev,size=4912348k,nr_inodes=819200,mode=755
├─/tmp tmpfs tmpfs rw,nosuid,nodev,size=12280872k,nr_inodes=409600
├─/dev none tmpfs rw,relatime,size=492k,mode=755
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/ptmx devpts[/ptmx] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/tty1 devpts[/0] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/tty2 devpts[/1] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/tty3 devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/tty4 devpts[/3] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666,ma
│ ├─/dev/shm tmpfs tmpfs rw,nosuid,nodev
│ ├─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,pagesize=2M
│ ├─/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime
│ ├─/dev/console devpts[/2] devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000
│ ├─/dev/.lxc/proc proc proc rw,relatime
│ └─/dev/.lxc/sys sys sysfs rw,relatime
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
│ ├─/proc/sys proc[/sys] proc ro,nosuid,nodev,noexec,relatime
│ │ ├─/proc/sys/kernel/random/boot_id
│ │ │ none[/.lxc-boot-id] tmpfs ro,nosuid,nodev,noexec,relatime,size=492k,mode=755
│ │ └─/proc/sys/net proc[/sys/net] proc rw,nosuid,nodev,noexec,relatime
│ └─/proc/sysrq-trigger proc[/sysrq-trigger] proc ro,nosuid,nodev,noexec,relatime
└─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
└─/sys sysfs sysfs ro,nosuid,nodev,noexec,relatime
├─/sys/devices/virtual/net sysfs sysfs rw,relatime
│ └─/sys/devices/virtual/net
│ sysfs[/devices/virtual/net]
│ sysfs rw,nosuid,nodev,noexec,relatime
├─/sys/fs/fuse/connections fusectl fusectl rw,nosuid,nodev,noexec,relatime
└─/sys/fs/cgroup cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recurs
Fixes https://github.com/systemd/systemd/issues/20032
This makes use of the new kernel 5.12 APIs to add an idmap to a mount
point. It does so by cloning the mountpoint, changing it, and then
unmounting the old mountpoint, replacing it later with the new one.
In bind_remount_one_with_mountinfo() let's handle mount failures
gracefully if the flags already match anyway. This isn't perfect, since
it mixes up superblock and mount point flags, but it's close enough.
And get rid of get_mount_flags() altogether.
(This drops the statvfs() fallback that get_mount_flags() did. That
fallback was incomplete however, and mostly hid errors. Our primary
avenue to get mount flags is /proc/self/mountinfo and we should trust
it, and fix bugs we might encounter with it, but not tape over it.
Dropping the fallback is relevant in particular as it actually returned
mount flags for any path, not just mount points, which was very icky.)
This replaces the "todo" set with a "todo" hash map that stores the
mount flags we found. This makes an explicit call to get_mount_flags()
unncessary, since we have the flags handy right-away, and lowers our
work from O(n^2) to O(n). Nice!
The "done" set is also improved slightly: we'll use more modern ways to
allocate it, via set_ensure_consume(), and freeing-via-hash_ops.
Finally, failures on submount remounts are now handled gracefully,
there are just too many reasons why they might fail, given NFS, autofs,
FUSE which weird access controls, where even root might lack the privs
to do something.
Fixes: #16156
Instead of marking the bind mount read-only right-away, let's just
restart the loop, so that we'll pick it up like any other mount and then
remount like that.
Let's always query one property, check it, and then query the next,
preferring "cheap" ones over "slow" ones (i.e. cheap are the ones we can
check directly, and slow are the ones we need to check with some loop of
some kind).
Implement directives `NoExecPaths=` and `ExecPaths=` to control `MS_NOEXEC`
mount flag for the file system tree. This can be used to implement file system
W^X policies, and for example with allow-listing mode (NoExecPaths=/) a
compromised service would not be able to execute a shell, if that was not
explicitly allowed.
Example:
[Service]
NoExecPaths=/
ExecPaths=/usr/bin/daemon /usr/lib64 /usr/lib
Closes: #17942.
machinectl fails since 21935150a0 as it's now
mounting onto a file descriptor in a target namespace, without joining the
target's PID namespace.
Note that it's not enough to setns CLONE_NEWPID, but a double-fork is required
as well, as implemented by namespace_fork().
Add a test case to TEST-13-NSPAWN to cover this use case.
Let's make umount_verbose() more like mount_verbose_xyz(), i.e. take log
level and flags param. In particular the latter matters, since we
typically don't actually want to follow symlinks when unmounting.
On new kernels (>= 5.8) unprivileged users may create the 0:0 character
device node. Which is great, as we can use that as inaccessible device
nodes if we run unprivileged. Hence, change how we find the right
inaccessible device inodes: when the user asks for a block device node,
but we have none, try the char device node first. If that doesn't exist,
fall back to the socket node as before.
This means that:
1. in the best case we'll return a node if the right device node type
2. otherwise we hopefully at least can return a device node if one asked
for even if the type doesn't match (i.e. we return char instead of
the requested block device node)
3. in the worst case (old kernels…) we'll return a socket node
https://tools.ietf.org/html/draft-knodel-terminology-02https://lwn.net/Articles/823224/
This gets rid of most but not occasions of these loaded terms:
1. scsi_id and friends are something that is supposed to be removed from
our tree (see #7594)
2. The test suite defines an API used by the ubuntu CI. We can remove
this too later, but this needs to be done in sync with the ubuntu CI.
3. In some cases the terms are part of APIs we call or where we expose
concepts the kernel names the way it names them. (In particular all
remaining uses of the word "slave" in our codebase are like this,
it's used by the POSIX PTY layer, by the network subsystem, the mount
API and the block device subsystem). Getting rid of the term in these
contexts would mean doing some major fixes of the kernel ABI first.
Regarding the replacements: when whitelist/blacklist is used as noun we
replace with with allow list/deny list, and when used as verb with
allow-list/deny-list.
Let's make sure $XDG_RUNTIME_DIR for the user instance and /run for the
system instance is always organized the same way: the "inaccessible"
device nodes should be placed in a subdir of either called "systemd" and
a subdir of that called "inaccessible".
This way we can emphasize the common behaviour, and only differ where
really necessary.
Follow-up for #13823
If we're using a set with _put_strdup(), most of the time we want to use
string hash ops on the set, and free the strings when done. This defines
the appropriate a new string_hash_ops_free structure to automatically free
the keys when removing the set, and makes set_put_strdup() and set_put_strdupv()
instantiate the set with those hash ops.
hashmap_put_strdup() was already doing something similar.
(It is OK to instantiate the set earlier, possibly with a different hash ops
structure. set_put_strdup() will then use the existing set. It is also OK
to call set_free_free() instead of set_free() on a set with
string_hash_ops_free, the effect is the same, we're just overriding the
override of the cleanup function.)
No functional change intended.
When in a userns environment we cannot take away per-mount point flags
set on a mount point that was passed to us. Hence we need to be careful
to always check the actual mount flags in place and manipulate only
those flags of them that we actually want to change and not reset more
as side-effect.
We mostly got this right already in
bind_remount_recursive_with_mountinfo(), but didn't in the simpler
bind_remount_one_with_mountinfo(). Catch up.
(The old code assumed that the MountEntry.flags field contained the
right flag settings, but it actually doesn't for new mounts we just
established as for those mount() establishes the initial flags for us,
and we have to read them back to figure out which ones the kernel
picked.)
Fixes: #13622
This cleans up the function in multiple ways:
- change order of parameters to follow our usualy system of putting
return parameters last
- rename return parameter "ret" as we usually do
- don't initialize local variables we override immediately anyway
- downgrade log messages to LOG_DEBUG (since we don't log about any
other errors here above LOG_DEBUG, as this is mostly an "API"-style
function)
- handle that mnt_fs_get_vfs_options() may return NULL (according to
docs)
- manually map the ST_xyz to MS_xyz flags on statvfs(), because while
they are mostly the same, they aren't entirely the same, MS_RELATIME and
ST_RELATIME are defined differently (sad!)
To support ProtectHome=y in a user namespace (which mounts the inaccessible
nodes), the nodes need to be accessible by the user. Create these paths and
devices in the user runtime directory so they can be used later if needed.
The commit
"util: Do not clear parent mount flags when setting up namespaces"
introduced a statvfs call read the flags of the original mount
and have them applied to the bind mount.
This has two problems:
(1) The mount flags returned by statvfs(2) do not match the flags
accepted by mount(2). For example, the value 4096 means ST_RELATIME
when returned by statvfs(2), but means MS_BIND when passed to mount(2).
(2) A call to statvfs blocks indefinitely when ran against a disconnected
network drive ( https://github.com/systemd/systemd/issues/12667 ).
We already use libmount to parse `/proc/self/mountinfo` but did not use the
mount flag information from there. This patch changes that to use the mount
flags parsed by libmount instead of calling statvfs. Only if getting the
flags through libmount fails we call statvfs.
Fixes https://github.com/systemd/systemd/issues/12667