]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-resolved-stream: before entering user/network namespaces check if that's safe 22934/head
authorLennart Poettering <lennart@poettering.net>
Fri, 1 Apr 2022 08:56:41 +0000 (10:56 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 1 Apr 2022 09:14:56 +0000 (11:14 +0200)
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

index 2f6245f4069ceaf3ce03e3dbfc3f62121d99d734..87136975ad3f51f6917f3f2c88d5c427a07f6f7a 100644 (file)
@@ -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 };