From ed59b44309bff0039f0a41718119b305bf017140 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Apr 2022 10:56:41 +0200 Subject: [PATCH] test-resolved-stream: before entering user/network namespaces check if that's safe I regularly run my tests also as root, since some of the tested code uses privileged APIs. The test-resolved-stream so far tried to run its tests in a user/network namespace if that can be allocated. This caused the tests to fail on my system where once the user namespace is opened access to the build tree in my $HOME is prohibited (due to restricted access modes on my home dir). Let's add a check for that: before actually isolating the test in a user/network namespace, let's see if that would make it impossible for us to access the build tree (which we need to do load the TLS certificates the test requires). This should make the test pass when run as root from a build tree with restrictive access mode. --- src/resolve/test-resolved-stream.c | 32 +++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/resolve/test-resolved-stream.c b/src/resolve/test-resolved-stream.c index 2f6245f406..87136975ad 100644 --- a/src/resolve/test-resolved-stream.c +++ b/src/resolve/test-resolved-stream.c @@ -16,6 +16,7 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "path-util.h" #include "process-util.h" #include "resolved-dns-packet.h" #include "resolved-dns-question.h" @@ -330,11 +331,36 @@ static void test_dns_stream(bool tls) { static void try_isolate_network(void) { _cleanup_close_ int socket_fd = -1; + int r; - if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { - log_warning("test-resolved-stream: Can't create user and network ns, running on host"); - return; + /* First test if CLONE_NEWUSER/CLONE_NEWNET can actually work for us, i.e. we can open the namespaces + * and then still access the build dir we are run from. We do that in a child process since it's + * nasty if we have to go back from the namespace once we entered it and realized it cannot work. */ + r = safe_fork("(usernstest)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r == 0) { /* child */ + _cleanup_free_ char *rt = NULL, *d = NULL; + + if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { + log_warning_errno(errno, "test-resolved-stream: Can't create user and network ns, running on host: %m"); + _exit(EXIT_FAILURE); + } + + assert_se(get_process_exe(0, &rt) >= 0); + assert_se(path_extract_directory(rt, &d) >= 0); + + if (access(d, F_OK) < 0) { + log_warning_errno(errno, "test-resolved-stream: Can't access /proc/self/exe from user/network ns, running on host: %m"); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); } + if (r == -EPROTO) /* EPROTO means nonzero exit code of child, i.e. the tests in the child failed */ + return; + assert_se(r > 0); + + /* Now that we know that the unshare() is safe, let's actually do it */ + assert_se(unshare(CLONE_NEWUSER | CLONE_NEWNET) >= 0); /* Bring up the loopback interfaceon the newly created network namespace */ struct ifreq req = { .ifr_ifindex = 1 };