The most important fix here is actually the one that allows pasta and passt to create user namespaces (and hence, to start -- we need that for sandboxing) with recent kernels on e.g. Fedora Rawhide -- that's patch 3/7. But there are a number of other issues, some old, some new, that would currently prevent pasta from e.g. starting a shell, or simply run 'ip address show', again at least on Fedora Rawhide. Fix them. v2: Actually override pasta symlinks with hard links in 1/7 Stefano Brivio (7): fedora: Install pasta as hard link to ensure SELinux file context match selinux: Use explicit paths for binaries in file context selinux: Fix user namespace creation after breaking kernel change selinux: Update policy to fix user/group settings selinux: Add rules for sysctl and /proc/net accesses selinux: Allow pasta_t to read nsfs entries selinux: Fix domain transitions for typical commands pasta might run contrib/fedora/passt.spec | 7 +++++++ contrib/selinux/passt.fc | 3 ++- contrib/selinux/passt.te | 10 ++++++++-- contrib/selinux/pasta.fc | 3 ++- contrib/selinux/pasta.te | 33 ++++++++++++++++++++++++++++++--- 5 files changed, 49 insertions(+), 7 deletions(-) -- 2.39.2
The Makefile installs symbolic links by default, which actually worked at some point (not by design) with SELinux, but at least on recent kernel versions it doesn't anymore: override pasta (and pasta.avx2) with hard links. Otherwise, even if the links are labeled as pasta_exec_t, SELinux will "resolve" them to passt_exec_t, and we'll have pasta running as passt_t instead of pasta_t. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> --- contrib/fedora/passt.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/fedora/passt.spec b/contrib/fedora/passt.spec index 8d28ef6..d0c6895 100644 --- a/contrib/fedora/passt.spec +++ b/contrib/fedora/passt.spec @@ -54,10 +54,17 @@ This package adds SELinux enforcement to passt(1) and pasta(1). %make_build VERSION="%{version}-%{release}.%{_arch}" %install + %make_install DESTDIR=%{buildroot} prefix=%{_prefix} bindir=%{_bindir} mandir=%{_mandir} docdir=%{_docdir}/%{name} +# The Makefile creates symbolic links for pasta, but we need hard links for +# SELinux file contexts to work as intended. Same with pasta.avx2 if present. +ln -f %{buildroot}%{_bindir}/passt %{buildroot}%{_bindir}/pasta %ifarch x86_64 +ln -f %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 + ln -sr %{buildroot}%{_mandir}/man1/passt.1 %{buildroot}%{_mandir}/man1/passt.avx2.1 ln -sr %{buildroot}%{_mandir}/man1/pasta.1 %{buildroot}%{_mandir}/man1/pasta.avx2.1 +install -p -m 755 %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 %endif pushd contrib/selinux -- 2.39.2
On Wed, Aug 16, 2023 at 08:17:24PM +0200, Stefano Brivio wrote:The Makefile installs symbolic links by default, which actually worked at some point (not by design) with SELinux, but at least on recent kernel versions it doesn't anymore: override pasta (and pasta.avx2) with hard links. Otherwise, even if the links are labeled as pasta_exec_t, SELinux will "resolve" them to passt_exec_t, and we'll have pasta running as passt_t instead of pasta_t. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> --- contrib/fedora/passt.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/fedora/passt.spec b/contrib/fedora/passt.spec index 8d28ef6..d0c6895 100644 --- a/contrib/fedora/passt.spec +++ b/contrib/fedora/passt.spec @@ -54,10 +54,17 @@ This package adds SELinux enforcement to passt(1) and pasta(1). %make_build VERSION="%{version}-%{release}.%{_arch}" %install + %make_install DESTDIR=%{buildroot} prefix=%{_prefix} bindir=%{_bindir} mandir=%{_mandir} docdir=%{_docdir}/%{name} +# The Makefile creates symbolic links for pasta, but we need hard links for +# SELinux file contexts to work as intended. Same with pasta.avx2 if present. +ln -f %{buildroot}%{_bindir}/passt %{buildroot}%{_bindir}/pasta %ifarch x86_64 +ln -f %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 + ln -sr %{buildroot}%{_mandir}/man1/passt.1 %{buildroot}%{_mandir}/man1/passt.avx2.1 ln -sr %{buildroot}%{_mandir}/man1/pasta.1 %{buildroot}%{_mandir}/man1/pasta.avx2.1 +install -p -m 755 %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 %endifAcked-by: Richard W.M. Jones <rjones(a)redhat.com> ... although why not change the Makefile install rule instead so everyone gets this change? Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
On Thu, 17 Aug 2023 08:53:55 +0100 "Richard W.M. Jones" <rjones(a)redhat.com> wrote:On Wed, Aug 16, 2023 at 08:17:24PM +0200, Stefano Brivio wrote:Because that's only needed for SELinux "based" distributions. I have the feeling that symlinks are in general more desirable -- at least personally I find them less confusing. Also, David pointed out that hard links are not supported by a number of filesystems, and we probably don't want to mess this up for embedded environments. On the other hand, I didn't check yet if AppArmor would also benefit from this -- there we have at the moment a single profile for passt and pasta (the symlink behaviour is documented)... if it does, I guess it might make sense to switch to hard links in the Makefile (assuming there are no issues with other distributions), and perhaps export a Makefile variable to have symlinks instead. -- StefanoThe Makefile installs symbolic links by default, which actually worked at some point (not by design) with SELinux, but at least on recent kernel versions it doesn't anymore: override pasta (and pasta.avx2) with hard links. Otherwise, even if the links are labeled as pasta_exec_t, SELinux will "resolve" them to passt_exec_t, and we'll have pasta running as passt_t instead of pasta_t. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> --- contrib/fedora/passt.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/fedora/passt.spec b/contrib/fedora/passt.spec index 8d28ef6..d0c6895 100644 --- a/contrib/fedora/passt.spec +++ b/contrib/fedora/passt.spec @@ -54,10 +54,17 @@ This package adds SELinux enforcement to passt(1) and pasta(1). %make_build VERSION="%{version}-%{release}.%{_arch}" %install + %make_install DESTDIR=%{buildroot} prefix=%{_prefix} bindir=%{_bindir} mandir=%{_mandir} docdir=%{_docdir}/%{name} +# The Makefile creates symbolic links for pasta, but we need hard links for +# SELinux file contexts to work as intended. Same with pasta.avx2 if present. +ln -f %{buildroot}%{_bindir}/passt %{buildroot}%{_bindir}/pasta %ifarch x86_64 +ln -f %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 + ln -sr %{buildroot}%{_mandir}/man1/passt.1 %{buildroot}%{_mandir}/man1/passt.avx2.1 ln -sr %{buildroot}%{_mandir}/man1/pasta.1 %{buildroot}%{_mandir}/man1/pasta.avx2.1 +install -p -m 755 %{buildroot}%{_bindir}/passt.avx2 %{buildroot}%{_bindir}/pasta.avx2 %endifAcked-by: Richard W.M. Jones <rjones(a)redhat.com> ... although why not change the Makefile install rule instead so everyone gets this change?
There's no reason to use wildcards, and we don't want any similarly-named binary (not that I'm aware of any) to risk being associated to passt_exec_t and pasta_exec_t by accident. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> Reviewed-by: Richard W.M. Jones <rjones(a)redhat.com> --- contrib/selinux/passt.fc | 3 ++- contrib/selinux/pasta.fc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/selinux/passt.fc b/contrib/selinux/passt.fc index fb5b5d4..09bcaab 100644 --- a/contrib/selinux/passt.fc +++ b/contrib/selinux/passt.fc @@ -8,5 +8,6 @@ # Copyright (c) 2022 Red Hat GmbH # Author: Stefano Brivio <sbrivio(a)redhat.com> -/usr/bin/passt(\.*)? system_u:object_r:passt_exec_t:s0 +/usr/bin/passt system_u:object_r:passt_exec_t:s0 +/usr/bin/passt.avx2 system_u:object_r:passt_exec_t:s0 /tmp/passt\.pcap system_u:object_r:passt_log_t:s0 diff --git a/contrib/selinux/pasta.fc b/contrib/selinux/pasta.fc index 2ffb41a..41ee46d 100644 --- a/contrib/selinux/pasta.fc +++ b/contrib/selinux/pasta.fc @@ -8,6 +8,7 @@ # Copyright (c) 2022 Red Hat GmbH # Author: Stefano Brivio <sbrivio(a)redhat.com> -/usr/bin/pasta(\.*)? system_u:object_r:pasta_exec_t:s0 +/usr/bin/pasta system_u:object_r:pasta_exec_t:s0 +/usr/bin/pasta.avx2 system_u:object_r:pasta_exec_t:s0 /tmp/pasta\.pcap system_u:object_r:pasta_log_t:s0 /var/run/pasta\.pid system_u:object_r:pasta_pid_t:s0 -- 2.39.2
Kernel commit ed5d44d42c95 ("selinux: Implement userns_create hook") seems to just introduce a new functionality, but given that SELinux implements a form of mandatory access control, introducing the new permission breaks any application (shipping with SELinux policies) that needs to create user namespaces, such as passt and pasta for sandboxing purposes. Add the new 'allow' rules. They appear to be backward compatible, kernel-wise, and the policy now requires the new 'user_namespace' class to build, but that's something distributions already ship. Reported-by: Richard W.M. Jones <rjones(a)redhat.com> Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> Reviewed-by: Richard W.M. Jones <rjones(a)redhat.com> --- contrib/selinux/passt.te | 2 ++ contrib/selinux/pasta.te | 2 ++ 2 files changed, 4 insertions(+) diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te index 687ae40..5868a41 100644 --- a/contrib/selinux/passt.te +++ b/contrib/selinux/passt.te @@ -51,6 +51,7 @@ require { class capability sys_tty_config; class cap_userns { setpcap sys_admin sys_ptrace }; + class user_namespace create; } type passt_t; @@ -90,6 +91,7 @@ allow syslogd_t self:cap_userns sys_ptrace; allow passt_t self:process setcap; allow passt_t self:capability { sys_tty_config setpcap net_bind_service }; allow passt_t self:cap_userns { setpcap sys_admin sys_ptrace }; +allow passt_t self:user_namespace create; allow passt_t proc_net_t:file read; allow passt_t net_conf_t:file { open read }; diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te index 367d09f..645ccee 100644 --- a/contrib/selinux/pasta.te +++ b/contrib/selinux/pasta.te @@ -80,6 +80,7 @@ require { type init_t; class cap_userns { setpcap sys_admin sys_ptrace net_bind_service net_admin }; + class user_namespace create; } type pasta_t; @@ -104,6 +105,7 @@ init_daemon_domain(pasta_t, pasta_exec_t) allow pasta_t self:capability { setpcap net_bind_service sys_tty_config dac_read_search net_admin sys_resource }; allow pasta_t self:cap_userns { setpcap sys_admin sys_ptrace net_admin net_bind_service }; +allow pasta_t self:user_namespace create; allow pasta_t bin_t:file { execute execute_no_trans map }; allow pasta_t nsfs_t:file { open read }; -- 2.39.2
Somehow most of this used to work on older kernels, but now we need to explicitly permit setuid, setgid, and setcap capabilities, as well as read-only access to passwd (as we support running under a given login name) and sssd library facilities. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> Reviewed-by: Richard W.M. Jones <rjones(a)redhat.com> --- contrib/selinux/passt.te | 7 +++++-- contrib/selinux/pasta.te | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te index 5868a41..a0c0526 100644 --- a/contrib/selinux/passt.te +++ b/contrib/selinux/passt.te @@ -49,7 +49,7 @@ require { class netlink_route_socket { bind create nlmsg_read }; - class capability sys_tty_config; + class capability { sys_tty_config setuid setgid }; class cap_userns { setpcap sys_admin sys_ptrace }; class user_namespace create; } @@ -89,10 +89,13 @@ logging_send_syslog_msg(passt_t) allow syslogd_t self:cap_userns sys_ptrace; allow passt_t self:process setcap; -allow passt_t self:capability { sys_tty_config setpcap net_bind_service }; +allow passt_t self:capability { sys_tty_config setpcap net_bind_service setuid setgid}; allow passt_t self:cap_userns { setpcap sys_admin sys_ptrace }; allow passt_t self:user_namespace create; +auth_read_passwd_file(passt_t) +sssd_search_lib(passt_t) + allow passt_t proc_net_t:file read; allow passt_t net_conf_t:file { open read }; allow passt_t net_conf_t:lnk_file read; diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te index 645ccee..28265dc 100644 --- a/contrib/selinux/pasta.te +++ b/contrib/selinux/pasta.te @@ -79,6 +79,7 @@ require { type shell_exec_t; type init_t; + class capability { sys_tty_config setuid setgid }; class cap_userns { setpcap sys_admin sys_ptrace net_bind_service net_admin }; class user_namespace create; } @@ -103,10 +104,13 @@ allow unconfined_t pasta_t : process transition ; init_daemon_domain(pasta_t, pasta_exec_t) -allow pasta_t self:capability { setpcap net_bind_service sys_tty_config dac_read_search net_admin sys_resource }; +allow pasta_t self:capability { setpcap net_bind_service sys_tty_config dac_read_search net_admin sys_resource setuid setgid }; allow pasta_t self:cap_userns { setpcap sys_admin sys_ptrace net_admin net_bind_service }; allow pasta_t self:user_namespace create; +auth_read_passwd_file(pasta_t) +sssd_search_lib(pasta_t) + allow pasta_t bin_t:file { execute execute_no_trans map }; allow pasta_t nsfs_t:file { open read }; @@ -162,7 +166,7 @@ allow pasta_t unconfined_t:dir search; allow pasta_t unconfined_t:file read; allow pasta_t unconfined_t:lnk_file read; allow pasta_t passwd_file_t:file { getattr open read }; -allow pasta_t self:process setpgid; +allow pasta_t self:process { setpgid setcap }; allow pasta_t shell_exec_t:file { execute execute_no_trans map }; allow pasta_t sssd_var_lib_t:dir search; -- 2.39.2
That's what we actually need to check networking-related sysctls, to scan for bound ports, and to manipulate bits of network configuration inside pasta's target namespaces. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> Acked-by: Richard W.M. Jones <rjones(a)redhat.com> --- contrib/selinux/passt.te | 1 + contrib/selinux/pasta.te | 3 +++ 2 files changed, 4 insertions(+) diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te index a0c0526..948d1b1 100644 --- a/contrib/selinux/passt.te +++ b/contrib/selinux/passt.te @@ -101,6 +101,7 @@ allow passt_t net_conf_t:file { open read }; allow passt_t net_conf_t:lnk_file read; allow passt_t tmp_t:sock_file { create unlink write }; allow passt_t self:netlink_route_socket { bind create nlmsg_read read write setopt }; +kernel_search_network_sysctl(passt_t) corenet_tcp_bind_all_nodes(passt_t) corenet_udp_bind_all_nodes(passt_t) diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te index 28265dc..b3ddc6a 100644 --- a/contrib/selinux/pasta.te +++ b/contrib/selinux/pasta.te @@ -133,6 +133,7 @@ allow syslogd_t self:cap_userns sys_ptrace; allow pasta_t proc_net_t:file { open read }; allow pasta_t net_conf_t:file { open read }; allow pasta_t self:netlink_route_socket { bind create nlmsg_read nlmsg_write setopt read write }; +kernel_search_network_sysctl(pasta_t) allow pasta_t tmp_t:sock_file { create unlink write }; @@ -186,4 +187,6 @@ allow pasta_t sysctl_net_t:dir search; allow pasta_t sysctl_net_t:file { open write }; allow pasta_t kernel_t:system module_request; +allow pasta_t net_conf_t:lnk_file read; +allow pasta_t proc_net_t:lnk_file read; -- 2.39.2
This is needed to monitor filesystem-bound namespaces and quit when they're gone -- this feature never really worked with SELinux. Fixes: 745a9ba4284c ("pasta: By default, quit if filesystem-bound net namespace goes away") Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> Acked-by: Richard W.M. Jones <rjones(a)redhat.com> --- contrib/selinux/pasta.te | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te index b3ddc6a..31e82dc 100644 --- a/contrib/selinux/pasta.te +++ b/contrib/selinux/pasta.te @@ -187,6 +187,8 @@ allow pasta_t sysctl_net_t:dir search; allow pasta_t sysctl_net_t:file { open write }; allow pasta_t kernel_t:system module_request; +allow pasta_t nsfs_t:file read; + allow pasta_t net_conf_t:lnk_file read; allow pasta_t proc_net_t:lnk_file read; -- 2.39.2
...now it gets ugly. If we use pasta without an existing target namespace, and run commands directly or spawn a shell, and keep the pasta_t domain when we do, they won't be able to do much: a shell might even start, but it's not going to be usable, or to even display a prompt. Ideally, pasta should behave like a shell when it spawns a command: start as unconfined_t and automatically transition to whatever domain is associated in the specific policy for that command. But we can't run as unconfined_t, of course. It would seem natural to switch to unconfined_t "just before", so that the default transitions happen. But transitions can only happen when we execvp(), and that's one single transition -- not two. That is, this approach would work for: pasta -- sh -c 'ip address show' but not for: pasta -- ip address show If we configure a transition to unconfined_t when we run ip(8), we'll really try to start that as unconfined_t -- but unconfined_t isn't allowed as entrypoint for ip(8) itself, and execvp() will fail. However, there aren't many different types of binaries pasta might commonly run -- for example, we're unlikely to see pasta used to run a mount(8) command. Explicitly set up domain transition for common stuff -- switching to unconfined_t for bin_t and shells works just fine, ip(8), ping(8), arping(8) and similar need a different treatment. While at it, allow commands we spawn to inherit resource limits and signal masks, because that's what happens by default, and don't require AT_SECURE sanitisation of the environment (because that won't happen by default). Slightly unrelated: we also need to explicitly allow pasta_t to use TTYs, not just PTYs, otherwise we can't keep stdin and stdout open for shells. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> --- contrib/selinux/pasta.te | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te index 31e82dc..c37a847 100644 --- a/contrib/selinux/pasta.te +++ b/contrib/selinux/pasta.te @@ -51,6 +51,7 @@ require { type tun_tap_device_t; type sysctl_net_t; class tun_socket create; + type user_tty_device_t; attribute port_type; type port_t; @@ -77,6 +78,11 @@ require { type kernel_t; class process setpgid; type shell_exec_t; + type ifconfig_exec_t; + type netutils_exec_t; + type ping_exec_t; + type ifconfig_t; + type ping_t; type init_t; class capability { sys_tty_config setuid setgid }; @@ -111,7 +117,12 @@ allow pasta_t self:user_namespace create; auth_read_passwd_file(pasta_t) sssd_search_lib(pasta_t) -allow pasta_t bin_t:file { execute execute_no_trans map }; +domain_auto_trans(pasta_t, bin_t, unconfined_t); +domain_auto_trans(pasta_t, shell_exec_t, unconfined_t); +domain_auto_trans(pasta_t, ifconfig_exec_t, ifconfig_t); +domain_auto_trans(pasta_t, netutils_exec_t, netutils_t); +domain_auto_trans(pasta_t, ping_exec_t, ping_t); + allow pasta_t nsfs_t:file { open read }; allow pasta_t user_home_t:dir getattr; @@ -192,3 +203,8 @@ allow pasta_t nsfs_t:file read; allow pasta_t net_conf_t:lnk_file read; allow pasta_t proc_net_t:lnk_file read; +allow pasta_t unconfined_t:process { noatsecure rlimitinh siginh }; +allow pasta_t ifconfig_t:process { noatsecure rlimitinh siginh }; +allow pasta_t netutils_t:process { noatsecure rlimitinh siginh }; +allow pasta_t ping_t:process { noatsecure rlimitinh siginh }; +allow pasta_t user_tty_device_t:chr_file { append read write }; -- 2.39.2