added stuff

This commit is contained in:
vali 2024-04-09 23:11:33 +02:00
commit 7d4f626b7d
907 changed files with 70990 additions and 0 deletions

View file

@ -0,0 +1,7 @@
{
imports = [
./docs # generated system documentation of my nested module system
./system # system configurations, from bootloader to desktop environment
./secrets # secrets management
];
}

View file

@ -0,0 +1,16 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf;
cfg = config.modules.documentation;
in {
config = mkIf cfg.enable {
environment.etc = {
"nyxos/options.md".source = cfg.markdownPackage;
"nyxos/options.html".source = cfg.htmlPackage;
};
};
}

View file

@ -0,0 +1,180 @@
{
self,
lib,
config,
...
}: let
inherit (lib) mkIf optionalString;
sys = config.modules.system;
cfg = sys.services;
# mkSecret is an abstraction over agenix secrets
# it allows for secrets to be written conditionally and with
# relatively secure defaults without having to set each one of them
# manually.
mkSecret = enableCondition: {
file,
owner ? "root",
group ? "root",
mode ? "400",
}:
mkIf enableCondition {
file = "${self}/secrets/${file}";
inherit group owner mode;
};
in {
age.identityPaths = [
"${optionalString sys.impermanence.root.enable "/persist"}/etc/ssh/ssh_host_ed25519_key"
"${optionalString sys.impermanence.home.enable "/persist"}/home/notashelf/.ssh/id_ed25519"
];
age.secrets = {
# TODO: system option for declaring host as a potential builder
nix-builderKey = mkSecret true {
file = "common-nix-builder.age";
};
tailscale-client = mkSecret true {
file = "client-tailscale.age";
owner = "notashelf";
group = "users";
mode = "400";
};
# secrets needed for peers
spotify-secret = mkSecret config.modules.system.programs.spotify.enable {
file = "client-spotify.age";
owner = "notashelf";
group = "users";
mode = "400";
};
wg-client = mkSecret true {
file = "client-wg.age";
owner = "notashelf";
group = "users";
mode = "700";
};
client-email = mkSecret true {
file = "client-email.age";
owner = "notashelf";
group = "users";
mode = "400";
};
# database secrets
mongodb-secret = mkSecret cfg.database.mongodb.enable {
file = "db-mongodb.age";
};
garage-env = mkSecret cfg.database.garage.enable {
file = "db-garage.age";
mode = "400";
owner = "garage";
group = "garage";
};
# service secrets
wg-server = mkSecret cfg.networking.wireguard.enable {
file = "service-wg.age";
};
mkm-web = mkSecret cfg.mkm.enable {
file = "service-mkm-web.age";
mode = "400";
};
matrix-secret = mkSecret cfg.social.matrix.enable {
file = "service-matrix.age";
owner = "matrix-synapse";
mode = "400";
};
vaultwarden-env = mkSecret cfg.vaultwarden.enable {
file = "service-vaultwarden.age";
owner = "vaultwarden";
mode = "400";
};
searx-secretkey = mkSecret cfg.searxng.enable {
file = "service-searx.age";
mode = "400";
owner = "searx";
group = "searx";
};
nextcloud-secret = mkSecret cfg.nextcloud.enable {
file = "service-nextcloud.age";
mode = "400";
owner = "nextcloud";
group = "nextcloud";
};
attic-env = mkSecret cfg.bincache.atticd.enable {
file = "service-attic.age";
mode = "400";
owner = "atticd";
group = "atticd";
};
harmonia-privateKey = mkSecret cfg.bincache.harmonia.enable {
file = "service-harmonia.age";
mode = "770";
owner = "harmonia";
group = "harmonia";
};
forgejo-runner-token = mkSecret cfg.forgejo.enable {
file = "service-forgejo-runner-token.age";
mode = "400";
owner = "gitea-runner";
group = "gitea-runner";
};
forgejo-runner-config = mkSecret cfg.forgejo.enable {
file = "service-forgejo-runner-config.age";
mode = "400";
owner = "gitea-runner";
group = "gitea-runner";
};
# mailserver secrets
mailserver-secret = mkSecret cfg.mailserver.enable {
file = "mailserver-postmaster.age";
mode = "400";
};
mailserver-forgejo-secret = mkSecret cfg.forgejo.enable {
file = "mailserver-forgejo.age";
owner = "forgejo";
group = "forgejo";
mode = "400";
};
mailserver-vaultwarden-secret = mkSecret cfg.vaultwarden.enable {
file = "mailserver-vaultwarden.age";
owner = "vaultwarden";
mode = "400";
};
mailserver-cloud-secret = mkSecret cfg.nextcloud.enable {
file = "mailserver-cloud.age";
owner = "nextcloud";
mode = "400";
};
mailserver-matrix-secret = mkSecret cfg.social.matrix.enable {
file = "mailserver-matrix.age";
owner = "matrix-synapse";
mode = "400";
};
mailserver-noreply-secret = mkSecret cfg.social.mastodon.enable {
file = "mailserver-noreply.age";
owner = "mastodon";
mode = "400";
};
};
}

View file

@ -0,0 +1,86 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
hostConfig = config;
in {
config = mkIf (builtins.elem "alpha" config.modules.system.containers.enabledContainers) {
systemd = {
services."container@alpha".after = ["container@firewall.service"];
tmpfiles.rules = [
"D /srv/containers/home 755 root root"
];
};
containers."alpha" = {
autoStart = false;
enableTun = true;
ephemeral = true;
privateNetwork = true;
localAddress = "10.1.0.1";
hostAddress = "10.1.0.2";
config = _: {
_module.args = {inherit lib;};
nixpkgs.pkgs = pkgs;
system.stateVersion = "23.05";
users = {
groups.alpha = {};
users.alpha = {
isNormalUser = true;
extraGroups = ["alpha"];
home = "/home/alpha";
createHome = true;
initialPassword = "alpha";
};
};
environment.systemPackages = with pkgs; [
gcc
openjdk17_headless
gitMinimal
];
networking.interfaces.ve-alpha = {
useDHCP = true;
ipv4 = {
addresses = [
{
address = "10.1.0.1";
prefixLength = 32;
}
];
routes = [
{
address = "10.1.0.2";
prefixLength = 32;
options = {src = "10.1.0.1";};
}
];
};
};
};
bindMounts = {
"/home" = {
hostPath = "/srv/containers/home";
isReadOnly = false;
};
"/run/systemd/ask-password" = {
hostPath = "/run/systemd/ask-password";
isReadOnly = false;
};
"/run/systemd/ask-password-block" = {
hostPath = "/run/systemd/ask-password-block";
isReadOnly = false;
};
};
};
};
}

View file

@ -0,0 +1,99 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
in {
config = mkIf (builtins.elem "beta" config.modules.system.containers.enabledContainers) {
containers."beta" = {
autoStart = false;
enableTun = true;
ephemeral = true;
privateNetwork = true;
localAddress = "10.2.0.1";
hostAddress = "10.2.0.2";
config = _: let
backup_path = "/var/backup/postgresql";
in {
system.stateVersion = "23.05";
services.openssh.enable = true;
users = {
groups.beta = {};
users = {
root.hashedPassword = "!"; # disable root login
beta = {
isNormalUser = true;
createHome = true;
group = "beta";
};
};
};
time.timeZone = "Europe/Berlin";
networking.interfaces = {
eth0 = {
useDHCP = false;
ipv4.addresses = [
{
address = "192.168.6.1";
prefixLength = 23;
}
];
ipv6.addresses = [];
};
};
networking.firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [5432];
};
services.postgresql = {
enable = true;
enableTCPIP = true;
package = pkgs.postgresql;
dataDir = "/var/db/postgresql";
authentication = ''
host selfoss selfoss 192.168.6.2/32 trust
'';
initialScript = builtins.toFile "pg_initial_script" ''
CREATE ROLE selfoss LOGIN CREATEDB;
CREATE DATABASE selfoss OWNER selfoss;
'';
};
systemd.services.postgresql.preStart = ''
if [ ! -d ${backup_path} ]; then
mkdir -p ${backup_path}
chown postgres ${backup_path}
fi
'';
systemd.services.postgresql-dump = {
path = with pkgs; [postgresql gzip];
serviceConfig = {
User = "root";
};
script = let
db_list_command = "psql -l -t -A |cut -d'|' -f 1 |grep -v -e template0 -e template1 -e 'root=CT'";
in ''
${db_list_command}
for db in `${db_list_command}`; do
echo "Dumping $db"
pg_dump --format directory --file ${backup_path}/$db $db
done
echo "Dumping all in one gzip"
pg_dumpall |gzip > ${backup_path}/complete_dump.sql.gz
'';
startAt = "daily";
};
};
};
};
}

View file

@ -0,0 +1,11 @@
_: {
# this imports all container directories unconditionally, regardless of whether or not
# they are included in containers.enabledContainers option definition
# however, as a safeguard, we are required to check if a container is actually meant to be enabled
# so each container does it's own "builtins.elem ..." bullshit before evaluating the container
# configuration - hacky? yes. working? also yes.
imports = [
./alpha # sandbox
./beta # postgresql
];
}

View file

@ -0,0 +1,14 @@
{
imports = [
./containers # hotpluggable systemd-nspawn containers
./emulation # emulation via binfmt for cross-building
./encryption # LUKS encryption
./gaming # available games and gaming utilities such as steam and mangohud
./hardware # hardware capabilities - i.e bluetooth, sound, tpm etc.
./impermanence # impermanence configuration
./nix # configuration for the nix package manager and build tool
./os # configurations for how the system should operate
./security # anything from kernel hardening to audit daemeons
./virtualization # hypervisor and virtualisation related options - docker, QEMU, waydroid etc.
];
}

View file

@ -0,0 +1,25 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
in {
config = mkIf sys.emulation.enable {
nix.settings.extra-sandbox-paths = ["/run/binfmt" "${pkgs.qemu}"];
boot.binfmt = {
emulatedSystems = sys.emulation.systems;
registrations = {
# aarch64 interpreter
aarch64-linux.interpreter = "${pkgs.qemu}/bin/qemu-aarch64";
# i686 interpreter
i686-linux.interpreter = "${pkgs.qemu}/bin/qemu-i686";
};
};
};
}

View file

@ -0,0 +1,47 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
cfg = config.modules.system.encryption;
in {
config = mkIf cfg.enable {
boot = {
# mildly improves performance for the disk encryption
initrd.availableKernelModules = [
"aesni_intel"
"cryptd"
"usb_storage"
];
kernelParams = [
# Disable password timeout
"luks.options=timeout=0"
"rd.luks.options=timeout=0"
"rootflags=x-systemd.device-timeout=0"
];
};
services.lvm.enable = true;
# TODO: account for multiple encrypted devices
boot.initrd.luks.devices."${cfg.device}" = {
# improve performance on ssds
bypassWorkqueues = true;
# handle LUKS decryption before LVM
preLVM = true;
# the device with the maching id will be searched for the key file
keyFile = mkIf (cfg.keyFile != null) "${cfg.keyFile}";
# the size of the key file in bytes
keyFileSize = cfg.keySize;
# if keyfile is not there, fall back to cryptsetup password
fallbackToPassword = cfg.fallbackToPassword; # IMPLIED BY config.boot.initrd.systemd.enable
};
};
}

View file

@ -0,0 +1,7 @@
{
imports = [
./gamescope.nix
./gamemode.nix
./steam.nix
];
}

View file

@ -0,0 +1,75 @@
{
inputs,
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf makeBinPath optionalString;
inherit (config) modules;
env = modules.usrEnv;
sys = modules.system;
prg = sys.programs;
programs = makeBinPath (with pkgs; [
inputs.hyprland.packages.${stdenv.system}.default
coreutils
power-profiles-daemon
systemd
libnotify
]);
startscript = pkgs.writeShellScript "gamemode-start" ''
${optionalString (env.desktop == "Hyprland") ''
export PATH=$PATH:${programs}
export HYPRLAND_INSTANCE_SIGNATURE=$(ls -w1 /tmp/hypr | tail -1)
hyprctl --batch 'keyword decoration:blur 0 ; keyword animations:enabled 0 ; keyword misc:vfr 0'
''}
powerprofilesctl set performance
notify-send -a 'Gamemode' 'Optimizations activated' -u 'low'
'';
endscript = pkgs.writeShellScript "gamemode-end" ''
${optionalString (env.desktop == "Hyprland") ''
export PATH=$PATH:${programs}
export HYPRLAND_INSTANCE_SIGNATURE=$(ls -w1 /tmp/hypr | tail -1)
hyprctl --batch 'keyword decoration:blur 1 ; keyword animations:enabled 1 ; keyword misc:vfr 1'
''}
powerprofilesctl set balanced
notify-send -a 'Gamemode' 'Optimizations deactivated' -u 'low'
'';
in {
config = mkIf prg.gaming.gamemode.enable {
programs.gamemode = {
enable = true;
enableRenice = true;
settings = {
general = {
softrealtime = "auto";
renice = 15;
};
custom = {
start = startscript.outPath;
end = endscript.outPath;
};
};
};
security.wrappers.gamemode = {
owner = "root";
group = "root";
source = "${pkgs.gamemode}/bin/gamemoderun";
capabilities = "cap_sys_ptrace,cap_sys_nice+pie";
};
boot.kernel.sysctl = {
# default on some gaming (SteamOS) and desktop (Fedora) distributions
# might help with gaming performance
"vm.max_map_count" = 2147483642;
};
};
}

View file

@ -0,0 +1,28 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf;
inherit (config) modules;
sys = modules.system;
prg = sys.programs;
in {
config = mkIf prg.gaming.gamescope.enable {
programs.gamescope = {
enable = true;
package = pkgs.gamescope; # the default, here in case I want to override it
};
# workaround attempt for letting gamescope bypass YAMA LSM
# doesn't work, but doesn't hurt to keep this here
security.wrappers.gamescope = {
owner = "root";
group = "root";
source = "${config.programs.gamescope.package}/bin/gamescope";
capabilities = "cap_sys_ptrace,cap_sys_nice+pie";
};
};
}

View file

@ -0,0 +1,83 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf;
prg = config.modules.system.programs;
in {
config = mkIf prg.gaming.steam.enable {
nixpkgs = {
config = {
allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) [
"steam"
"steam-original"
"steam-runtime"
];
};
overlays = [
(_: prev: {
steam = prev.steam.override ({extraPkgs ? _: [], ...}: {
extraPkgs = pkgs':
(extraPkgs pkgs')
# Add missing dependencies
++ (with pkgs'; [
# Generic dependencies
libgdiplus
keyutils
libkrb5
libpng
libpulseaudio
libvorbis
stdenv.cc.cc.lib
xorg.libXcursor
xorg.libXi
xorg.libXinerama
xorg.libXScrnSaver
at-spi2-atk
fmodex
gtk3
gtk3-x11
harfbuzz
icu
glxinfo
inetutils
libthai
mono5
pango
stdenv.cc.cc.lib
strace
zlib
# for Titanfall 2 Northstar launcher
libunwind
]);
});
})
];
};
programs.steam = {
# Enable steam
enable = true;
# Whether to open ports in the firewall for Steam Remote Play
remotePlay.openFirewall = false;
# Whether to open ports in the firewall for Source Dedicated Server
dedicatedServer.openFirewall = false;
# Compatibility tools to install
# For the accepted format (and the reason behind)
# the "compattool" attribute, see:
# <https://github.com/NixOS/nixpkgs/pull/296009>
extraCompatPackages = [
pkgs.proton-ge-bin.steamcompattool
];
};
};
}

View file

@ -0,0 +1,32 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system.bluetooth;
in {
config = mkIf sys.enable {
modules.system.boot.extraKernelParams = ["btusb"];
hardware.bluetooth = {
enable = true;
package = pkgs.bluez5-experimental;
#hsphfpd.enable = true;
powerOnBoot = true;
disabledPlugins = ["sap"];
settings = {
General = {
JustWorksRepairing = "always";
MultiProfile = "multiple";
Experimental = true;
};
};
};
# https://nixos.wiki/wiki/Bluetooth
services.blueman.enable = true;
};
}

View file

@ -0,0 +1,60 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf mkMerge versionOlder versionAtLeast;
dev = config.modules.device;
kver = config.boot.kernelPackages.kernel.version;
inherit (dev.cpu.amd) pstate zenpower;
in {
config = mkIf (builtins.elem dev.cpu.type ["amd" "vm-amd"]) {
environment.systemPackages = [pkgs.amdctl];
hardware.cpu.amd.updateMicrocode = true;
boot = mkMerge [
{
kernelModules = [
"kvm-amd" # amd virtualization
"amd-pstate" # load pstate module in case the device has a newer gpu
"zenpower" # zenpower is for reading cpu info, i.e voltage
"msr" # x86 CPU MSR access device
];
extraModulePackages = [config.boot.kernelPackages.zenpower];
}
(mkIf (pstate.enable && (versionAtLeast kver "5.17") && (versionOlder kver "6.1")) {
kernelParams = ["initcall_blacklist=acpi_cpufreq_init"];
kernelModules = ["amd-pstate"];
})
(mkIf (pstate.enable && (versionAtLeast kver "6.1") && (versionOlder kver "6.3")) {
kernelParams = ["amd_pstate=passive"];
})
# for older kernels
# see <https://github.com/NixOS/nixos-hardware/blob/c256df331235ce369fdd49c00989fdaa95942934/common/cpu/amd/pstate.nix>
(mkIf (pstate.enable && (versionAtLeast kver "6.3")) {
kernelParams = ["amd_pstate=active"];
})
];
# Ryzen cpu control
systemd.services.zenstates = mkIf zenpower.enable {
enable = true;
description = "Undervolt via Zenstates";
after = ["syslog.target" "systemd-modules-load.service"];
unitConfig = {ConditionPathExists = "${pkgs.zenstates}/bin/zenstates";};
serviceConfig = {
User = "root";
ExecStart = "${pkgs.zenstates}/bin/zenstates ${zenpower.args}";
};
wantedBy = ["multi-user.target"];
};
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./amd
./intel
];
}

View file

@ -0,0 +1,20 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
dev = config.modules.device;
in {
config = mkIf (builtins.elem dev.cpu.type ["intel" "vm-intel"]) {
hardware.cpu.intel.updateMicrocode = true;
boot = {
kernelModules = ["kvm-intel"];
kernelParams = ["i915.fastboot=1" "enable_gvt=1"];
};
environment.systemPackages = with pkgs; [intel-gpu-tools];
};
}

View file

@ -0,0 +1,12 @@
{
imports = [
./cpu # cpu specific options
./gpu # gpu specific options
./multimedia # enable multimedia: e.g. sound and video
./bluetooth.nix # bluetooth and device management
./generic.nix # host-agnostic options and settings
./tpm.nix # trusted platform module
./yubikey.nix # yubikey device support and management tools
];
}

View file

@ -0,0 +1,8 @@
{lib, ...}: {
# This enables non-free firmware on devices not recognized by `nixos-generate-config`.
# Disabling this option will make the system unbootable if such devices are critical
# in your boot chain - therefore this should remain true until you are running a device
# with mostly libre firmware. Which there is not many of.
# on 2021-06-14: disabled this by accident and nuked my GPU drivers
hardware.enableRedistributableFirmware = lib.mkDefault true;
}

View file

@ -0,0 +1,49 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
dev = config.modules.device;
in {
config = mkIf (builtins.elem dev.gpu.type ["amd" "hybrid-amd"]) {
# enable amdgpu xorg drivers in case Hyprland breaks again
services.xserver.videoDrivers = lib.mkDefault ["modesetting" "amdgpu"];
# enable amdgpu kernel module
boot = {
initrd.kernelModules = ["amdgpu"]; # load amdgpu kernel module as early as initrd
kernelModules = ["amdgpu"]; # if loading somehow fails during initrd but the boot continues, try again later
};
environment.systemPackages = [pkgs.nvtopPackages.amd];
# enables AMDVLK & OpenCL support
hardware.opengl = {
extraPackages = with pkgs;
[
amdvlk
# mesa
mesa
# vulkan
vulkan-tools
vulkan-loader
vulkan-validation-layers
vulkan-extension-layer
]
++ (
# this is a backwards-compatible way of loading appropriate opencl packages
# in case the host runs an older revision of nixpkgs
if pkgs ? rocmPackages.clr
then with pkgs.rocmPackages; [clr clr.icd]
else with pkgs; [rocm-opencl-icd rocm-opencl-runtime]
);
extraPackages32 = [pkgs.driversi686Linux.amdvlk];
};
};
}

View file

@ -0,0 +1,7 @@
_: {
imports = [
./intel
./nvidia
./amd
];
}

View file

@ -0,0 +1,43 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
dev = config.modules.device;
# let me play youtube videos without h.264, please and thank you
vaapiIntel = pkgs.vaapiIntel.override {enableHybridCodec = true;};
in {
config = mkIf (builtins.elem dev.gpu.type ["intel" "hybrid-intel"]) {
# enable the i915 kernel module
boot.initrd.kernelModules = ["i915"];
# better performance than the actual Intel driver
services.xserver.videoDrivers = ["modesetting"];
# OpenCL support and VAAPI
hardware.opengl = {
extraPackages = with pkgs; [
intel-compute-runtime
intel-media-driver
vaapiIntel
vaapiVdpau
libvdpau-va-gl
];
extraPackages32 = with pkgs.pkgsi686Linux; [
# intel-compute-runtime # FIXME does not build due to unsupported system
intel-media-driver
vaapiIntel
vaapiVdpau
libvdpau-va-gl
];
};
environment.variables = mkIf (config.hardware.opengl.enable && dev.gpu != "hybrid-nv") {
VDPAU_DRIVER = "va_gl";
};
};
}

View file

@ -0,0 +1,117 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf mkDefault mkMerge versionOlder;
# use the latest possible nvidia package
nvStable = config.boot.kernelPackages.nvidiaPackages.stable.version;
nvBeta = config.boot.kernelPackages.nvidiaPackages.beta.version;
nvidiaPackage =
if (versionOlder nvBeta nvStable)
then config.boot.kernelPackages.nvidiaPackages.stable
else config.boot.kernelPackages.nvidiaPackages.beta;
dev = config.modules.device;
env = config.modules.usrEnv;
in {
config = mkIf (builtins.elem dev.gpu.type ["nvidia" "hybrid-nv"]) {
# nvidia drivers are unfree software
nixpkgs.config.allowUnfree = true;
services.xserver = mkMerge [
{
videoDrivers = ["nvidia"];
}
# xorg settings
(mkIf (!env.isWayland) {
# disable DPMS
monitorSection = ''
Option "DPMS" "false"
'';
# disable screen blanking in general
serverFlagsSection = ''
Option "StandbyTime" "0"
Option "SuspendTime" "0"
Option "OffTime" "0"
Option "BlankTime" "0"
'';
})
];
# blacklist nouveau module so that it does not conflict with nvidia drm stuff
# also the nouveau performance is godawful, I'd rather run linux on a piece of paper than use nouveau
# no offense to nouveau devs, I'm sure they're doing their best and they have my respect for that
# but their best does not constitute a usable driver for me
boot.blacklistedKernelModules = ["nouveau"];
environment = {
sessionVariables = mkMerge [
{LIBVA_DRIVER_NAME = "nvidia";}
(mkIf env.isWayland {
WLR_NO_HARDWARE_CURSORS = "1";
#__GLX_VENDOR_LIBRARY_NAME = "nvidia";
#GBM_BACKEND = "nvidia-drm"; # breaks firefox apparently
})
(mkIf (env.isWayland && (dev.gpu == "hybrid-nv")) {
#__NV_PRIME_RENDER_OFFLOAD = "1";
#WLR_DRM_DEVICES = mkDefault "/dev/dri/card1:/dev/dri/card0";
})
];
systemPackages = with pkgs; [
nvtopPackages.nvidia
# mesa
mesa
# vulkan
vulkan-tools
vulkan-loader
vulkan-validation-layers
vulkan-extension-layer
# libva
libva
libva-utils
];
};
hardware = {
nvidia = {
package = mkDefault nvidiaPackage;
modesetting.enable = mkDefault true;
prime.offload = let
isHybrid = dev.gpu == "hybrid-nv";
in {
enable = isHybrid;
enableOffloadCmd = isHybrid;
};
powerManagement = {
enable = mkDefault true;
finegrained = mkDefault false;
};
# use open source drivers by default, hosts may override this option if their gpu is
# not supported by the open source drivers
open = mkDefault true;
nvidiaSettings = false; # add nvidia-settings to pkgs, useless on nixos
nvidiaPersistenced = true;
forceFullCompositionPipeline = true;
};
opengl = {
extraPackages = with pkgs; [nvidia-vaapi-driver];
extraPackages32 = with pkgs.pkgsi686Linux; [nvidia-vaapi-driver];
};
};
};
}

View file

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

View file

@ -0,0 +1,17 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf mkDefault;
cfg = config.modules.system.sound;
dev = config.modules.device;
in {
imports = [./pipewire.nix];
config = mkIf (cfg.enable && dev.hasSound) {
sound = {
enable = mkDefault false; # this just enables ALSA, which we don't really care abouyt
mediaKeys.enable = true;
};
};
}

View file

@ -0,0 +1,130 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) isx86Linux;
inherit (lib.modules) mkIf;
inherit (lib.lists) optionals;
inherit (lib.generators) toLua;
cfg = config.modules.system.sound;
dev = config.modules.device;
in {
config = mkIf (cfg.enable && dev.hasSound) {
# if the device advertises sound enabled, and pipewire is disabled
# for whatever reason, we may fall back to PulseAudio to ensure
# that we still have audio. I do not like PA, but bad audio
# is better than no audio. Though we should always use
# PipeWire where available
hardware.pulseaudio.enable = !config.services.pipewire.enable;
# able to change scheduling policies, e.g. to SCHED_RR
# sounds server use RealtimeKit (rtkti) to acquire
# realtime priority
security.rtkit.enable = config.services.pipewire.enable;
# enable pipewire and configure it for low latency
# the below configuration may not fit every use case
# and you are recommended to experiment with the values
# in order to find the perfect configuration
services = {
pipewire = let
quantum = 64;
rate = 48000;
qr = "${toString quantum}/${toString rate}";
in {
enable = true;
# emulation layers
audio.enable = true;
pulse.enable = true; # PA server emulation
jack.enable = true; # JACK audio emulation
alsa = {
enable = true;
support32Bit = isx86Linux pkgs; # if we're on x86 linux, we can support 32 bit
};
extraConfig.pipewire."99-lowlatency" = {
context = {
properties.default.clock.min-quantum = quantum;
modules = [
{
name = "libpipewire-module-rtkit";
flags = ["ifexists" "nofail"];
args = {
nice.level = -15;
rt = {
prio = 88;
time.soft = 200000;
time.hard = 200000;
};
};
}
{
name = "libpipewire-module-protocol-pulse";
args = {
server.address = ["unix:native"];
pulse.min = {
req = qr;
quantum = qr;
frag = qr;
};
};
}
];
stream.properties = {
node.latency = qr;
resample.quality = 1;
};
};
};
wireplumber = {
enable = true;
configPackages = let
# generate "matches" section of the rules
matches = toLua {
multiline = false; # looks better while inline
indent = false;
} [[["node.name" "matches" "alsa_output.*"]]]; # nested lists are to produce `{{{ }}}` in the output
# generate "apply_properties" section of the rules
apply_properties = toLua {} {
"audio.format" = "S32LE";
"audio.rate" = rate * 2;
"api.alsa.period-size" = 2;
};
in
[
(pkgs.writeTextDir "share/lowlatency.lua.d/99-alsa-lowlatency.lua" ''
alsa_monitor.rules = {
{
matches = ${matches};
apply_properties = ${apply_properties};
}
}
'')
]
++ optionals dev.hasBluetooth [
(pkgs.writeTextDir "share/bluetooth.lua.d/51-bluez-config.lua" ''
bluez_monitor.properties = {
["bluez5.enable-sbc-xq"] = true,
["bluez5.enable-msbc"] = true,
["bluez5.enable-hw-volume"] = true,
["bluez5.headset-roles"] = "[ hsp_hs hsp_ag hfp_hf hfp_ag ]"
}
'')
];
};
};
};
systemd.user.services = {
pipewire.wantedBy = ["default.target"];
pipewire-pulse.wantedBy = ["default.target"];
};
};
}

View file

@ -0,0 +1,26 @@
{
pkgs,
lib,
config,
...
}: let
inherit (lib) mkIf isx86Linux;
sys = config.modules.system;
in {
config = mkIf sys.video.enable {
hardware = {
opengl = {
enable = true;
driSupport = true;
driSupport32Bit = isx86Linux pkgs;
};
};
# benchmarking tools
environment.systemPackages = with pkgs; [
glxinfo
glmark2
];
};
}

View file

@ -0,0 +1,28 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf mkDefault;
dev = config.modules.device;
in {
config = mkIf dev.hasTPM {
security.tpm2 = {
# enable Trusted Platform Module 2 support
enable = true;
# enable Trusted Platform 2 userspace resource manager daemon
abrmd.enable = mkDefault false;
# The TCTI is the "Transmission Interface" that is used to communicate with a
# TPM. this option sets TCTI environment variables to the specified values if enabled
# - TPM2TOOLS_TCTI
# - TPM2_PKCS11_TCTI
tctiEnvironment.enable = mkDefault true;
# enable TPM2 PKCS#11 tool and shared library in system path
pkcs11.enable = mkDefault false;
};
};
}

View file

@ -0,0 +1,33 @@
{
config,
lib,
pkgs,
...
}: {
config = lib.mkIf config.modules.system.yubikeySupport.enable {
hardware.gpgSmartcards.enable = true;
services = {
pcscd.enable = true;
udev.packages = [pkgs.yubikey-personalization];
};
programs = {
ssh.startAgent = false;
gnupg.agent = {
enable = true;
enableSSHSupport = true;
};
};
environment.systemPackages = with pkgs; [
# Yubico's official tools
yubikey-manager # cli
yubikey-manager-qt # gui
yubikey-personalization # cli
yubikey-personalization-gui # gui
yubico-piv-tool # cli
#yubioath-flutter # gui
];
};
}

View file

@ -0,0 +1,169 @@
{
config,
lib,
inputs,
...
}: let
inherit (lib) optionalString mkIf mkForce;
cfg = config.modules.system.impermanence;
in {
imports = [
inputs.impermanence.nixosModules.impermanence
];
config = mkIf cfg.enable {
users = {
# this option makes it that users are not mutable outside our configurations
# if you are on nixos, you are probably smart enough to not try and edit users
# manually outside your configuration.nix or whatever
# P.S: This option requires you to define a password file for your users
# inside your configuration.nix - you can generate this password with
# mkpasswd -m sha-512 > /persist/passwords/notashelf after you confirm /persist/passwords exists
mutableUsers = false;
# each existing user needs to have a passwordFile defined here
# otherwise, they will not be available for a login
users = {
root = {
# passwordFile needs to be in a volume marked with `neededForBoot = true`
hashedPasswordFile = "/persist/passwords/root";
};
notashelf = {
hashedPasswordFile = "/persist/passwords/notashelf";
};
};
};
# home.persistence."/persist/home/notashelf" = {};
environment.persistence."/persist" = {
directories =
[
"/etc/nixos"
"/etc/nix"
"/etc/NetworkManager/system-connections"
"/etc/secureboot"
"/var/db/sudo"
"/var/lib/flatpak"
"/var/lib/libvirt"
"/var/lib/bluetooth"
"/var/lib/nixos"
"/var/lib/pipewire"
"/var/lib/systemd/coredump"
"/var/cache/tailscale"
"/var/lib/tailscale"
]
++ [config.programs.ccache.cacheDir];
files = [
# important state
"/etc/machine-id"
# ssh stuff
/*
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
*/
# other
# TODO: optionalstring for /var/lib/${lxd, docker}
];
# builtins.concatMap (key: [key.path (key.path + ".pub")]) config.services.openssh.hostKeys
};
# for some reason *this* is what makes networkmanager not get screwed completely instead of the impermanence module
systemd.tmpfiles.rules = [
"L /var/lib/NetworkManager/secret_key - - - - /persist/var/lib/NetworkManager/secret_key"
"L /var/lib/NetworkManager/seen-bssids - - - - /persist/var/lib/NetworkManager/seen-bssids"
"L /var/lib/NetworkManager/timestamps - - - - /persist/var/lib/NetworkManager/timestamps"
];
services.openssh.hostKeys = mkForce [
{
bits = 4096;
path = "/persist/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
bits = 4096;
path = "/persist/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
boot.initrd.systemd.services.rollback = {
description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = ["initrd.target"];
# make sure it's done after encryption
# i.e. LUKS/TPM process
after = ["systemd-cryptsetup@enc.service"];
# mount the root fs before clearing
before = ["sysroot.mount"];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /mnt
# We first mount the btrfs root to /mnt
# so we can manipulate btrfs subvolumes.
mount -o subvol=/ /dev/mapper/enc /mnt
# If home is meant to be impermanent, also mount the home subvolume to be deleted later
${optionalString cfg.home.enable "mount -o subvol=/home /dev/mapper/enc /mnt/home"}
# While we're tempted to just delete /root and create
# a new snapshot from /root-blank, /root is already
# populated at this point with a number of subvolumes,
# which makes `btrfs subvolume delete` fail.
# So, we remove them first.
#
# /root contains subvolumes:
# - /root/var/lib/portables
# - /root/var/lib/machines
btrfs subvolume list -o /mnt/root |
cut -f9 -d' ' |
while read subvolume; do
echo "deleting /$subvolume subvolume..."
btrfs subvolume delete "/mnt/$subvolume"
done &&
echo "deleting /root subvolume..." &&
btrfs subvolume delete /mnt/root
echo "restoring blank /root subvolume..."
btrfs subvolume snapshot /mnt/root-blank /mnt/root
${optionalString cfg.home.enable ''
echo "restoring blank /home subvolume..."
mount -o subvol=/home /dev/mapper/enc /mnt/home
''}
# Once we're done rolling back to a blank snapshot,
# we can unmount /mnt and continue on the boot process.
umount /mnt
'';
};
assertions = [
{
assertion = cfg.home.enable -> !cfg.root.enable;
message = ''
You have enabled home impermanence without root impermanence. This
is not supported due to the fact that we handle all all impermanence
related deletions and creations in a single service. Please enable
`modules.system.impermanence.root.enable` if you wish to proceed.
'';
}
];
# home impermanence is not very safe, and chances are I don't want it. Warn any potential
# users (which may or may not be me) when it is enabled just to be safe.
# p.s. I really don't like nix's warnings syntax. why can't it be the same
# as the assertions format? /rant
warnings =
if cfg.home.enable
then ["Home impermanence is enabled. This is experimental, beware."]
else [];
};
}

View file

@ -0,0 +1,52 @@
{
config,
lib,
...
}: let
inherit (lib.attrsets) recursiveUpdate;
inherit (lib.lists) filter;
# a generic builder configuration
builder = {
systems = ["x86_64-linux"];
speedFactor = 4;
maxJobs = 4;
supportedFeatures = ["benchmark" "nixos-test"];
sshKey = "/home/notashelf/.ssh/builder";
protocol = "ssh-ng";
};
# override generic config builder with the assumption that more
# resources and features are available to us
bigBuilder = recursiveUpdate builder {
maxJobs = 16;
speedFactor = 16;
supportedFeatures = builder.supportedFeatures ++ ["kvm" "big-parallel"];
systems = builder.systems ++ ["aarch64-linux" "i686-linux"];
};
mkBuilder = {
builderBase ? builder,
sshProtocol ? "ssh-ng",
user ? "root",
host,
...
}:
recursiveUpdate builderBase {
hostName = host;
sshUser = user;
protocol = sshProtocol;
};
in {
nix = {
distributedBuilds = true;
buildMachines = filter (builder: builder.hostName != config.networking.hostName) [
# large build machine
(mkBuilder {
builderBase = bigBuilder;
user = "builder";
host = "build.neushore.dev";
sshProtocol = "ssh"; # ssh-ng is not supported by this device
})
];
};
}

View file

@ -0,0 +1,222 @@
{
inputs,
self,
config,
pkgs,
lib,
...
}: let
inherit (lib.trivial) pipe;
inherit (lib.types) isType;
inherit (lib.attrsets) mapAttrsToList optionalAttrs filterAttrs mapAttrs;
inherit (lib.modules) mkDefault;
in {
imports = [
./transcend # module that merges trees outside central nixpkgs with our system's
./builders.nix # import builders config
./overlays.nix
];
system = {
autoUpgrade.enable = false;
stateVersion = mkDefault "23.05";
};
environment = {
etc = with inputs; {
# set channels (backwards compatibility)
"nix/flake-channels/system".source = self;
"nix/flake-channels/nixpkgs".source = nixpkgs;
"nix/flake-channels/home-manager".source = home-manager;
# preserve current flake in /etc
"nixos/flake".source = self;
};
# we need git for flakes, don't we
systemPackages = [pkgs.git];
};
nixpkgs = {
# https://github.com/NixOS/nixpkgs/commit/eb8ce7930d14dafcc7eff56c2f9efca6a3b2f622
# pkgs = self.legacyPackages.${config.nixpkgs.system};
config = {
allowUnfree = true; # really a pain in the ass to deal with when disabled
allowBroken = false;
allowUnsupportedSystem = true;
# default to none, add more as necessary
permittedInsecurePackages = [
"electron-24.8.6"
"electron-25.9.0"
"freeimage-unstable-2021-11-01"
];
};
};
# faster rebuilding
documentation = {
doc.enable = false;
nixos.enable = true;
info.enable = false;
man = {
enable = mkDefault true;
generateCaches = mkDefault true;
};
};
nix = let
# mappedRegistry = mapAttrs (_: v: {flake = v;}) inputs;
mappedRegistry = pipe inputs [
(filterAttrs (_: isType "flake"))
(mapAttrs (_: flake: {inherit flake;}))
(x: x // {nixpkgs.flake = inputs.nixpkgs;})
];
in {
package = pkgs.nixSuper; # pkgs.nixVersions.unstable;
# pin the registry to avoid downloading and evaluating a new nixpkgs version every time
# this will add each flake input as a registry to make nix3 commands consistent with your flake
# additionally we also set `registry.default`, which was added by nix-super
registry = mappedRegistry // optionalAttrs (config.nix.package == pkgs.nixSuper) {default = mappedRegistry.nixpkgs;};
# This will additionally add your inputs to the system's legacy channels
# Making legacy nix commands consistent as well
nixPath = mapAttrsToList (key: _: "${key}=flake:${key}") config.nix.registry;
# make builds run with low priority so my system stays responsive
# this is especially helpful if you have auto-upgrade on
daemonCPUSchedPolicy = "batch";
daemonIOSchedClass = "idle";
daemonIOSchedPriority = 7;
# set up garbage collection to run weekly,
# removing unused packages that are older than 7 days
gc = {
automatic = true;
dates = "Mon *-*-* 03:00";
options = "--delete-older-than 7d";
};
# automatically optimize nix store my removing hard links
# do it after the gc
optimise = {
automatic = true;
dates = ["04:00"];
};
settings = {
# tell nix to use the xdg spec for base directories
# while transitioning, any state must be carried over
# manually, as Nix won't do it for us
use-xdg-base-directories = true;
# specify the path to the nix registry
flake-registry = "/etc/nix/registry.json";
# Free up to 10GiB whenever there is less than 5GB left.
# this setting is in bytes, so we multiply with 1024 thrice
min-free = "${toString (5 * 1024 * 1024 * 1024)}";
max-free = "${toString (10 * 1024 * 1024 * 1024)}";
# automatically optimise symlinks
auto-optimise-store = true;
# allow sudo users to mark the following values as trusted
allowed-users = ["root" "@wheel" "nix-builder"];
# only allow sudo users to manage the nix store
trusted-users = ["root" "@wheel" "nix-builder"];
# let the system decide the number of max jobs
max-jobs = "auto";
# build inside sandboxed environments
sandbox = true;
sandbox-fallback = false;
# supported system features
system-features = ["nixos-test" "kvm" "recursive-nix" "big-parallel"];
# extra architectures supported by my builders
extra-platforms = config.boot.binfmt.emulatedSystems;
# continue building derivations if one fails
keep-going = true;
# show more log lines for failed builds
log-lines = 30;
# enable new nix command and flakes
# and also "unintended" recursion as well as content addresssed nix
extra-experimental-features = [
"flakes" # flakes
"nix-command" # experimental nix commands
"recursive-nix" # let nix invoke itself
"ca-derivations" # content addressed nix
"auto-allocate-uids" # allow nix to automatically pick UIDs, rather than creating nixbld* user accounts
"configurable-impure-env" # allow impure environments
"cgroups" # allow nix to execute builds inside cgroups
"git-hashing" # allow store objects which are hashed via Git's hashing algorithm
"verified-fetches" # enable verification of git commit signatures for fetchGit
];
# don't warn me that my git tree is dirty, I know
warn-dirty = false;
# maximum number of parallel TCP connections used to fetch imports and binary caches, 0 means no limit
http-connections = 50;
# whether to accept nix configuration from a flake without prompting
accept-flake-config = false;
# execute builds inside cgroups
use-cgroups = true;
# for direnv GC roots
keep-derivations = true;
keep-outputs = true;
# use binary cache, this is not gentoo
# external builders can also pick up those substituters
builders-use-substitutes = true;
# substituters to use
substituters = [
"https://cache.ngi0.nixos.org" # content addressed nix cache (TODO)
"https://cache.nixos.org" # funny binary cache
"https://cache.privatevoid.net" # for nix-super
"https://nixpkgs-wayland.cachix.org" # automated builds of *some* wayland packages
"https://nix-community.cachix.org" # nix-community cache
"https://hyprland.cachix.org" # hyprland
"https://nixpkgs-unfree.cachix.org" # unfree-package cache
"https://numtide.cachix.org" # another unfree package cache
"https://anyrun.cachix.org" # anyrun program launcher
"https://nyx.cachix.org" # cached stuff from my flake outputs
"https://neovim-flake.cachix.org" # a cache for my neovim flake
"https://cache.garnix.io" # garnix binary cache, hosts prismlauncher
"https://cache.notashelf.dev" # my own binary cache, served over https
"https://ags.cachix.org" # ags
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA="
"cache.privatevoid.net:SErQ8bvNWANeAvtsOESUwVYr2VJynfuc9JRwlzTTkVg="
"nixpkgs-wayland.cachix.org-1:3lwxaILxMRkVhehr5StQprHdEo4IrE8sRho9R9HOLYA="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
"nixpkgs-unfree.cachix.org-1:hqvoInulhbV4nJ9yJOEr+4wxhDV4xq2d1DK7S6Nj6rs="
"numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE="
"anyrun.cachix.org-1:pqBobmOjI7nKlsUMV25u9QHa9btJK65/C8vnO3p346s="
"notashelf.cachix.org-1:VTTBFNQWbfyLuRzgm2I7AWSDJdqAa11ytLXHBhrprZk="
"neovim-flake.cachix.org-1:iyQ6lHFhnB5UkVpxhQqLJbneWBTzM8LBYOFPLNH4qZw="
"nyx.cachix.org-1:xH6G0MO9PrpeGe7mHBtj1WbNzmnXr7jId2mCiq6hipE="
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
"cache.notashelf.dev-1:DhlmJBtURj+XS3j4F8SFFukT8dYgSjtFcd3egH8rE6U="
"ags.cachix.org-1:naAvMrz0CuYqeyGNyLgE010iUiuf/qx6kYrUv3NwAJ8="
];
};
};
}

View file

@ -0,0 +1,52 @@
{
inputs',
lib,
...
}: let
inherit (builtins) concatStringsSep length;
inherit (lib.lists) zipListsWith;
inherit (lib.strings) escapeShellArg;
in {
nixpkgs.overlays = [
(_: prev: let
oldIcons = [
""
""
""
""
""
""
""
""
""
];
newIcons = [
"f062" # 
"f063" # 
"f520" # 
"f04b" # 
"f00c" # 
"f04c" # 
"f071" # 
"f1da" # 
"f04a0" # 󰒠
];
in {
nixSuper = inputs'.nix-super.packages.default;
nixSchemas = inputs'.nixSchemas.packages.default;
nix-output-monitor = assert length oldIcons == length newIcons;
prev.nix-output-monitor.overrideAttrs (o: {
postPatch =
(o.postPatch or "")
+ ''
sed -i ${escapeShellArg (
concatStringsSep "\n" (zipListsWith (a: b: "s/${a}/\\\\x${b}/") oldIcons newIcons)
)} lib/NOM/Print.hs
sed -i 's///' lib/NOM/Print/Tree.hs
'';
});
})
];
}

View file

@ -0,0 +1,37 @@
# credits go to @eclairevoyant on this one
# lets us import modules from PRs that are not yet merged
# and handles disabling of the relevant module locally
# I've extracted the modules section to make this system more robust and explicit
{
lib,
modulesPath,
...
}: let
inherit (builtins) fetchTree getAttr map;
inherit (lib.attrsets) attrValues;
modules = import ./modules.nix;
transcendModules =
map ({
# repo details
owner,
repo,
rev,
narHash,
# module path
module,
}: {
disabledModules = modulesPath + module;
importedModules =
(fetchTree {
type = "github";
inherit owner repo rev narHash;
})
+ "/nixos/modules/${module}";
})
(attrValues modules);
in {
disabledModules = map (getAttr "disabledModules") transcendModules;
imports = map (getAttr "importedModules") transcendModules;
}

View file

@ -0,0 +1,12 @@
{
# the name here is arbitrary, and is used as an identifier
# what matters is the presence of owner, module and rev
"nix-gc" = {
# https://github.com/NixOS/nixpkgs/pull/260620
owner = "nobbz";
repo = "nixpkgs";
module = "/services/misc/nix-gc.nix";
rev = "10ec045f1dc82c72630c85906e1ae1d54340a7e0";
narHash = "sha256-AV3TXXWp0AxM98wCbEa3iThUQ5AbTMC/3fZAa50lfKI=";
};
}

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?
};
};
};
}

Some files were not shown because too many files have changed in this diff Show more