From d587f4f5f9b05f8bdd75a9bfe2b317d96a5e0820 Mon Sep 17 00:00:00 2001 From: Alois Wohlschlager Date: Wed, 8 May 2024 19:15:00 +0200 Subject: [PATCH] libstore/build: always enable seccomp filtering and no-new-privileges Seccomp filtering and the no-new-privileges functionality improve the security of the sandbox, and have been enabled by default for a long time. In https://git.lix.systems/lix-project/lix/issues/265 it was decided that they should be enabled unconditionally. Accordingly, remove the allow-new-privileges (which had weird behavior anyway) and filter-syscall settings, and force the security features on. This turns libseccomp into a required dependency on Linux. Change-Id: Iedbfa18d720ae557dee07a24f69b2520f30119cb --- doc/manual/redirects.js | 1 - .../rl-next/linux-sandbox-consistency.md | 10 +++++++ .../src/installation/prerequisites-source.md | 5 +--- meson.build | 6 +--- meson.options | 4 --- src/libstore/build/derivation-goal.cc | 3 -- src/libstore/build/local-derivation-goal.cc | 18 +++--------- src/libstore/globals.hh | 23 --------------- src/libstore/linux/fchmodat2-compat.hh | 28 +++++++++---------- src/libstore/meson.build | 1 - tests/nixos/default.nix | 4 ++- tests/nixos/no-new-privileges/no-sandbox.nix | 21 ++++++++++++++ tests/nixos/no-new-privileges/package.nix | 8 ++++++ tests/nixos/no-new-privileges/sandbox.nix | 18 ++++++++++++ tests/nixos/root-in-sandbox/default.nix | 15 ---------- tests/nixos/root-in-sandbox/package.nix | 8 ------ 16 files changed, 79 insertions(+), 94 deletions(-) create mode 100644 doc/manual/rl-next/linux-sandbox-consistency.md create mode 100644 tests/nixos/no-new-privileges/no-sandbox.nix create mode 100644 tests/nixos/no-new-privileges/package.nix create mode 100644 tests/nixos/no-new-privileges/sandbox.nix delete mode 100644 tests/nixos/root-in-sandbox/default.nix delete mode 100644 tests/nixos/root-in-sandbox/package.nix diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 436564baa..f270d31a4 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -24,7 +24,6 @@ const redirects = { "chap-writing-nix-expressions": "language/index.html", "part-command-ref": "command-ref/command-ref.html", "conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation", - "conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges", "conf-allowed-uris": "command-ref/conf-file.html#conf-allowed-uris", "conf-allowed-users": "command-ref/conf-file.html#conf-allowed-users", "conf-auto-optimise-store": "command-ref/conf-file.html#conf-auto-optimise-store", diff --git a/doc/manual/rl-next/linux-sandbox-consistency.md b/doc/manual/rl-next/linux-sandbox-consistency.md new file mode 100644 index 000000000..7c50dfe55 --- /dev/null +++ b/doc/manual/rl-next/linux-sandbox-consistency.md @@ -0,0 +1,10 @@ +--- +synopsis: Enforce syscall filtering and no-new-privileges on Linux +cls: 1063 +category: Breaking Changes +credits: alois31 +--- + +In order to improve consistency of the build environment, system call filtering and no-new-privileges are now unconditionally enabled on Linux. +The `filter-syscalls` and `allow-new-privileges` options which could be used to disable these features under some circumstances have been removed. +Furthermore, libseccomp is now a required dependency on Linux, and syscall filtering cannot be disabled at build time any more either. diff --git a/doc/manual/src/installation/prerequisites-source.md b/doc/manual/src/installation/prerequisites-source.md index 8584f295a..88a411b7a 100644 --- a/doc/manual/src/installation/prerequisites-source.md +++ b/doc/manual/src/installation/prerequisites-source.md @@ -68,10 +68,7 @@ The most current alternative to this section is to read `package.nix` and see wh may also work, but ancient versions like the ubiquitous 2.5.4a won't. - - The `libseccomp` is used to provide syscall filtering on Linux. This - is an optional dependency and can be disabled passing a - `--disable-seccomp-sandboxing` option to the `configure` script (Not - recommended unless your system doesn't support `libseccomp`). To get + - The `libseccomp` is used to provide syscall filtering on Linux. To get the library, visit . - On 64-bit x86 machines only, `libcpuid` library diff --git a/meson.build b/meson.build index 0d59ff751..26ab1bd7c 100644 --- a/meson.build +++ b/meson.build @@ -180,11 +180,7 @@ configdata += { deps += cpuid # seccomp only makes sense on Linux -seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false -seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required, version : '>=2.5.5') -configdata += { - 'HAVE_SECCOMP': seccomp.found().to_int(), -} +seccomp = dependency('libseccomp', 'seccomp', required : is_linux, version : '>=2.5.5') libarchive = dependency('libarchive', required : true) deps += libarchive diff --git a/meson.options b/meson.options index 6b13fa8a0..ae4449a1b 100644 --- a/meson.options +++ b/meson.options @@ -16,10 +16,6 @@ option('cpuid', type : 'feature', description : 'determine microarchitecture levels with libcpuid (only relevant on x86_64)', ) -option('seccomp-sandboxing', type : 'feature', - description : 'build support for seccomp sandboxing (recommended unless your arch doesn\'t support libseccomp, only relevant on Linux)', -) - option('sandbox-shell', type : 'string', value : 'busybox', description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)', ) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 5fa5deb7c..a7fae50ad 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -45,9 +45,6 @@ #include #include #include -#if HAVE_SECCOMP -#include -#endif #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 5c36a3ac2..47494a511 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -43,9 +43,7 @@ #include #include #include -#if HAVE_SECCOMP #include -#endif #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif @@ -1612,8 +1610,6 @@ void LocalDerivationGoal::chownToBuilder(const Path & path) void setupSeccomp() { #if __linux__ - if (!settings.filterSyscalls) return; -#if HAVE_SECCOMP scmp_filter_ctx ctx; if (!(ctx = seccomp_init(SCMP_ACT_ALLOW))) @@ -1678,16 +1674,14 @@ void setupSeccomp() seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); - if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, settings.allowNewPrivileges ? 0 : 1) != 0) + // Set the NO_NEW_PRIVS prctl flag. + // This both makes loading seccomp filters work for unprivileged users, + // and is an additional security measure in its own right. + if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 1) != 0) throw SysError("unable to set 'no new privileges' seccomp attribute"); if (seccomp_load(ctx) != 0) throw SysError("unable to load seccomp BPF program"); -#else - throw Error( - "seccomp is not supported on this platform; " - "you can bypass this error by setting the option 'filter-syscalls' to false, but note that untrusted builds can then create setuid binaries!"); -#endif #endif } @@ -1954,10 +1948,6 @@ void LocalDerivationGoal::runChild() throw SysError("setuid failed"); setUser = false; - - // Make sure we can't possibly gain new privileges in the sandbox - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - throw SysError("PR_SET_NO_NEW_PRIVS failed"); } #endif diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 8856d8fae..85789f6b5 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -912,29 +912,6 @@ public: )"}; #if __linux__ - Setting filterSyscalls{ - this, true, "filter-syscalls", - R"( - Whether to prevent certain dangerous system calls, such as - creation of setuid/setgid files or adding ACLs or extended - attributes. Only disable this if you're aware of the - security implications. - )"}; - - Setting allowNewPrivileges{ - this, false, "allow-new-privileges", - R"( - (Linux-specific.) By default, builders on Linux cannot acquire new - privileges by calling setuid/setgid programs or programs that have - file capabilities. For example, programs such as `sudo` or `ping` - will fail. (Note that in sandbox builds, no such programs are - available unless you bind-mount them into the sandbox via the - `sandbox-paths` option.) You can allow the use of such programs by - enabling this option. This is impure and usually undesirable, but - may be useful in certain scenarios (e.g. to spin up containers or - set up userspace network interfaces in tests). - )"}; - Setting ignoredAcls{ this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls", R"( diff --git a/src/libstore/linux/fchmodat2-compat.hh b/src/libstore/linux/fchmodat2-compat.hh index b05da6786..6ad8a5578 100644 --- a/src/libstore/linux/fchmodat2-compat.hh +++ b/src/libstore/linux/fchmodat2-compat.hh @@ -20,18 +20,16 @@ #pragma once ///@file -#if HAVE_SECCOMP -# if defined(__alpha__) -# define NIX_SYSCALL_FCHMODAT2 562 -# elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32 -# define NIX_SYSCALL_FCHMODAT2 1073742276 -# elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64 -# define NIX_SYSCALL_FCHMODAT2 5452 -# elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32 -# define NIX_SYSCALL_FCHMODAT2 6452 -# elif defined(__mips__) && defined(_ABIO32) // mips32 -# define NIX_SYSCALL_FCHMODAT2 4452 -# else -# define NIX_SYSCALL_FCHMODAT2 452 -# endif -#endif // HAVE_SECCOMP +#if defined(__alpha__) +# define NIX_SYSCALL_FCHMODAT2 562 +#elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32 +# define NIX_SYSCALL_FCHMODAT2 1073742276 +#elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64 +# define NIX_SYSCALL_FCHMODAT2 5452 +#elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32 +# define NIX_SYSCALL_FCHMODAT2 6452 +#elif defined(__mips__) && defined(_ABIO32) // mips32 +# define NIX_SYSCALL_FCHMODAT2 4452 +#else +# define NIX_SYSCALL_FCHMODAT2 452 +#endif diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 4ccb03df7..98549f6d9 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -210,7 +210,6 @@ libstore = library( seccomp, sqlite, sodium, - seccomp, curl, openssl, aws_sdk, diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 354413a7c..5e8bc69ce 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -184,7 +184,9 @@ in symlinkResolvconf = runNixOSTestFor "x86_64-linux" ./symlink-resolvconf.nix; - rootInSandbox = runNixOSTestFor "x86_64-linux" ./root-in-sandbox; + noNewPrivilegesInSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/sandbox.nix; + + noNewPrivilegesOutsideSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/no-sandbox.nix; broken-userns = runNixOSTestFor "x86_64-linux" ./broken-userns.nix; diff --git a/tests/nixos/no-new-privileges/no-sandbox.nix b/tests/nixos/no-new-privileges/no-sandbox.nix new file mode 100644 index 000000000..d952a6862 --- /dev/null +++ b/tests/nixos/no-new-privileges/no-sandbox.nix @@ -0,0 +1,21 @@ +let + inherit (import ../util.nix) mkNixBuildTest; +in +mkNixBuildTest { + name = "no-new-privileges-outside-sandbox"; + extraMachineConfig = + { pkgs, ... }: + { + security.wrappers.ohno = { + owner = "root"; + group = "root"; + capabilities = "cap_sys_nice=eip"; + source = "${pkgs.libcap}/bin/getpcaps"; + }; + nix.settings = { + extra-sandbox-paths = [ "/run/wrappers/bin/ohno" ]; + sandbox = false; + }; + }; + expressionFile = ./package.nix; +} diff --git a/tests/nixos/no-new-privileges/package.nix b/tests/nixos/no-new-privileges/package.nix new file mode 100644 index 000000000..b97c9a85f --- /dev/null +++ b/tests/nixos/no-new-privileges/package.nix @@ -0,0 +1,8 @@ +{ runCommand, libcap }: +runCommand "cant-get-capabilities" { nativeBuildInputs = [ libcap.out ]; } '' + if /run/wrappers/bin/ohno; then + echo "Oh no! We gained capabilities!" + exit 1 + fi + touch $out +'' diff --git a/tests/nixos/no-new-privileges/sandbox.nix b/tests/nixos/no-new-privileges/sandbox.nix new file mode 100644 index 000000000..e2475555e --- /dev/null +++ b/tests/nixos/no-new-privileges/sandbox.nix @@ -0,0 +1,18 @@ +let + inherit (import ../util.nix) mkNixBuildTest; +in +mkNixBuildTest { + name = "no-new-privileges-in-sandbox"; + extraMachineConfig = + { pkgs, ... }: + { + security.wrappers.ohno = { + owner = "root"; + group = "root"; + capabilities = "cap_sys_nice=eip"; + source = "${pkgs.libcap}/bin/getpcaps"; + }; + nix.settings.extra-sandbox-paths = [ "/run/wrappers/bin/ohno" ]; + }; + expressionFile = ./package.nix; +} diff --git a/tests/nixos/root-in-sandbox/default.nix b/tests/nixos/root-in-sandbox/default.nix deleted file mode 100644 index 110d83f86..000000000 --- a/tests/nixos/root-in-sandbox/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -let - inherit (import ../util.nix) mkNixBuildTest; -in mkNixBuildTest { - name = "root-in-sandbox"; - extraMachineConfig = { pkgs, ... }: { - security.wrappers.ohno = { - owner = "root"; - group = "root"; - setuid = true; - source = "${pkgs.coreutils}/bin/whoami"; - }; - nix.settings.extra-sandbox-paths = ["/run/wrappers/bin"]; - }; - expressionFile = ./package.nix; -} diff --git a/tests/nixos/root-in-sandbox/package.nix b/tests/nixos/root-in-sandbox/package.nix deleted file mode 100644 index a1069160c..000000000 --- a/tests/nixos/root-in-sandbox/package.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ runCommand }: -runCommand "cant-get-root-in-sandbox" {} '' - if /run/wrappers/bin/ohno; then - echo "Oh no! We're root in the sandbox!" - exit 1 - fi - touch $out -''