grsecurity: implement a single NixOS kernel

This patch replaces the old grsecurity kernels with a single NixOS
specific grsecurity kernel.  This kernel is intended as a general
purpose kernel, tuned for casual desktop use.

Providing only a single kernel may seem like a regression compared to
offering a multitude of flavors.  It is impossible, however, to
effectively test and support that many options.  This is amplified by
the reality that very few seem to actually use grsecurity on NixOS,
meaning that bugs go unnoticed for long periods of time, simply because
those code paths end up never being exercised.  More generally, it is
hopeless to anticipate imagined needs.  It is better to start from a
solid foundation and possibly add more flavours on demand.

While the generic kernel is intended to cover a wide range of use cases,
it cannot cover everything.  For some, the configuration will be either
too restrictive or too lenient.  In those cases, the recommended
solution is to build a custom kernel --- this is *strongly* recommended
for security sensitive deployments.

Building a custom grsec kernel should be as simple as
```nix
linux_grsec_nixos.override {
  extraConfig = ''
    GRKERNSEC y
    PAX y
    # and so on ...
  '';
}
```

The generic kernel should be usable both as a KVM guest and host.  When
running as a host, the kernel assumes hardware virtualisation support.
Virtualisation systems other than KVM are *unsupported*: users of
non-KVM systems are better served by compiling a custom kernel.

Unlike previous Grsecurity kernels, this configuration disables `/proc`
restrictions in favor of `security.hideProcessInformation`.

Known incompatibilities:
- ZFS: can't load spl and zfs kernel modules; claims incompatibility
  with KERNEXEC method `or` and RAP; changing to `bts` does not fix the
  problem, which implies we'd have to disable RAP as well for ZFS to
  work
- `kexec()`: likely incompatible with KERNEXEC (unverified)
- Xen: likely incompatible with KERNEXEC and UDEREF (unverified)
- Virtualbox: likely incompatible with UDEREF (unverified)
This commit is contained in:
Joachim Fasting 2016-06-14 00:04:56 +02:00
parent 4ae5eb97f1
commit 75b9a7beac
No known key found for this signature in database
GPG key ID: 4330820E1E04DCF4
7 changed files with 132 additions and 245 deletions

View file

@ -1,158 +1,33 @@
{ grsecOptions, lib, pkgs }:
{ stdenv
, overrideDerivation
with lib;
# required for gcc plugins
, gmp, libmpc, mpfr
let
cfg = {
kernelPatch = grsecOptions.kernelPatch;
config = {
mode = "auto";
sysctl = false;
denyChrootCaps = false;
denyChrootChmod = false;
denyUSB = false;
restrictProc = false;
restrictProcWithGroup = true;
unrestrictProcGid = 121; # Ugh, an awful hack. See grsecurity NixOS gid
disableRBAC = false;
disableSimultConnect = false;
redistKernel = true;
verboseVersion = false;
kernelExtraConfig = "";
} // grsecOptions.config;
};
# the base kernel
, kernel
vals = rec {
, grsecPatch
, kernelPatches ? []
mkKernel = patch:
{
inherit patch;
inherit (patch) kernel patches grversion revision;
};
, localver ? "-grsec"
, modDirVersion ? "${kernel.version}${localver}"
, extraConfig ? ""
, ...
} @ args:
grKernel = mkKernel cfg.kernelPatch;
assert (kernel.version == grsecPatch.kver);
## -- grsecurity configuration ---------------------------------------------
grsecPrioCfg =
if cfg.config.priority == "security" then
"GRKERNSEC_CONFIG_PRIORITY_SECURITY y"
else
"GRKERNSEC_CONFIG_PRIORITY_PERF y";
grsecSystemCfg =
if cfg.config.system == "desktop" then
"GRKERNSEC_CONFIG_DESKTOP y"
else
"GRKERNSEC_CONFIG_SERVER y";
grsecVirtCfg =
if cfg.config.virtualisationConfig == null then
"GRKERNSEC_CONFIG_VIRT_NONE y"
else if cfg.config.virtualisationConfig == "host" then
"GRKERNSEC_CONFIG_VIRT_HOST y"
else
"GRKERNSEC_CONFIG_VIRT_GUEST y";
grsecHwvirtCfg = if cfg.config.virtualisationConfig == null then "" else
if cfg.config.hardwareVirtualisation == true then
"GRKERNSEC_CONFIG_VIRT_EPT y"
else
"GRKERNSEC_CONFIG_VIRT_SOFT y";
grsecVirtswCfg =
let virtCfg = opt: "GRKERNSEC_CONFIG_VIRT_"+opt+" y";
in
if cfg.config.virtualisationConfig == null then ""
else if cfg.config.virtualisationSoftware == "xen" then virtCfg "XEN"
else if cfg.config.virtualisationSoftware == "kvm" then virtCfg "KVM"
else if cfg.config.virtualisationSoftware == "vmware" then virtCfg "VMWARE"
else virtCfg "VIRTUALBOX";
grsecMainConfig = if cfg.config.mode == "custom" then "" else ''
GRKERNSEC_CONFIG_AUTO y
${grsecPrioCfg}
${grsecSystemCfg}
${grsecVirtCfg}
${grsecHwvirtCfg}
${grsecVirtswCfg}
'';
grsecConfig =
let boolToKernOpt = b: if b then "y" else "n";
# Disable RANDSTRUCT under virtualbox, as it has some kind of
# breakage with the vbox guest drivers
#randstruct = optionalString config.virtualisation.virtualbox.guest.enable
# "GRKERNSEC_RANDSTRUCT n";
# Disable restricting links under the testing kernel, as something
# has changed causing it to fail miserably during boot.
#restrictLinks = optionalString cfg.testing
# "GRKERNSEC_LINK n";
in ''
GRKERNSEC y
${grsecMainConfig}
# Disable features rendered useless by redistributing the kernel
${optionalString cfg.config.redistKernel ''
GRKERNSEC_RANDSTRUCT n
GRKERNSEC_HIDESYM n
''}
# The paxmarks mechanism relies on ELF header markings, but the default
# grsecurity configuration only enables xattr markings
PAX_PT_PAX_FLAGS y
${if cfg.config.restrictProc then
"GRKERNSEC_PROC_USER y"
else
optionalString cfg.config.restrictProcWithGroup ''
GRKERNSEC_PROC_USERGROUP y
GRKERNSEC_PROC_GID ${toString cfg.config.unrestrictProcGid}
''
}
GRKERNSEC_SYSCTL ${boolToKernOpt cfg.config.sysctl}
GRKERNSEC_CHROOT_CAPS ${boolToKernOpt cfg.config.denyChrootCaps}
GRKERNSEC_CHROOT_CHMOD ${boolToKernOpt cfg.config.denyChrootChmod}
GRKERNSEC_DENYUSB ${boolToKernOpt cfg.config.denyUSB}
GRKERNSEC_NO_RBAC ${boolToKernOpt cfg.config.disableRBAC}
GRKERNSEC_NO_SIMULT_CONNECT ${boolToKernOpt cfg.config.disableSimultConnect}
${cfg.config.kernelExtraConfig}
'';
## -- grsecurity kernel packages -------------------------------------------
localver = grkern:
"-grsec" + optionalString cfg.config.verboseVersion
"-${grkern.grversion}-${grkern.revision}";
grsecurityOverrider = args: grkern: {
# additional build inputs for gcc plugins, required by some PaX/grsec features
nativeBuildInputs = args.nativeBuildInputs ++ (with pkgs; [ gmp libmpc mpfr ]);
preConfigure = (args.preConfigure or "") + ''
echo ${localver grkern} > localversion-grsec
'';
};
mkGrsecKern = grkern:
lowPrio (overrideDerivation (grkern.kernel.override (args: {
kernelPatches = args.kernelPatches ++ [ grkern.patch ] ++ grkern.patches;
argsOverride = {
modDirVersion = "${grkern.kernel.modDirVersion}${localver grkern}";
};
extraConfig = grsecConfig;
features.grsecurity = true;
ignoreConfigErrors = true; # Too lazy to model the config options that work with grsecurity and don't for now
})) (args: grsecurityOverrider args grkern));
mkGrsecPkg = grkern: pkgs.linuxPackagesFor grkern (mkGrsecPkg grkern);
## -- Kernel packages ------------------------------------------------------
grsecKernel = mkGrsecKern grKernel;
grsecPackage = mkGrsecPkg grsecKernel;
};
in vals
overrideDerivation (kernel.override {
inherit modDirVersion;
kernelPatches = [ { inherit (grsecPatch) name patch; } ] ++ kernelPatches ++ (kernel.kernelPatches or []);
features = (kernel.features or {}) // { grsecurity = true; };
inherit extraConfig;
ignoreConfigErrors = true;
}) (attrs: {
nativeBuildInputs = [ gmp libmpc mpfr ] ++ (attrs.nativeBuildInputs or []);
preConfigure = ''
echo ${localver} >localversion-grsec
${attrs.preConfigure or ""}
'';
})

View file

@ -1,17 +0,0 @@
let
mkOpts = prio: sys: virt: swvirt: hwvirt:
{ config.priority = prio;
config.system = sys;
config.virtualisationConfig = virt;
config.hardwareVirtualisation = hwvirt;
config.virtualisationSoftware = swvirt;
};
in
{
desktop =
mkOpts "performance" "desktop" "host" "kvm" true;
server =
mkOpts "security" "server" "host" "kvm" true;
server_xen =
mkOpts "security" "server" "guest" "xen" true;
}

View file

@ -0,0 +1,43 @@
{ stdenv }:
with stdenv.lib;
''
GRKERNSEC y
PAX y
GRKERNSEC_CONFIG_AUTO y
GRKERNSEC_CONFIG_DESKTOP y
GRKERNSEC_CONFIG_VIRT_HOST y
GRKERNSEC_CONFIG_VIRT_EPT y
GRKERNSEC_CONFIG_VIRT_KVM y
GRKERNSEC_CONFIG_PRIORITY_SECURITY y
PAX_PT_PAX_FLAGS y
PAX_XATTR_PAX_FLAGS n
PAX_EI_PAX n
GRKERNSEC_PROC_GID 0
PAX_LATENT_ENTROPY n
PAX_SIZE_OVERFLOW n
GRKERNSEC_HIDESYM n
GRKERNSEC_RANDSTRUCT n
GRKERNSEC_PROC n
GRKERNSEC_SYSFS_RESTRICT n
GRKERNSEC_KMEM n
GRKERNSEC_MODHARDEN n
GRKERNSEC_NO_SIMULT_CONNECT n
PAX_KERNEXEC_PLUGIN_METHOD_BTS y
GRKERNSEC_ACL_HIDEKERN y
GRKERNSEC_IO y
GRKERNSEC_AUDIT_PTRACE y
GRKERNSEC_FORKFAIL y
GRKERNSEC_SYSCTL y
GRKERNSEC_SYSCTL_DISTRO y
GRKERNSEC_SYSCTL_ON y
''

View file

@ -18,20 +18,20 @@ let
};
};
grsecPatch = { grversion ? "3.1", kernel, patches, kversion, revision, branch ? "test", sha256 }:
assert kversion == kernel.version;
{ name = "grsecurity-${grversion}-${kversion}";
inherit grversion kernel patches kversion revision;
grsecPatch = { grbranch ? "test", grver ? "3.1", kver, grrev, sha256 }: rec {
name = "grsecurity-${grver}-${kver}-${grrev}";
# Pass these along to allow the caller to determine compatibility
inherit grver kver grrev;
patch = fetchurl {
# When updating versions/hashes, ALWAYS use the official version; we use
# this mirror only because upstream removes sources files immediately upon
# releasing a new version ...
patch = fetchurl {
url = "https://raw.githubusercontent.com/slashbeast/grsecurity-scrape/master/test/grsecurity-${grversion}-${kversion}-${revision}.patch";
inherit sha256;
};
features.grsecurity = true;
url = "https://raw.githubusercontent.com/slashbeast/grsecurity-scrape/master/${grbranch}/${name}.patch";
inherit sha256;
};
};
in
rec {
@ -92,19 +92,18 @@ rec {
grsecurity_4_4 = throw "grsecurity stable is no longer supported";
grsecurity_4_5 = grsecPatch
{ kernel = pkgs.grsecurity_base_linux_4_5;
patches = [ grsecurity_fix_path_4_5 ];
kversion = "4.5.7";
revision = "201606080852";
sha256 = "1vgc314nh6bd7zw9r927lnbjq29z32g0s02jgvf635y9zz550nsh";
grsecurity_testing = grsecPatch
{ kver = "4.5.7";
grrev = "201606080852";
sha256 = "1vgc314nh6bd7zw9r927lnbjq29z32g0s02jgvf635y9zz550nsh";
};
grsecurity_latest = grsecurity_4_5;
grsecurity_fix_path_4_5 =
{ name = "grsecurity-fix-path-4.5";
patch = ./grsecurity-path-4.5.patch;
# This patch relaxes grsec constraints on the location of usermode helpers,
# e.g., modprobe, to allow calling into the Nix store.
grsecurity_nixos_kmod =
{
name = "grsecurity-nixos-kmod";
patch = ./grsecurity-nixos-kmod.patch;
};
crc_regression =

View file

@ -10822,32 +10822,6 @@ in
linux_chromiumos_latest = self.linux_chromiumos_3_18;
# grsecurity configuration
grsecurity_base_linux_4_5 = callPackage ../os-specific/linux/kernel/linux-grsecurity-4.5.nix {
inherit (linux_4_5) kernelPatches;
};
grFlavors = import ../build-support/grsecurity/flavors.nix;
mkGrsecurity = patch: opts:
(callPackage ../build-support/grsecurity {
grsecOptions = { kernelPatch = patch; } // opts;
});
grKernel = patch: opts: (self.mkGrsecurity patch opts).grsecKernel;
grPackage = patch: opts: recurseIntoAttrs (self.mkGrsecurity patch opts).grsecPackage;
# grsecurity kernels (see also linuxPackages_grsec_*)
linux_grsec_desktop_4_5 = self.grKernel kernelPatches.grsecurity_4_5 self.grFlavors.desktop;
linux_grsec_server_4_5 = self.grKernel kernelPatches.grsecurity_4_5 self.grFlavors.server;
linux_grsec_server_xen_4_5 = self.grKernel kernelPatches.grsecurity_4_5 self.grFlavors.server_xen;
linux_grsec_desktop_latest = self.grKernel kernelPatches.grsecurity_latest self.grFlavors.desktop;
linux_grsec_server_latest = self.grKernel kernelPatches.grsecurity_latest self.grFlavors.server;
linux_grsec_server_xen_latest = self.grKernel kernelPatches.grsecurity_latest self.grFlavors.server_xen;
/* Linux kernel modules are inherently tied to a specific kernel. So
rather than provide specific instances of those packages for a
specific kernel, we have a function that builds those packages
@ -10997,52 +10971,65 @@ in
# Build a kernel for Xen dom0
linuxPackages_latest_xen_dom0 = recurseIntoAttrs (self.linuxPackagesFor (self.linux_latest.override { features.xen_dom0=true; }) linuxPackages_latest);
# grsecurity packages
# Grsecurity packages
linuxPackages_grsec_desktop_4_5 = self.grPackage kernelPatches.grsecurity_4_5 self.grFlavors.desktop;
linuxPackages_grsec_server_4_5 = self.grPackage kernelPatches.grsecurity_4_5 self.grFlavors.server;
linuxPackages_grsec_server_xen_4_5 = self.grPackage kernelPatches.grsecurity_4_5 self.grFlavors.server_xen;
linux_grsec_nixos = callPackage ../build-support/grsecurity {
inherit (lib) overrideDerivation;
kernel = callPackage ../os-specific/linux/kernel/linux-grsecurity.nix {
inherit (self.linux_4_5) kernelPatches;
};
grsecPatch = self.kernelPatches.grsecurity_testing;
kernelPatches = [ self.kernelPatches.grsecurity_nixos_kmod ];
extraConfig = callPackage ../os-specific/linux/kernel/grsecurity-nixos-config.nix { };
};
linuxPackages_grsec_desktop_latest = self.grPackage kernelPatches.grsecurity_latest self.grFlavors.desktop;
linuxPackages_grsec_server_latest = self.grPackage kernelPatches.grsecurity_latest self.grFlavors.server;
linuxPackages_grsec_server_xen_latest = self.grPackage kernelPatches.grsecurity_latest self.grFlavors.server_xen;
linuxPackages_grsec_nixos =
let self = linuxPackagesFor linux_grsec_nixos self;
in recurseIntoAttrs self;
# grsecurity: legacy
grsecurity_base_linux_3_14 = throw "grsecurity stable is no longer supported";
grsecurity_base_linux_4_4 = throw "grsecurity stable is no longer supported";
linuxPackages_grsec_desktop_3_14 = throw "linuxPackages_grsec_desktop has been removed";
linuxPackages_grsec_desktop_4_4 = throw "linuxPackages_grsec_desktop has been removed";
linuxPackages_grsec_desktop_4_5 = throw "linuxPackages_grsec_desktop has been removed";
linuxPackages_grsec_desktop_latest = throw "linuxPackages_grsec_desktop has been removed";
linuxPackages_grsec_server_3_14 = throw "linuxPackages_grsec_server has been removed";
linuxPackages_grsec_server_4_4 = throw "linuxPackages_grsec_server has been removed";
linuxPackages_grsec_server_4_5 = throw "linuxPackages_grsec_server has been removed";
linuxPackages_grsec_server_latest = throw "linuxPackages_grsec_server has been removed";
linuxPackages_grsec_server_xen_3_14 = throw "linuxPackages_grsec_server_xen has been removed";
linuxPackages_grsec_server_xen_4_4 = throw "linuxPackages_grsec_server_xen has been removed";
linuxPackages_grsec_server_xen_4_5 = throw "linuxPackages_grsec_server_xen has been removed";
linuxPackages_grsec_server_xen_latest = throw "linuxPackages_grsec_server_xen has been removed";
linux_grsec_desktop_3_14 = throw "grsecurity stable is no longer supported";
linux_grsec_server_3_14 = throw "grsecurity stable is no longer supported";
linux_grsec_server_xen_3_14 = throw "grsecurity stable is no longer supported";
linux_grsec_desktop_4_4 = throw "grsecurity stable is no longer supported";
linux_grsec_server_4_4 = throw "grsecurity stable is no longer supported";
linux_grsec_server_xen_4_4 = throw "grsecurity stable is no longer supported";
linux_grsec_desktop_4_5 = throw "linux_grsec_desktop has been removed";
linux_grsec_desktop_latest = throw "linux_grsec_desktop has been removed";
linux_grsec_testing_desktop = self.linux_grsec_desktop_latest;
linux_grsec_testing_server = self.linux_grsec_server_latest;
linux_grsec_testing_server_xen = self.linux_grsec_server_xen_latest;
linux_grsec_server_3_14 = throw "grsecurity stable is no longer supported";
linux_grsec_server_4_4 = throw "grsecurity stable is no longer supported";
linux_grsec_server_4_5 = throw "linux_grsec_server has been removed";
linux_grsec_server_latest = throw "linux_grsec_server_latest has been removed";
linux_grsec_server_xen_3_14 = throw "grsecurity stable is no longer supported";
linux_grsec_server_xen_4_4 = throw "grsecurity stable is no longer supported";
linux_grsec_server_xen_4_5 = throw "linux_grsec_server_xen has been removed";
linux_grsec_server_xen_latest = throw "linux_grsec_server_xen has been removed";
linux_grsec_stable_desktop = self.linux_grsec_desktop_3_14;
linux_grsec_stable_server = self.linux_grsec_server_3_14;
linux_grsec_stable_server_xen = self.linux_grsec_server_xen_3_14;
linuxPackages_grsec_desktop_3_14 = self.grPackage kernelPatches.grsecurity_3_14 self.grFlavors.desktop;
linuxPackages_grsec_server_3_14 = self.grPackage kernelPatches.grsecurity_3_14 self.grFlavors.server;
linuxPackages_grsec_server_xen_3_14 = self.grPackage kernelPatches.grsecurity_3_14 self.grFlavors.server_xen;
linux_grsec_testing_desktop = self.linux_grsec_desktop_latest;
linux_grsec_testing_server = self.linux_grsec_server_latest;
linux_grsec_testing_server_xen = self.linux_grsec_server_xen_latest;
linuxPackages_grsec_desktop_4_4 = self.grPackage kernelPatches.grsecurity_4_4 self.grFlavors.desktop;
linuxPackages_grsec_server_4_4 = self.grPackage kernelPatches.grsecurity_4_4 self.grFlavors.server;
linuxPackages_grsec_server_xen_4_4 = self.grPackage kernelPatches.grsecurity_4_4 self.grFlavors.server_xen;
linuxPackages_grsec_testing_desktop = self.linuxPackages_grsec_desktop_latest;
linuxPackages_grsec_testing_server = self.linuxPackages_grsec_server_latest;
linuxPackages_grsec_testing_server_xen = self.linuxPackages_grsec_server_xen_latest;
linuxPackages_grsec_stable_desktop = self.linuxPackages_grsec_desktop_3_14;
linuxPackages_grsec_stable_server = self.linuxPackages_grsec_server_3_14;
linuxPackages_grsec_stable_server_xen = self.linuxPackages_grsec_server_xen_3_14;
# ChromiumOS kernels
linuxPackages_chromiumos_3_14 = recurseIntoAttrs (self.linuxPackagesFor self.linux_chromiumos_3_14 linuxPackages_chromiumos_3_14);