added stuff

This commit is contained in:
Charlie Root 2024-04-09 23:11:33 +02:00
commit 9d0ebdfbd0
907 changed files with 70990 additions and 0 deletions

View file

@ -0,0 +1,39 @@
{
config,
pkgs,
lib,
...
}: {
system.activationScripts = {
# if system declares that it wants closure diffs, then run the diff script on activation
# this is useless if you are using nh, which does this for you in a different way
diff = lib.mkIf config.modules.system.activation.diffGenerations {
supportsDryActivation = true;
text = ''
if [[ -e /run/current-system ]]; then
echo "=== diff to current-system ==="
${pkgs.nvd}/bin/nvd --nix-bin-dir='${config.nix.package}/bin' diff /run/current-system "$systemConfig"
echo "=== end of the system diff ==="
fi
'';
};
# <https://github.com/colemickens/nixcfg/blob/main/mixins/ssh.nix>
# symlink root's ssh config to ours
# to fix nix-daemon's ability to remote build since it sshs from the root account
root_ssh_config = let
sshDir = "/home/notashelf/.ssh";
in {
text = ''
(
# symlink root ssh config to ours so daemon can use our agent/keys/etc...
mkdir -p /root/.ssh
ln -sf ${sshDir}/config /root/.ssh/config
ln -sf ${sshDir}/known_hosts /root/.ssh/known_hosts
ln -sf ${sshDir}/known_hosts /root/.ssh/known_hosts
)
'';
deps = [];
};
};
}

View file

@ -0,0 +1,8 @@
{
imports = [
./loaders # per-bootloader configurations
./secure-boot.nix # secure boot module
./generic.nix # generic configuration, such as kernel and tmpfs setup
./plymouth.nix # plymouth boot splash
];
}

View file

@ -0,0 +1,180 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkDefault mkForce mkOverride mkMerge mkIf optionals;
sys = config.modules.system;
in {
config.boot = {
# kernel console loglevel
consoleLogLevel = 3;
# always use the latest kernel instead of the old-ass lts one
kernelPackages = mkOverride 500 sys.boot.kernel;
# additional packages supplying kernel modules
extraModulePackages = mkDefault sys.boot.extraModulePackages;
# configuration to be appended to the generated modprobe.conf
extraModprobeConfig = mkDefault sys.boot.extraModprobeConfig;
# whether to enable support for Linux MD RAID arrays
# I don't know why this defaults to true, how many people use RAID anyway?
# also on > 23.11, this will throw a warning if neither MAILADDR nor PROGRAM are set
swraid.enable = mkDefault false;
# settings shared between bootloaders
# they are set unless system.boot.loader != none
loader = {
# if set to 0, space needs to be held to get the boot menu to appear
timeout = mkForce 2;
# whether to copy the necessary boot files into /boot
# so that /nix/store is not needed by the boot loader.
generationsDir.copyKernels = true;
# allow installation to modify EFI variables
efi.canTouchEfiVariables = true;
};
# instructions on how /tmp should be handled
# if your system is low on ram, you should avoid tmpfs to prevent hangups while compiling
tmp = {
# /tmp on tmpfs, lets it live on your ram
# it defaults to FALSE, which means you will use disk space instead of ram
# enable tmpfs tmp on anything except servers and builders
useTmpfs = sys.boot.tmpOnTmpfs;
# If not using tmpfs, which is naturally purged on reboot, we must clean
# /tmp ourselves. /tmp should be volatile storage!
cleanOnBoot = mkDefault (!config.boot.tmp.useTmpfs);
# The size of the tmpfs, in percentage form
# this defaults to 50% of your ram, which is a good default
# but should be tweaked based on your systems capabilities
tmpfsSize = mkDefault "75%";
};
# initrd and kernel tweaks
# if you intend to copy paste this section, read what each parameter or module does before doing so
# or perish, I am not responsible for your broken system. if you copy paste this section without reading
# and later realise your mistake, you are a moron.
initrd = mkMerge [
(mkIf sys.boot.initrd.enableTweaks {
# Verbosity of the initrd
# disabling verbosity removes only the mandatory messages generated by the NixOS
verbose = false;
systemd = {
# enable systemd in initrd
# extremely experimental, just the way I like it on a production machine
enable = true;
# strip copied binaries and libraries from inframs
# saves 30~ mb space according to the nix derivation
strip = true;
# packages that will be added to the PATH in initrd
# this is useful for debugging if the host provides
# emergency access
storePaths = with pkgs; [util-linux pciutils];
extraBin = {
fdisk = "${pkgs.util-linux}/bin/fdisk";
lsblk = "${pkgs.util-linux}/bin/lsblk";
lspci = "${pkgs.pciutils}/bin/lspci";
};
};
# List of modules that are always loaded by the initrd
kernelModules = [
"ahci"
"nvme"
"xhci_pci"
"btrfs"
"sd_mod"
"dm_mod"
"tpm"
];
# the set of kernel modules in the initial ramdisk used during the boot process
availableKernelModules = [
"usbhid"
"sd_mod"
"sr_mod"
"dm_mod"
"uas"
"usb_storage"
"rtsx_usb_sdmmc"
"rtsx_pci_sdmmc" # Realtek PCI-Express SD/MMC Card Interface driver
"ata_piix"
"virtio_pci"
"virtio_scsi"
"ehci_pci"
];
})
(mkIf sys.boot.initrd.optimizeCompressor
{
compressor = "zstd";
compressorArgs = ["-19" "-T0"];
})
];
# https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
kernelParams =
(optionals sys.boot.enableKernelTweaks [
# https://en.wikipedia.org/wiki/Kernel_page-table_isolation
# auto means kernel will automatically decide the pti state
"pti=auto" # on | off
# CPU idle behaviour
# poll: slightly improve performance at cost of a hotter system (not recommended)
# halt: halt is forced to be used for CPU idle
# nomwait: Disable mwait for CPU C-states
"idle=nomwait" # poll | halt | nomwait
# enable IOMMU for devices used in passthrough
# and provide better host performance in virtualization
"iommu=pt"
# disable usb autosuspend
"usbcore.autosuspend=-1"
# disables resume and restores original swap space
"noresume"
# allows systemd to set and save the backlight state
"acpi_backlight=native" # none | vendor | video | native
# prevent the kernel from blanking plymouth out of the fb
"fbcon=nodefer"
# disable the cursor in vt to get a black screen during intermissions
"vt.global_cursor_default=0"
# disable displaying of the built-in Linux logo
"logo.nologo"
])
++ (optionals sys.boot.silentBoot [
# tell the kernel to not be verbose
"quiet"
# kernel log message level
"loglevel=3" # 1: sustem is unusable | 3: error condition | 7: very verbose
# udev log message level
"udev.log_level=3"
# lower the udev log level to show only errors or worse
"rd.udev.log_level=3"
# disable systemd status messages
# rd prefix means systemd-udev will be used instead of initrd
"systemd.show_status=auto"
"rd.systemd.show_status=auto"
]);
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./grub
./systemd-boot
];
}

View file

@ -0,0 +1,24 @@
{
config,
lib,
...
}: let
inherit (lib) mkDefault mkIf;
cfg = config.modules.system;
in {
config = mkIf (cfg.boot.loader == "grub") {
boot.loader = {
grub = {
enable = mkDefault true;
useOSProber = true;
efiSupport = true;
enableCryptodisk = mkDefault false;
device = cfg.boot.grub.device;
theme = null;
backgroundColor = null;
splashImage = null;
};
};
};
}

View file

@ -0,0 +1,16 @@
{
lib,
config,
...
}: let
inherit (lib) mkIf mkForce;
cfg = config.modules.system;
in {
config = mkIf (cfg.boot.loader == "none") {
boot.loader = {
grub.enable = mkForce false;
systemd-boot.enable = mkForce false;
};
};
}

View file

@ -0,0 +1,32 @@
{
config,
lib,
...
}: let
inherit (lib) mkDefault mkIf optionalAttrs;
cfg = config.modules.system;
in {
config = mkIf (cfg.boot.loader == "systemd-boot") {
boot.loader = {
systemd-boot =
{
enable = mkDefault true;
configurationLimit = null; # unlimited
consoleMode = mkDefault "max"; # the default is "keep", can be overriden per host if need be
# Fix a security hole in place for backwards compatibility. See desc in
# nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
editor = false;
}
// optionalAttrs cfg.boot.memtest.enable {
# https://matrix.to/#/!sgkZKRutwatDMkYBHU:nixos.org/$iKnJUt1L_7E5bq7hStDPwv6_2HTBvNjwfcWxlKlF-k8?via=nixos.org&via=matrix.org&via=nixos.dev
extraFiles."efi/memtest86plus/memtest.efi" = "${cfg.boot.memtest.package}/memtest.efi";
extraEntries."memtest86plus.conf" = ''
title MemTest86+
efi /efi/memtest86plus/memtest.efi
'';
};
};
};
}

View file

@ -0,0 +1,39 @@
{
config,
lib,
pkgs,
self',
...
}: let
inherit (pkgs) plymouth;
inherit (lib) mkIf;
cfg = config.modules.system.boot.plymouth;
in {
config = mkIf cfg.enable {
# configure plymouth theme
# <https://github.com/adi1090x/plymouth-themes>
boot.plymouth = let
pack = cfg.pack;
theme = cfg.theme;
in
{
enable = true;
}
// lib.optionalAttrs cfg.withThemes {
themePackages = [(self'.packages.plymouth-themes.override {inherit pack theme;})];
inherit theme;
};
# make plymouth work with sleep
powerManagement = {
powerDownCommands = ''
${plymouth} --show-splash
'';
resumeCommands = ''
${plymouth} --quit
'';
};
};
}

View file

@ -0,0 +1,36 @@
{
config,
pkgs,
lib,
inputs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system.boot;
in {
imports = [
inputs.lanzaboote.nixosModules.lanzaboote
];
config = mkIf sys.secureBoot {
environment.systemPackages = [
# For debugging and troubleshooting Secure Boot.
pkgs.sbctl
];
# Lanzaboote currently replaces the systemd-boot module.
# This setting is usually set to true in configuration.nix
# generated at installation time. So we force it to false
# for now.
boot.loader.systemd-boot.enable = lib.mkForce false;
boot = {
bootspec.enable = true;
lanzaboote = {
enable = true;
pkiBundle = "/etc/secureboot";
};
};
};
}

View file

@ -0,0 +1,15 @@
{
imports = [
./activation # activation system for nixos-rebuild
./boot # boot and bootloader configurations
./display # display protocol (wayland/xorg)
./environment # system environment e.g. locale, timezone, packages
./fs # filesystem support options
./misc # things that don't fit anywhere else
./networking # network configuration & tcp optimizations
./programs # general programs
./services # gemeral services
./theme # theming for desktop toolkits e.g. gtk, qt
./users # per user configurations
];
}

View file

@ -0,0 +1,6 @@
{
imports = [
./wayland
./xorg
];
}

View file

@ -0,0 +1,9 @@
{
imports = [
./wms
./environment.nix
./xdg-portals.nix
./services.nix
];
}

View file

@ -0,0 +1,34 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system.video;
env = config.modules.usrEnv;
in {
config = mkIf (sys.enable && env.isWayland) {
environment.etc."greetd/environments".text = ''
${lib.optionalString (env.desktop == "Hyprland") "Hyprland"}
zsh
'';
environment = {
variables = {
NIXOS_OZONE_WL = "1";
_JAVA_AWT_WM_NONEREPARENTING = "1";
GDK_BACKEND = "wayland,x11";
ANKI_WAYLAND = "1";
MOZ_ENABLE_WAYLAND = "1";
XDG_SESSION_TYPE = "wayland";
SDL_VIDEODRIVER = "wayland";
CLUTTER_BACKEND = "wayland";
#WLR_DRM_NO_ATOMIC = "1";
#WLR_BACKEND = "vulkan";
#__GL_GSYNC_ALLOWED = "0";
#__GL_VRR_ALLOWED = "0";
};
};
};
}

View file

@ -0,0 +1,27 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf getExe;
cfg = config.modules.system.video;
env = config.modules.usrEnv;
in {
config = mkIf (cfg.enable && env.isWayland) {
systemd.services = {
seatd = {
enable = true;
description = "Seat management daemon";
script = "${getExe pkgs.seatd} -g wheel";
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "1";
};
wantedBy = ["multi-user.target"];
};
};
};
}

View file

@ -0,0 +1,5 @@
{
imports = [
./hyprland
];
}

View file

@ -0,0 +1,24 @@
{
config,
lib,
inputs',
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
env = config.modules.usrEnv;
in {
# disables Nixpkgs Hyprland module to avoid conflicts
disabledModules = ["programs/hyprland.nix"];
config = mkIf (sys.video.enable && (env.desktop == "Hyprland" && env.isWayland)) {
services.xserver.displayManager.sessionPackages = [inputs'.hyprland.packages.default];
xdg.portal = {
extraPortals = [
inputs'.xdg-portal-hyprland.packages.xdg-desktop-portal-hyprland
];
};
};
}

View file

@ -0,0 +1,53 @@
{
config,
lib,
pkgs,
...
}: let
sys = config.modules.system;
env = config.modules.usrEnv;
inherit (lib) mkForce mkIf;
in {
config = mkIf sys.video.enable {
xdg.portal = {
enable = true;
extraPortals = [pkgs.xdg-desktop-portal-gtk];
config = {
common = let
portal =
if env.desktop == "Hyprland"
then "hyprland"
else if env.desktop == "sway"
then "wlr"
else "gtk"; # FIXME: does this actually implement what we need?
in {
default = [
"hyprland"
"gtk"
];
# for flameshot to work
# https://github.com/flameshot-org/flameshot/issues/3363#issuecomment-1753771427
"org.freedesktop.impl.portal.Screencast" = "${portal}";
"org.freedesktop.impl.portal.Screenshot" = "${portal}";
};
};
# xdg-desktop-wlr (this section) is no longer needed, xdg-desktop-portal-hyprland
# will (and should) override this one
# however in case I run a different compositor on a Wayland host, it can be enabled
wlr = {
enable = mkForce (env.isWayland && env.desktop != "Hyprland");
settings = {
screencast = {
max_fps = 60;
chooser_type = "simple";
chooser_cmd = "${pkgs.slurp}/bin/slurp -f %o -or";
};
};
};
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./environment.nix
./xserver.nix
];
}

View file

@ -0,0 +1,18 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system.video;
env = config.modules.usrEnv;
in {
config = mkIf (sys.enable && !env.isWayland) {
environment.etc."greetd/environments".text = ''
${lib.optionalString (env.desktop == "i3") "i3"}
${lib.optionalString (env.desktop == "awesome") "awesome"}
zsh
'';
};
}

View file

@ -0,0 +1,5 @@
{pkgs, ...}: {
services.xserver.excludePackages = [
pkgs.xterm
];
}

View file

@ -0,0 +1,19 @@
{
pkgs,
lib,
...
}: {
environment.shellAliases = let
nr = "${pkgs.nixos-rebuild}/bin/nixos-rebuild";
in {
# nix aliases
rebuild = "nix-store --verify; pushd ~dotfiles ; ${nr} switch --flake .#$1 --use-remote-sudo && notify-send \"Done\" ; popd";
deploy = "${nr} switch --flake .#$1 --target-host $1 --use-remote-sudo -Lv";
# things I do to keep my home directory clean
wget = "wget --hsts-file='\${XDG_DATA_HOME}/wget-hsts'";
# init a LICENSE file with my go-to gpl3 license
"gpl" = "${lib.getExe pkgs.curl} https://www.gnu.org/licenses/gpl-3.0.txt -o LICENSE";
};
}

View file

@ -0,0 +1,10 @@
{
imports = [
./aliases.nix
./etc.nix
./locale.nix
./packages.nix
./paths.nix
./variables.nix
];
}

View file

@ -0,0 +1,8 @@
{
# https://github.com/NixOS/nixpkgs/issues/72394#issuecomment-549110501
# the service is enabled by default, but this is not set. so by default, you will seee the error
# why?
environment.etc."mdadm.conf".text = ''
MAILADDR root
'';
}

View file

@ -0,0 +1,59 @@
{
pkgs,
lib,
...
}: let
inherit (lib) mkDefault;
in {
time = {
timeZone = "Europe/Istanbul";
hardwareClockInLocalTime = true;
};
services.xserver.xkb = {
layout = "tr";
variant = "";
};
i18n = let
defaultLocale = "en_US.UTF-8";
tr = "tr_TR.UTF-8";
in {
inherit defaultLocale;
extraLocaleSettings = {
LANG = defaultLocale;
LC_COLLATE = defaultLocale;
LC_CTYPE = defaultLocale;
LC_MESSAGES = defaultLocale;
LC_ADDRESS = tr;
LC_IDENTIFICATION = tr;
LC_MEASUREMENT = tr;
LC_MONETARY = tr;
LC_NAME = tr;
LC_NUMERIC = tr;
LC_PAPER = tr;
LC_TELEPHONE = tr;
LC_TIME = tr;
};
supportedLocales = mkDefault [
"en_US.UTF-8/UTF-8"
"tr_TR.UTF-8/UTF-8"
];
# ime configuration
inputMethod = {
enabled = "fcitx5"; # Needed for fcitx5 to work in qt6
fcitx5.addons = with pkgs; [
fcitx5-gtk
fcitx5-lua
libsForQt5.fcitx5-qt
# themes
fcitx5-material-color
];
};
};
}

View file

@ -0,0 +1,27 @@
{
pkgs,
lib,
...
}: {
environment = {
# nixos ships a bunch of packages by default under environment.defaultPackages
# those do not add much to the system closure, but for a little added extra security
# and in an attempt to reduce my system closure size, I would like those to be
# removed from my packages
defaultPackages = lib.mkForce [];
# packages that will be shared across all users and and all systems
# this should generally include tools used for debugging
# or system administration
systemPackages = with pkgs; [
git
curl
wget
pciutils
lshw
man-pages
rsync
bind.dnsutils
];
};
}

View file

@ -0,0 +1,9 @@
{
# enable completions for system packages
# and other stuff
environment.pathsToLink = [
"/share/zsh" # zsh completions
"/share/bash-completion" # bash completions
"/share/nix-direnv" # direnv completions
];
}

View file

@ -0,0 +1,18 @@
_: {
# variables that I want to set globally on all systems
environment.variables = {
FLAKE = "/home/notashelf/.config/nyx";
SSH_AUTH_SOCK = "/run/user/\${UID}/keyring/ssh";
# editors
EDITOR = "nvim";
VISUAL = "nvim";
SUDO_EDITOR = "nvim";
# pager stuff
SYSTEMD_PAGERSECURE = "true";
PAGER = "less -FR";
MANPAGER = "nvim +Man!";
};
}

View file

@ -0,0 +1,52 @@
{
lib,
config,
...
}: let
inherit (lib) mkIf mkMerge;
sys = config.modules.system;
in {
config = mkMerge [
(mkIf (builtins.elem "btrfs" sys.fs) {
# scrub btrfs devices
services.btrfs.autoScrub = {
enable = true;
fileSystems = ["/"];
};
# this fixes initrd.systemd.enable for whatever reason
boot = {
supportedFilesystems = ["btrfs"];
initrd = {
supportedFilesystems = ["btrfs"];
};
};
})
(mkIf (builtins.elem "ext4" sys.fs) {
boot = {
supportedFilesystems = ["ext4"];
initrd = {
supportedFilesystems = ["ext4"];
};
};
})
(mkIf (builtins.elem "exfat" sys.fs) {
boot = {
supportedFilesystems = ["exfat"];
initrd = {
supportedFilesystems = ["exfat"];
};
};
})
# accept both ntfs and ntfs3 as valid values
(mkIf ((builtins.elem "ntfs" sys.fs) || (builtins.elem "ntfs3" sys.fs)) {
boot = {
supportedFilesystems = ["ntfs"];
};
})
];
}

View file

@ -0,0 +1,41 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) attrValues mkDefault;
in {
console = let
variant = "v18n";
in {
enable = mkDefault true;
earlySetup = true;
keyMap = "trq";
font = "ter-powerline-${variant}";
packages = attrValues {inherit (pkgs) terminus_font powerline-fonts;};
};
# FIXME: kmscon, in my testing, is working as advertised and a performance difference
# is observable. However, enabling kmscon seems to *completely* ignore silent boot
# parameters. Not sure if this is a potential conflict with earlySetup (probably not)
# or kmscon not respecting the kernel parameters (more likely). Either way, I will
# revisit this in the future.
services.kmscon = {
enable = false;
hwRender = true;
fonts = [
{
name = "Source Code Pro";
package = pkgs.source-code-pro;
}
];
extraOptions = "--term xterm-256color";
extraConfig = ''
font-size=14
xkb-layout=${config.console.layout}
'';
};
}

View file

@ -0,0 +1,12 @@
{
systemd = {
# TODO: those tend to include sensitie information, maybe we want to disable this?
# it could be an override in the security module
tmpfiles.rules = [
# Enables storing of the kernel log (including stack trace) into pstore upon a panic or crash.
"w /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y"
# Enables storing of the kernel log upon a normal shutdown (shutdown, reboot, halt).
"w /sys/module/printk/parameters/always_kmsg_dump - - - - N"
];
};
}

View file

@ -0,0 +1,9 @@
{
imports = [
./realtime.nix
./crash.nix
./journald.nix
./console.nix
./xdg-portals.nix
];
}

View file

@ -0,0 +1,20 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
dev = config.modules.device;
in {
# https://wiki.archlinux.org/title/Systemd/Journal#Persistent_journals
# limit systemd journal size
# journals get big really fasti and on desktops they are not audited often
# on servers, however, they are important for both security and stability
# thus, persisting them as is remains a good idea
services.journald.extraConfig = mkIf (dev.type != "server") ''
SystemMaxUse=100M
RuntimeMaxUse=50M
SystemMaxFileSize=50M
'';
}

View file

@ -0,0 +1,35 @@
{config, ...}: {
# port of https://gitlab.archlinux.org/archlinux/packaging/packages/realtime-privileges
# see https://wiki.archlinux.org/title/Realtime_process_management
# tldr: realtime processes have higher priority than normal processes
# and that's a good thing
users = {
users."${config.modules.system.mainUser}".extraGroups = ["realtime"];
groups.realtime = {};
};
services.udev.extraRules = ''
KERNEL=="cpu_dma_latency", GROUP="realtime"
'';
security.pam.loginLimits = [
{
domain = "@realtime";
type = "-";
item = "rtprio";
value = 98;
}
{
domain = "@realtime";
type = "-";
item = "memlock";
value = "unlimited";
}
{
domain = "@realtime";
type = "-";
item = "nice";
value = -11;
}
];
}

View file

@ -0,0 +1,18 @@
{
config,
lib,
...
}: let
sys = config.modules.system;
inherit (lib) mkIf;
in {
config = mkIf sys.video.enable {
xdg.portal.config = {
common = {
"org.freedesktop.impl.portal.Secret" = [
"gnome-keyring"
];
};
};
};
}

View file

@ -0,0 +1,16 @@
{config, ...}: let
dev = config.modules.device;
in {
# this should block *most* junk sites
networking = {
stevenblack = {
enable = dev.type != "server"; # don't touch hosts file on a server
block = [
"fakenews"
"gambling"
"porn"
#"social" # blocks stuff like reddit, which I occasionally visit
];
};
};
}

View file

@ -0,0 +1,108 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkForce mkDefault;
in {
imports = [
./firewall
./blocker.nix
./network-manager.nix
./optimize.nix
./resolved.nix
./ssh.nix
./tailscale.nix
./tcpcrypt.nix
./wireless.nix
];
# network tools that are helpful and nice to have
boot.kernelModules = ["af_packet"];
environment.systemPackages = with pkgs; [
mtr
tcpdump
traceroute
];
networking = {
# generate a unique hostname by hashing the hostname
# with md5 and taking the first 8 characters of the hash
# this is especially helpful while using zfs but still
# ensures that there will be a unique hostId even when
# we are not using zfs
hostId = builtins.substring 0 8 (builtins.hashString "md5" config.networking.hostName);
# global dhcp has been deprecated upstream
# use the new networkd service instead of the legacy
# "script-based" network setups. Host may contain individual
# dhcp interfaces or systemd-networkd configurations in host
# specific directories
useDHCP = mkForce false;
useNetworkd = mkForce true;
# interfaces are assigned names that contain topology information (e.g. wlp3s0)
# and thus should be consistent across reboots
# this already defaults to true, we set it in case it changes upstream
usePredictableInterfaceNames = mkDefault true;
# dns
nameservers = [
# cloudflare, yuck
# shares data
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
# quad9, said to be the best
# shares *less* data
"9.9.9.9"
"149.112.112.112"
"2620:fe::fe"
"2620:fe::9"
];
# search paths used when resolving domain names
# this can allow us to find hosts on private networks
# e.g. wireguard, tailscale and headscale
search = [
"notashelf.dev" # referss to the server itself
"notashelf.notashelf.dev" # headscale network
];
};
# enable wireless database, it helps with finding the right channels
hardware.wirelessRegulatoryDatabase = true;
# allow for the system to boot without waiting for the network interfaces are online
# speeds up boot times
systemd = let
# TODO: allow for the hosts to define those interfaces
ethernetDevices = [
"wlp1s0f0u8" # wifi dongle
];
in {
# according to 23.11 release notes, wait-online target has long been fixed
# spoiler: no it's not.
network.wait-online.enable = false;
services =
{
NetworkManager-wait-online.enable = false;
# disable networkd and resolved from being restarted on configuration changes
# lets me avoid a short network outage when I change the configuration
# also means that I **must** reboot to make sure my network changes are
# are properly propagated
systemd-networkd.stopIfChanged = false;
systemd-resolved.stopIfChanged = false;
}
// lib.concatMapAttrs (_: v: v) (lib.genAttrs ethernetDevices (device: {
# Assign an IP address when the device is plugged in rather than on startup. Needed to prevent
# blocking the boot sequence when the device is unavailable, as it is hotpluggable.
"network-addresses-${device}".wantedBy = lib.mkForce ["sys-subsystem-net-devices-${device}.device"];
}));
};
}

View file

@ -0,0 +1,42 @@
{
pkgs,
lib,
config,
...
}: let
inherit (lib) mkForce;
dev = config.modules.device;
cfg = config.networking.nftables;
in {
imports = [
./nftables
./fail2ban.nix
./tarpit.nix
];
config = {
# enable opensnitch firewall
# inactive until opensnitch UI is opened
# since the UI cannot be opened on servers, we
# disable it if dev.type is server
services.opensnitch.enable = dev.type != "server";
networking.firewall = {
enable = !cfg.enable;
package =
if cfg.enable
then pkgs.iptables-nftables-compat
else pkgs.iptables;
allowedTCPPorts = [443 8080];
allowedUDPPorts = [];
allowPing = dev.type == "server";
logReversePathDrops = true;
logRefusedConnections = false; # avoid log spam
# Leaks IPv6 route table entries due to kernel bug. This leads to degraded
# IPv6 performance in some situations.
# checkReversePath = config.boot.kernelPackages.kernelAtLeast "5.16";
checkReversePath = mkForce false; # Don't filter DHCP packets, according to nixops-libvirtd
};
};
}

View file

@ -0,0 +1,77 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf mkMerge mkForce;
inherit (lib.strings) concatStringsSep;
sys = config.modules.system;
in {
# fail2ban firewall jail
services.fail2ban = {
enable = true;
extraPackages = [pkgs.nftables]; # make nftables accessible to fail2ban service
ignoreIP = [
"127.0.0.0/8" # localhost
"10.0.0.0/8" # wireguard
"100.64.0.0/16" # tailscale
"192.168.0.0/16" # local network
];
maxretry = 7;
bantime-increment = {
enable = true;
rndtime = "12m";
overalljails = true;
multipliers = "4 8 16 32 64 128 256 512 1024 2048";
maxtime = "5000h"; # get banned for 5000 hours idiot
};
jails = mkMerge [
{
# sshd jail
sshd = mkForce ''
enabled = true
port = ${concatStringsSep "," (map toString config.services.openssh.ports)}
mode = aggressive
'';
}
{
# nftables jail
nftables-common = mkForce ''
enabled = true
banaction = nftables-multiport
chain = input
'';
}
(mkIf sys.services.vaultwarden.enable {
# vaultwarden and vaultwarden admin interface jails
vaultwarden = ''
enabled = true
port = 80,443,8822
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /var/log/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400
'';
vaultwarden-admin = ''
enabled = true
port = 80,443
filter = vaultwarden-admin
banaction = %(banaction_allports)s
logpath = /var/log/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400
'';
})
];
};
}

View file

@ -0,0 +1,70 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf mkRuleset;
sys = config.modules.system;
cfg = config.networking.nftables;
check-results =
pkgs.runCommand "check-nft-ruleset" {
ruleset = pkgs.writeText "nft-ruleset" cfg.ruleset;
} ''
mkdir -p $out
${pkgs.nftables}/bin/nft -c -f $ruleset 2>&1 > $out/message \
&& echo false > $out/assertion \
|| echo true > $out/assertion
'';
in {
imports = [./rules.nix];
config = mkIf sys.networking.nftables.enable {
networking.nftables = {
enable = true;
# flush ruleset on each reload
flushRuleset = true;
# nftables.tables is semi-verbatim configuration
# that is inserted **before** nftables.ruleset
# as per the nftables module.
tables = {
"fail2ban" = {
name = "fail2ban-nftables";
family = "ip";
content = ''
# <https://wiki.gbe0.com/en/linux/firewalling-and-filtering/nftables/fail2ban>
chain input {
type filter hook input priority 100;
}
'';
};
};
# our ruleset, built with our local ruleset builder from lib/network/firewall.nix
# I prefer using this to the nftables.tables.* and verbatim nftables.ruleset = ""
# kinds of configs, as it allows me to programmatically approach to my ruleset
# instead of structuring it inside a multiline string. nftables.rules, which is
# located in ./rules.nix, is easily parsable and modifiable with the help of Nix.
ruleset = mkRuleset cfg.rules;
};
assertions = [
{
assertion = import "${check-results}/assertion";
message = ''
Bad config:
${builtins.readFile "${check-results}/message"}
'';
}
];
# pin IFD as a system dependency
# this makes sure the IFD result is realised in time
# without making the IFD a part of the system
# unlike system.extraDependencies
system.checks = [check-results];
};
}

View file

@ -0,0 +1,97 @@
{
config,
lib,
...
}: let
inherit (lib) entryBefore entryBetween entryAfter entryAnywhere;
in {
networking.nftables.rules = {
inet = {
filter = {
input = {
loopback = entryAnywhere {
field = "iifname";
value = "lo";
policy = "accept";
};
established-locally = entryAfter ["loopback"] {
protocol = "ct";
field = "state";
value = ["established" "related"];
policy = "accept";
};
basic-icmp6 = entryAfter ["loopback" "established-locally"] {
protocol = "ip6 nexthdr icmpv6 icmpv6";
field = "type";
value = [
"destination-unreachable"
"packet-too-big"
"time-exceeded"
"parameter-problem"
"nd-router-advert"
"nd-neighbor-solicit"
"nd-neighbor-advert"
#"mld-listener-query" "nd-router-solicit" # for routers
];
policy = "accept";
};
basic-icmp = entryAfter ["loopback" "established-locally"] {
protocol = "ip protocol icmp icmp";
field = "type";
value = [
"destination-unreachable"
"router-advertisement"
"time-exceeded"
"parameter-problem"
];
policy = "accept";
};
ping6 = entryBefore ["basic-icmp6"] {
protocol = "ip6 nexthdr icmpv6 icmpv6";
field = "type";
value = "echo-request";
policy = "accept";
};
ping = entryBefore ["basic-icmp"] {
protocol = "ip protocol icmp icmp";
field = "type";
value = "echo-request";
policy = "accept";
};
ssh = entryBetween ["basic-icmp6" "basic-icmp" "ping6" "ping"] ["default"] {
protocol = "tcp";
field = "dport";
value = config.services.openssh.ports;
policy = "accept";
};
default = entryAfter ["loopback" "established-locally" "basic-icmp6" "basic-icmp" "ping6" "ping"] {
policy = lib.mkDefault "drop";
};
};
# accept all outgoing traffic
output = {
default = entryAnywhere {
policy = "accept";
};
};
# let nftables forward traffic
# we decide whether the host can forward traffic
# via sysctl settings elsewhere
forward = {
default = entryAnywhere {
policy = "accept";
};
};
};
};
};
}

View file

@ -0,0 +1,32 @@
# jebait agressive port scanners by wasting their time with connection that'll never make it in
# this *does* have performance implications, however, so be careful which hosts you enable it for
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.networking.tarpit;
in {
config = mkIf cfg.enable {
services.endlessh-go = {
enable = true;
port = 22;
openFirewall = true;
extraOptions = [
"-alsologtostderr"
"-geoip_supplier max-mind-db"
"-max_mind_db ${pkgs.clash-geoip}/etc/clash/Country.mmdb"
];
prometheus = {
enable = true;
port = 9105;
};
};
};
}

View file

@ -0,0 +1,36 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf mkForce;
dev = config.modules.device;
sys = config.modules.system;
inherit (sys.networking) wireless;
in {
# we use networkmanager manage network devices locally
networking.networkmanager = {
enable = true;
plugins = mkForce []; # disable all plugins, we don't need them
dns = "systemd-resolved"; # use systemd-resolved as dns backend
unmanaged = [
"interface-name:tailscale*"
"interface-name:br-*"
"interface-name:rndis*"
"interface-name:docker*"
"interface-name:virbr*"
];
wifi = {
inherit (wireless) backend; # this can be iwd or wpa_supplicant, use wpa_supp. until iwd support is stable
macAddress = "random"; # use a random mac address on every boot
powersave = true; # enable wifi powersaving
scanRandMacAddress = true; # MAC address randomization of a Wi-Fi device during scanning
};
ethernet.macAddress = mkIf (dev.type != "server") "random"; # causes server to be unreachable over SSH
};
}

View file

@ -0,0 +1,78 @@
{
config,
lib,
...
}: let
sys = config.modules.system.networking;
inherit (lib) mkIf;
in {
config = mkIf sys.optimizeTcp {
boot = {
kernelModules = ["tls" "tcp_bbr"];
kernel.sysctl = {
# TCP hardening
# Prevent bogus ICMP errors from filling up logs.
"net.ipv4.icmp_ignore_bogus_error_responses" = 1;
# Reverse path filtering causes the kernel to do source validation of
# packets received from all interfaces. This can mitigate IP spoofing.
"net.ipv4.conf.default.rp_filter" = 1;
"net.ipv4.conf.all.rp_filter" = 1;
# Do not accept IP source route packets (we're not a router)
"net.ipv4.conf.all.accept_source_route" = 0;
"net.ipv6.conf.all.accept_source_route" = 0;
# Don't send ICMP redirects (again, we're on a router)
"net.ipv4.conf.all.send_redirects" = 0;
"net.ipv4.conf.default.send_redirects" = 0;
# Refuse ICMP redirects (MITM mitigations)
"net.ipv4.conf.all.accept_redirects" = 0;
"net.ipv4.conf.default.accept_redirects" = 0;
"net.ipv4.conf.all.secure_redirects" = 0;
"net.ipv4.conf.default.secure_redirects" = 0;
"net.ipv6.conf.all.accept_redirects" = 0;
"net.ipv6.conf.default.accept_redirects" = 0;
# Protects against SYN flood attacks
"net.ipv4.tcp_syncookies" = 1;
# Incomplete protection again TIME-WAIT assassination
"net.ipv4.tcp_rfc1337" = 1;
# And other stuff
"net.ipv4.conf.all.log_martians" = true;
"net.ipv4.conf.default.log_martians" = true;
"net.ipv4.icmp_echo_ignore_broadcasts" = true;
"net.ipv6.conf.default.accept_ra" = 0;
"net.ipv6.conf.all.accept_ra" = 0;
"net.ipv4.tcp_timestamps" = 0;
# TCP optimization
# TCP Fast Open is a TCP extension that reduces network latency by packing
# data in the senders initial TCP SYN. Setting 3 = enable TCP Fast Open for
# both incoming and outgoing connections:
"net.ipv4.tcp_fastopen" = 3;
# Bufferbloat mitigations + slight improvement in throughput & latency
"net.ipv4.tcp_congestion_control" = "bbr";
"net.core.default_qdisc" = "cake";
# Other stuff that I am too lazy to document
"net.core.optmem_max" = 65536;
"net.core.rmem_default" = 1048576;
"net.core.rmem_max" = 16777216;
"net.core.somaxconn" = 8192;
"net.core.wmem_default" = 1048576;
"net.core.wmem_max" = 16777216;
"net.ipv4.ip_local_port_range" = "16384 65535";
"net.ipv4.tcp_max_syn_backlog" = 8192;
"net.ipv4.tcp_max_tw_buckets" = 2000000;
"net.ipv4.tcp_mtu_probing" = 1;
"net.ipv4.tcp_rmem" = "4096 1048576 2097152";
"net.ipv4.tcp_slow_start_after_idle" = 0;
"net.ipv4.tcp_tw_reuse" = 1;
"net.ipv4.tcp_wmem" = "4096 65536 16777216";
"net.ipv4.udp_rmem_min" = 8192;
"net.ipv4.udp_wmem_min" = 8192;
"net.netfilter.nf_conntrack_generic_timeout" = 60;
"net.netfilter.nf_conntrack_max" = 1048576;
"net.netfilter.nf_conntrack_tcp_timeout_established" = 600;
"net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 1;
};
};
};
}

View file

@ -0,0 +1,34 @@
{
services = {
resolved = {
# enable systemd DNS resolver daemon
enable = true;
# this is necessary to get tailscale picking up your headscale instance
# and allows you to ping connected hosts by hostname
domains = ["~."];
# DNSSEC provides to DNS clients (resolvers) origin authentication of DNS data, authenticated denial of existence
# and data integrity but not availability or confidentiality.
# this is considered EXPERIMENTAL and UNSTABLE according to upstream
# PLEASE SEE <https://github.com/systemd/systemd/issues/25676#issuecomment-1634810897>
# before you decide to set this. I have it set to false as the issue
# does not inspire confidence in systemd's ability to manage this
dnssec = "false";
# additional configuration to be appeneded to systemd-resolved configuration
extraConfig = ''
# <https://wiki.archlinux.org/title/Systemd-resolved#DNS_over_TLS>
# apparently upstream (systemd) recommends this to be false
# `allow-downgrade` is vulnerable to downgrade attacks
DNSOverTLS=yes # or allow-downgrade
'';
# ideally our fallbackDns should be something more widely available
# but I do not want my last resort to sell my data to every company available
# NOTE: DNS fallback is not a recovery DNS
# See <https://github.com/systemd/systemd/issues/5771#issuecomment-296673115>
fallbackDns = ["9.9.9.9"];
};
};
}

View file

@ -0,0 +1,131 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkForce mkDefault;
inherit (lib.strings) concatStringsSep;
inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) elemAt;
in {
services.openssh = {
# enable openssh
enable = true;
openFirewall = true; # the ssh port(s) should be automatically passed to the firewall's allowedTCPports
ports = [30]; # the port(s) openssh daemon should listen on
startWhenNeeded = true; # automatically start the ssh daemon when it's required
settings = {
# no root login
PermitRootLogin = mkForce "no";
# no password auth
# force publickey authentication only
PasswordAuthentication = false;
AuthenticationMethods = "publickey";
PubkeyAuthentication = "yes";
ChallengeResponseAuthentication = "no";
UsePAM = "no";
# remove sockets as they get stale
# this will unbind gnupg sockets if they exists
StreamLocalBindUnlink = "yes";
KbdInteractiveAuthentication = mkDefault false;
UseDns = false; # no
X11Forwarding = false; # ew xorg
# key exchange algorithms recommended by `nixpkgs#ssh-audit`
KexAlgorithms = [
"curve25519-sha256"
"curve25519-sha256@libssh.org"
"diffie-hellman-group16-sha512"
"diffie-hellman-group18-sha512"
"diffie-hellman-group-exchange-sha256"
"sntrup761x25519-sha512@openssh.com"
];
# message authentication code algorithms recommended by `nixpkgs#ssh-audit`
Macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
];
# kick out inactive sessions
ClientAliveCountMax = 5;
ClientAliveInterval = 60;
# max auth attempts
MaxAuthTries = 3;
};
hostKeys = mkDefault [
{
bits = 4096;
path = "/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
bits = 4096;
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
};
programs.ssh = let
# a list of hosts that are connected over Tailscale
# it would be better to construct this list dynamically
# but we hardcode it because we cannot check if a host is
# authenticated - that needs manual intervention
hosts = ["helios" "enyo" "hermes"];
# generate the ssh config for the hosts
mkHostConfig = hostname: ''
# Configuration for ${hostname}
Host ${hostname}
HostName ${hostname}
Port ${toString (elemAt config.services.openssh.ports 0)}
StrictHostKeyChecking=accept-new
'';
hostConfig = concatStringsSep "\n" (map mkHostConfig hosts);
in {
startAgent = !config.modules.system.yubikeySupport.enable;
extraConfig = ''
${hostConfig}
'';
# ship github/gitlab/sourcehut host keys to avoid MiM (man in the middle) attacks
knownHosts = mapAttrs (_: mkForce) {
github-rsa = {
hostNames = ["github.com"];
publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==";
};
github-ed25519 = {
hostNames = ["github.com"];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
};
gitlab-rsa = {
hostNames = ["gitlab.com"];
publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9";
};
gitlab-ed25519 = {
hostNames = ["gitlab.com"];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
};
sourcehut-rsa = {
hostNames = ["git.sr.ht"];
publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ+l/lvYmaeOAPeijHL8d4794Am0MOvmXPyvHTtrqvgmvCJB8pen/qkQX2S1fgl9VkMGSNxbp7NF7HmKgs5ajTGV9mB5A5zq+161lcp5+f1qmn3Dp1MWKp/AzejWXKW+dwPBd3kkudDBA1fa3uK6g1gK5nLw3qcuv/V4emX9zv3P2ZNlq9XRvBxGY2KzaCyCXVkL48RVTTJJnYbVdRuq8/jQkDRA8lHvGvKI+jqnljmZi2aIrK9OGT2gkCtfyTw2GvNDV6aZ0bEza7nDLU/I+xmByAOO79R1Uk4EYCvSc1WXDZqhiuO2sZRmVxa0pQSBDn1DB3rpvqPYW+UvKB3SOz";
};
sourcehut-ed25519 = {
hostNames = ["git.sr.ht"];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60";
};
};
};
}

View file

@ -0,0 +1,96 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf mkDefault optionals mkBefore;
inherit (config.services) tailscale;
sys = config.modules.system.networking;
cfg = sys.tailscale;
upFlags =
cfg.flags.default
++ ["--authkey file:${config.age.secrets.tailscale-client.path}"]
++ optionals cfg.isServer ["--advertise-exit-node"]
++ optionals (cfg.endpoint != null) ["--login-server ${cfg.endpoint}"]
# TODO: test if specifying an operator messes with the autologin service
# which, as you expect, does not run as the operator user
++ optionals (cfg.operator != null) ["--operator" cfg.operator];
in {
config = mkIf cfg.enable {
# make the tailscale command usable to users
environment.systemPackages = [pkgs.tailscale];
networking.firewall = {
# always allow traffic from the designated tailscale interface
trustedInterfaces = ["${tailscale.interfaceName}"];
checkReversePath = "loose";
# allow
allowedUDPPorts = [tailscale.port];
};
boot.kernel = {
sysctl = {
# # Enable IP forwarding
# required for Wireguard & Tailscale/Headscale subnet feature
# See <https://tailscale.com/kb/1019/subnets/?tab=linux#step-1-install-the-tailscale-client>
"net.ipv4.ip_forward" = true;
"net.ipv6.conf.all.forwarding" = true;
};
};
# enable tailscale, inter-machine VPN service
services.tailscale = {
enable = true;
permitCertUid = "root";
useRoutingFeatures = mkDefault "both";
# TODO: these flags still need to be specified with `tailscale up`
# for some reason
extraUpFlags = upFlags;
};
systemd = {
services = {
# lets not send our logs to log.tailscale.io
# unless I get to know what they do with the logs
tailscaled.serviceConfig.Environment = mkBefore ["TS_NO_LOGS_NO_SUPPORT=true"];
# oneshot tailscale authentication servcie
# TODO: this implies tailscale has been authenticated before with our own login server
# ideally we should have a way to authenticate tailscale with our own login server in
# this service, likely through an option in the system module
tailscale-autoconnect = {
description = "Automatic connection to Tailscale";
# make sure tailscale is running before trying to connect to tailscale
after = ["network-pre.target" "tailscale.service"];
wants = ["network-pre.target" "tailscale.service"];
wantedBy = ["multi-user.target"];
# set this service as a oneshot job
serviceConfig.Type = "oneshot";
# have the job run this shell script
script = ''
# wait for tailscaled to settle
sleep 2
# check if we are already authenticated to tailscale
status="$(${pkgs.tailscale}/bin/tailscale status -json | ${pkgs.jq}/bin/jq -r .BackendState)"
if [ $status = "Running" ]; then # if so, then do nothing
exit 0
fi
# otherwise authenticate with tailscale
${pkgs.tailscale}/bin/tailscale up ${toString upFlags}
'';
};
};
network.wait-online.ignoredInterfaces = ["${tailscale.interfaceName}"];
};
};
}

View file

@ -0,0 +1,101 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
dev = config.modules.device;
in {
# get rid of the tcpcrypt module provided by nixpkgs
# it is unmaintained and I cannot be arsed to PR a fix
disabledModules = ["services/networking/tcpcrypt.nix"];
config = mkIf (dev.type != "server") {
# FIXME: the upstream tcpcrypd service is unmaintained and poorly designed
# networking.tcpcrypt.enable = true;
# create a system user for the tcpcrypt service
# this is the user we will use the systemd service as
users = {
groups.tcpcryptd = {};
users.tcpcryptd = {
description = "tcpcrypt daemon user";
group = "tcpcryptd";
uid = config.ids.uids.tcpcryptd; # nixpkgs already defines a hardcoded uid, use it
};
};
# enable opportunistic TCP encryption
# this is NOT a pancea, however, if the receiver supports encryption and the attacker is passive
# privacy will be more plausible (but not guaranteed, unlike what the option docs suggest)
# NOTE: the systemd service below is rewritten to be an alternative to networking.tcpcrypt.enable
# it lacks hardening and SHOULD NOT BE USED until further notice.
systemd.services.tcpcrypt = let
# borrowed from fedora's tcpcrypt rpm spec
# <https://src.fedoraproject.org/rpms/tcpcrypt/blob/rawhide/f/tcpcryptd-firewall>
tcpcryptd-firewall = pkgs.writeShellApplication {
name = "tcpcryptd-firewall";
runtimeInputs = [pkgs.iptables];
text = ''
# use iptables manually
if [ "$1" = "start" ]; then
iptables -t raw -N nixos-tcpcrypt
iptables -t raw -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
iptables -t raw -I PREROUTING -j nixos-tcpcrypt
iptables -t mangle -N nixos-tcpcrypt
iptables -t mangle -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
iptables -t mangle -I POSTROUTING -j nixos-tcpcrypt
fi
if [ "$1" = "stop" ]; then
iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true
iptables -t raw -D PREROUTING -j nixos-tcpcrypt || true
iptables -t raw -F nixos-tcpcrypt || true
iptables -t raw -X nixos-tcpcrypt || true
iptables -t mangle -F nixos-tcpcrypt || true
iptables -t mangle -X nixos-tcpcrypt || true
fi
'';
};
in {
description = "tcpcrypt, opportunistic tcp encryption";
wantedBy = ["multi-user.target"];
after = ["network.target" "syslog.target"];
serviceConfig = {
Restart = "on-failure";
RestartSec = 10;
RuntimeDirectory = "tcpcryptd";
RuntimeDirectoryMode = "0750";
};
preStart = ''
echo -en "Starting tcpcryptd\n"
${pkgs.procps}/bin/sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state
${pkgs.procps}/bin/sysctl -w net.ipv4.tcp_ecn=0
# start the firewall
${tcpcryptd-firewall}/bin/tcpcryptd-firewall start
'';
# -f disables network test
script = "${pkgs.tcpcrypt}/bin/tcpcryptd -v -f -x 0x10 ";
postStop = ''
echo -en "Stopped tcpcrypd, restoring tcp_enc state\n"
if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then
${pkgs.procps}/bin/sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state)
fi
# stop the firewall
${tcpcryptd-firewall}/bin/tcpcryptd-firewall stop
'';
};
};
}

View file

@ -0,0 +1,71 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.lists) optionals;
inherit (lib.attrsets) optionalAttrs;
inherit (lib.meta) getExe;
sys = config.modules.system;
inherit (sys.networking) wireless;
in {
config = {
environment.systemPackages = optionals (wireless.backend == "iwd") pkgs.iwgtk;
networking.wireless =
{
enable = wireless.backend == "wpa_supplicant";
# configure iwd
iwd = {
enable = wireless.backend == "iwd";
settings = {
#Rank.BandModifier5Ghz = 2.0;
#Scan.DisablePeriodicScan = true;
Settings.AutoConnect = true;
General = {
AddressRandomization = "network";
AddressRandomizationRange = "full";
EnableNetworkConfiguration = true;
RoamRetryInterval = 15;
};
Network = {
EnableIPv6 = true;
RoutePriorityOffset = 300;
# NameResolvingService = "resolvconf";
};
};
};
}
// optionalAttrs wireless.allowImperative {
# Imperative Configuration
userControlled.enable = true;
allowAuxiliaryImperativeNetworks = true; # patches wpa_supplicant
extraConfig = ''
update_config=1
'';
};
# launch indicator as a daemon on login if wireless backend
# is defined as iwd
systemd = {
# make sure we ensure the existence of wpa_supplicant config
# before we run the wpa_supplicant service
services.wpa_supplicant.preStart = ''
touch /etc/wpa_supplicant.conf
'';
user.services.iwgtk = mkIf (wireless.backend == "iwd") {
serviceConfig.ExecStart = "${getExe pkgs.iwgtk} -i";
wantedBy = ["graphical-session.target"];
partOf = ["graphical-session.target"];
};
};
};
}

View file

@ -0,0 +1,39 @@
{
pkgs,
lib,
...
}: {
imports = [
./direnv.nix
./nano.nix
];
programs = {
bash = {
# when entering the interactive shell, set the history file to
# the config directory to avoid cluttering the $HOME directory
interactiveShellInit = ''
export HISTFILE="$XDG_STATE_HOME"/bash_history
'';
# initialize starship in impromptu bash sessions
# (e.g. when running a command without entering a shell)
promptInit = ''
eval "$(${lib.getExe pkgs.starship} init bash)"
'';
};
# less pager
less.enable = true;
# home-manager is quirky as ever, and wants this to be set in system config
# instead of just home-manager
zsh.enable = true;
# run commands without installing the programs
comma.enable = true;
# type "fuck" to fix the last command that made you go "fuck"
thefuck.enable = true;
};
}

View file

@ -0,0 +1,103 @@
{
config,
pkgs,
...
}: {
programs.direnv = {
enable = true;
# shut up. SHUT UP
silent = true;
# faster, persistent implementation of use_nix and use_flake
nix-direnv = {
enable = true;
package = pkgs.nix-direnv.override {
nix = config.nix.package;
};
};
# enable loading direnv in nix-shell nix shell or nix develop
loadInNixShell = true;
direnvrcExtra = ''
: ''${XDG_CACHE_HOME:=$HOME/.cache}
declare -A direnv_layout_dirs
# https://github.com/direnv/direnv/wiki/Customizing-cache-location#hashed-directories
direnv_layout_dir() {
echo "''${direnv_layout_dirs[$PWD]:=$(
echo -n "$XDG_CACHE_HOME"/direnv/layouts/
echo -n "$PWD" | ${pkgs.perl}/bin/shasum | cut -d ' ' -f 1
)}"
}
# Usage: daemonize <name> [<command> [...<args>]]
#
# Starts the command in the background with an exclusive lock on $name.
#
# If no command is passed, it uses the name as the command.
#
# Logs are in .direnv/$name.log
#
# To kill the process, run `kill $(< .direnv/$name.pid)`.
daemonize() {
local name=$1
shift
local pid_file=$(direnv_layout_dir)/$name.pid
local log_file=$(direnv_layout_dir)/$name.log
if [[ $# -lt 1 ]]; then
cmd=$name
else
cmd=$1
shift
fi
if ! has "$cmd"; then
echo "ERROR: $cmd not found"
return 1
fi
mkdir -p "$(direnv_layout_dir)"
# Open pid_file on file descriptor 200
exec 200>"$pid_file"
# Check that we have exclusive access
if ! flock --nonblock 200; then
echo "daemonize[$name] is already running as pid $(< "$pid_file")"
return
fi
# Start the process in the background. This requires two forks to escape the
# control of bash.
# First fork
(
# Second fork
(
echo "daemonize[$name:$BASHPID]: starting $cmd $*" >&2
# Record the PID for good measure
echo "$BASHPID" >&200
# Redirect standard file descriptors
exec 0</dev/null
exec 1>"$log_file"
exec 2>&1
# Used by direnv
exec 3>&-
exec 4>&-
# Run command
exec "$cmd" "$@"
) &
) &
# Release that file descriptor
exec 200>&-
}
'';
};
}

View file

@ -0,0 +1,48 @@
{pkgs, ...}: {
programs.nano = {
# enabled by default anyway, we can keep it in case my neovim config breaks
enable = true;
nanorc = ''
include ${pkgs.nanorc}/share/*.nanorc # extended syntax highlighting
# Options
# https://github.com/davidhcefx/Modern-Nano-Keybindings
set tabsize 4
set tabstospaces
set linenumbers
set numbercolor yellow,normal
set indicator # side-bar for indicating cur position
set smarthome # `Home` jumps to line start first
set afterends # `Ctrl+Right` move to word ends instead of word starts
set wordchars "_" # recognize '_' as part of a word
set zap # delete selected text as a whole
set historylog # remember search history
set multibuffer # read files into multibuffer instead of insert
set mouse # enable mouse support
bind M-R redo main
bind ^C copy main
bind ^X cut main
bind ^V paste main
bind ^K zap main
bind ^H chopwordleft all
bind ^Q exit all
bind ^Z suspend main
bind M-/ comment main
bind ^Space complete main
bind M-C location main
bind ^E wherewas all
bind M-E findprevious all
bind ^R replace main
bind ^B pageup all # vim-like support
bind ^F pagedown all
bind ^G firstline all
bind M-G lastline all
bind M-1 help all # fix ^G been used
bind Sh-M-C constantshow main # fix M-C, M-F and M-b been used
bind Sh-M-F formatter main
bind Sh-M-B linter main
'';
};
}

View file

@ -0,0 +1,12 @@
{
imports = [
./systemd
./fstrim.nix
./fwupd.nix
./logrotate.nix
./lvm.nix
./thermald.nix
./zram.nix
];
}

View file

@ -0,0 +1,39 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf;
in {
# if lvm is enabled, then tell it to issue discards
# (this is good for SSDs and has almost no downsides on HDDs, so
# it's a good idea to enable it unconditionally)
environment.etc."lvm/lvm.conf".text = mkIf config.services.lvm.enable ''
devices {
issue_discards = 1
}
'';
# discard blocks that are not in use by the filesystem, good for SSDs
services.fstrim = {
# we may enable this unconditionally across all systems becuase it's performance
# impact is negligible on systems without a SSD - which means it's a no-op with
# almost no downsides aside from the service firing once per week
enable = true;
# the default value, good enough for average-load systems
interval = "weekly";
};
# tweak fstim service to run only when on AC power
# and to be nice to other processes
# (this is a good idea for any service that runs periodically)
systemd.services.fstrim = {
unitConfig.ConditionACPower = true;
serviceConfig = {
Nice = 19;
IOSchedulingClass = "idle";
};
};
}

View file

@ -0,0 +1,13 @@
{config, ...}: {
# firmware updater for machine hardware
services.fwupd = {
enable = true;
daemonSettings.EspLocation = config.boot.loader.efi.efiSysMountPoint;
# newer hardware may have their firmware in testing
# e.g. Framework devices don't have their firmware in stable yet
# TODO: make a system-level option that sets this value for hosts
# that have testing firmware
# extraRemotes = [ "lvfs-testing" ];
};
}

View file

@ -0,0 +1,28 @@
{
pkgs,
lib,
...
}: {
services.logrotate.settings.header = {
# general
global = true;
dateext = true;
dateformat = "-%Y-%m-%d";
nomail = true;
missingok = true;
copytruncate = true;
# rotation frequency
priority = 1;
frequency = "weekly";
rotate = 7; # special value, means every 7 days
minage = 7; # avoid removing logs that are less than 7 days old
# compression
compress = true; # lets compress logs to save space
compresscmd = "${lib.getExe' pkgs.zstd "zstd"}";
compressoptions = " -Xcompression-level 10";
compressext = "zst";
uncompresscmd = "${lib.getExe' pkgs.zstd "unzstd"}";
};
}

View file

@ -0,0 +1,6 @@
{lib, ...}: let
inherit (lib) mkDefault;
in {
# I don't use lvm, can be disabled
services.lvm.enable = mkDefault false;
}

View file

@ -0,0 +1,32 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
inherit (config) modules;
env = modules.usrEnv;
cfg = env.brightness;
in {
config = mkIf cfg.enable {
systemd.services."system-brightnessd" = {
description = "Automatic backlight management with systemd";
# TODO: maybe this needs to be a part of graphical-session.target?
# I am not very sure how wantedBy and partOf really work
wantedBy = ["default.target"];
partOf = ["graphical-session.target"];
# TODO: this needs to be hardened
# not that a backlight service is a security risk, but it's a good habit
# to keep our systemd services as secure as possible
serviceConfig = {
Type = "${cfg.serviceType}";
ExecStart = "${lib.getExe cfg.package}";
Restart = "never";
RestartSec = "5s";
};
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./brightnessd.nix
./oomd.nix
];
}

View file

@ -0,0 +1,31 @@
{
config,
lib,
...
}: {
systemd = {
# Systemd OOMd
# Fedora enables these options by default. See the 10-oomd-* files here:
# https://src.fedoraproject.org/rpms/systemd/tree/3211e4adfcca38dfe24188e28a65b1cf385ecfd6
# by default it only kills cgroups. So either systemd services marked for killing under OOM
# or (disabled by default, enabled by us) the entire user slice. Fedora used to kill root
# and system slices, but their oomd configuration has since changed.
# TODO: maybe disable user slice by default?
oomd = {
enable = !config.systemd.enableUnifiedCgroupHierarchy;
enableRootSlice = true;
enableSystemSlice = true;
enableUserSlices = true;
extraConfig = {
"DefaultMemoryPressureDurationSec" = "20s";
};
};
# make it that nix builds are more likely killed than important services.
# 100 is the default for user slices and 500 is systemd-coredumpd@
# this is important because as my system got huge, nix flake check started
# causing OOMs and killing my desktop environment - which I do not like
# nuke nix-daemon if it gets too memory hungry
services.nix-daemon.serviceConfig.OOMScoreAdjust = lib.mkDefault 350;
};
}

View file

@ -0,0 +1,4 @@
{
# monitor and control temparature
services.thermald.enable = true;
}

View file

@ -0,0 +1,34 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
in {
# compress half of the ram to use as swap
# basically, get more memory per memory
zramSwap = {
enable = true;
algorithm = "zstd";
memoryPercent = 90; # defaults to 50
};
# <https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html>
boot.kernel.sysctl = mkIf config.zramSwap.enable {
# zram is relatively cheap, prefer swap
# swappiness refers to the kernel's willingness prefer swap
# over memory. higher values mean that we'll utilize swap more often
# which preserves memory, but will cause performance issues as well
# as wear on the drive
"vm.swappiness" = 180; # 0-200
# level of reclaim when memory is being fragmented
"vm.watermark_boost_factor" = 0; # 0 to disable
# aggressiveness of kswapd
# it defines the amount of memory left in a node/system before kswapd is woken up
"vm.watermark_scale_factor" = 125; # 0-300
# zram is in memory, no need to readahead
# page-cluster refers to the number of pages up to which
# consecutive pages are read in from swap in a single attempt
"vm.page-cluster" = 0;
};
}

View file

@ -0,0 +1,5 @@
{
imports = [
./qt.nix
];
}

View file

@ -0,0 +1,16 @@
{
lib,
pkgs,
...
}: {
environment.variables = let
qmlPackages = with pkgs; [
plasma5Packages.qqc2-desktop-style
plasma5Packages.kirigami2
];
qtVersion = pkgs.qt515.qtbase.version;
in {
"QML2_IMPORT_PATH" = "${lib.concatStringsSep ":" (builtins.map (p: "${p}/lib/qt-${qtVersion}/qml") qmlPackages)}";
};
}

View file

@ -0,0 +1,9 @@
{
# we want to handle user configurations on a per-file basis
# users that are not in users/<username>.nix don't get to be a real user
imports = [
./notashelf.nix
./nix-builder.nix
./root.nix
];
}

View file

@ -0,0 +1,17 @@
{
users = {
groups.nix = {};
users.nix-builder = {
useDefaultShell = true;
isSystemUser = true;
createHome = true;
group = "nix";
home = "/var/tmp/nix-builder";
openssh.authorizedKeys = {
keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK3Oglg7aVYQJzGa4JhnvDhRYx0jkLHwDT/9IyiLUNS2 notashelf@enyo"];
# keyFiles = []; # TODO: can this be used with agenix?
};
};
};
}

View file

@ -0,0 +1,42 @@
{
pkgs,
config,
lib,
...
}: let
keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIABG2T60uEoq4qTZtAZfSBPtlqWs2b4V4O+EptQ6S/ru notashelf@prometheus"
];
in {
boot.initrd.network.ssh.authorizedKeys = keys;
users.users.notashelf = {
isNormalUser = true;
shell = pkgs.zsh;
initialPassword = "changeme";
openssh.authorizedKeys.keys = keys;
extraGroups =
[
"wheel"
"systemd-journal"
"audio"
"video"
"input"
"plugdev"
"lp"
"tss"
"power"
"nix"
]
++ lib.ifTheyExist config [
"network"
"networkmanager"
"wireshark"
"mysql"
"docker"
"podman"
"git"
"libvirtd"
];
};
}

View file

@ -0,0 +1,3 @@
{
users.users.root.hashedPassword = "*"; # lock root account
}