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
#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. Syscall filtering can still be enabled at build time to
support building on architectures libseccomp doesn't support.
Change-Id: Iedbfa18d720ae557dee07a24f69b2520f30119cb
This commit is contained in:
parent
19ea351642
commit
f047e4357b
|
@ -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",
|
||||
|
|
12
doc/manual/rl-next/linux-sandbox-consistency.md
Normal file
12
doc/manual/rl-next/linux-sandbox-consistency.md
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -210,7 +210,6 @@ libstore = library(
|
|||
seccomp,
|
||||
sqlite,
|
||||
sodium,
|
||||
seccomp,
|
||||
curl,
|
||||
openssl,
|
||||
aws_sdk,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
21
tests/nixos/no-new-privileges/no-sandbox.nix
Normal file
21
tests/nixos/no-new-privileges/no-sandbox.nix
Normal 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;
|
||||
}
|
8
tests/nixos/no-new-privileges/package.nix
Normal file
8
tests/nixos/no-new-privileges/package.nix
Normal 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
|
||||
''
|
18
tests/nixos/no-new-privileges/sandbox.nix
Normal file
18
tests/nixos/no-new-privileges/sandbox.nix
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
''
|
Loading…
Reference in a new issue