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
lix-project/lix#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
This commit is contained in:
alois31 2024-05-08 19:15:00 +02:00
parent bf2dcbe427
commit 209c0046ea
No known key found for this signature in database
GPG key ID: E0F59EA5E5216914
16 changed files with 79 additions and 94 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,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.

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

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

View file

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

View file

@ -45,9 +45,6 @@
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#if HAVE_SECCOMP
#include <seccomp.h>
#endif
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
#endif

View file

@ -43,9 +43,7 @@
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#if HAVE_SECCOMP
#include <seccomp.h>
#endif
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
#endif
@ -1618,8 +1616,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)))
@ -1684,16 +1680,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
}
@ -1960,10 +1954,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

@ -163,7 +163,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; 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
''