Merge "libstore/build: always enable seccomp filtering and no-new-privileges" into main

This commit is contained in:
alois31 2024-05-25 04:21:53 +00:00 committed by Gerrit Code Review
commit ddfe379a6b
14 changed files with 87 additions and 78 deletions

View file

@ -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",

View file

@ -0,0 +1,12 @@
---
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.
In order to support building on architectures without libseccomp support, the option to disable syscall filtering at build time remains.
However, other uses of this option are heavily discouraged, since it would reduce the security of the sandbox substantially.

View file

@ -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 <https://github.com/seccomp/libseccomp>.
- On 64-bit x86 machines only, `libcpuid` library

View file

@ -182,6 +182,9 @@ 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')
if is_linux and not seccomp.found()
warning('Sandbox security is reduced because libseccomp has not been found! Please provide libseccomp if it supports your CPU architecture.')
endif
configdata += {
'HAVE_SECCOMP': seccomp.found().to_int(),
}

View file

@ -34,7 +34,6 @@
/* Includes required for chroot support. */
#if __linux__
#include <sys/ioctl.h>
#include "linux/fchmodat2-compat.hh"
#include <net/if.h>
#include <netinet/ip.h>
#include <sys/mman.h>
@ -44,6 +43,7 @@
#include <sys/prctl.h>
#include <sys/syscall.h>
#if HAVE_SECCOMP
#include "linux/fchmodat2-compat.hh"
#include <seccomp.h>
#endif
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
@ -1612,7 +1612,6 @@ void LocalDerivationGoal::chownToBuilder(const Path & path)
void setupSeccomp()
{
#if __linux__
if (!settings.filterSyscalls) return;
#if HAVE_SECCOMP
scmp_filter_ctx ctx;
@ -1678,15 +1677,18 @@ 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!");
// Still set the no-new-privileges flag if libseccomp is not available.
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
throw SysError("PR_SET_NO_NEW_PRIVS failed");
#endif
#endif
}
@ -1954,10 +1956,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

View file

@ -912,29 +912,6 @@ public:
)"};
#if __linux__
Setting<bool> 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<bool> 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<StringSet> ignoredAcls{
this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls",
R"(

View file

@ -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

View file

@ -210,7 +210,6 @@ libstore = library(
seccomp,
sqlite,
sodium,
seccomp,
curl,
openssl,
aws_sdk,

View file

@ -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;

View file

@ -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;
}

View file

@ -0,0 +1,8 @@
{ runCommand, libcap }:
runCommand "cant-get-capabilities" { nativeBuildInputs = [ libcap.out ]; } ''
if [ "$(/run/wrappers/bin/ohno 2>&1)" != "failed to inherit capabilities: Operation not permitted" ]; then
echo "Oh no! We gained capabilities!"
exit 1
fi
touch $out
''

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
''