From db41f74af39850d0aeb2741304c1eacf90ceea88 Mon Sep 17 00:00:00 2001
From: Yorick van Pelt <yorick@yorickvanpelt.nl>
Date: Tue, 14 Feb 2023 12:03:34 +0100
Subject: [PATCH 1/3] Don't allow writing to /etc

---
 src/libstore/build/local-derivation-goal.cc | 4 +++-
 tests/linux-sandbox.sh                      | 3 +++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 7c4892c96..de023f336 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -670,7 +670,6 @@ void LocalDerivationGoal::startBuilder()
            nobody account.  The latter is kind of a hack to support
            Samba-in-QEMU. */
         createDirs(chrootRootDir + "/etc");
-        chownToBuilder(chrootRootDir + "/etc");
 
         if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
             throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name);
@@ -970,6 +969,9 @@ void LocalDerivationGoal::startBuilder()
                 "nobody:x:65534:65534:Nobody:/:/noshell\n",
                 sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
 
+        /* Make /etc unwritable */
+        chmod_(chrootRootDir + "/etc", 0555);
+
         /* Save the mount- and user namespace of the child. We have to do this
            *before* the child does a chroot. */
         sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
diff --git a/tests/linux-sandbox.sh b/tests/linux-sandbox.sh
index 3f304ac2f..e62039567 100644
--- a/tests/linux-sandbox.sh
+++ b/tests/linux-sandbox.sh
@@ -37,3 +37,6 @@ nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link
 (! nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link --check -K 2> $TEST_ROOT/log)
 if grep -q 'error: renaming' $TEST_ROOT/log; then false; fi
 grep -q 'may not be deterministic' $TEST_ROOT/log
+
+# Test that sandboxed builds cannot write to /etc easily
+(! nix-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' --no-out-link --sandbox-paths /nix/store)

From ad1f61c39b716f4876d5f4c1dd9e37681631edb3 Mon Sep 17 00:00:00 2001
From: Yorick van Pelt <yorick@yorickvanpelt.nl>
Date: Tue, 14 Feb 2023 12:26:40 +0100
Subject: [PATCH 2/3] container test: make /etc writable

---
 tests/nixos/containers/systemd-nspawn.nix | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/nixos/containers/systemd-nspawn.nix b/tests/nixos/containers/systemd-nspawn.nix
index 424436b3f..457af6064 100644
--- a/tests/nixos/containers/systemd-nspawn.nix
+++ b/tests/nixos/containers/systemd-nspawn.nix
@@ -62,6 +62,7 @@ runCommand "test"
 
     mkdir -p $out
 
+    chmod +w /etc
     touch /etc/os-release
     echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id
 

From 49fd72a903b7bc2fdc4735111ca5569122cf55ee Mon Sep 17 00:00:00 2001
From: Yorick van Pelt <yorick@yorickvanpelt.nl>
Date: Tue, 14 Feb 2023 13:29:30 +0100
Subject: [PATCH 3/3] Make /etc writability conditional on uid-range feature

---
 src/libstore/build/local-derivation-goal.cc | 5 ++++-
 tests/nixos/containers/systemd-nspawn.nix   | 1 -
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index de023f336..7b125f5d2 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -670,6 +670,8 @@ void LocalDerivationGoal::startBuilder()
            nobody account.  The latter is kind of a hack to support
            Samba-in-QEMU. */
         createDirs(chrootRootDir + "/etc");
+        if (parsedDrv->useUidRange())
+            chownToBuilder(chrootRootDir + "/etc");
 
         if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
             throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name);
@@ -970,7 +972,8 @@ void LocalDerivationGoal::startBuilder()
                 sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
 
         /* Make /etc unwritable */
-        chmod_(chrootRootDir + "/etc", 0555);
+        if (!parsedDrv->useUidRange())
+            chmod_(chrootRootDir + "/etc", 0555);
 
         /* Save the mount- and user namespace of the child. We have to do this
            *before* the child does a chroot. */
diff --git a/tests/nixos/containers/systemd-nspawn.nix b/tests/nixos/containers/systemd-nspawn.nix
index 457af6064..f54f32f2a 100644
--- a/tests/nixos/containers/systemd-nspawn.nix
+++ b/tests/nixos/containers/systemd-nspawn.nix
@@ -56,7 +56,6 @@ runCommand "test"
     # Make /run a tmpfs to shut up a systemd warning.
     mkdir /run
     mount -t tmpfs none /run
-    chmod 0700 /run
 
     mount -t cgroup2 none /sys/fs/cgroup