Files
nixos-blueprint/modules/shared/nixos/security/nix-mineral/nix-mineral.nix
2025-11-27 14:33:34 +02:00

1136 lines
42 KiB
Nix

# This file is part of nix-mineral (https://github.com/cynicsketch/nix-mineral/).
# Copyright (c) 2025 cynicsketch
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This is the main module for nix-mineral, containing the default configuration.
### CREDITS ###
# Please, see the README and give your thanks and support to the people and projects
# which have helped this project's development!
# sysctl omitted from K4YT3X config that are out of scope of nix-mineral and
# hardening but may be useful anyways to some, see their repo for details:
# kernel.core_uses_pid = 1
# kernel.pid_max = 4194304
# kernel.panic = 10
# fs.file-max = 9223372036854775807
# fs.inotify.max_user_watches = 524288
# net.core.netdev_max_backlog = 250000
# net.core.rmem_default = 8388608
# net.core.wmem_default = 8388608
# net.core.rmem_max = 536870912
# net.core.wmem_max = 536870912
# net.core.optmem_max = 40960
# net.ipv4.tcp_congestion_control = bbr
# net.ipv4.tcp_synack_retries = 5
# net.ipv4.ip_local_port_range = 1024 65535
# net.ipv4.tcp_slow_start_after_idle = 0
# net.ipv4.tcp_mtu_probing = 1
# net.ipv4.tcp_base_mss = 1024
# net.ipv4.tcp_rmem = 8192 262144 536870912
# net.ipv4.tcp_wmem = 4096 16384 536870912
# net.ipv4.tcp_adv_win_scale = -2
# net.ipv4.tcp_notsent_lowat = 131072
# Sections from madaidan's guide that are IRRELEVANT/NON-APPLICABLE:
# 1. (Advice)
# 2.1 (Advice)
# 2.3.3 (Advice)
# 2.5.1 (Advice)
# 2.5.3 (Advice)
# 2.6 (Advice)
# 2.10 (Package is broken)
# 7 (Advice)
# 10.5.4 (The problem of NTP being unencrypted is fixed by using NTS instead.
# Note that this means using chrony, as in "Software Choice" in the overrides,
# which is not default behavior!)
# 11 (Partially, there seems to be no way to edit the permissions of /boot
# whether with mount options or through tmpfiles)
# 15 (Implemented by default)
# 19 (Advice)
# 20 (Not relevant)
# 21.7 (Advice, not in threat model)
# 22 (Advice)
# Sections from madaidan's guide requiring manual user intervention:
# 2.7 (systemd service hardening must be done manually)
# 2.9 (Paid software)
# 2.11 (Unique for all hardware, inconvenient)
# 4 (Sandboxing must be done manually)
# 6 (Compiling everything is inconvenient)
# 8.6 (No option, not for all systems)
# 8.7 (Inconvenient, depends on specific user behavior)
# 10.1 (Up to user to determine hostname and username)
# 10.2 (Up to user to determine timezone, locale, and keymap)
# 10.5.3 (Not packaged)
# 10.6 (Not packaged, inconvenient and not within threat model)
# 11.1 (Manual removal of SUID/SGID is manual)
# 11.2 (No known way to set umask declaratively systemwide, use your shellrc
# or home manager to do so)
# 14 (Rather than enforce password quality with PAM, expect user
# to enforce their own password quality; faildelay is, however,
# implemented here)
# 21.1 (Out of scope)
# 21.2 (See above)
# 21.3 (User's job to set passwords)
# 21.3.1 (See above)
# 21.3.2 (See above)
# 21.3.3 (See above)
# 21.4 (Non-declarative setup, experimental)
# ABOUT THE DEFAULTS:
# The default config harms performance and usability in many ways, focusing
# almost entirely on hardening alone.
#
# There are also some optional software substitutions and additions in the
# overrides that are recommended but *not enabled* by default:
#
# sudo ---> doas (For reduced attack surface; although less audited)
# systemd-timesyncd ---> chrony (For NTS support)
# linux_hardened kernel*
#
# As of Decemeber 26, 2024, linux_hardened is up to date with mainline linux in
# unstable NixOS. However, it is cautioned that users regularly check the
# status of the linux_hardened package in NixOS, because it has been left
# unupdated for long periods of time in the past, which would be a severe
# security risk since an outdated kernel means the existence of many known
# vulnerabilities in the most privileged component of the operating system.
#
# USBGuard is also *enabled* by default, which may inconvenience some users.
#
# All of this can, and should be addressed using the overrides file.
# "nm-overrides.nix"
{
config,
lib,
pkgs,
...
}: let
# l = lib // builtins;
inherit (lib) mkDefault mkForce mkIf mkMerge mkOption mkOverride types;
inherit (builtins) fromTOML readFile;
sources = fromTOML (readFile ./sources.toml);
/*
helper function to fetch a file from a github repository
example usage to fetch https://raw.githubusercontent.com/Kicksecure/security-misc/de6f3ea74a5a1408e4351c955ecb7010825364c5/usr/lib/issue.d/20_security-misc.issue
fetchGhFile {
user = "Kicksecure";
repo = "security-misc";
rev = "de6f3ea74a5a1408e4351c955ecb7010825364c5";
file = "usr/lib/issue.d/20_security-misc.issue";
sha256 = "00ilswn1661h8rwfrq4w3j945nr7dqd1g519d3ckfkm0dr49f26b";
}
*/
fetchGhFile = {
user,
repo,
rev,
file,
sha256,
...
}:
builtins.fetchurl {
url = "https://raw.githubusercontent.com/${user}/${repo}/${rev}/${file}";
inherit sha256;
};
cfg = config.nix-mineral;
in {
options.nix-mineral = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable all nix-mineral defaults.
'';
};
overrides = {
compatibility = {
allow-unsigned-modules = mkOption {
type = types.bool;
default = false;
description = ''
Allow loading unsigned kernel modules.
'';
};
allow-busmaster-bit = mkOption {
type = types.bool;
default = false;
description = ''
Reenable the busmaster bit at boot.
'';
};
allow-ip-forward = mkOption {
type = types.bool;
default = false;
description = ''
Reenable ip forwarding.
'';
};
no-lockdown = mkOption {
type = types.bool;
default = false;
description = ''
Disable Linux Kernel Lockdown.
'';
};
};
desktop = {
allow-multilib = mkOption {
type = types.bool;
default = false;
description = ''
Reenable support for 32 bit applications.
'';
};
doas-sudo-wrapper = mkOption {
type = types.bool;
default = false;
description = ''
Enable doas-sudo wrapper, with nano to utilize rnano as a "safe"
editor for editing as root.
'';
};
hideproc-ptraceable = mkOption {
type = types.bool;
default = false;
description = ''
Allow processes that can ptrace a process to read its corresponding /proc
information.
'';
};
hideproc-off = mkOption {
type = types.bool;
default = false;
description = ''
Disable access restriction on /proc.
Fix Gnome/Wayland.
'';
};
home-exec = mkOption {
type = types.bool;
default = false;
description = ''
Allow programs to execute in /home.
'';
};
skip-restrict-home-permission = mkOption {
type = types.bool;
default = false;
description = ''
Disable recursively restricting permisions of /home directories,
as this can takes several minutes on large home directories.
'';
};
nix-allow-all = mkOption {
type = types.bool;
default = false;
description = ''
Allow all users to use nix.
'';
};
tmp-exec = mkOption {
type = types.bool;
default = false;
description = ''
Allow executing programs in /tmp.
'';
};
usbguard-gnome-integration = mkOption {
type = types.bool;
default = false;
description = ''
Enable USBGuard dbus daemon and polkit rules for integration with GNOME
Shell.
'';
};
var-lib-exec = mkOption {
type = types.bool;
default = false;
description = ''
Allow executing programs in /var/lib.
'';
};
};
performance = {
allow-smt = mkOption {
type = types.bool;
default = false;
description = ''
Reenable symmetric multithreading.
'';
};
iommu-passthrough = mkOption {
type = types.bool;
default = false;
description = ''
Enable bypassing the IOMMU for direct memory access.
'';
};
no-mitigations = mkOption {
type = types.bool;
default = false;
description = ''
Disable all CPU vulnerability mitigations.
'';
};
no-pti = mkOption {
type = types.bool;
default = false;
description = ''
Disable page table isolation.
'';
};
};
security = {
disable-bluetooth-kmodules = mkOption {
type = types.bool;
default = false;
description = ''
Disable bluetooth related kernel modules.
'';
};
disable-intelme-kmodules = mkOption {
type = types.bool;
default = false;
description = ''
Disable Intel ME related kernel modules and partially disable ME interface.
'';
};
disable-amd-iommu-forced-isolation = mkOption {
type = types.bool;
default = false;
description = ''
Do not set amd_iommu=force_isolation kernel parameter.
Workaround hanging issue on linux kernel 6.13.
'';
};
lock-root = mkOption {
type = types.bool;
default = false;
description = ''
Lock the root user.
'';
};
minimize-swapping = mkOption {
type = types.bool;
default = false;
description = ''
Reduce frequency of swapping to bare minimum.
'';
};
sysrq-sak = mkOption {
type = types.bool;
default = false;
description = ''
Enable Secure Attention Key with the sysrq key.
'';
};
};
software-choice = {
doas-no-sudo = mkOption {
type = types.bool;
default = false;
description = ''
Replace sudo with doas.
'';
};
no-firewall = mkOption {
type = types.bool;
default = false;
description = ''
Disable default firewall as chosen by nix-mineral.
'';
};
secure-chrony = mkOption {
type = types.bool;
default = false;
description = ''
Replace systemd-timesyncd with chrony for NTP, and configure chrony for NTS
and to use the seccomp filter for security.
'';
};
};
};
};
config = mkIf cfg.enable (mkMerge [
# Main module
{
boot = {
kernel = {
sysctl = {
# NOTE: `mkOverride 900` is used when a default value is already defined in NixOS.
# Yama restricts ptrace, which allows processes to read and modify the
# memory of other processes. This has obvious security implications.
# Default value is 1, to only allow parent processes to ptrace child
# processes. May be modified to restrict ptrace further.
# See overrides.
"kernel.yama.ptrace_scope" = mkDefault "1";
# Disables magic sysrq key. See overrides file regarding SAK (Secure
# attention key).
"kernel.sysrq" = mkDefault "0";
# Disable binfmt. Breaks Roseta, among other applications.
# See overrides file and https://en.wikipedia.org/wiki/Binfmt_misc for more info.
"fs.binfmt_misc.status" = mkDefault "0";
# Disable io_uring. May be desired for Proxmox, but is responsible
# for many vulnerabilities and is disabled on Android + ChromeOS.
# See overrides file.
"kernel.io_uring_disabled" = mkDefault "2";
# Disable ip forwarding to reduce attack surface. May be needed for
# VM networking. See overrides file.
"net.ipv4.ip_forward" = mkDefault "0";
"net.ipv4.conf.all.forwarding" = mkOverride 900 "0";
"net.ipv4.conf.default.forwarding" = mkDefault "0";
"net.ipv6.conf.all.forwarding" = mkDefault "0";
"net.ipv6.conf.default.forwarding" = mkDefault "0";
# Privacy/security split.
# By default, nix-mineral enables
# tcp-timestamps. Disabling prevents leaking system time, enabling protects
# against wrapped sequence numbers and improves performance.
#
# Read more about the issue here:
# URL: (In favor of disabling): https://madaidans-insecurities.github.io/guides/linux-hardening.html#tcp-timestamps
# URL: (In favor of enabling): https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf
"net.ipv4.tcp_timestamps" = mkDefault "1";
"dev.tty.ldisc_autoload" = mkDefault "0";
"fs.protected_fifos" = mkDefault "2";
"fs.protected_hardlinks" = mkDefault "1";
"fs.protected_regular" = mkDefault "2";
"fs.protected_symlinks" = mkDefault "1";
"fs.suid_dumpable" = mkDefault "0";
"kernel.dmesg_restrict" = mkDefault "1";
"kernel.kexec_load_disabled" = mkOverride 900 "1";
"kernel.kptr_restrict" = mkOverride 900 "2";
"kernel.perf_event_paranoid" = mkDefault "3";
"kernel.printk" = mkOverride 900 "3 3 3 3";
"kernel.unprivileged_bpf_disabled" = mkDefault "1";
"net.core.bpf_jit_harden" = mkDefault "2";
# Disable ICMP redirects to prevent some MITM attacks
# See https://askubuntu.com/questions/118273/what-are-icmp-redirects-and-should-they-be-blocked
"net.ipv4.conf.all.accept_redirects" = mkOverride 900 "0";
"net.ipv4.conf.default.accept_redirects" = mkOverride 900 "0";
"net.ipv4.conf.all.send_redirects" = mkOverride 900 "0";
"net.ipv4.conf.default.send_redirects" = mkOverride 900 "0";
"net.ipv6.conf.all.accept_redirects" = mkOverride 900 "0";
"net.ipv6.conf.default.accept_redirects" = mkOverride 900 "0";
# Use secure ICMP redirects by default. Helpful if ICMP redirects are
# reenabled only.
"net.ipv4.conf.all.secure_redirects" = mkOverride 900 "1";
"net.ipv4.conf.default.secure_redirects" = mkOverride 900 "1";
"net.ipv4.conf.all.accept_source_route" = mkDefault "0";
"net.ipv4.conf.all.rp_filter" = mkOverride 900 "1";
"net.ipv4.conf.default.accept_source_route" = mkDefault "0";
"net.ipv4.conf.default.rp_filter" = mkOverride 900 "1";
"net.ipv4.icmp_echo_ignore_all" = mkDefault "1";
"net.ipv6.icmp_echo_ignore_all" = mkDefault "1";
"net.ipv4.tcp_dsack" = mkDefault "0";
"net.ipv4.tcp_fack" = mkDefault "0";
"net.ipv4.tcp_rfc1337" = mkDefault "1";
"net.ipv4.tcp_sack" = mkDefault "0";
"net.ipv4.tcp_syncookies" = mkDefault "1";
"net.ipv6.conf.all.accept_ra" = mkDefault "0";
"net.ipv6.conf.all.accept_source_route" = mkDefault "0";
"net.ipv6.conf.default.accept_source_route" = mkDefault "0";
"net.ipv6.default.accept_ra" = mkDefault "0";
"kernel.core_pattern" = mkDefault "|/bin/false";
"vm.mmap_rnd_bits" = mkDefault "32";
"vm.mmap_rnd_compat_bits" = mkDefault "16";
"vm.unprivileged_userfaultfd" = mkDefault "0";
"net.ipv4.icmp_ignore_bogus_error_responses" = mkDefault "1";
# enable ASLR
# turn on protection and randomize stack, vdso page and mmap + randomize brk base address
"kernel.randomize_va_space" = mkDefault "2";
# restrict perf subsystem usage (activity) further
"kernel.perf_cpu_time_max_percent" = mkDefault "1";
"kernel.perf_event_max_sample_rate" = mkDefault "1";
# do not allow mmap in lower addresses
"vm.mmap_min_addr" = mkDefault "65536";
# log packets with impossible addresses to kernel log
# No active security benefit, just makes it easier to spot a DDOS/DOS by giving
# extra logs
"net.ipv4.conf.default.log_martians" = mkOverride 900 "1";
"net.ipv4.conf.all.log_martians" = mkOverride 900 "1";
# disable sending and receiving of shared media redirects
# this setting overwrites net.ipv4.conf.all.secure_redirects
# refer to RFC1620
"net.ipv4.conf.default.shared_media" = mkDefault "0";
"net.ipv4.conf.all.shared_media" = mkDefault "0";
# always use the best local address for announcing local IP via ARP
# Seems to be most restrictive option
"net.ipv4.conf.default.arp_announce" = mkDefault "2";
"net.ipv4.conf.all.arp_announce" = mkDefault "2";
# reply only if the target IP address is local address configured on the incoming interface
"net.ipv4.conf.default.arp_ignore" = mkDefault "1";
"net.ipv4.conf.all.arp_ignore" = mkDefault "1";
# drop Gratuitous ARP frames to prevent ARP poisoning
# this can cause issues when ARP proxies are used in the network
"net.ipv4.conf.default.drop_gratuitous_arp" = mkDefault "1";
"net.ipv4.conf.all.drop_gratuitous_arp" = mkDefault "1";
# ignore all ICMP echo and timestamp requests sent to broadcast/multicast
"net.ipv4.icmp_echo_ignore_broadcasts" = mkOverride 900 "1";
# number of Router Solicitations to send until assuming no routers are present
"net.ipv6.conf.default.router_solicitations" = mkDefault "0";
"net.ipv6.conf.all.router_solicitations" = mkDefault "0";
# do not accept Router Preference from RA
"net.ipv6.conf.default.accept_ra_rtr_pref" = mkDefault "0";
"net.ipv6.conf.all.accept_ra_rtr_pref" = mkDefault "0";
# learn prefix information in router advertisement
"net.ipv6.conf.default.accept_ra_pinfo" = mkDefault "0";
"net.ipv6.conf.all.accept_ra_pinfo" = mkDefault "0";
# setting controls whether the system will accept Hop Limit settings from a router advertisement
"net.ipv6.conf.default.accept_ra_defrtr" = mkDefault "0";
"net.ipv6.conf.all.accept_ra_defrtr" = mkDefault "0";
# router advertisements can cause the system to assign a global unicast address to an interface
"net.ipv6.conf.default.autoconf" = mkDefault "0";
"net.ipv6.conf.all.autoconf" = mkDefault "0";
# number of neighbor solicitations to send out per address
"net.ipv6.conf.default.dad_transmits" = mkDefault "0";
"net.ipv6.conf.all.dad_transmits" = mkDefault "0";
# number of global unicast IPv6 addresses can be assigned to each interface
"net.ipv6.conf.default.max_addresses" = mkDefault "1";
"net.ipv6.conf.all.max_addresses" = mkDefault "1";
# enable IPv6 Privacy Extensions (RFC3041) and prefer the temporary address
# https://grapheneos.org/features#wifi-privacy
# GrapheneOS devs seem to believe it is relevant to use IPV6 privacy
# extensions alongside MAC randomization, so that's why we do both
# Commented, as this is already explicitly defined by default in NixOS
# "net.ipv6.conf.default.use_tempaddr" = mkForce "2";
# "net.ipv6.conf.all.use_tempaddr" = mkForce "2";
# ignore all ICMPv6 echo requests
"net.ipv6.icmp.echo_ignore_all" = mkDefault "1";
"net.ipv6.icmp.echo_ignore_anycast" = mkDefault "1";
"net.ipv6.icmp.echo_ignore_multicast" = mkDefault "1";
};
};
kernelParams =
[
# Requires all kernel modules to be signed. This prevents out-of-tree
# kernel modules from working unless signed. See overrides.
"module.sig_enforce=1"
# May break some drivers, same reason as the above. Also breaks
# hibernation. See overrides.
"lockdown=confidentiality"
# May prevent some systems from booting. See overrides.
"efi=disable_early_pci_dma"
# Forces DMA to go through IOMMU to mitigate some DMA attacks. See
# overrides.
"iommu.passthrough=0"
# Apply relevant CPU exploit mitigations, and disable symmetric
# multithreading. May harm performance. See overrides.
"mitigations=auto,nosmt"
# Mitigates Meltdown, some KASLR bypasses. Hurts performance. See
# overrides.
"pti=on"
# Gather more entropy on boot. Only works with the linux_hardened
# patchset, but does nothing if using another kernel. Slows down boot
# time by a bit.
"extra_latent_entropy"
"slab_nomerge"
"init_on_alloc=1"
"init_on_free=1"
"page_alloc.shuffle=1"
"randomize_kstack_offset=on"
"vsyscall=none"
"debugfs=off"
"oops=panic"
"quiet"
"loglevel=0"
"random.trust_cpu=off"
"random.trust_bootloader=off"
"intel_iommu=on"
"iommu=force"
"iommu.strict=1"
]
++ lib.optional (!cfg.overrides.security.disable-amd-iommu-forced-isolation)
"amd_iommu=force_isolation";
# Disable the editor in systemd-boot, the default bootloader for NixOS.
# This prevents access to the root shell or otherwise weakening
# security by tampering with boot parameters. If you use a different
# boatloader, this does not provide anything. You may also want to
# consider disabling similar functions in your choice of bootloader.
loader.systemd-boot.editor = mkDefault false;
};
environment.etc = {
# Empty /etc/securetty to prevent root login on tty.
securetty.text = ''
# /etc/securetty: list of terminals on which root is allowed to login.
# See securetty(5) and login(1).
'';
# Set machine-id to the Kicksecure machine-id, for privacy reasons.
# /var/lib/dbus/machine-id doesn't exist on dbus enabled NixOS systems,
# so we don't have to worry about that.
machine-id.text = ''
b08dfa6083e7567a1921a715000001fb
'';
# Borrow Kicksecure banner/issue.
issue.source = fetchGhFile sources.issue;
# Borrow Kicksecure gitconfig, disabling git symlinks and enabling fsck
# by default for better git security.
gitconfig.source = fetchGhFile sources.gitconfig;
# Borrow Kicksecure bluetooth configuration for better bluetooth privacy
# and security. Disables bluetooth automatically when not connected to
# any device.
"bluetooth/main.conf".source = mkForce (fetchGhFile sources.bluetooth);
# Borrow Kicksecure module blacklist.
# "install "foobar" /bin/not-existent" prevents the module from being
# loaded at all. "blacklist "foobar"" prevents the module from being
# loaded automatically at boot, but it can still be loaded afterwards.
"modprobe.d/nm-module-blacklist.conf".source = fetchGhFile sources.module-blacklist;
};
### Filesystem hardening
# Based on Kicksecure/security-misc's remount-secure
# Kicksecure/security-misc
# usr/bin/remount-secure - Last updated July 31st, 2024
# Inapplicable:
# /sys (Already hardened by default in NixOS)
# /media, /mnt, /opt (Doesn't even exist on NixOS)
# /var/tmp, /var/log (Covered by toplevel hardening on /var,)
# Bind mounting /usr with nodev causes boot failure
# Bind mounting /boot/efi at all causes complete system failure
fileSystems = {
# noexec on /home can be very inconvenient for desktops. See overrides.
"/home" = {
device = mkDefault "/home";
options = [
"bind"
"nosuid"
"noexec"
"nodev"
];
};
# You do not want to install applications here anyways.
"/root" = {
device = mkDefault "/root";
options = [
"bind"
"nosuid"
"noexec"
"nodev"
];
};
# Some applications may need to be executable in /tmp. See overrides.
"/tmp" = {
device = mkDefault "/tmp";
options = [
"bind"
"nosuid"
"noexec"
"nodev"
];
};
# noexec on /var(/lib) may cause breakage. See overrides.
"/var" = {
device = mkDefault "/var";
options = [
"bind"
"nosuid"
"noexec"
"nodev"
];
};
"/boot" = lib.mkIf (!config.boot.isContainer) {
options = [
"nosuid"
"noexec"
"nodev"
];
};
"/srv" = {
device = mkDefault "/srv";
options = [
"bind"
"nosuid"
"noexec"
"nodev"
];
};
"/etc" = lib.mkIf (!config.boot.isContainer) {
device = mkDefault "/etc";
options = [
"bind"
"nosuid"
"nodev"
];
};
};
# Harden special filesystems while maintaining NixOS defaults as outlined
# here:
# https://github.com/NixOS/nixpkgs/blob/e2dd4e18cc1c7314e24154331bae07df76eb582f/nixos/modules/tasks/filesystems.nix
boot.specialFileSystems = {
# Add noexec to /dev/shm
"/dev/shm" = {
options = [
"noexec"
];
};
# Add noexec to /run
"/run" = {
options = [
"noexec"
];
};
# Add noexec to /dev
"/dev" = {
options = [
"noexec"
];
};
# Hide processes from other users except root, may cause breakage.
# See overrides, in desktop section.
"/proc" = {
device = mkDefault "proc";
options = [
"hidepid=2"
"gid=${toString config.users.groups.proc.gid}"
];
};
};
# Add "proc" group to whitelist /proc access and allow systemd-logind to view
# /proc in order to unbreak it, as well as to user@ for similar reasons.
# See https://github.com/systemd/systemd/issues/12955, and https://github.com/Kicksecure/security-misc/issues/208
users.groups.proc.gid = mkDefault config.ids.gids.proc;
systemd.services.systemd-logind.serviceConfig.SupplementaryGroups = ["proc"];
systemd.services."user@".serviceConfig.SupplementaryGroups = ["proc"];
# Enables firewall. You may need to tweak your firewall rules depending on
# your usecase. On a desktop, this shouldn't cause problems.
networking = {
firewall = {
allowedTCPPorts = mkDefault [];
allowedUDPPorts = mkDefault [];
enable = mkDefault true;
};
networkmanager = {
ethernet.macAddress = mkDefault "random";
wifi = {
macAddress = mkDefault "random";
scanRandMacAddress = mkDefault true;
};
# Enable IPv6 privacy extensions in NetworkManager.
connectionConfig."ipv6.ip6-privacy" = mkDefault 2;
};
};
# Enabling MAC doesn't magically make your system secure. You need to set up
# policies yourself for it to be effective.
security = {
apparmor = {
enable = mkDefault true;
killUnconfinedConfinables = mkDefault true;
};
pam = {
loginLimits = [
{
domain = mkDefault "*";
item = mkDefault "core";
type = mkDefault "hard";
value = mkDefault "0";
}
];
services = {
# Increase hashing rounds for /etc/shadow; this doesn't automatically
# rehash your passwords, you'll need to set passwords for your accounts
# again for this to work.
passwd.rules.password."unix".settings.rounds = mkDefault 65536;
# Enable PAM support for securetty, to prevent root login.
# https://unix.stackexchange.com/questions/670116/debian-bullseye-disable-console-tty-login-for-root
login.rules.auth = {
"nologin" = {
enable = mkDefault true;
order = mkDefault 0;
control = mkDefault "requisite";
modulePath = mkDefault "${config.security.pam.package}/lib/security/pam_nologin.so";
};
"securetty" = {
enable = mkDefault true;
order = mkDefault 1;
control = mkDefault "requisite";
modulePath = mkDefault "${config.security.pam.package}/lib/security/pam_securetty.so";
};
};
su.requireWheel = mkDefault true;
su-l.requireWheel = mkDefault true;
system-login.failDelay.delay = mkDefault "4000000";
};
};
};
services = {
# Disallow root login over SSH. Doesn't matter on systems without SSH.
openssh.settings.PermitRootLogin = mkDefault "no";
# DNS connections will fail if not using a DNS server supporting DNSSEC.
resolved.dnssec = mkDefault "true";
};
# Get extra entropy since we disabled hardware entropy sources
# Read more about why at the following URLs:
# https://github.com/smuellerDD/jitterentropy-rngd/issues/27
# https://blogs.oracle.com/linux/post/rngd1
services.jitterentropy-rngd.enable = mkDefault (!config.boot.isContainer);
boot.kernelModules = ["jitterentropy_rng"];
# Don't store coredumps from systemd-coredump.
systemd.coredump.extraConfig = ''
Storage=none
'';
# Enable IPv6 privacy extensions for systemd-networkd.
systemd.network.config.networkConfig.IPv6PrivacyExtensions = mkDefault "kernel";
systemd.tmpfiles.settings =
{
# Make all files in /etc/nixos owned by root, and only readable by root.
# /etc/nixos is not owned by root by default, and configuration files can
# on occasion end up also not owned by root. This can be hazardous as files
# that are included in the rebuild may be editable by unprivileged users,
# so this mitigates that.
"restrictetcnixos"."/etc/nixos/*".Z = {
mode = mkDefault "0000";
user = mkDefault "root";
group = mkDefault "root";
};
}
// lib.optionalAttrs (!cfg.overrides.desktop.skip-restrict-home-permission) {
# Restrict permissions of /home/$USER so that only the owner of the
# directory can access it (the user). systemd-tmpfiles also has the benefit
# of recursively setting permissions too, with the "Z" option as seen below.
"restricthome"."/home/*".Z.mode = mkDefault "~0700";
};
# zram allows swapping to RAM by compressing memory. This reduces the chance
# that sensitive data is written to disk, and eliminates it if zram is used
# to completely replace swap to disk. Generally *improves* storage lifespan
# and performance, there usually isn't a need to disable this.
zramSwap.enable = mkDefault true;
# Limit access to nix to users with the "wheel" group. ("sudoers")
nix.settings.allowed-users = mkDefault ["@wheel"];
}
# Compatibility
(mkIf cfg.overrides.compatibility.allow-unsigned-modules {
boot.kernelParams = mkOverride 100 ["module.sig_enforce=0"];
})
(mkIf cfg.overrides.compatibility.allow-busmaster-bit {
boot.kernelParams = mkOverride 100 ["efi=no_disable_early_pci_dma"];
})
(mkIf cfg.overrides.compatibility.allow-ip-forward {
boot.kernel.sysctl."net.ipv4.ip_forward" = mkForce "1";
boot.kernel.sysctl."net.ipv4.conf.all.forwarding" = mkForce "1";
boot.kernel.sysctl."net.ipv4.conf.default.forwarding" = mkForce "1";
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = mkForce "1";
boot.kernel.sysctl."net.ipv6.conf.default.forwarding" = mkForce "1";
})
(mkIf cfg.overrides.compatibility.no-lockdown {
boot.kernelParams = mkOverride 100 ["lockdown="];
})
# Desktop
(mkIf cfg.overrides.desktop.allow-multilib {
boot.kernelParams = mkOverride 100 ["ia32_emulation=1"];
})
(mkIf cfg.overrides.desktop.doas-sudo-wrapper {
environment.systemPackages = with pkgs; [
(writeScriptBin "sudo" ''exec ${l.getExe doas} "$@"'')
(writeScriptBin "sudoedit" ''exec ${l.getExe doas} ${l.getExe' nano "rnano"} "$@"'')
(writeScriptBin "doasedit" ''exec ${l.getExe doas} ${l.getExe' nano "rnano"} "$@"'')
];
})
(mkIf cfg.overrides.desktop.hideproc-ptraceable {
boot.specialFileSystems."/proc" = {
options = mkForce [
"nosuid"
"nodev"
"noexec"
"hidepid=4"
"gid=${toString config.users.groups.proc.gid}"
];
};
})
(mkIf cfg.overrides.desktop.hideproc-off {
boot.specialFileSystems."/proc" = {
options = mkForce [
"nosuid"
"nodev"
"noexec"
"gid=${toString config.users.groups.proc.gid}"
];
};
})
(mkIf cfg.overrides.desktop.home-exec {
fileSystems."/home" = {
device = mkForce "/home";
options = mkForce [
"bind"
"nosuid"
"exec"
"nodev"
];
};
})
(mkIf cfg.overrides.desktop.nix-allow-all {nix.settings.allowed-users = mkForce ["*"];})
(mkIf cfg.overrides.desktop.tmp-exec {
fileSystems."/tmp" = {
device = mkForce "/tmp";
options = mkForce [
"bind"
"nosuid"
"exec"
"nodev"
];
};
})
(mkIf cfg.overrides.desktop.usbguard-gnome-integration {
services.usbguard.dbus.enable = mkForce true;
security.polkit = {
extraConfig = ''
polkit.addRule(function(action, subject) {
if ((action.id == "org.usbguard.Policy1.listRules" ||
action.id == "org.usbguard.Policy1.appendRule" ||
action.id == "org.usbguard.Policy1.removeRule" ||
action.id == "org.usbguard.Devices1.applyDevicePolicy" ||
action.id == "org.usbguard.Devices1.listDevices" ||
action.id == "org.usbguard1.getParameter" ||
action.id == "org.usbguard1.setParameter") &&
subject.active == true && subject.local == true &&
subject.isInGroup("wheel")) { return polkit.Result.YES; }
});
'';
};
})
(mkIf cfg.overrides.desktop.var-lib-exec {
fileSystems."/var/lib" = {
device = mkForce "/var/lib";
options = mkForce [
"bind"
"nosuid"
"exec"
"nodev"
];
};
})
# Performance
(mkIf cfg.overrides.performance.allow-smt {
boot.kernelParams = mkOverride 100 ["mitigations=auto"];
})
(mkIf cfg.overrides.performance.iommu-passthrough {
boot.kernelParams = mkOverride 100 ["iommu.passthrough=1"];
})
(mkIf cfg.overrides.performance.no-mitigations {
boot.kernelParams = mkOverride 100 ["mitigations=off"];
})
(mkIf cfg.overrides.performance.no-pti {boot.kernelParams = mkOverride 100 ["pti=off"];})
# Security
(mkIf cfg.overrides.security.disable-bluetooth-kmodules {
environment.etc."modprobe.d/nm-disable-bluetooth.conf" = {
text = ''
install bluetooth /usr/bin/disabled-bluetooth-by-security-misc
install bluetooth_6lowpan /usr/bin/disabled-bluetooth-by-security-misc
install bt3c_cs /usr/bin/disabled-bluetooth-by-security-misc
install btbcm /usr/bin/disabled-bluetooth-by-security-misc
install btintel /usr/bin/disabled-bluetooth-by-security-misc
install btmrvl /usr/bin/disabled-bluetooth-by-security-misc
install btmrvl_sdio /usr/bin/disabled-bluetooth-by-security-misc
install btmtk /usr/bin/disabled-bluetooth-by-security-misc
install btmtksdio /usr/bin/disabled-bluetooth-by-security-misc
install btmtkuart /usr/bin/disabled-bluetooth-by-security-misc
install btnxpuart /usr/bin/disabled-bluetooth-by-security-misc
install btqca /usr/bin/disabled-bluetooth-by-security-misc
install btrsi /usr/bin/disabled-bluetooth-by-security-misc
install btrtl /usr/bin/disabled-bluetooth-by-security-misc
install btsdio /usr/bin/disabled-bluetooth-by-security-misc
install btusb /usr/bin/disabled-bluetooth-by-security-misc
install virtio_bt /usr/bin/disabled-bluetooth-by-security-misc
'';
};
})
(mkIf cfg.overrides.security.disable-intelme-kmodules {
environment.etc."modprobe.d/nm-disable-intelme-kmodules.conf" = {
text = ''
install mei /usr/bin/disabled-intelme-by-security-misc
install mei-gsc /usr/bin/disabled-intelme-by-security-misc
install mei_gsc_proxy /usr/bin/disabled-intelme-by-security-misc
install mei_hdcp /usr/bin/disabled-intelme-by-security-misc
install mei-me /usr/bin/disabled-intelme-by-security-misc
install mei_phy /usr/bin/disabled-intelme-by-security-misc
install mei_pxp /usr/bin/disabled-intelme-by-security-misc
install mei-txe /usr/bin/disabled-intelme-by-security-misc
install mei-vsc /usr/bin/disabled-intelme-by-security-misc
install mei-vsc-hw /usr/bin/disabled-intelme-by-security-misc
install mei_wdt /usr/bin/disabled-intelme-by-security-misc
install microread_mei /usr/bin/disabled-intelme-by-security-misc
'';
};
})
(mkIf cfg.overrides.security.lock-root {
users = {
users = {
root = {
hashedPassword = mkDefault "!";
};
};
};
})
(mkIf cfg.overrides.security.minimize-swapping {
boot.kernel.sysctl."vm.swappiness" = mkForce "1";
})
(mkIf cfg.overrides.security.sysrq-sak {boot.kernel.sysctl."kernel.sysrq" = mkForce "4";})
# Software Choice
(mkIf cfg.overrides.software-choice.doas-no-sudo {
security.sudo.enable = mkDefault false;
security.doas = {
enable = mkDefault true;
extraRules = [
{
keepEnv = mkDefault true;
persist = mkDefault true;
users = mkDefault ["user"];
}
];
};
})
(mkIf cfg.overrides.software-choice.no-firewall {networking.firewall.enable = mkForce false;})
(mkIf cfg.overrides.software-choice.secure-chrony {
services.timesyncd = {
enable = mkDefault false;
};
services.chrony = {
enable = mkDefault true;
extraFlags = mkDefault [
"-F 1"
"-r"
];
# Enable seccomp filter for chronyd (-F 1) and reload server history on
# restart (-r). The -r flag is added to match GrapheneOS's original
# chronyd configuration.
enableRTCTrimming = mkDefault false;
# Disable 'rtcautotrim' so that 'rtcsync' can be used instead. Either
# this or 'rtcsync' must be disabled to complete a successful rebuild,
# or an error will be thrown due to these options conflicting with
# eachother.
servers = mkDefault [];
# Since servers are declared by the fetched chrony config, set the
# NixOS option to [ ] to prevent the default values from interfering.
initstepslew.enabled = mkDefault false;
# Initstepslew "is deprecated in favour of the makestep directive"
# according to:
# https://chrony-project.org/doc/4.6/chrony.conf.html#initstepslew.
# The fetched chrony config already has makestep enabled, so
# initstepslew is disabled (it is enabled by default).
# The below config is borrowed from GrapheneOS server infrastructure.
# It enables NTS to secure NTP requests, among some other useful
# settings.
extraConfig = ''
${builtins.readFile (fetchGhFile sources.chrony)}
leapseclist ${pkgs.tzdata}/share/zoneinfo/leap-seconds.list
'';
# Override the leapseclist path with the NixOS-compatible path to
# leap-seconds.list using the tzdata package. This is necessary because
# NixOS doesn't use standard FHS paths like /usr/share/zoneinfo.
};
})
]);
}