NixOS Hardening
linux-hardened
linux-hardened is a Linux kernel with additional hardening patches applied.
boot.kernelPackages = pkgs.linuxKernel.packages.linux_hardened;
To get the latest updates and security patches as soon as possible, you might want to build the kernel right after new release.
boot.kernelPackages = let
linux_hardened_pkg = { fetchFromGitHub, buildLinux, linux_6_12_hardened, ... } @ args:
buildLinux (args // rec {
version = "6.12.77-hardened1";
modDirVersion = version;
extraMeta.branch = "6.12";
src = fetchFromGitHub {
owner = "anthraxx";
repo = "linux-hardened";
tag = "v${version}";
hash = "sha256-txcatuTkp0gmJ4vHp//Ju4/j9d2RiVU8UuE7zUXnixw=";
};
# Patches are already applied in the source tarball
kernelPatches = [];
structuredExtraConfig = linux_6_12_hardened.structuredExtraConfig;
# If using different kernel version than the one used in nixpkgs, you might have to remove some unsupported parameters.
structuredExtraConfig = lib.removeAttrs linux_6_12_hardened.structuredExtraConfig [ "GCC_PLUGIN_STACKLEAK" ];
} // (args.argsOverride or {}));
linux_hardened = pkgs.callPackage linux_hardened_pkg{};
in
lib.recurseIntoAttrs (pkgs.linuxPackagesFor linux_hardened);
Lock kernel modules
This option locks kernel modules after the system is initialized. For example it prevents malicious USB devices from exploiting vulnerable kernel modules.
security.lockKernelModules = true;
All needed modules must be loaded at boot by adding them to boot.kernelModules. One way of knowing what modules must be enabled is to disable this option and then list all enabled modules with lsmod.
boot.kernelModules = [
# USB
"usb_storage" "uinput" "usbhid" "usbserial"
# DVD
"udf" "iso9660"
# GPU
"amdgpu" "i915"
# Networking
"nft_chain_nat" "xt_conntrack" "xt_CHECKSUM" "xt_MASQUERADE" "ipt_REJECT" "ip6t_REJECT" "nf_reject_ipv4" "nf_reject_ipv6" "xt_mark" "xt_comment" "xt_multiport" "xt_addrtype" "xt_connmark" "nf_conntrack_netlink"
];
Module blacklist
boot.blacklistedKernelModules = [
# Obscure network protocols
"ax25" "netrom" "rose"
# Old or rare or insufficiently audited filesystems
"adfs" "affs"
"bfs" "befs"
"cramfs"
"efs" "erofs" "exofs"
"freevxfs" "f2fs"
"hfs" "hpfs"
"jfs"
"minix"
"nilfs2" "ntfs"
"omfs"
"qnx4" "qnx6"
"sysv"
"ufs"
];
Kernel image protection
Prevents replacing the running kernel image.
security.protectKernelImage = true;
Kernel parameters
boot.kernelParams = [
# Don't merge slabs
"slab_nomerge"
# Overwrite free'd pages
"page_poison=1"
# Enable page allocator randomization
"page_alloc.shuffle=1"
# Disable debugfs
"debugfs=off"
];
Sysctl parameters
# Hide kptrs even for processes with CAP_SYSLOG
boot.kernel.sysctl."kernel.kptr_restrict" = "2";
# Disable bpf() JIT (to eliminate spray attacks)
boot.kernel.sysctl."net.core.bpf_jit_enable" = false;
# Disable ftrace debugging
boot.kernel.sysctl."kernel.ftrace_enabled" = false;
# Enable strict reverse path filtering (that is, do not attempt to route
# packets that "obviously" do not belong to the iface's network; dropped
# packets are logged as martians).
boot.kernel.sysctl."net.ipv4.conf.all.log_martians" = true;
boot.kernel.sysctl."net.ipv4.conf.all.rp_filter" = "1";
boot.kernel.sysctl."net.ipv4.conf.default.log_martians" = true;
boot.kernel.sysctl."net.ipv4.conf.default.rp_filter" = "1";
# Ignore broadcast ICMP (mitigate SMURF)
boot.kernel.sysctl."net.ipv4.icmp_echo_ignore_broadcasts" = true;
# Ignore incoming ICMP redirects (note: default is needed to ensure that the
# setting is applied to interfaces added after the sysctls are set)
boot.kernel.sysctl."net.ipv4.conf.all.accept_redirects" = false;
boot.kernel.sysctl."net.ipv4.conf.all.secure_redirects" = false;
boot.kernel.sysctl."net.ipv4.conf.default.accept_redirects" = false;
boot.kernel.sysctl."net.ipv4.conf.default.secure_redirects" = false;
boot.kernel.sysctl."net.ipv6.conf.all.accept_redirects" = false;
boot.kernel.sysctl."net.ipv6.conf.default.accept_redirects" = false;
# Ignore outgoing ICMP redirects (this is ipv4 only)
boot.kernel.sysctl."net.ipv4.conf.all.send_redirects" = false;
boot.kernel.sysctl."net.ipv4.conf.default.send_redirects" = false;
Disable Simultaneous Multithreading (SMT)
Might cause significant performance cost.
security.allowSimultaneousMultithreading = false;
Force Page Table Isolation
security.forcePageTableIsolation = true;
Memory allocator
You can use security-focused memory allocator like scudo or GrapheneOS hardened_malloc.
# scudo
environment.memoryAllocator.provider = "scudo";
environment.variables.SCUDO_OPTIONS = "zero_contents=true";
# hardened_malloc
environment.memoryAllocator.provider = "graphene-hardened";
Some programs may not work with these memory allocators. You can force them to use the default libc allocator by blacklisting /etc/ld-nix.so.preload with a firejail wrap.
programs.firejail = {
enable = true;
wrappedBinaries = {
chromium = {
executable = "${pkgs.chromium}/bin/chromium-browser";
profile = "${pkgs.firejail}/etc/firejail/chromium-browser.profile";
extraArgs = [
"--blacklist=/etc/ld-nix.so.preload"
];
};
};
};
Nix allowed users
This option allows only users group to connect to the Nix daemon.
nix.settings.allowed-users = [ "@users" ];
Flush L1 data cache
Might cause significant performance cost.
security.virtualisation.flushL1DataCache = "always";
AppArmor
security.apparmor.enable = true;
security.apparmor.killUnconfinedConfinables = true;
Secure Boot
See Secure Boot. Limine bootloader supports coreboot's Secure Boot.