NixOS Hardening: Difference between revisions
→Sysctl parameters: Add kernel.io_uring_disabled=2 |
→linux-hardened: Update to config that is compatible with Binary Cache |
||
| (7 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
== Kernel == | |||
=== linux-hardened === | === linux-hardened === | ||
[https://github.com/anthraxx/linux-hardened linux-hardened] is a Linux kernel with additional hardening patches applied. You can check for latest releases [https://github.com/anthraxx/linux-hardened/releases here]. | [https://github.com/anthraxx/linux-hardened linux-hardened] is a Linux kernel with additional hardening patches applied. It can be built from source, but the kernel must be kept up to date to receive security patches. [[Binary Cache]] can be used to distribute compiled kernel binaries to multiple computers. You can check for latest releases [https://github.com/anthraxx/linux-hardened/releases here]. | ||
linux-hardened.nix: | |||
<syntaxhighlight lang="nix">{ pkgs, lib }: | |||
structuredExtraConfig = with lib.kernel; { | let | ||
linux_hardened = pkgs.callPackage ({ fetchFromGitHub, buildLinux, ... }: | |||
buildLinux rec { | |||
version = "6.18.44-hardened1"; | |||
extraMeta.branch = "6.18"; | |||
hash = "sha256-ncV1k+cLKfWV36oCAkmKb8LnHRJTe6M3IByfcDi8VLI="; | |||
src = fetchFromGitHub { | |||
inherit hash; | |||
owner = "anthraxx"; | |||
repo = "linux-hardened"; | |||
tag = "v${version}"; | |||
}; | |||
kernelPatches = []; | |||
structuredExtraConfig = with lib.kernel; { | |||
# Perform additional validation of commonly targeted structures. | |||
DEBUG_NOTIFIERS = yes; | |||
DEBUG_PLIST = yes; | |||
DEBUG_SG = yes; | |||
DEBUG_VIRTUAL = yes; | |||
SCHED_STACK_END_CHECK = yes; | |||
# tell EFI to wipe memory during reset | |||
# https://lwn.net/Articles/730006/ | |||
RESET_ATTACK_MITIGATION = yes; | |||
# restricts loading of line disciplines via TIOCSETD ioctl to CAP_SYS_MODULE | |||
CONFIG_LDISC_AUTOLOAD = option no; | |||
# Enable init_on_free by default | |||
INIT_ON_FREE_DEFAULT_ON = yes; | |||
# Initialize all stack variables on function entry | |||
INIT_STACK_ALL_ZERO = yes; | |||
# Wipe all caller-used registers on exit from a function | |||
ZERO_CALL_USED_REGS = yes; | |||
# Enable the SafeSetId LSM | |||
SECURITY_SAFESETID = yes; | |||
# Reboot devices immediately if kernel experiences an Oops. | |||
PANIC_TIMEOUT = freeform "-1"; | |||
# Enable gcc plugin options | |||
GCC_PLUGINS = yes; | |||
# Runtime undefined behaviour checks | |||
# https://www.kernel.org/doc/html/latest/dev-tools/ubsan.html | |||
# https://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan | |||
UBSAN = yes; | |||
UBSAN_TRAP = yes; | |||
UBSAN_BOUNDS = yes; | |||
UBSAN_LOCAL_BOUNDS = option yes; # clang only | |||
CFI_CLANG = option yes; # clang only Control Flow Integrity since 6.1 | |||
# Disable various dangerous settings | |||
PROC_KCORE = no; # Exposes kernel text image layout | |||
INET_DIAG = no; # Has been used for heap based attacks in the past | |||
# INET_DIAG=n causes the following options to not exist anymore, but since they are defined in common-config.nix, | |||
# make them optional | |||
INET_DIAG_DESTROY = option no; | |||
INET_RAW_DIAG = option no; | |||
INET_TCP_DIAG = option no; | |||
INET_UDP_DIAG = option no; | |||
INET_MPTCP_DIAG = option no; | |||
# CONFIG_DEVMEM=n causes these to not exist anymore. | |||
STRICT_DEVMEM = option no; | |||
IO_STRICT_DEVMEM = option no; | |||
# stricter IOMMU TLB invalidation | |||
IOMMU_DEFAULT_DMA_STRICT = option yes; | |||
IOMMU_DEFAULT_DMA_LAZY = option no; | |||
# not needed for less than a decade old glibc versions | |||
LEGACY_VSYSCALL_NONE = yes; | |||
}; | |||
modDirVersion = version; | |||
} | |||
) {}; | |||
in | |||
} | pkgs.linuxPackagesFor linux_hardened</syntaxhighlight>configuration.nix:<syntaxhighlight lang="nix"> | ||
boot.kernelPackages = let | |||
pkgs = import <nixpkgs> {}; | |||
in | in | ||
import ./linux-hardened.nix { inherit pkgs lib; }; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Lock kernel modules === | === 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.<syntaxhighlight lang="nix"> | This option locks kernel modules after the system is initialized. For example it prevents malicious USB devices from exploiting vulnerable kernel modules. | ||
<syntaxhighlight lang="nix"> | |||
security.lockKernelModules = true; | security.lockKernelModules = true; | ||
</syntaxhighlight>All needed modules must be loaded at boot by adding them to <code>boot.kernelModules</code>. One way of knowing what modules must be enabled is to disable this option and then list all enabled modules with <code>lsmod</code>.<syntaxhighlight lang="nix"> | </syntaxhighlight> | ||
All needed modules must be loaded at boot by adding them to <code>boot.kernelModules</code>. One way of knowing what modules must be enabled is to disable this option and then list all enabled modules with <code>lsmod</code>. | |||
<syntaxhighlight lang="nix"> | |||
boot.kernelModules = [ | boot.kernelModules = [ | ||
# USB | # USB | ||
| Line 107: | Line 117: | ||
]; | ]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Module blacklist === | === Module blacklist === | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
boot.blacklistedKernelModules = [ | boot.blacklistedKernelModules = [ | ||
| Line 131: | Line 143: | ||
=== Kernel image protection === | === Kernel image protection === | ||
Prevents replacing the running kernel image.<syntaxhighlight lang="nix"> | Prevents replacing the running kernel image. | ||
<syntaxhighlight lang="nix"> | |||
security.protectKernelImage = true; | security.protectKernelImage = true; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Kernel parameters === | === Kernel parameters === | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
boot.kernelParams = [ | boot.kernelParams = [ | ||
| Line 153: | Line 168: | ||
=== Sysctl parameters === | === Sysctl parameters === | ||
<syntaxhighlight lang="nix"># Hide kptrs even for processes with CAP_SYSLOG | |||
<syntaxhighlight lang="nix"> | |||
# Hide kptrs even for processes with CAP_SYSLOG | |||
boot.kernel.sysctl."kernel.kptr_restrict" = "2"; | boot.kernel.sysctl."kernel.kptr_restrict" = "2"; | ||
| Line 188: | Line 205: | ||
# Ignore outgoing ICMP redirects (this is ipv4 only) | # Ignore outgoing ICMP redirects (this is ipv4 only) | ||
boot.kernel.sysctl."net.ipv4.conf.all.send_redirects" = false; | boot.kernel.sysctl."net.ipv4.conf.all.send_redirects" = false; | ||
boot.kernel.sysctl."net.ipv4.conf.default.send_redirects" = false;</syntaxhighlight> | boot.kernel.sysctl."net.ipv4.conf.default.send_redirects" = false; | ||
</syntaxhighlight> | |||
=== Disable Simultaneous Multithreading (SMT) === | === Disable Simultaneous Multithreading (SMT) === | ||
Might cause significant performance cost.<syntaxhighlight lang="nix"> | |||
Might cause significant performance cost. | |||
<syntaxhighlight lang="nix"> | |||
security.allowSimultaneousMultithreading = false; | security.allowSimultaneousMultithreading = false; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Force Page Table Isolation === | === Force Page Table Isolation === | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
security.forcePageTableIsolation = true; | security.forcePageTableIsolation = true; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Nix settings == | |||
=== Nix allowed users === | |||
This option allows only <code>users</code> group to connect to the Nix daemon. | |||
<syntaxhighlight lang="nix"> | |||
nix.settings.allowed-users = [ "@users" ]; | |||
</syntaxhighlight> | |||
== Other settings == | |||
=== Memory allocator === | === Memory allocator === | ||
You can use security-focused memory allocator like [https://llvm.org/docs/ScudoHardenedAllocator.html scudo] or [https://github.com/GrapheneOS/hardened_malloc GrapheneOS hardened_malloc].<syntaxhighlight lang="nix"> | |||
You can use security-focused memory allocator like [https://llvm.org/docs/ScudoHardenedAllocator.html scudo] or [https://github.com/GrapheneOS/hardened_malloc GrapheneOS hardened_malloc]. | |||
<syntaxhighlight lang="nix"> | |||
# scudo | # scudo | ||
environment.memoryAllocator.provider = "scudo"; | environment.memoryAllocator.provider = "scudo"; | ||
environment.variables.SCUDO_OPTIONS = "zero_contents=true"; | environment.variables.SCUDO_OPTIONS = "zero_contents=true"; | ||
# hardened_malloc | # hardened_malloc | ||
environment.memoryAllocator.provider = "graphene-hardened"; | environment.memoryAllocator.provider = "graphene-hardened"; | ||
</syntaxhighlight>Some programs may not work with these memory allocators. You can force them to use the default libc allocator by blacklisting <code>/etc/ld-nix.so.preload</code> with a firejail wrap.<syntaxhighlight lang="nix">programs.firejail = { | </syntaxhighlight> | ||
Some programs may not work with these memory allocators. You can force them to use the default libc allocator by blacklisting <code>/etc/ld-nix.so.preload</code> with a firejail wrap. | |||
<syntaxhighlight lang="nix"> | |||
programs.firejail = { | |||
enable = true; | enable = true; | ||
wrappedBinaries = { | wrappedBinaries = { | ||
| Line 218: | Line 262: | ||
}; | }; | ||
}; | }; | ||
} | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Flush L1 data cache === | === Flush L1 data cache === | ||
Might cause significant performance cost.<syntaxhighlight lang="nix"> | |||
Might cause significant performance cost. | |||
<syntaxhighlight lang="nix"> | |||
security.virtualisation.flushL1DataCache = "always"; | security.virtualisation.flushL1DataCache = "always"; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 237: | Line 280: | ||
security.apparmor.killUnconfinedConfinables = true; | security.apparmor.killUnconfinedConfinables = true; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Lower-level == | |||
=== Secure Boot === | === Secure Boot === | ||
See [[Secure Boot]]. [[Limine]] bootloader supports coreboot's Secure Boot. | See [[Secure Boot]]. [[Limine]] bootloader supports coreboot's Secure Boot. | ||
== See Also == | |||
* [https://wiki.archlinux.org/title/Security Arch Wiki Security Page] | |||
[[Category:Guide]] | [[Category:Guide]] | ||
[[Category:NixOS]] | [[Category:NixOS]] | ||
[[Category:Security]] | [[Category:Security]] | ||
Revision as of 19:48, 16 August 2026
Kernel
linux-hardened
linux-hardened is a Linux kernel with additional hardening patches applied. It can be built from source, but the kernel must be kept up to date to receive security patches. Binary Cache can be used to distribute compiled kernel binaries to multiple computers. You can check for latest releases here.
linux-hardened.nix:
{ pkgs, lib }:
let
linux_hardened = pkgs.callPackage ({ fetchFromGitHub, buildLinux, ... }:
buildLinux rec {
version = "6.18.44-hardened1";
extraMeta.branch = "6.18";
hash = "sha256-ncV1k+cLKfWV36oCAkmKb8LnHRJTe6M3IByfcDi8VLI=";
src = fetchFromGitHub {
inherit hash;
owner = "anthraxx";
repo = "linux-hardened";
tag = "v${version}";
};
kernelPatches = [];
structuredExtraConfig = with lib.kernel; {
# Perform additional validation of commonly targeted structures.
DEBUG_NOTIFIERS = yes;
DEBUG_PLIST = yes;
DEBUG_SG = yes;
DEBUG_VIRTUAL = yes;
SCHED_STACK_END_CHECK = yes;
# tell EFI to wipe memory during reset
# https://lwn.net/Articles/730006/
RESET_ATTACK_MITIGATION = yes;
# restricts loading of line disciplines via TIOCSETD ioctl to CAP_SYS_MODULE
CONFIG_LDISC_AUTOLOAD = option no;
# Enable init_on_free by default
INIT_ON_FREE_DEFAULT_ON = yes;
# Initialize all stack variables on function entry
INIT_STACK_ALL_ZERO = yes;
# Wipe all caller-used registers on exit from a function
ZERO_CALL_USED_REGS = yes;
# Enable the SafeSetId LSM
SECURITY_SAFESETID = yes;
# Reboot devices immediately if kernel experiences an Oops.
PANIC_TIMEOUT = freeform "-1";
# Enable gcc plugin options
GCC_PLUGINS = yes;
# Runtime undefined behaviour checks
# https://www.kernel.org/doc/html/latest/dev-tools/ubsan.html
# https://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan
UBSAN = yes;
UBSAN_TRAP = yes;
UBSAN_BOUNDS = yes;
UBSAN_LOCAL_BOUNDS = option yes; # clang only
CFI_CLANG = option yes; # clang only Control Flow Integrity since 6.1
# Disable various dangerous settings
PROC_KCORE = no; # Exposes kernel text image layout
INET_DIAG = no; # Has been used for heap based attacks in the past
# INET_DIAG=n causes the following options to not exist anymore, but since they are defined in common-config.nix,
# make them optional
INET_DIAG_DESTROY = option no;
INET_RAW_DIAG = option no;
INET_TCP_DIAG = option no;
INET_UDP_DIAG = option no;
INET_MPTCP_DIAG = option no;
# CONFIG_DEVMEM=n causes these to not exist anymore.
STRICT_DEVMEM = option no;
IO_STRICT_DEVMEM = option no;
# stricter IOMMU TLB invalidation
IOMMU_DEFAULT_DMA_STRICT = option yes;
IOMMU_DEFAULT_DMA_LAZY = option no;
# not needed for less than a decade old glibc versions
LEGACY_VSYSCALL_NONE = yes;
};
modDirVersion = version;
}
) {};
in
pkgs.linuxPackagesFor linux_hardened
configuration.nix:
boot.kernelPackages = let
pkgs = import <nixpkgs> {};
in
import ./linux-hardened.nix { inherit pkgs lib; };
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;
# Disable io_uring, a large source of security vulnerabilities
# https://security.googleblog.com/2023/06/learnings-from-kctf-vrps-42-linux.html
boot.kernel.sysctl."kernel.io_uring_disabled" = 2;
# 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;
Nix settings
Nix allowed users
This option allows only users group to connect to the Nix daemon.
nix.settings.allowed-users = [ "@users" ];
Other settings
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"
];
};
};
};
Flush L1 data cache
Might cause significant performance cost.
security.virtualisation.flushL1DataCache = "always";
AppArmor
See Security#AppArmor for more details.
security.apparmor.enable = true;
security.apparmor.killUnconfinedConfinables = true;
Lower-level
Secure Boot
See Secure Boot. Limine bootloader supports coreboot's Secure Boot.