summary refs log tree commit diff
path: root/nixos/modules/security/grsecurity.nix
blob: 60e9058dd69e225feb2b945a0f941a7555920fba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{ config, pkgs, lib, ... }:

with lib;

let
  cfg = config.security.grsecurity;
  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) != [];

  # Ascertain whether NixOS container support is required
  containerSupportRequired =
    config.boot.enableContainers && config.containers != {};
in

{
  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 runtime configuration of grsecurity features.  Activate
        the <literal>grsec-lock</literal> service unit to prevent further
        configuration until the next reboot.
      '';
    };

    disableEfiRuntimeServices = mkOption {
      type = types.bool;
      example = false;
      default = true;
      description = ''
        Whether to disable access to EFI runtime services.  Enabling EFI runtime
        services creates a venue for code injection attacks on the kernel and
        should be disabled if at all possible.  Changing this option enters into
        effect upon reboot.
      '';
    };

  };

  config = mkIf cfg.enable {

    # Allow the user to select a different package set, subject to the stated
    # required kernel config
    boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;

    boot.kernelParams = optional cfg.disableEfiRuntimeServices "noefi";

    system.requiredKernelConfig = with config.lib.kernelConfig;
      [ (isEnabled "GRKERNSEC")
        (isEnabled "PAX")
        (isYES "GRKERNSEC_SYSCTL")
        (isYES "GRKERNSEC_SYSCTL_DISTRO")
        (isNO "GRKERNSEC_NO_RBAC")
      ];

    # Install PaX related utillities into the system profile.
    environment.systemPackages = with pkgs; [ gradm paxctl pax-utils ];

    # Install rules for the grsec device node
    services.udev.packages = [ pkgs.gradm ];

    # 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";

      wantedBy = optional cfg.lockTunables "multi-user.target";

      wants = [ "local-fs.target" "systemd-sysctl.service" ];
      after = [ "local-fs.target" "systemd-sysctl.service" ];
      conflicts = [ "shutdown.target" ];

      restartIfChanged = false;

      script = ''
        if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
          echo -n 1 > ${grsecLockPath}
        fi
      '';

      unitConfig = {
        ConditionPathIsReadWrite = grsecLockPath;
        DefaultDependencies = false;
      } // optionalAttrs cfg.lockTunables {
        OnFailure = "emergency.target";
      };

      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
    };

    # Configure system tunables
    boot.kernel.sysctl = {
      # Read-only 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 containerSupportRequired {
      # 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;
    };

    assertions = [
      { assertion = !zfsNeededForBoot;
        message = "grsecurity is currently incompatible with ZFS";
      }
    ];

  };
}