From 25c69e3c18150efc5ef045a18a11d1878c68a1f5 Mon Sep 17 00:00:00 2001 From: William Date: Fri, 6 Feb 2026 22:36:50 -0300 Subject: [PATCH] add aspects/hosts/ configurations Host-specific NixOS configurations for: - alexandria (server) - io (desktop) - rotterdam (desktop) - trantor (server, aarch64) Each host has a main config file and _hostname/ directory with hardware-configuration and other host-specific modules. Co-Authored-By: Claude Opus 4.5 --- .../_alexandria/hardware-configuration.nix | 49 ++++++++++ aspects/hosts/_alexandria/jellyfin.nix | 15 +++ aspects/hosts/_alexandria/kanidm.nix | 83 ++++++++++++++++ aspects/hosts/_alexandria/nextcloud.nix | 97 +++++++++++++++++++ aspects/hosts/_alexandria/nginx.nix | 59 +++++++++++ aspects/hosts/_alexandria/unbound.nix | 58 +++++++++++ aspects/hosts/_alexandria/vaultwarden.nix | 26 +++++ aspects/hosts/_io/boot.nix | 14 +++ aspects/hosts/_io/disko.nix | 79 +++++++++++++++ aspects/hosts/_io/hardware-configuration.nix | 37 +++++++ aspects/hosts/_io/programs.nix | 27 ++++++ aspects/hosts/_io/services.nix | 61 ++++++++++++ aspects/hosts/_rotterdam/boot.nix | 31 ++++++ .../_rotterdam/hardware-configuration.nix | 82 ++++++++++++++++ aspects/hosts/_rotterdam/hardware.nix | 8 ++ aspects/hosts/_rotterdam/programs.nix | 31 ++++++ aspects/hosts/_rotterdam/services.nix | 11 +++ aspects/hosts/_trantor/boot.nix | 6 ++ aspects/hosts/_trantor/disko.nix | 64 ++++++++++++ aspects/hosts/_trantor/fail2ban.nix | 23 +++++ aspects/hosts/_trantor/forgejo.nix | 72 ++++++++++++++ .../hosts/_trantor/hardware-configuration.nix | 20 ++++ aspects/hosts/_trantor/networking.nix | 8 ++ aspects/hosts/_trantor/nginx.nix | 61 ++++++++++++ aspects/hosts/_trantor/openssh.nix | 23 +++++ aspects/hosts/_trantor/unbound.nix | 58 +++++++++++ aspects/hosts/alexandria.nix | 42 ++++++++ aspects/hosts/io.nix | 51 ++++++++++ aspects/hosts/rotterdam.nix | 56 +++++++++++ aspects/hosts/trantor.nix | 46 +++++++++ 30 files changed, 1298 insertions(+) create mode 100644 aspects/hosts/_alexandria/hardware-configuration.nix create mode 100644 aspects/hosts/_alexandria/jellyfin.nix create mode 100644 aspects/hosts/_alexandria/kanidm.nix create mode 100644 aspects/hosts/_alexandria/nextcloud.nix create mode 100644 aspects/hosts/_alexandria/nginx.nix create mode 100644 aspects/hosts/_alexandria/unbound.nix create mode 100644 aspects/hosts/_alexandria/vaultwarden.nix create mode 100644 aspects/hosts/_io/boot.nix create mode 100644 aspects/hosts/_io/disko.nix create mode 100644 aspects/hosts/_io/hardware-configuration.nix create mode 100644 aspects/hosts/_io/programs.nix create mode 100644 aspects/hosts/_io/services.nix create mode 100644 aspects/hosts/_rotterdam/boot.nix create mode 100644 aspects/hosts/_rotterdam/hardware-configuration.nix create mode 100644 aspects/hosts/_rotterdam/hardware.nix create mode 100644 aspects/hosts/_rotterdam/programs.nix create mode 100644 aspects/hosts/_rotterdam/services.nix create mode 100644 aspects/hosts/_trantor/boot.nix create mode 100644 aspects/hosts/_trantor/disko.nix create mode 100644 aspects/hosts/_trantor/fail2ban.nix create mode 100644 aspects/hosts/_trantor/forgejo.nix create mode 100644 aspects/hosts/_trantor/hardware-configuration.nix create mode 100644 aspects/hosts/_trantor/networking.nix create mode 100644 aspects/hosts/_trantor/nginx.nix create mode 100644 aspects/hosts/_trantor/openssh.nix create mode 100644 aspects/hosts/_trantor/unbound.nix create mode 100644 aspects/hosts/alexandria.nix create mode 100644 aspects/hosts/io.nix create mode 100644 aspects/hosts/rotterdam.nix create mode 100644 aspects/hosts/trantor.nix diff --git a/aspects/hosts/_alexandria/hardware-configuration.nix b/aspects/hosts/_alexandria/hardware-configuration.nix new file mode 100644 index 0000000..63ecba5 --- /dev/null +++ b/aspects/hosts/_alexandria/hardware-configuration.nix @@ -0,0 +1,49 @@ +{ + config, + lib, + modulesPath, + ... +}: + +{ + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; + + boot = { + initrd = { + availableKernelModules = [ + "xhci_pci" + "ahci" + "usbhid" + "usb_storage" + "sd_mod" + ]; + kernelModules = [ ]; + }; + kernelModules = [ "kvm-intel" ]; + extraModulePackages = [ ]; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/31289617-1d84-4432-a833-680b52e88525"; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/disk/by-uuid/4130-BE54"; + fsType = "vfat"; + }; + }; + + swapDevices = [ + { + device = "/swapfile"; + size = 8192; + } + ]; + + networking.useDHCP = lib.mkDefault true; + + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/aspects/hosts/_alexandria/jellyfin.nix b/aspects/hosts/_alexandria/jellyfin.nix new file mode 100644 index 0000000..591403d --- /dev/null +++ b/aspects/hosts/_alexandria/jellyfin.nix @@ -0,0 +1,15 @@ +{ lib, inputs, ... }: +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts; +in +{ + services.jellyfin = { + enable = true; + openFirewall = true; + }; + + services.nginx.virtualHosts = mkNginxVHosts { + domains."jellyfin.baduhai.dev".locations."/".proxyPass = "http://127.0.0.1:8096/"; + }; +} diff --git a/aspects/hosts/_alexandria/kanidm.nix b/aspects/hosts/_alexandria/kanidm.nix new file mode 100644 index 0000000..d51eb14 --- /dev/null +++ b/aspects/hosts/_alexandria/kanidm.nix @@ -0,0 +1,83 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts; + kanidmCertDir = "/var/lib/kanidm/certs"; +in + +{ + services.kanidm = { + enableServer = true; + enableClient = true; + package = pkgs.kanidm; + + serverSettings = { + domain = "auth.baduhai.dev"; + origin = "https://auth.baduhai.dev"; + bindaddress = "127.0.0.1:8443"; + ldapbindaddress = "127.0.0.1:636"; + trust_x_forward_for = true; + # Use self-signed certificates for internal TLS + tls_chain = "${kanidmCertDir}/cert.pem"; + tls_key = "${kanidmCertDir}/key.pem"; + }; + + clientSettings = { + uri = "https://auth.baduhai.dev"; + }; + }; + + services.nginx.virtualHosts = mkNginxVHosts { + domains."auth.baduhai.dev" = { + locations."/" = { + proxyPass = "https://127.0.0.1:8443"; + extraConfig = '' + proxy_ssl_verify off; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + ''; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 636 ]; + + # Generate self-signed certificates for kanidm's internal TLS + systemd.services.kanidm-generate-certs = { + description = "Generate self-signed TLS certificates for Kanidm"; + wantedBy = [ "multi-user.target" ]; + before = [ "kanidm.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + mkdir -p ${kanidmCertDir} + if [ ! -f ${kanidmCertDir}/key.pem ]; then + ${pkgs.openssl}/bin/openssl req -x509 -newkey rsa:4096 \ + -keyout ${kanidmCertDir}/key.pem \ + -out ${kanidmCertDir}/cert.pem \ + -days 3650 -nodes \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + chown -R kanidm:kanidm ${kanidmCertDir} + chmod 600 ${kanidmCertDir}/key.pem + chmod 644 ${kanidmCertDir}/cert.pem + fi + ''; + }; + + # Ensure certificate generation runs before kanidm starts + systemd.services.kanidm = { + after = [ "kanidm-generate-certs.service" ]; + wants = [ "kanidm-generate-certs.service" ]; + }; +} diff --git a/aspects/hosts/_alexandria/nextcloud.nix b/aspects/hosts/_alexandria/nextcloud.nix new file mode 100644 index 0000000..9d69199 --- /dev/null +++ b/aspects/hosts/_alexandria/nextcloud.nix @@ -0,0 +1,97 @@ +{ + lib, + config, + pkgs, + inputs, + ... +}: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts; +in + +{ + services = { + nextcloud = { + enable = true; + package = pkgs.nextcloud32; + datadir = "/data/nextcloud"; + hostName = "cloud.baduhai.dev"; + configureRedis = true; + https = true; + secretFile = config.age.secrets."nextcloud-secrets.json".path; + database.createLocally = true; + maxUploadSize = "16G"; + extraApps = { + inherit (config.services.nextcloud.package.packages.apps) + calendar + contacts + notes + tasks + user_oidc + ; + }; + extraAppsEnable = true; + caching = { + apcu = true; + redis = true; + }; + settings = { + trusted_proxies = [ "127.0.0.1" ]; + default_phone_region = "BR"; + maintenance_window_start = "4"; + allow_local_remote_servers = true; + enabledPreviewProviders = [ + "OC\\Preview\\BMP" + "OC\\Preview\\EMF" + "OC\\Preview\\Font" + "OC\\Preview\\GIF" + "OC\\Preview\\HEIC" + "OC\\Preview\\Illustrator" + "OC\\Preview\\JPEG" + "OC\\Preview\\Krita" + "OC\\Preview\\MarkDown" + "OC\\Preview\\Movie" + "OC\\Preview\\MP3" + "OC\\Preview\\MSOffice2003" + "OC\\Preview\\MSOffice2007" + "OC\\Preview\\MSOfficeDoc" + "OC\\Preview\\OpenDocument" + "OC\\Preview\\PDF" + "OC\\Preview\\Photoshop" + "OC\\Preview\\PNG" + "OC\\Preview\\Postscript" + "OC\\Preview\\SVG" + "OC\\Preview\\TIFF" + "OC\\Preview\\TXT" + "OC\\Preview\\XBitmap" + ]; + }; + config = { + dbtype = "pgsql"; + adminpassFile = config.age.secrets.nextcloud-adminpass.path; + }; + phpOptions = { + "opcache.interned_strings_buffer" = "16"; + }; + }; + + nginx.virtualHosts = mkNginxVHosts { + domains."cloud.baduhai.dev" = { }; + }; + }; + + age.secrets = { + "nextcloud-secrets.json" = { + file = ../../../secrets/nextcloud-secrets.json.age; + owner = "nextcloud"; + group = "nextcloud"; + }; + nextcloud-adminpass = { + file = ../../../secrets/nextcloud-adminpass.age; + owner = "nextcloud"; + group = "nextcloud"; + }; + }; +} diff --git a/aspects/hosts/_alexandria/nginx.nix b/aspects/hosts/_alexandria/nginx.nix new file mode 100644 index 0000000..19258dd --- /dev/null +++ b/aspects/hosts/_alexandria/nginx.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + inputs, + ... +}: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts services; + + # Get all unique domains from shared services that have LAN IPs (served by this host) + localDomains = lib.unique (map (s: s.domain) (lib.filter (s: s.host == "alexandria") services)); + + # Generate ACME cert configs for all local domains + acmeCerts = lib.genAttrs localDomains (domain: { + group = "nginx"; + }); +in + +{ + security.acme = { + acceptTerms = true; + defaults = { + email = "baduhai@proton.me"; + dnsResolver = "1.1.1.1:53"; + dnsProvider = "cloudflare"; + credentialsFile = config.age.secrets.cloudflare.path; + }; + certs = acmeCerts; + }; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + virtualHosts = { + "_" = { + default = true; + locations."/".return = "444"; + }; + }; + }; + + users.users.nginx.extraGroups = [ "acme" ]; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + + age.secrets.cloudflare = { + file = ../../../secrets/cloudflare.age; + owner = "nginx"; + group = "nginx"; + }; +} diff --git a/aspects/hosts/_alexandria/unbound.nix b/aspects/hosts/_alexandria/unbound.nix new file mode 100644 index 0000000..6e46cdd --- /dev/null +++ b/aspects/hosts/_alexandria/unbound.nix @@ -0,0 +1,58 @@ +{ inputs, lib, ... }: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; +in + +{ + services.unbound = { + enable = true; + enableRootTrustAnchor = true; + settings = { + server = { + interface = [ + "0.0.0.0" + "::" + ]; + access-control = [ + "127.0.0.0/8 allow" + "192.168.0.0/16 allow" + "::1/128 allow" + ]; + + num-threads = 2; + msg-cache-size = "50m"; + rrset-cache-size = "100m"; + cache-min-ttl = 300; + cache-max-ttl = 86400; + prefetch = true; + prefetch-key = true; + hide-identity = true; + hide-version = true; + so-rcvbuf = "1m"; + so-sndbuf = "1m"; + + # LAN-only DNS records + local-zone = ''"baduhai.dev." transparent''; + local-data = map (e: ''"${e.domain}. IN A ${e.lanIP}"'') + (lib.filter (e: e ? lanIP) utils.services); + }; + + forward-zone = [ + { + name = "."; + forward-addr = [ + "1.1.1.1@853#cloudflare-dns.com" + "1.0.0.1@853#cloudflare-dns.com" + ]; + forward-tls-upstream = true; + } + ]; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; +} diff --git a/aspects/hosts/_alexandria/vaultwarden.nix b/aspects/hosts/_alexandria/vaultwarden.nix new file mode 100644 index 0000000..81e65b1 --- /dev/null +++ b/aspects/hosts/_alexandria/vaultwarden.nix @@ -0,0 +1,26 @@ +{ + config, + lib, + inputs, + ... +}: +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts; +in +{ + services.vaultwarden = { + enable = true; + config = { + DOMAIN = "https://pass.baduhai.dev"; + SIGNUPS_ALLOWED = false; + ROCKET_ADDRESS = "127.0.0.1"; + ROCKET_PORT = 58222; + }; + }; + + services.nginx.virtualHosts = mkNginxVHosts { + domains."pass.baduhai.dev".locations."/".proxyPass = + "http://${config.services.vaultwarden.config.ROCKET_ADDRESS}:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; + }; +} diff --git a/aspects/hosts/_io/boot.nix b/aspects/hosts/_io/boot.nix new file mode 100644 index 0000000..326f7dc --- /dev/null +++ b/aspects/hosts/_io/boot.nix @@ -0,0 +1,14 @@ +{ + boot = { + # TODO check if future kernel versions fix boot issue with systemd initrd with tpm + initrd.systemd.tpm2.enable = false; + kernelParams = [ + "nosgx" + "i915.fastboot=1" + "mem_sleep_default=deep" + ]; + extraModprobeConfig = '' + options snd-intel-dspcfg dsp_driver=3 + ''; + }; +} diff --git a/aspects/hosts/_io/disko.nix b/aspects/hosts/_io/disko.nix new file mode 100644 index 0000000..4e6c9d5 --- /dev/null +++ b/aspects/hosts/_io/disko.nix @@ -0,0 +1,79 @@ +{ inputs, ... }: + +{ + imports = [ inputs.disko.nixosModules.default ]; + + disko.devices.disk.main = { + type = "disk"; + device = "/dev/disk/by-id/mmc-hDEaP3_0x1041b689"; + content = { + type = "gpt"; + partitions = { + ESP = { + priority = 1; + name = "ESP"; + start = "1MiB"; + end = "1GiB"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot/efi"; + mountOptions = [ + "noatime" + "fmask=0077" + "dmask=0077" + ]; + }; + }; + cryptroot = { + priority = 2; + name = "root"; + size = "100%"; + content = { + type = "luks"; + name = "cryptroot"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "@root" = { + mountpoint = "/"; + mountOptions = [ + "noatime" + "compress=zstd" + "subvol=@root" + ]; + }; + "@home" = { + mountpoint = "/home"; + mountOptions = [ + "noatime" + "compress=zstd" + "subvol=@home" + ]; + }; + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ + "noatime" + "compress=zstd" + "subvol=@nix" + ]; + }; + "@persistent" = { + mountpoint = "/persistent"; + mountOptions = [ + "noatime" + "compress=zstd" + "subvol=@persistent" + ]; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/aspects/hosts/_io/hardware-configuration.nix b/aspects/hosts/_io/hardware-configuration.nix new file mode 100644 index 0000000..8e4dae4 --- /dev/null +++ b/aspects/hosts/_io/hardware-configuration.nix @@ -0,0 +1,37 @@ +{ + config, + lib, + modulesPath, + inputs, + ... +}: + +{ + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; + + boot = { + initrd = { + availableKernelModules = [ + "xhci_pci" + "ahci" + "usb_storage" + "sd_mod" + "sdhci_pci" + ]; + }; + kernelModules = [ "kvm-intel" ]; + }; + + zramSwap = { + enable = true; + memoryPercent = 100; + }; + + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/aspects/hosts/_io/programs.nix b/aspects/hosts/_io/programs.nix new file mode 100644 index 0000000..e272d22 --- /dev/null +++ b/aspects/hosts/_io/programs.nix @@ -0,0 +1,27 @@ +{ pkgs, ... }: + +let + cml-ucm-conf = pkgs.alsa-ucm-conf.overrideAttrs { + wttsrc = pkgs.fetchFromGitHub { + owner = "WeirdTreeThing"; + repo = "chromebook-ucm-conf"; + rev = "b6ce2a7"; + hash = "sha256-QRUKHd3RQmg1tnZU8KCW0AmDtfw/daOJ/H3XU5qWTCc="; + }; + postInstall = '' + echo "v0.4.1" > $out/chromebook.patched + cp -R $wttsrc/{common,codecs,platforms} $out/share/alsa/ucm2 + cp -R $wttsrc/{cml,sof-rt5682} $out/share/alsa/ucm2/conf.d + ''; + }; +in + +{ + environment = { + systemPackages = with pkgs; [ + maliit-keyboard + sof-firmware + ]; + sessionVariables.ALSA_CONFIG_UCM2 = "${cml-ucm-conf}/share/alsa/ucm2"; + }; +} diff --git a/aspects/hosts/_io/services.nix b/aspects/hosts/_io/services.nix new file mode 100644 index 0000000..df41a6f --- /dev/null +++ b/aspects/hosts/_io/services.nix @@ -0,0 +1,61 @@ +{ pkgs, ... }: + +{ + services = { + keyd = { + enable = true; + keyboards.main = { + ids = [ "0001:0001" ]; + settings = { + main = { + meta = "overload(meta, esc)"; + f1 = "back"; + f2 = "forward"; + f3 = "refresh"; + f4 = "M-f11"; + f5 = "M-w"; + f6 = "brightnessdown"; + f7 = "brightnessup"; + f8 = "timeout(mute, 200, micmute)"; + f9 = "play"; + f10 = "timeout(nextsong, 200, previoussong)"; + f13 = "delete"; + "102nd" = "layer(function)"; + }; + shift = { + leftshift = "capslock"; + rightshift = "capslock"; + }; + function = { + escape = "f1"; + f1 = "f2"; + f2 = "f3"; + f3 = "f4"; + f4 = "f5"; + f5 = "f6"; + f6 = "f7"; + f7 = "f8"; + f8 = "f9"; + f9 = "f10"; + f10 = "f11"; + f13 = "f12"; + y = "sysrq"; + k = "home"; + l = "pageup"; + "," = "end"; + "." = "pagedown"; + }; + }; + }; + }; + upower.enable = true; + power-profiles-daemon.enable = true; + }; + + # TODO: remove once gmodena/nix-flatpak/issues/45 fixed + systemd.services."flatpak-managed-install" = { + serviceConfig = { + ExecStartPre = "${pkgs.coreutils}/bin/sleep 5"; + }; + }; +} diff --git a/aspects/hosts/_rotterdam/boot.nix b/aspects/hosts/_rotterdam/boot.nix new file mode 100644 index 0000000..d038ede --- /dev/null +++ b/aspects/hosts/_rotterdam/boot.nix @@ -0,0 +1,31 @@ +{ pkgs, ... }: + +let + qubesnsh = pkgs.writeTextFile { + name = "qubes.nsh"; + text = "HD1f65535a1:EFI\\qubes\\grubx64.efi"; + }; +in + +{ + boot = { + kernelParams = [ + "processor.max_cstate=1" # Fixes bug where ryzen cpus freeze when in highest C state + "clearcpuid=514" + "amdgpu.ppfeaturemask=0xfffd3fff" # Fixes amdgpu freezing + ]; + # QubesOS boot entry + loader.systemd-boot = { + extraFiles = { + "efi/edk2-shell/shell.efi" = "${pkgs.edk2-uefi-shell}/shell.efi"; + "qubes.nsh" = qubesnsh; + }; + extraEntries."qubes.conf" = '' + title Qubes OS + efi /efi/edk2-shell/shell.efi + options -nointerrupt qubes.nsh + sort-key ab + ''; + }; + }; +} diff --git a/aspects/hosts/_rotterdam/hardware-configuration.nix b/aspects/hosts/_rotterdam/hardware-configuration.nix new file mode 100644 index 0000000..169c53f --- /dev/null +++ b/aspects/hosts/_rotterdam/hardware-configuration.nix @@ -0,0 +1,82 @@ +{ + config, + lib, + modulesPath, + ... +}: + +{ + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; + + boot = { + initrd = { + availableKernelModules = [ + "amdgpu" + "nvme" + "xhci_pci" + "ahci" + "usbhid" + "sd_mod" + ]; + luks.devices."cryptroot" = { + device = "/dev/disk/by-uuid/f7dd4142-7109-4493-834d-4a831777f08d"; + keyFile = "/dev/disk/by-partuuid/add5fc14-e20f-48be-8b2a-0799ef04d3cb"; + }; + }; + kernelModules = [ "kvm-amd" ]; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/3287dbc3-c0fa-4096-a0b3-59b017cfecc8"; + fsType = "btrfs"; + options = [ + "subvol=@root" + "noatime" + "compress=zstd" + ]; + }; + "/home" = { + device = "/dev/disk/by-uuid/3287dbc3-c0fa-4096-a0b3-59b017cfecc8"; + fsType = "btrfs"; + options = [ + "subvol=@home" + "noatime" + "compress=zstd" + ]; + }; + "/boot/efi" = { + device = "/dev/disk/by-uuid/F2A2-CF5A"; + fsType = "vfat"; + options = [ + "noatime" + "fmask=0077" + "dmask=0077" + ]; + }; + "/nix" = { + device = "/dev/disk/by-uuid/3287dbc3-c0fa-4096-a0b3-59b017cfecc8"; + fsType = "btrfs"; + options = [ + "subvol=@nix" + "noatime" + "compress=zstd" + ]; + }; + "/persistent" = { + device = "/dev/disk/by-uuid/3287dbc3-c0fa-4096-a0b3-59b017cfecc8"; + fsType = "btrfs"; + options = [ + "subvol=@persistent" + "noatime" + "compress=zstd" + ]; + }; + }; + + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/aspects/hosts/_rotterdam/hardware.nix b/aspects/hosts/_rotterdam/hardware.nix new file mode 100644 index 0000000..6f76e99 --- /dev/null +++ b/aspects/hosts/_rotterdam/hardware.nix @@ -0,0 +1,8 @@ +{ pkgs, ... }: + +{ + hardware = { + amdgpu.opencl.enable = true; + graphics.extraPackages = with pkgs; [ rocmPackages.clr.icd ]; + }; +} diff --git a/aspects/hosts/_rotterdam/programs.nix b/aspects/hosts/_rotterdam/programs.nix new file mode 100644 index 0000000..b4dcfd9 --- /dev/null +++ b/aspects/hosts/_rotterdam/programs.nix @@ -0,0 +1,31 @@ +{ pkgs, ... }: + +let + reboot-into-qubes = pkgs.makeDesktopItem { + name = "reboot-into-qubes"; + icon = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/vinceliuice/Qogir-icon-theme/31f267e1f5fd4e9596bfd78dfb41a03d3a9f33ee/src/scalable/apps/distributor-logo-qubes.svg"; + sha256 = "sha256-QbHr7s5Wcs7uFtfqZctMyS0iDbMfiiZOKy2nHhDOfn0="; + }; + desktopName = "Qubes OS"; + genericName = "Reboot into Qubes OS"; + categories = [ "System" ]; + startupNotify = true; + exec = pkgs.writeShellScript "reboot-into-qubes" '' + ${pkgs.yad}/bin/yad --form \ + --title="Qubes OS" \ + --image distributor-logo-qubes \ + --text "Are you sure you want to reboot into Qubes OS?" \ + --button="Yes:0" --button="Cancel:1" + if [ $? -eq 0 ]; then + systemctl reboot --boot-loader-entry=qubes.conf + fi + ''; + }; +in + +{ + environment.systemPackages = [ reboot-into-qubes ]; + + programs.steam.dedicatedServer.openFirewall = true; +} diff --git a/aspects/hosts/_rotterdam/services.nix b/aspects/hosts/_rotterdam/services.nix new file mode 100644 index 0000000..0bf276f --- /dev/null +++ b/aspects/hosts/_rotterdam/services.nix @@ -0,0 +1,11 @@ +{ + services.keyd = { + enable = true; + keyboards.main = { + ids = [ "5653:0001" ]; + settings.main = { + esc = "overload(meta, esc)"; + }; + }; + }; +} diff --git a/aspects/hosts/_trantor/boot.nix b/aspects/hosts/_trantor/boot.nix new file mode 100644 index 0000000..0498818 --- /dev/null +++ b/aspects/hosts/_trantor/boot.nix @@ -0,0 +1,6 @@ +{ + boot = { + initrd.systemd.enable = true; + loader.efi.efiSysMountPoint = "/boot/efi"; + }; +} diff --git a/aspects/hosts/_trantor/disko.nix b/aspects/hosts/_trantor/disko.nix new file mode 100644 index 0000000..0e47058 --- /dev/null +++ b/aspects/hosts/_trantor/disko.nix @@ -0,0 +1,64 @@ +{ inputs, ... }: + +{ + imports = [ inputs.disko.nixosModules.default ]; + + disko.devices.disk.main = { + type = "disk"; + device = "/dev/disk/by-id/scsi-360b207ed25d84372a95d1ecf842f8e20"; + content = { + type = "gpt"; + partitions = { + ESP = { + priority = 1; + name = "ESP"; + start = "1MiB"; + end = "512MiB"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot/efi"; + mountOptions = [ + "noatime" + "fmask=0077" + "dmask=0077" + ]; + }; + }; + root = { + priority = 2; + name = "root"; + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "@root" = { + mountpoint = "/"; + mountOptions = [ + "noatime" + "compress=zstd" + ]; + }; + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ + "noatime" + "compress=zstd" + ]; + }; + "@persistent" = { + mountpoint = "/persistent"; + mountOptions = [ + "noatime" + "compress=zstd" + ]; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/aspects/hosts/_trantor/fail2ban.nix b/aspects/hosts/_trantor/fail2ban.nix new file mode 100644 index 0000000..bc05139 --- /dev/null +++ b/aspects/hosts/_trantor/fail2ban.nix @@ -0,0 +1,23 @@ +{ config, pkgs, ... }: + +{ + services.fail2ban = { + enable = true; + maxretry = 5; + ignoreIP = [ + "127.0.0.0/8" + "::1" + "10.0.0.0/8" + "172.16.0.0/12" + "192.168.0.0/16" + "100.64.0.0/10" + ]; + bantime = "1h"; + bantime-increment = { + enable = true; + multipliers = "1 2 4 8 16 32 64"; + maxtime = "10000h"; + overalljails = true; + }; + }; +} diff --git a/aspects/hosts/_trantor/forgejo.nix b/aspects/hosts/_trantor/forgejo.nix new file mode 100644 index 0000000..e6b2159 --- /dev/null +++ b/aspects/hosts/_trantor/forgejo.nix @@ -0,0 +1,72 @@ +{ + config, + lib, + inputs, + ... +}: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts; +in + +{ + services = { + forgejo = { + enable = true; + settings = { + session.COOKIE_SECURE = true; + server = { + PROTOCOL = "http+unix"; + DOMAIN = "git.baduhai.dev"; + ROOT_URL = "https://git.baduhai.dev"; + OFFLINE_MODE = true; # disable use of CDNs + SSH_DOMAIN = "git.baduhai.dev"; + }; + log.LEVEL = "Warn"; + mailer.ENABLED = false; + actions.ENABLED = false; + service.DISABLE_REGISTRATION = true; + oauth2_client = { + ENABLE_AUTO_REGISTRATION = true; + UPDATE_AVATAR = true; + ACCOUNT_LINKING = "login"; + USERNAME = "preferred_username"; + }; + }; + }; + nginx.virtualHosts = mkNginxVHosts { + domains."git.baduhai.dev".locations."/".proxyPass = + "http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}:/"; + }; + fail2ban.jails.forgejo = { + settings = { + enabled = true; + filter = "forgejo"; + maxretry = 3; + findtime = "10m"; + bantime = "1h"; + }; + }; + }; + + environment = { + etc."fail2ban/filter.d/forgejo.conf".text = '' + [Definition] + failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from + ignoreregex = + journalmatch = _SYSTEMD_UNIT=forgejo.service + ''; + + persistence.main.directories = [ + { + directory = config.services.forgejo.stateDir; + inherit (config.services.forgejo) user group; + mode = "0700"; + } + ]; + }; + + # Disable PrivateMounts to allow LoadCredential to work with bind-mounted directories + systemd.services.forgejo.serviceConfig.PrivateMounts = lib.mkForce false; +} diff --git a/aspects/hosts/_trantor/hardware-configuration.nix b/aspects/hosts/_trantor/hardware-configuration.nix new file mode 100644 index 0000000..039129e --- /dev/null +++ b/aspects/hosts/_trantor/hardware-configuration.nix @@ -0,0 +1,20 @@ +{ + lib, + modulesPath, + ... +}: + +{ + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + + boot.initrd.availableKernelModules = [ + "xhci_pci" + "virtio_pci" + "virtio_scsi" + "usbhid" + ]; + + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; +} diff --git a/aspects/hosts/_trantor/networking.nix b/aspects/hosts/_trantor/networking.nix new file mode 100644 index 0000000..4dcbe1e --- /dev/null +++ b/aspects/hosts/_trantor/networking.nix @@ -0,0 +1,8 @@ +{ + networking = { + firewall = { + allowedTCPPorts = [ 25566 ]; + allowedUDPPorts = [ 25566 ]; + }; + }; +} diff --git a/aspects/hosts/_trantor/nginx.nix b/aspects/hosts/_trantor/nginx.nix new file mode 100644 index 0000000..1fd6b5c --- /dev/null +++ b/aspects/hosts/_trantor/nginx.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + inputs, + ... +}: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; + inherit (utils) mkNginxVHosts services; + + # Get all unique domains from shared services on trantor (host = "trantor") + localDomains = lib.unique ( + map (s: s.domain) (lib.filter (s: s.host == "trantor") services) + ); + + # Generate ACME cert configs for all local domains + acmeCerts = lib.genAttrs localDomains (domain: { + group = "nginx"; + }); +in + +{ + security.acme = { + acceptTerms = true; + defaults = { + email = "baduhai@proton.me"; + dnsResolver = "1.1.1.1:53"; + dnsProvider = "cloudflare"; + credentialsFile = config.age.secrets.cloudflare.path; + }; + certs = acmeCerts; + }; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + virtualHosts = { + "_" = { + default = true; + locations."/".return = "444"; + }; + }; + }; + + users.users.nginx.extraGroups = [ "acme" ]; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + + age.secrets.cloudflare = { + file = ../../../secrets/cloudflare.age; + owner = "nginx"; + group = "nginx"; + }; +} diff --git a/aspects/hosts/_trantor/openssh.nix b/aspects/hosts/_trantor/openssh.nix new file mode 100644 index 0000000..704b3df --- /dev/null +++ b/aspects/hosts/_trantor/openssh.nix @@ -0,0 +1,23 @@ +{ ... }: + +{ + services = { + openssh = { + settings = { + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + }; + }; + fail2ban.jails.sshd = { + settings = { + enabled = true; + port = "ssh"; + filter = "sshd"; + logpath = "/var/log/auth.log"; + maxretry = 3; + findtime = "10m"; + bantime = "1h"; + }; + }; + }; +} diff --git a/aspects/hosts/_trantor/unbound.nix b/aspects/hosts/_trantor/unbound.nix new file mode 100644 index 0000000..367c2c8 --- /dev/null +++ b/aspects/hosts/_trantor/unbound.nix @@ -0,0 +1,58 @@ +{ inputs, lib, ... }: + +let + utils = import ../../../utils.nix { inherit inputs lib; }; +in + +{ + services.unbound = { + enable = true; + enableRootTrustAnchor = true; + settings = { + server = { + interface = [ + "0.0.0.0" + "::" + ]; + access-control = [ + "127.0.0.0/8 allow" + "100.64.0.0/10 allow" # Tailscale CGNAT range + "::1/128 allow" + "fd7a:115c:a1e0::/48 allow" # Tailscale IPv6 + ]; + + num-threads = 2; + msg-cache-size = "50m"; + rrset-cache-size = "100m"; + cache-min-ttl = 300; + cache-max-ttl = 86400; + prefetch = true; + prefetch-key = true; + hide-identity = true; + hide-version = true; + so-rcvbuf = "1m"; + so-sndbuf = "1m"; + + # Tailnet DNS records from shared services + local-zone = ''"baduhai.dev." transparent''; + local-data = map (e: ''"${e.domain}. IN A ${e.tailscaleIP}"'') utils.services; + }; + + forward-zone = [ + { + name = "."; + forward-addr = [ + "1.1.1.1@853#cloudflare-dns.com" + "1.0.0.1@853#cloudflare-dns.com" + ]; + forward-tls-upstream = true; + } + ]; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; +} diff --git a/aspects/hosts/alexandria.nix b/aspects/hosts/alexandria.nix new file mode 100644 index 0000000..28a367d --- /dev/null +++ b/aspects/hosts/alexandria.nix @@ -0,0 +1,42 @@ +{ inputs, ... }: +{ + flake.nixosConfigurations.alexandria = inputs.nixpkgs-stable.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + inputs.agenix.nixosModules.default + { networking.hostName = "alexandria"; } + { nixpkgs.overlays = [ inputs.agenix.overlays.default inputs.self.overlays.default ]; } + + # Common aspects (always included) + inputs.self.modules.nixos.common-boot + inputs.self.modules.nixos.common-console + inputs.self.modules.nixos.common-firewall + inputs.self.modules.nixos.common-locale + inputs.self.modules.nixos.common-nix + inputs.self.modules.nixos.common-openssh + inputs.self.modules.nixos.common-programs + inputs.self.modules.nixos.common-security + inputs.self.modules.nixos.common-services + inputs.self.modules.nixos.common-tailscale + inputs.self.modules.nixos.common-users + + # Server aspects + inputs.self.modules.nixos.server-boot + inputs.self.modules.nixos.server-nix + inputs.self.modules.nixos.server-tailscale + + # Other aspects based on tags + inputs.self.modules.nixos.fwupd + + # Host-specific files (from _alexandria/) + ./_alexandria/hardware-configuration.nix + ./_alexandria/jellyfin.nix + ./_alexandria/kanidm.nix + ./_alexandria/nextcloud.nix + ./_alexandria/nginx.nix + ./_alexandria/unbound.nix + ./_alexandria/vaultwarden.nix + ]; + }; +} diff --git a/aspects/hosts/io.nix b/aspects/hosts/io.nix new file mode 100644 index 0000000..1d7f15a --- /dev/null +++ b/aspects/hosts/io.nix @@ -0,0 +1,51 @@ +{ inputs, ... }: +{ + flake.nixosConfigurations.io = inputs.nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + inputs.agenix.nixosModules.default + { networking.hostName = "io"; } + { nixpkgs.overlays = [ inputs.agenix.overlays.default inputs.self.overlays.default ]; } + + # Common aspects (always included) + inputs.self.modules.nixos.common-boot + inputs.self.modules.nixos.common-console + inputs.self.modules.nixos.common-firewall + inputs.self.modules.nixos.common-locale + inputs.self.modules.nixos.common-nix + inputs.self.modules.nixos.common-openssh + inputs.self.modules.nixos.common-programs + inputs.self.modules.nixos.common-security + inputs.self.modules.nixos.common-services + inputs.self.modules.nixos.common-tailscale + inputs.self.modules.nixos.common-users + + # Desktop aspects + inputs.self.modules.nixos.desktop-boot + inputs.self.modules.nixos.desktop-desktop + inputs.self.modules.nixos.desktop-nix + inputs.self.modules.nixos.desktop-services + + # Other aspects based on tags + inputs.self.modules.nixos.ai + inputs.self.modules.nixos.bluetooth + inputs.self.modules.nixos.dev + inputs.self.modules.nixos.libvirtd + inputs.self.modules.nixos.networkmanager + inputs.self.modules.nixos.podman + + # Factory-generated ephemeral module + (inputs.self.factory.ephemeral { + rootDevice = "/dev/mapper/cryptroot"; + }) + + # Host-specific files (from _io/) + ./_io/hardware-configuration.nix + ./_io/disko.nix + ./_io/boot.nix + ./_io/programs.nix + ./_io/services.nix + ]; + }; +} diff --git a/aspects/hosts/rotterdam.nix b/aspects/hosts/rotterdam.nix new file mode 100644 index 0000000..e9d3f18 --- /dev/null +++ b/aspects/hosts/rotterdam.nix @@ -0,0 +1,56 @@ +{ inputs, ... }: +{ + flake.nixosConfigurations.rotterdam = inputs.nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + inputs.agenix.nixosModules.default + { networking.hostName = "rotterdam"; } + { nixpkgs.overlays = [ inputs.agenix.overlays.default inputs.self.overlays.default ]; } + + # Common aspects (always included) + inputs.self.modules.nixos.common-boot + inputs.self.modules.nixos.common-console + inputs.self.modules.nixos.common-firewall + inputs.self.modules.nixos.common-locale + inputs.self.modules.nixos.common-nix + inputs.self.modules.nixos.common-openssh + inputs.self.modules.nixos.common-programs + inputs.self.modules.nixos.common-security + inputs.self.modules.nixos.common-services + inputs.self.modules.nixos.common-tailscale + inputs.self.modules.nixos.common-users + + # Desktop aspects + inputs.self.modules.nixos.desktop-boot + inputs.self.modules.nixos.desktop-desktop + inputs.self.modules.nixos.desktop-nix + inputs.self.modules.nixos.desktop-services + + # Other aspects based on tags + inputs.self.modules.nixos.ai + inputs.self.modules.nixos.bluetooth + inputs.self.modules.nixos.dev + inputs.self.modules.nixos.fwupd + inputs.self.modules.nixos.gaming-steam + inputs.self.modules.nixos.gaming-hardware + inputs.self.modules.nixos.gaming-flatpak + inputs.self.modules.nixos.gaming-launchers + inputs.self.modules.nixos.libvirtd + inputs.self.modules.nixos.networkmanager + inputs.self.modules.nixos.podman + + # Factory-generated ephemeral module + (inputs.self.factory.ephemeral { + rootDevice = "/dev/mapper/cryptroot"; + }) + + # Host-specific files (from _rotterdam/) + ./_rotterdam/hardware-configuration.nix + ./_rotterdam/boot.nix + ./_rotterdam/hardware.nix + ./_rotterdam/programs.nix + ./_rotterdam/services.nix + ]; + }; +} diff --git a/aspects/hosts/trantor.nix b/aspects/hosts/trantor.nix new file mode 100644 index 0000000..646cc73 --- /dev/null +++ b/aspects/hosts/trantor.nix @@ -0,0 +1,46 @@ +{ inputs, ... }: +{ + flake.nixosConfigurations.trantor = inputs.nixpkgs-stable.lib.nixosSystem { + system = "aarch64-linux"; + specialArgs = { inherit inputs; }; + modules = [ + inputs.agenix.nixosModules.default + { networking.hostName = "trantor"; } + { nixpkgs.overlays = [ inputs.agenix.overlays.default inputs.self.overlays.default ]; } + + # Common aspects (always included) + inputs.self.modules.nixos.common-boot + inputs.self.modules.nixos.common-console + inputs.self.modules.nixos.common-firewall + inputs.self.modules.nixos.common-locale + inputs.self.modules.nixos.common-nix + inputs.self.modules.nixos.common-openssh + inputs.self.modules.nixos.common-programs + inputs.self.modules.nixos.common-security + inputs.self.modules.nixos.common-services + inputs.self.modules.nixos.common-tailscale + inputs.self.modules.nixos.common-users + + # Server aspects + inputs.self.modules.nixos.server-boot + inputs.self.modules.nixos.server-nix + inputs.self.modules.nixos.server-tailscale + + # Factory-generated ephemeral module + (inputs.self.factory.ephemeral { + rootDevice = "/dev/disk/by-id/scsi-360b207ed25d84372a95d1ecf842f8e20-part2"; + }) + + # Host-specific files (from _trantor/) + ./_trantor/hardware-configuration.nix + ./_trantor/disko.nix + ./_trantor/boot.nix + ./_trantor/fail2ban.nix + ./_trantor/forgejo.nix + ./_trantor/networking.nix + ./_trantor/nginx.nix + ./_trantor/openssh.nix + ./_trantor/unbound.nix + ]; + }; +}