1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.security.grsecurity;
7 grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
8
9 # Ascertain whether ZFS is required for booting the system; grsecurity is
10 # currently incompatible with ZFS, rendering the system unbootable.
11 zfsNeededForBoot = filter
12 (fs: (fs.neededForBoot
13 || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
14 && fs.fsType == "zfs")
15 config.system.build.fileSystems != [];
16
17 # Ascertain whether NixOS container support is required
18 containerSupportRequired =
19 config.boot.enableContainers && config.containers != {};
20in
21
22{
23 meta = {
24 maintainers = with maintainers; [ joachifm ];
25 doc = ./grsecurity.xml;
26 };
27
28 options.security.grsecurity = {
29
30 enable = mkEnableOption "grsecurity/PaX";
31
32 lockTunables = mkOption {
33 type = types.bool;
34 example = false;
35 default = true;
36 description = ''
37 Whether to automatically lock grsecurity tunables
38 (<option>boot.kernel.sysctl."kernel.grsecurity.*"</option>). Disable
39 this to allow runtime configuration of grsecurity features. Activate
40 the <literal>grsec-lock</literal> service unit to prevent further
41 configuration until the next reboot.
42 '';
43 };
44
45 disableEfiRuntimeServices = mkOption {
46 type = types.bool;
47 example = false;
48 default = true;
49 description = ''
50 Whether to disable access to EFI runtime services. Enabling EFI runtime
51 services creates a venue for code injection attacks on the kernel and
52 should be disabled if at all possible. Changing this option enters into
53 effect upon reboot.
54 '';
55 };
56
57 };
58
59 config = mkIf cfg.enable {
60
61 # Allow the user to select a different package set, subject to the stated
62 # required kernel config
63 boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;
64
65 boot.kernelParams = optional cfg.disableEfiRuntimeServices "noefi";
66
67 system.requiredKernelConfig = with config.lib.kernelConfig;
68 [ (isEnabled "GRKERNSEC")
69 (isEnabled "PAX")
70 (isYES "GRKERNSEC_SYSCTL")
71 (isYES "GRKERNSEC_SYSCTL_DISTRO")
72 (isNO "GRKERNSEC_NO_RBAC")
73 ];
74
75 # Install PaX related utillities into the system profile.
76 environment.systemPackages = with pkgs; [ gradm paxctl pax-utils ];
77
78 # Install rules for the grsec device node
79 services.udev.packages = [ pkgs.gradm ];
80
81 # This service unit is responsible for locking the grsecurity tunables. The
82 # unit is always defined, but only activated on bootup if lockTunables is
83 # toggled. When lockTunables is toggled, failure to activate the unit will
84 # enter emergency mode. The intent is to make it difficult to silently
85 # enter multi-user mode without having locked the tunables. Some effort is
86 # made to ensure that starting the unit is an idempotent operation.
87 systemd.services.grsec-lock = {
88 description = "Lock grsecurity tunables";
89
90 wantedBy = optional cfg.lockTunables "multi-user.target";
91
92 wants = [ "local-fs.target" "systemd-sysctl.service" ];
93 after = [ "local-fs.target" "systemd-sysctl.service" ];
94 conflicts = [ "shutdown.target" ];
95
96 restartIfChanged = false;
97
98 script = ''
99 if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
100 echo -n 1 > ${grsecLockPath}
101 fi
102 '';
103
104 unitConfig = {
105 ConditionPathIsReadWrite = grsecLockPath;
106 DefaultDependencies = false;
107 } // optionalAttrs cfg.lockTunables {
108 OnFailure = "emergency.target";
109 };
110
111 serviceConfig = {
112 Type = "oneshot";
113 RemainAfterExit = true;
114 };
115 };
116
117 # Configure system tunables
118 boot.kernel.sysctl = {
119 # Read-only under grsecurity
120 "kernel.kptr_restrict" = mkForce null;
121 } // optionalAttrs config.nix.useSandbox {
122 # chroot(2) restrictions that conflict with sandboxed Nix builds
123 "kernel.grsecurity.chroot_caps" = mkForce 0;
124 "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
125 "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
126 "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
127 "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
128 } // optionalAttrs containerSupportRequired {
129 # chroot(2) restrictions that conflict with NixOS lightweight containers
130 "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
131 "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
132 "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
133 "kernel.grsecurity.chroot_caps" = mkForce 0;
134 };
135
136 assertions = [
137 { assertion = !zfsNeededForBoot;
138 message = "grsecurity is currently incompatible with ZFS";
139 }
140 ];
141
142 };
143}