about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorJoachim Fasting <joachifm@users.noreply.github.com>2016-06-14 03:52:50 +0200
committerGitHub <noreply@github.com>2016-06-14 03:52:50 +0200
commit886c03ad2ec5fed59831bd552d34ba03327f2ac7 (patch)
treec201f8766bb9e8b97ada0f1852cfcb99258ea8ae /nixos
parent3123c7df37ac2fa69bc3fa4c561ac94d529d2fc5 (diff)
parent7bda8f0a8fc38e3cccd565521342ec9aaeffb297 (diff)
downloadnixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar.gz
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar.bz2
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar.lz
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar.xz
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.tar.zst
nixlib-886c03ad2ec5fed59831bd552d34ba03327f2ac7.zip
Merge pull request #16107 from joachifm/grsec-ng
Rework grsecurity support
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/rename.nix20
-rw-r--r--nixos/modules/security/grsecurity.nix376
-rw-r--r--nixos/tests/grsecurity.nix40
5 files changed, 144 insertions, 296 deletions
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index e643b2d059b5..8231907d7999 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -468,7 +468,6 @@ in {
         home = "/root";
         shell = mkDefault cfg.defaultUserShell;
         group = "root";
-        extraGroups = [ "grsecurity" ];
         initialHashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
@@ -497,7 +496,6 @@ in {
       nixbld.gid = ids.gids.nixbld;
       utmp.gid = ids.gids.utmp;
       adm.gid = ids.gids.adm;
-      grsecurity.gid = ids.gids.grsecurity;
       input.gid = ids.gids.input;
     };
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index d421167c859c..61c49f07abbb 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -147,7 +147,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      #grsecurity = 121; # unused
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
@@ -396,7 +395,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      grsecurity = 121;
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 3440261c3965..634f91a275d3 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -114,6 +114,26 @@ with lib;
     (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ])
     (mkRemovedOptionModule [ "services" "iodined" "client" ])
 
+    # Grsecurity
+    (mkRemovedOptionModule [ "security" "grsecurity" "kernelPatch" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "mode" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "priority" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "system" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationConfig" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "hardwareVirtualisation" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationSoftware" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "sysctl" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootChmod" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootCaps" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyUSB" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProc" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProcWithGroup" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "unrestrictProcGid" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableRBAC" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableSimultConnect" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "verboseVersion" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "kernelExtraConfig" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ])
     (mkRemovedOptionModule [ "programs" "bash" "enable" ])
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index 3f24118ea1cb..9d0249820d5d 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -1,312 +1,122 @@
-{ config, lib, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 with lib;
 
 let
   cfg = config.security.grsecurity;
-
-  customGrsecPkg =
-    (import ../../../pkgs/build-support/grsecurity {
-      grsecOptions = cfg;
-      inherit pkgs lib;
-    }).grsecPackage;
+  grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
+
+  # Ascertain whether ZFS is required for booting the system; grsecurity is
+  # currently incompatible with ZFS, rendering the system unbootable.
+  zfsNeededForBoot = filter
+    (fs: (fs.neededForBoot
+          || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
+          && fs.fsType == "zfs")
+    (attrValues config.fileSystems) != [];
 in
-{
-  options = {
-    security.grsecurity = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable grsecurity support. This enables advanced exploit
-          hardening for the Linux kernel, and adds support for
-          administrative Role-Based Acess Control (RBAC) via
-          <literal>gradm</literal>. It also includes traditional
-          utilities for PaX.
-        '';
-      };
-
-      kernelPatch = mkOption {
-        type = types.attrs;
-        example = lib.literalExample "pkgs.kernelPatches.grsecurity_4_1";
-        description = ''
-          Grsecurity patch to use.
-        '';
-      };
-
-      config = {
-        mode = mkOption {
-          type = types.enum [ "auto" "custom" ];
-          default = "auto";
-          description = ''
-            grsecurity configuration mode. This specifies whether
-            grsecurity is auto-configured or otherwise completely
-            manually configured.
-          '';
-        };
-
-        priority = mkOption {
-          type = types.enum [ "security" "performance" ];
-          default = "security";
-          description = ''
-            grsecurity configuration priority. This specifies whether
-            the kernel configuration should emphasize speed or
-            security.
-          '';
-        };
-
-        system = mkOption {
-          type = types.enum [ "desktop" "server" ];
-          default = "desktop";
-          description = ''
-            grsecurity system configuration.
-          '';
-        };
 
-        virtualisationConfig = mkOption {
-          type = types.nullOr (types.enum [ "host" "guest" ]);
-          default = null;
-          description = ''
-            grsecurity virtualisation configuration. This specifies
-            the virtualisation role of the machine - that is, whether
-            it will be a virtual machine guest, a virtual machine
-            host, or neither.
-          '';
-        };
-
-        hardwareVirtualisation = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          example = true;
-          description = ''
-            grsecurity hardware virtualisation configuration. Set to
-            <literal>true</literal> if your machine supports hardware
-            accelerated virtualisation.
-          '';
-        };
-
-        virtualisationSoftware = mkOption {
-          type = types.nullOr (types.enum [ "kvm" "xen" "vmware" "virtualbox" ]);
-          default = null;
-          description = ''
-            Configure grsecurity for use with this virtualisation software.
-          '';
-        };
-
-        sysctl = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_SYSCTL y</literal>. If
-            enabled then grsecurity can be controlled using sysctl
-            (and turned off). You are advised to *never* enable this,
-            but if you do, make sure to always set the sysctl
-            <literal>kernel.grsecurity.grsec_lock</literal> to
-            non-zero as soon as all sysctl options are set. *THIS IS
-            EXTREMELY IMPORTANT*!
-          '';
-        };
-
-        denyChrootChmod = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_CHROOT_CHMOD
-            y</literal>. If enabled, this denies processes inside a
-            chroot from setting the suid or sgid bits using
-            <literal>chmod</literal> or <literal>fchmod</literal>.
-
-            By default this protection is disabled - it makes it
-            impossible to use Nix to build software on your system,
-            which is what most users want.
+{
+  options.security.grsecurity = {
+
+    enable = mkEnableOption "Grsecurity/PaX";
+
+    lockTunables = mkOption {
+      type = types.bool;
+      example = false;
+      default = true;
+      description = ''
+        Whether to automatically lock grsecurity tunables
+        (<option>boot.kernel.sysctl."kernel.grsecurity.*"</option>).  Disable
+        this to allow configuration of grsecurity features while the system is
+        running.  The lock can be manually engaged by activating the
+        <literal>grsec-lock</literal> service unit.
+      '';
+    };
 
-            If you are using NixOps to deploy your software to a
-            remote machine, you're encouraged to enable this as you
-            won't need to compile code.
-          '';
-        };
+  };
 
-        denyChrootCaps = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Whether to lower capabilities of all processes within a chroot,
-            preventing commands that require <literal>CAP_SYS_ADMIN</literal>.
+  config = mkIf cfg.enable {
 
-            This protection is disabled by default because it breaks
-            <literal>nixos-rebuild</literal>. Whenever possible, it is
-            highly recommended to enable this protection.
-          '';
-        };
+    # Allow the user to select a different package set, subject to the stated
+    # required kernel config
+    boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;
 
-        denyUSB = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERNSEC_DENYUSB y</literal>.
+    system.requiredKernelConfig = with config.lib.kernelConfig;
+      [ (isEnabled "GRKERNSEC")
+        (isEnabled "PAX")
+        (isYES "GRKERNSEC_SYSCTL")
+        (isYES "GRKERNSEC_SYSCTL_DISTRO")
+      ];
 
-            This enables a sysctl with name
-            <literal>kernel.grsecurity.deny_new_usb</literal>. Setting
-            its value to <literal>1</literal> will prevent any new USB
-            devices from being recognized by the OS.  Any attempted
-            USB device insertion will be logged.
+    # Crashing on an overflow in kernel land is user unfriendly and may prevent
+    # the system from booting, which is too severe for our use case.
+    boot.kernelParams = [ "pax_size_overflow_report_only" ];
 
-            This option is intended to be used against custom USB
-            devices designed to exploit vulnerabilities in various USB
-            device drivers.
-          '';
-        };
+    # Install PaX related utillities into the system profile.  Eventually, we
+    # also want to include gradm here.
+    environment.systemPackages = with pkgs; [ paxctl pax-utils ];
 
-        restrictProc = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USER
-            y</literal>. This restricts non-root users to only viewing
-            their own processes and restricts network-related
-            information, kernel symbols, and module information.
-          '';
-        };
+    # Install rules for the grsec device node
+    services.udev.packages = [ pkgs.gradm ];
 
-        restrictProcWithGroup = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USERGROUP
-            y</literal>. This is similar to
-            <literal>restrictProc</literal> except it allows a special
-            group (specified by <literal>unrestrictProcGid</literal>)
-            to still access otherwise classified information in
-            <literal>/proc</literal>.
-          '';
-        };
+    # This service unit is responsible for locking the Grsecurity tunables.  The
+    # unit is always defined, but only activated on bootup if lockTunables is
+    # toggled.  When lockTunables is toggled, failure to activate the unit will
+    # enter emergency mode.  The intent is to make it difficult to silently
+    # enter multi-user mode without having locked the tunables.  Some effort is
+    # made to ensure that starting the unit is an idempotent operation.
+    systemd.services.grsec-lock = {
+      description = "Lock grsecurity tunables";
 
-        unrestrictProcGid = mkOption {
-          type = types.int;
-          default = config.ids.gids.grsecurity;
-          description = ''
-            If set, specifies a GID which is exempt from
-            <literal>/proc</literal> restrictions (set by
-            <literal>GRKERN_PROC_USERGROUP</literal>). By default,
-            this is set to the GID for <literal>grsecurity</literal>,
-            a predefined NixOS group, which the
-            <literal>root</literal> account is a member of. You may
-            conveniently add other users to this group if you need
-            access to <literal>/proc</literal>
-          '';
-        };
+      wantedBy = optional cfg.lockTunables "multi-user.target";
 
-        disableRBAC = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_NO_RBAC
-            y</literal>. This disables the
-            <literal>/dev/grsec</literal> device, which in turn
-            disables the RBAC system (and <literal>gradm</literal>).
-          '';
-        };
+      wants = [ "local-fs.target" "systemd-sysctl.service" ];
+      after = [ "local-fs.target" "systemd-sysctl.service" ];
+      conflicts = [ "shutdown.target" ];
 
-        disableSimultConnect = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Disable TCP simultaneous connect.  The TCP simultaneous connect
-            feature allows two clients to connect without either of them
-            entering the listening state.  This feature of the TCP specification
-            is claimed to enable an attacker to deny the target access to a given
-            server by guessing the source port the target would use to make the
-            connection.
+      restartIfChanged = false;
 
-            This option is OFF by default because TCP simultaneous connect has
-            some legitimate uses.  Enable this option if you know what this TCP
-            feature is for and know that you do not need it.
-          '';
-        };
+      script = ''
+        if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
+          echo -n 1 > ${grsecLockPath}
+        fi
+      '';
 
-        verboseVersion = mkOption {
-          type = types.bool;
-          default = false;
-          description = "Use verbose version in kernel localversion.";
-        };
+      unitConfig = {
+        ConditionPathIsReadWrite = grsecLockPath;
+        DefaultDependencies = false;
+      } // optionalAttrs cfg.lockTunables {
+        OnFailure = "emergency.target";
+      };
 
-        kernelExtraConfig = mkOption {
-          type = types.str;
-          default = "";
-          description = "Extra kernel configuration parameters.";
-        };
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
       };
     };
-  };
 
-  config = mkIf cfg.enable {
-    assertions =
-      [
-        { assertion = (cfg.config.restrictProc -> !cfg.config.restrictProcWithGroup) ||
-                      (cfg.config.restrictProcWithGroup -> !cfg.config.restrictProc);
-          message   = "You cannot enable both restrictProc and restrictProcWithGroup";
-        }
-        { assertion = config.boot.kernelPackages.kernel.features ? grsecurity
-                   && config.boot.kernelPackages.kernel.features.grsecurity;
-          message = "grsecurity enabled, but kernel doesn't have grsec support";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.hardwareVirtualisation != null;
-          message   = "when using auto grsec mode with virtualisation, you must specify if your hardware has virtualisation extensions";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.virtualisationSoftware != null;
-         message   = "grsecurity configured for virtualisation but no virtualisation software specified";
-        }
-      ];
-
-    security.grsecurity.kernelPatch = lib.mkDefault pkgs.kernelPatches.grsecurity_latest;
-
-    systemd.services.grsec-lock = mkIf cfg.config.sysctl {
-      description     = "grsecurity sysctl-lock Service";
-      wants           = [ "systemd-sysctl.service" ];
-      after           = [ "systemd-sysctl.service" ];
-      wantedBy        = [ "multi-user.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = "yes";
-      unitConfig.ConditionPathIsReadWrite = "/proc/sys/kernel/grsecurity/grsec_lock";
-      script = ''
-        locked=`cat /proc/sys/kernel/grsecurity/grsec_lock`
-        if [ "$locked" == "0" ]; then
-            echo 1 > /proc/sys/kernel/grsecurity/grsec_lock
-            echo grsecurity sysctl lock - enabled
-        else
-            echo grsecurity sysctl lock already enabled - doing nothing
-        fi
-      '';
+    # Configure system tunables
+    boot.kernel.sysctl = {
+      # Removed under grsecurity
+      "kernel.kptr_restrict" = mkForce null;
+    } // optionalAttrs config.nix.useSandbox {
+      # chroot(2) restrictions that conflict with sandboxed Nix builds
+      "kernel.grsecurity.chroot_caps" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
+    } // optionalAttrs config.boot.enableContainers {
+      # chroot(2) restrictions that conflict with NixOS lightweight containers
+      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
     };
 
-#   systemd.services.grsec-learn = {
-#     description     = "grsecurity learning Service";
-#     wantedBy        = [ "local-fs.target" ];
-#     serviceConfig   = {
-#       Type = "oneshot";
-#       RemainAfterExit = "yes";
-#       ExecStart = "${pkgs.gradm}/sbin/gradm -VFL /etc/grsec/learning.logs";
-#       ExecStop  = "${pkgs.gradm}/sbin/gradm -D";
-#     };
-#   };
-
-    system.activationScripts = lib.optionalAttrs (!cfg.config.disableRBAC) { grsec = ''
-      mkdir -p /etc/grsec
-      if [ ! -f /etc/grsec/learn_config ]; then
-        cp ${pkgs.gradm}/etc/grsec/learn_config /etc/grsec
-      fi
-      if [ ! -f /etc/grsec/policy ]; then
-        cp ${pkgs.gradm}/etc/grsec/policy /etc/grsec
-      fi
-      chmod -R 0600 /etc/grsec
-    ''; };
+    assertions = [
+      { assertion = !zfsNeededForBoot;
+        message = "grsecurity is currently incompatible with ZFS";
+      }
+    ];
 
-    # Enable AppArmor, gradm udev rules, and utilities
-    security.apparmor.enable   = true;
-    boot.kernelPackages        = customGrsecPkg;
-    services.udev.packages     = lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
-    environment.systemPackages = [ pkgs.paxctl pkgs.pax-utils ] ++ lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
   };
 }
diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix
index 14f1aa9ff885..aadbfd8371ff 100644
--- a/nixos/tests/grsecurity.nix
+++ b/nixos/tests/grsecurity.nix
@@ -3,17 +3,39 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "grsecurity";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ copumpkin ];
+    maintainers = [ copumpkin joachifm ];
   };
 
   machine = { config, pkgs, ... }:
-    { boot.kernelPackages = pkgs.linuxPackages_grsec_testing_server; };
+    { security.grsecurity.enable = true;
+      boot.kernel.sysctl."kernel.grsecurity.deter_bruteforce" = 0;
+      security.apparmor.enable = true;
+    };
 
-  testScript =
-    ''
-      $machine->succeed("uname -a") =~ /grsec/;
-      # FIXME: this seems to hang the whole test. Unclear why, but let's fix it
-      # $machine->succeed("${pkgs.paxtest}/bin/paxtest blackhat");
-    '';
-})
+  testScript = ''
+    subtest "grsec-lock", sub {
+      $machine->succeed("systemctl is-active grsec-lock");
+      $machine->succeed("grep -Fq 1 /proc/sys/kernel/grsecurity/grsec_lock");
+      $machine->fail("echo -n 0 >/proc/sys/kernel/grsecurity/grsec_lock");
+    };
+
+    subtest "paxtest", sub {
+      # TODO: running paxtest blackhat hangs the vm
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/anonmap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execstack") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotanon") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotstack") =~ /Killed/ or die;
+    };
 
+    subtest "tcc", sub {
+      $machine->execute("echo -e '#include <stdio.h>\nint main(void) { puts(\"hello\"); return 0; }' >main.c");
+      $machine->succeed("${pkgs.tinycc.bin}/bin/tcc -run main.c");
+    };
+  '';
+})