From 7ccd7856eed93d2ba5b011d6211ff7c4f03a75f0 Mon Sep 17 00:00:00 2001 From: oddlama Date: Tue, 26 Nov 2024 13:34:55 +0100 Subject: [PATCH] chore: format everything --- config/boot.nix | 7 +- config/default.nix | 3 +- config/dev/default.nix | 7 +- config/dev/yubikey.nix | 14 +- config/graphical/default.nix | 255 ++-- config/graphical/fonts.nix | 5 +- config/graphical/steam.nix | 7 +- config/graphical/xserver.nix | 2 +- config/hardware/bluetooth.nix | 11 +- config/hardware/hetzner-cloud.nix | 7 +- config/hardware/nvidia.nix | 4 +- config/hardware/odroid-n2plus.nix | 8 +- config/hardware/physical.nix | 5 +- config/home-manager.nix | 5 +- config/impermanence.nix | 101 +- config/installer.nix | 6 +- config/issue.nix | 9 +- config/net.nix | 9 +- config/nftables.nix | 29 +- config/nix.nix | 9 +- config/optional/sound.nix | 5 +- config/optional/zfs.nix | 15 +- config/resolved.nix | 71 +- config/secrets.nix | 90 +- config/ssh.nix | 14 +- config/system.nix | 5 +- config/topology.nix | 3 +- config/users.nix | 76 +- globals.nix | 10 +- hosts/envoy/acme.nix | 11 +- hosts/envoy/default.nix | 9 +- hosts/envoy/fs.nix | 10 +- hosts/envoy/idmail.nix | 30 +- hosts/envoy/net.nix | 18 +- hosts/envoy/stalwart-mail.nix | 562 +++++---- hosts/kroma/default.nix | 13 +- hosts/kroma/fs.nix | 8 +- hosts/kroma/net.nix | 12 +- hosts/nom/default.nix | 14 +- hosts/nom/fs.nix | 8 +- hosts/nom/net.nix | 12 +- hosts/sentinel/acme.nix | 8 +- hosts/sentinel/blog.nix | 3 +- hosts/sentinel/coturn.nix | 43 +- hosts/sentinel/default.nix | 14 +- hosts/sentinel/fs.nix | 10 +- hosts/sentinel/net.nix | 25 +- hosts/sentinel/oauth2.nix | 55 +- hosts/sentinel/plausible.nix | 12 +- hosts/sentinel/postgresql.nix | 3 +- hosts/sire/default.nix | 133 +- hosts/sire/fs.nix | 16 +- hosts/sire/guests/actual.nix | 13 +- hosts/sire/guests/ai.nix | 14 +- hosts/sire/guests/common.nix | 15 +- hosts/sire/guests/grafana.nix | 38 +- hosts/sire/guests/immich.nix | 51 +- hosts/sire/guests/influxdb.nix | 110 +- hosts/sire/guests/loki.nix | 138 +- hosts/sire/guests/minecraft.nix | 103 +- hosts/sire/guests/paperless.nix | 44 +- hosts/sire/guests/samba.nix | 165 +-- hosts/sire/net.nix | 13 +- hosts/ward/default.nix | 85 +- hosts/ward/fs.nix | 14 +- hosts/ward/guests/adguardhome.nix | 46 +- hosts/ward/guests/common.nix | 15 +- hosts/ward/guests/forgejo.nix | 81 +- hosts/ward/guests/home-gateway.nix | 5 +- hosts/ward/guests/kanidm.nix | 85 +- hosts/ward/guests/netbird.nix | 20 +- hosts/ward/guests/radicale.nix | 17 +- hosts/ward/guests/vaultwarden.nix | 13 +- hosts/ward/guests/web-proxy.nix | 23 +- hosts/ward/kea.nix | 18 +- hosts/ward/net.nix | 45 +- hosts/zackbiene/default.nix | 15 +- hosts/zackbiene/esphome.nix | 12 +- hosts/zackbiene/fs.nix | 8 +- hosts/zackbiene/home-assistant.nix | 59 +- hosts/zackbiene/hostapd.nix | 14 +- hosts/zackbiene/kea.nix | 14 +- hosts/zackbiene/mosquitto.nix | 9 +- hosts/zackbiene/net.nix | 35 +- hosts/zackbiene/zigbee2mqtt.nix | 8 +- modules/acme-wildcard.nix | 61 +- modules/actual.nix | 23 +- modules/backups.nix | 72 +- modules/deterministic-ids.nix | 107 +- modules/distributed-config.nix | 98 +- modules/globals.nix | 325 ++--- modules/meta.nix | 9 +- modules/netbird-client.nix | 211 ++-- modules/nginx-upstream-monitoring.nix | 104 +- modules/oauth2-proxy.nix | 171 +-- modules/promtail.nix | 21 +- modules/secrets.nix | 30 +- modules/telegraf.nix | 263 ++-- nix/agenix-rekey.nix | 43 +- nix/extra-builtins.nix | 33 +- nix/globals.nix | 94 +- nix/hosts.nix | 143 ++- nix/installer-configuration.nix | 7 +- nix/iso.nix | 47 +- nix/pkgs.nix | 45 +- nix/storage-box.nix | 19 +- pkgs/actual-server.nix | 89 +- pkgs/awakened-poe-trade.nix | 29 +- pkgs/default.nix | 22 +- pkgs/deploy.nix | 5 +- pkgs/git-fuzzy/default.nix | 117 +- pkgs/ron.nix | 261 ++-- pkgs/scripts/clone-term.nix | 7 +- pkgs/scripts/default.nix | 12 +- pkgs/zsh-skim-histdb.nix | 2 +- topology/default.nix | 71 +- topology/flake-module.nix | 5 +- users/config/htop.nix | 15 +- users/config/impermanence.nix | 6 +- users/config/shell/default.nix | 5 +- users/config/shell/starship.nix | 3 +- users/config/shell/zsh/default.nix | 5 +- users/modules/secrets.nix | 6 +- users/myuser/default.nix | 121 +- users/myuser/dev/default.nix | 11 +- users/myuser/dev/gdb.nix | 24 +- users/myuser/dev/manpager.nix | 125 +- users/myuser/git.nix | 42 +- users/myuser/gpg.nix | 9 +- users/myuser/graphical/default.nix | 3 +- users/myuser/graphical/discord.nix | 3 +- users/myuser/graphical/firefox.nix | 38 +- users/myuser/graphical/flameshot.nix | 8 +- users/myuser/graphical/games/bottles.nix | 3 +- users/myuser/graphical/games/minecraft.nix | 3 +- users/myuser/graphical/games/poe.nix | 3 +- .../myuser/graphical/gpu-screen-recorder.nix | 8 +- users/myuser/graphical/hyprland.nix | 29 +- .../i3-per-workspace-layout/src/main.rs | 2 +- users/myuser/graphical/i3.nix | 166 +-- users/myuser/graphical/kitty.nix | 3 +- users/myuser/graphical/rofi.nix | 45 +- users/myuser/graphical/signal.nix | 3 +- users/myuser/graphical/swaync.nix | 3 +- users/myuser/graphical/swww.nix | 16 +- users/myuser/graphical/theme.nix | 47 +- users/myuser/graphical/thunderbird.nix | 17 +- users/myuser/graphical/ts3.nix | 8 +- users/myuser/graphical/waybar.nix | 24 +- users/myuser/graphical/wired-notify.nix | 1118 +++++++++-------- users/myuser/neovim/alpha.nix | 164 ++- users/myuser/neovim/appearance.nix | 44 +- users/myuser/neovim/completion.nix | 20 +- users/myuser/neovim/default.nix | 383 +++--- users/myuser/neovim/globals.nix | 9 +- users/myuser/neovim/languages.nix | 35 +- users/myuser/neovim/lsp.nix | 12 +- users/myuser/neovim/misc.nix | 22 +- users/myuser/neovim/neo-tree.nix | 2 +- users/myuser/neovim/onedark.nix | 133 +- users/myuser/ssh.nix | 3 +- users/root/default.nix | 7 +- 162 files changed, 4750 insertions(+), 3718 deletions(-) diff --git a/config/boot.nix b/config/boot.nix index 0e7d650..6a151b5 100644 --- a/config/boot.nix +++ b/config/boot.nix @@ -4,7 +4,8 @@ lib, pkgs, ... -}: { +}: +{ config = lib.mkIf (!config.boot.isContainer) { boot = { initrd.systemd = { @@ -16,11 +17,11 @@ extraBin.cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup"; # Give me a usable shell please users.root.shell = "${pkgs.bashInteractive}/bin/bash"; - storePaths = ["${pkgs.bashInteractive}/bin/bash"]; + storePaths = [ "${pkgs.bashInteractive}/bin/bash" ]; }; # NOTE: Add "rd.systemd.unit=rescue.target" to debug initrd - kernelParams = ["log_buf_len=16M"]; # must be {power of two}[KMG] + kernelParams = [ "log_buf_len=16M" ]; # must be {power of two}[KMG] tmp.useTmpfs = true; loader.timeout = lib.mkDefault 2; diff --git a/config/default.nix b/config/default.nix index ad87946..ac35002 100644 --- a/config/default.nix +++ b/config/default.nix @@ -1,4 +1,5 @@ -{inputs, ...}: { +{ inputs, ... }: +{ # Not setting this causes infinite recursion because it has a very weird default. # The default should probably be removed upstream and only applied with mkDefault # if hardware.nvidia.enable is true diff --git a/config/dev/default.nix b/config/dev/default.nix index 91b67df..676761a 100644 --- a/config/dev/default.nix +++ b/config/dev/default.nix @@ -15,7 +15,10 @@ lib.optionalAttrs (!minimal) { info.enable = lib.mkForce false; }; - environment.systemPackages = [pkgs.man-pages pkgs.man-pages-posix]; + environment.systemPackages = [ + pkgs.man-pages + pkgs.man-pages-posix + ]; environment.enableDebugInfo = true; environment.persistence."/state".directories = [ @@ -28,5 +31,5 @@ lib.optionalAttrs (!minimal) { services.nixseparatedebuginfod.enable = true; # For embedded development - services.udev.packages = [pkgs.stlink]; + services.udev.packages = [ pkgs.stlink ]; } diff --git a/config/dev/yubikey.nix b/config/dev/yubikey.nix index b84c20e..0027a51 100644 --- a/config/dev/yubikey.nix +++ b/config/dev/yubikey.nix @@ -1,5 +1,13 @@ -{pkgs, ...}: { - environment.systemPackages = with pkgs; [yubikey-manager yubikey-personalization age-plugin-yubikey]; - services.udev.packages = with pkgs; [yubikey-personalization libu2f-host]; +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + yubikey-manager + yubikey-personalization + age-plugin-yubikey + ]; + services.udev.packages = with pkgs; [ + yubikey-personalization + libu2f-host + ]; services.pcscd.enable = true; } diff --git a/config/graphical/default.nix b/config/graphical/default.nix index 4c636b2..68511fb 100644 --- a/config/graphical/default.nix +++ b/config/graphical/default.nix @@ -5,143 +5,146 @@ minimal, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) mkIf mkOption types optionalAttrs ; in - { - options.graphical.gaming.enable = mkOption { - description = "Enables gaming on this machine and will add a lot of gaming related packages and configuration."; - default = false; - type = types.bool; +{ + options.graphical.gaming.enable = mkOption { + description = "Enables gaming on this machine and will add a lot of gaming related packages and configuration."; + default = false; + type = types.bool; + }; +} +// optionalAttrs (!minimal) { + imports = [ + inputs.stylix.nixosModules.stylix + inputs.whisper-overlay.nixosModules.default + + ./fonts.nix + ./steam.nix + ./xserver.nix + ]; + + config = { + # For Star Citizen. See https://github.com/starcitizen-lug/knowledge-base/wiki for more info. + boot.kernel.sysctl = mkIf config.graphical.gaming.enable { + "vm.max_map_count" = 16777216; + "fs.file-max" = 524288; }; - } - // optionalAttrs (!minimal) { - imports = [ - inputs.stylix.nixosModules.stylix - inputs.whisper-overlay.nixosModules.default - ./fonts.nix - ./steam.nix - ./xserver.nix - ]; + # Needed for gtk + programs.dconf.enable = true; + # Required for gnome3 pinentry + services.dbus.packages = [ pkgs.gcr ]; - config = { - # For Star Citizen. See https://github.com/starcitizen-lug/knowledge-base/wiki for more info. - boot.kernel.sysctl = mkIf config.graphical.gaming.enable { - "vm.max_map_count" = 16777216; - "fs.file-max" = 524288; - }; - - # Needed for gtk - programs.dconf.enable = true; - # Required for gnome3 pinentry - services.dbus.packages = [pkgs.gcr]; - - xdg.portal = { - enable = true; - xdgOpenUsePortal = true; - config.common = { - default = ["gtk" "hyprland"]; - "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; - "org.freedesktop.impl.portal.ScreenCast" = ["hyprland"]; - "org.freedesktop.impl.portal.Screenshot" = ["hyprland"]; - "org.freedesktop.portal.FileChooser" = ["xdg-desktop-portal-gtk"]; - }; - extraPortals = [ - pkgs.xdg-desktop-portal-hyprland - pkgs.xdg-desktop-portal-gtk + xdg.portal = { + enable = true; + xdgOpenUsePortal = true; + config.common = { + default = [ + "gtk" + "hyprland" ]; + "org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ]; + "org.freedesktop.impl.portal.ScreenCast" = [ "hyprland" ]; + "org.freedesktop.impl.portal.Screenshot" = [ "hyprland" ]; + "org.freedesktop.portal.FileChooser" = [ "xdg-desktop-portal-gtk" ]; + }; + extraPortals = [ + pkgs.xdg-desktop-portal-hyprland + pkgs.xdg-desktop-portal-gtk + ]; + }; + + services.displayManager.enable = true; + programs.uwsm = { + enable = true; + waylandCompositors.sway = { + prettyName = "Sway"; + comment = "Sway"; + binPath = lib.getExe pkgs.sway; }; - services.displayManager.enable = true; - programs.uwsm = { - enable = true; - waylandCompositors.sway = { - prettyName = "Sway"; - comment = "Sway"; - binPath = lib.getExe pkgs.sway; - }; - - waylandCompositors.hyprland = { - prettyName = "Hyprland"; - comment = "Hyprland"; - binPath = lib.getExe pkgs.hyprland; - }; - }; - - stylix = { - enable = true; - # I want to choose what to style myself. - autoEnable = false; - image = config.lib.stylix.pixel "base00"; - - polarity = "dark"; - - # onedark - # base16Scheme = { - # base00 = "#282c34"; - # base01 = "#353b45"; - # base02 = "#3e4451"; - # base03 = "#545862"; - # base04 = "#565c64"; - # base05 = "#abb2bf"; - # base06 = "#b6bdca"; - # base07 = "#c8ccd4"; - # base08 = "#e06c75"; - # base09 = "#d19a66"; - # base0A = "#e5c07b"; - # base0B = "#98c379"; - # base0C = "#56b6c2"; - # base0D = "#61afef"; - # base0E = "#c678dd"; - # base0F = "#9378de"; - # }; - - # based on decaycs-dark, normal variant - base16Scheme = { - base00 = "#101419"; - base01 = "#171b20"; - base02 = "#21262e"; - base03 = "#242931"; - base04 = "#485263"; - base05 = "#b6beca"; - base06 = "#dee1e6"; - base07 = "#e3e6eb"; - base08 = "#e05f65"; - base09 = "#f9a872"; - base0A = "#f1cf8a"; - base0B = "#78dba9"; - base0C = "#74bee9"; - base0D = "#70a5eb"; - base0E = "#c68aee"; - base0F = "#9378de"; - }; - - ## based on decaycs-dark, bright variant - #base16Scheme = { - # base00 = "#101419"; - # base01 = "#171B20"; - # base02 = "#21262e"; - # base03 = "#242931"; - # base04 = "#485263"; - # base05 = "#b6beca"; - # base06 = "#dee1e6"; - # base07 = "#e3e6eb"; - # base08 = "#e5646a"; - # base09 = "#f7b77c"; - # base0A = "#f6d48f"; - # base0B = "#94F7C5"; - # base0C = "#79c3ee"; - # base0D = "#75aaf0"; - # base0E = "#cb8ff3"; - # base0F = "#9d85e1"; - #}; + waylandCompositors.hyprland = { + prettyName = "Hyprland"; + comment = "Hyprland"; + binPath = lib.getExe pkgs.hyprland; }; }; - } + + stylix = { + enable = true; + # I want to choose what to style myself. + autoEnable = false; + image = config.lib.stylix.pixel "base00"; + + polarity = "dark"; + + # onedark + # base16Scheme = { + # base00 = "#282c34"; + # base01 = "#353b45"; + # base02 = "#3e4451"; + # base03 = "#545862"; + # base04 = "#565c64"; + # base05 = "#abb2bf"; + # base06 = "#b6bdca"; + # base07 = "#c8ccd4"; + # base08 = "#e06c75"; + # base09 = "#d19a66"; + # base0A = "#e5c07b"; + # base0B = "#98c379"; + # base0C = "#56b6c2"; + # base0D = "#61afef"; + # base0E = "#c678dd"; + # base0F = "#9378de"; + # }; + + # based on decaycs-dark, normal variant + base16Scheme = { + base00 = "#101419"; + base01 = "#171b20"; + base02 = "#21262e"; + base03 = "#242931"; + base04 = "#485263"; + base05 = "#b6beca"; + base06 = "#dee1e6"; + base07 = "#e3e6eb"; + base08 = "#e05f65"; + base09 = "#f9a872"; + base0A = "#f1cf8a"; + base0B = "#78dba9"; + base0C = "#74bee9"; + base0D = "#70a5eb"; + base0E = "#c68aee"; + base0F = "#9378de"; + }; + + ## based on decaycs-dark, bright variant + #base16Scheme = { + # base00 = "#101419"; + # base01 = "#171B20"; + # base02 = "#21262e"; + # base03 = "#242931"; + # base04 = "#485263"; + # base05 = "#b6beca"; + # base06 = "#dee1e6"; + # base07 = "#e3e6eb"; + # base08 = "#e5646a"; + # base09 = "#f7b77c"; + # base0A = "#f6d48f"; + # base0B = "#94F7C5"; + # base0C = "#79c3ee"; + # base0D = "#75aaf0"; + # base0E = "#cb8ff3"; + # base0F = "#9d85e1"; + #}; + }; + }; +} diff --git a/config/graphical/fonts.nix b/config/graphical/fonts.nix index b8c2c58..3a95ea1 100644 --- a/config/graphical/fonts.nix +++ b/config/graphical/fonts.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ fonts = { # Always prefer emojis even if the original font would provide a glyph fontconfig.localConf = '' @@ -27,7 +28,7 @@ ''; packages = with pkgs; [ - (pkgs.nerdfonts.override {fonts = ["NerdFontsSymbolsOnly"];}) + (pkgs.nerdfonts.override { fonts = [ "NerdFontsSymbolsOnly" ]; }) noto-fonts noto-fonts-cjk-sans noto-fonts-cjk-serif diff --git a/config/graphical/steam.nix b/config/graphical/steam.nix index e1736f7..29ab925 100644 --- a/config/graphical/steam.nix +++ b/config/graphical/steam.nix @@ -3,13 +3,14 @@ config, pkgs, ... -}: { +}: +{ config = lib.mkIf config.graphical.gaming.enable { programs.steam = { enable = true; package = pkgs.steam.override { - extraPkgs = pkgs: - with pkgs; [ + extraPkgs = + pkgs: with pkgs; [ # add packages here in case any game needs them... ]; }; diff --git a/config/graphical/xserver.nix b/config/graphical/xserver.nix index b6171ad..01afc86 100644 --- a/config/graphical/xserver.nix +++ b/config/graphical/xserver.nix @@ -6,7 +6,7 @@ desktopManager.xterm.enable = false; autoRepeatDelay = 235; autoRepeatInterval = 60; - videoDrivers = ["modesetting"]; + videoDrivers = [ "modesetting" ]; xkb.layout = "de"; xkb.variant = "nodeadkeys"; }; diff --git a/config/hardware/bluetooth.nix b/config/hardware/bluetooth.nix index d376eb4..6e348a6 100644 --- a/config/hardware/bluetooth.nix +++ b/config/hardware/bluetooth.nix @@ -1,5 +1,6 @@ -{pkgs, ...}: { - environment.systemPackages = with pkgs; [bluetui]; +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ bluetui ]; environment.persistence."/persist".directories = [ "/var/lib/bluetooth" ]; @@ -7,7 +8,7 @@ hardware.bluetooth = { enable = true; powerOnBoot = true; - disabledPlugins = ["sap"]; + disabledPlugins = [ "sap" ]; settings = { General = { FastConnectable = "true"; @@ -19,12 +20,12 @@ }; hardware.pulseaudio = { - package = pkgs.pulseaudio.override {bluetoothSupport = true;}; + package = pkgs.pulseaudio.override { bluetoothSupport = true; }; extraConfig = '' load-module module-bluetooth-discover load-module module-bluetooth-policy load-module module-switch-on-connect ''; - extraModules = with pkgs; [pulseaudio-modules-bt]; + extraModules = with pkgs; [ pulseaudio-modules-bt ]; }; } diff --git a/config/hardware/hetzner-cloud.nix b/config/hardware/hetzner-cloud.nix index 6359204..a966d18 100644 --- a/config/hardware/hetzner-cloud.nix +++ b/config/hardware/hetzner-cloud.nix @@ -1,4 +1,9 @@ { - boot.initrd.availableKernelModules = ["virtio_pci" "virtio_net" "virtio_scsi" "virtio_blk"]; + boot.initrd.availableKernelModules = [ + "virtio_pci" + "virtio_net" + "virtio_scsi" + "virtio_blk" + ]; topology.self.icon = "devices.cloud-server"; } diff --git a/config/hardware/nvidia.nix b/config/hardware/nvidia.nix index c7f226c..2093bbd 100644 --- a/config/hardware/nvidia.nix +++ b/config/hardware/nvidia.nix @@ -5,8 +5,8 @@ ... }: lib.optionalAttrs (!minimal) { - boot.blacklistedKernelModules = ["nouveau"]; - services.xserver.videoDrivers = lib.mkForce ["nvidia"]; + boot.blacklistedKernelModules = [ "nouveau" ]; + services.xserver.videoDrivers = lib.mkForce [ "nvidia" ]; hardware = { graphics = { diff --git a/config/hardware/odroid-n2plus.nix b/config/hardware/odroid-n2plus.nix index 962a941..2412791 100644 --- a/config/hardware/odroid-n2plus.nix +++ b/config/hardware/odroid-n2plus.nix @@ -1,4 +1,5 @@ -{inputs, ...}: { +{ inputs, ... }: +{ imports = [ inputs.nixos-hardware.nixosModules.common-pc-ssd ]; @@ -22,5 +23,8 @@ "drm" "display_connector" ]; - boot.kernelParams = ["console=ttyAML0,115200n8" "console=tty0"]; + boot.kernelParams = [ + "console=ttyAML0,115200n8" + "console=tty0" + ]; } diff --git a/config/hardware/physical.nix b/config/hardware/physical.nix index 5a88e99..bfc180e 100644 --- a/config/hardware/physical.nix +++ b/config/hardware/physical.nix @@ -4,7 +4,8 @@ lib, minimal, ... -}: { +}: +{ hardware = { enableRedistributableFirmware = true; enableAllFirmware = true; @@ -13,6 +14,6 @@ services = lib.mkIf (!minimal) { fwupd.enable = true; smartd.enable = true; - thermald.enable = builtins.elem config.nixpkgs.hostPlatform.system ["x86_64-linux"]; + thermald.enable = builtins.elem config.nixpkgs.hostPlatform.system [ "x86_64-linux" ]; }; } diff --git a/config/home-manager.nix b/config/home-manager.nix index 4da7b18..7afbadc 100644 --- a/config/home-manager.nix +++ b/config/home-manager.nix @@ -3,7 +3,8 @@ config, minimal, ... -}: { +}: +{ home-manager = { useGlobalPkgs = true; useUserPackages = true; @@ -37,5 +38,5 @@ # But still link all completions from all packages so they # can be found by zsh - environment.pathsToLink = ["/share/zsh"]; + environment.pathsToLink = [ "/share/zsh" ]; } diff --git a/config/impermanence.nix b/config/impermanence.nix index a504a40..add2a6f 100644 --- a/config/impermanence.nix +++ b/config/impermanence.nix @@ -2,9 +2,9 @@ config, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) attrNames flip isAttrs @@ -14,9 +14,10 @@ optionals types ; -in { +in +{ # Give agenix access to the hostkey independent of impermanence activation - age.identityPaths = ["/persist/etc/ssh/ssh_host_ed25519_key"]; + age.identityPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ]; # Expose a home manager module for each user that allows extending # environment.persistence.${sourceDir}.users.${userName} simply by @@ -25,63 +26,57 @@ in { { options.home.persistence = mkOption { description = "Additional persistence config for the given source path"; - default = {}; - type = types.attrsOf (types.submodule { - options = { - files = mkOption { - description = "Additional files to persist via NixOS impermanence."; - type = types.listOf (types.either types.attrs types.str); - default = []; - }; + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + files = mkOption { + description = "Additional files to persist via NixOS impermanence."; + type = types.listOf (types.either types.attrs types.str); + default = [ ]; + }; - directories = mkOption { - description = "Additional directories to persist via NixOS impermanence."; - type = types.listOf (types.either types.attrs types.str); - default = []; + directories = mkOption { + description = "Additional directories to persist via NixOS impermanence."; + type = types.listOf (types.either types.attrs types.str); + default = [ ]; + }; }; - }; - }); + } + ); }; } ]; # For each user that has a home-manager config, merge the locally defined # persistence options that we defined above. - imports = let - mkUserFiles = map (x: - {parentDirectory.mode = "700";} - // ( - if isAttrs x - then x - else {file = x;} - )); - mkUserDirs = map (x: - {mode = "700";} - // ( - if isAttrs x - then x - else {directory = x;} - )); - in [ - { - environment.persistence = mkMerge ( - flip map - (attrNames config.home-manager.users) - ( - user: let - hmUserCfg = config.home-manager.users.${user}; - in - flip mapAttrs hmUserCfg.home.persistence - (_: sourceCfg: { - users.${user} = { - files = mkUserFiles sourceCfg.files; - directories = mkUserDirs sourceCfg.directories; - }; - }) - ) + imports = + let + mkUserFiles = map ( + x: { parentDirectory.mode = "700"; } // (if isAttrs x then x else { file = x; }) ); - } - ]; + mkUserDirs = map (x: { mode = "700"; } // (if isAttrs x then x else { directory = x; })); + in + [ + { + environment.persistence = mkMerge ( + flip map (attrNames config.home-manager.users) ( + user: + let + hmUserCfg = config.home-manager.users.${user}; + in + flip mapAttrs hmUserCfg.home.persistence ( + _: sourceCfg: { + users.${user} = { + files = mkUserFiles sourceCfg.files; + directories = mkUserDirs sourceCfg.directories; + }; + } + ) + ) + ); + } + ]; # State that should be kept across reboots, but is otherwise # NOT important information in any way that needs to be backed up. diff --git a/config/installer.nix b/config/installer.nix index 6952913..5ebc9e6 100644 --- a/config/installer.nix +++ b/config/installer.nix @@ -3,7 +3,8 @@ lib, pkgs, ... -}: let +}: +let disko-script = pkgs.writeShellScriptBin "disko-script" "${config.system.build.diskoScript}"; disko-mount = pkgs.writeShellScriptBin "disko-mount" "${config.system.build.mountScript}"; disko-format = pkgs.writeShellScriptBin "disko-format" "${config.system.build.formatScript}"; @@ -30,7 +31,8 @@ install-system ]; }; -in { +in +{ options.system.build.installFromLive = lib.mkOption { type = lib.types.package; description = '' diff --git a/config/issue.nix b/config/issue.nix index 478f8b1..9586815 100644 --- a/config/issue.nix +++ b/config/issue.nix @@ -2,13 +2,16 @@ config, lib, ... -}: { +}: +{ # IP addresses: ${"${interface} \e{halfbright}\4{${interface}}\e{reset} \e{halfbright}\6{${interface}}\e{reset}"} - environment.etc.issue.text = lib.concatStringsSep "\n" ([ + environment.etc.issue.text = lib.concatStringsSep "\n" ( + [ ''\d \t'' ''This is \e{cyan}\n\e{reset} [\e{lightblue}\l\e{reset}] (\s \m \r)'' ] # Disabled for guests because of frequent redraws (-> pushed to syslog on the host) ++ lib.optional (config.node.type == "host") ''\e{halfbright}\4\e{reset} \e{halfbright}\6\e{reset}'' - ++ [""]); + ++ [ "" ] + ); } diff --git a/config/net.nix b/config/net.nix index a75b8af..994f3e4 100644 --- a/config/net.nix +++ b/config/net.nix @@ -2,7 +2,8 @@ config, lib, ... -}: { +}: +{ systemd.network.enable = true; networking = { @@ -11,8 +12,8 @@ dhcpcd.enable = false; # Rename known network interfaces from local secrets - renameInterfacesByMac = - lib.mapAttrs (_: v: v.mac) - (config.repo.secrets.local.networking.interfaces or {}); + renameInterfacesByMac = lib.mapAttrs (_: v: v.mac) ( + config.repo.secrets.local.networking.interfaces or { } + ); }; } diff --git a/config/nftables.nix b/config/nftables.nix index 450551f..ee3fd84 100644 --- a/config/nftables.nix +++ b/config/nftables.nix @@ -2,7 +2,8 @@ config, lib, ... -}: { +}: +{ networking.nftables = { stopRuleset = lib.mkDefault '' table inet filter { @@ -36,17 +37,31 @@ nnf-ssh.enable = true; nnf-icmp = { enable = true; - ipv6Types = ["echo-request" "destination-unreachable" "packet-too-big" "time-exceeded" "parameter-problem" "nd-router-advert" "nd-neighbor-solicit" "nd-neighbor-advert"]; - ipv4Types = ["echo-request" "destination-unreachable" "router-advertisement" "time-exceeded" "parameter-problem"]; + ipv6Types = [ + "echo-request" + "destination-unreachable" + "packet-too-big" + "time-exceeded" + "parameter-problem" + "nd-router-advert" + "nd-neighbor-solicit" + "nd-neighbor-advert" + ]; + ipv4Types = [ + "echo-request" + "destination-unreachable" + "router-advertisement" + "time-exceeded" + "parameter-problem" + ]; }; }; rules.untrusted-to-local = { - from = ["untrusted"]; - to = ["local"]; + from = [ "untrusted" ]; + to = [ "local" ]; - inherit - (config.networking.firewall) + inherit (config.networking.firewall) allowedTCPPorts allowedTCPPortRanges allowedUDPPorts diff --git a/config/nix.nix b/config/nix.nix index 4405022..50d4560 100644 --- a/config/nix.nix +++ b/config/nix.nix @@ -2,7 +2,8 @@ inputs, pkgs, ... -}: { +}: +{ environment.etc."nixos/configuration.nix".source = pkgs.writeText "configuration.nix" '' assert builtins.trace "This is a dummy config, please deploy via the flake!" false; { } @@ -11,8 +12,8 @@ nix = { settings = { auto-optimise-store = true; - allowed-users = ["@wheel"]; - trusted-users = ["root"]; + allowed-users = [ "@wheel" ]; + trusted-users = [ "root" ]; substituters = [ "https://cache.nixos.org" "https://nix-community.cachix.org" @@ -30,7 +31,7 @@ experimental-features = nix-command flakes flake-registry = /etc/nix/registry.json ''; - nixPath = ["nixpkgs=/run/current-system/nixpkgs"]; + nixPath = [ "nixpkgs=/run/current-system/nixpkgs" ]; optimise.automatic = true; gc = { automatic = true; diff --git a/config/optional/sound.nix b/config/optional/sound.nix index adde0cc..7e8f94c 100644 --- a/config/optional/sound.nix +++ b/config/optional/sound.nix @@ -32,5 +32,8 @@ lib.optionalAttrs (!minimal) { ]; }; - environment.systemPackages = with pkgs; [pulseaudio pulsemixer]; + environment.systemPackages = with pkgs; [ + pulseaudio + pulsemixer + ]; } diff --git a/config/optional/zfs.nix b/config/optional/zfs.nix index f5c623d..abd002d 100644 --- a/config/optional/zfs.nix +++ b/config/optional/zfs.nix @@ -3,14 +3,15 @@ lib, pkgs, ... -}: { - boot.supportedFilesystems = ["zfs"]; +}: +{ + boot.supportedFilesystems = [ "zfs" ]; # The root pool should never be imported forcefully. # Failure to import is important to notice! boot.zfs.forceImportRoot = false; - environment.systemPackages = with pkgs; [zfs]; + environment.systemPackages = with pkgs; [ zfs ]; services.zfs = { autoScrub = { @@ -28,13 +29,13 @@ }; # TODO remove once this is upstreamed - boot.initrd.systemd.services."zfs-import-rpool".after = ["cryptsetup.target"]; + boot.initrd.systemd.services."zfs-import-rpool".after = [ "cryptsetup.target" ]; # After importing the rpool, rollback the root system to be empty. boot.initrd.systemd.services.impermanence-root = { - wantedBy = ["initrd.target"]; - after = ["zfs-import-rpool.service"]; - before = ["sysroot.mount"]; + wantedBy = [ "initrd.target" ]; + after = [ "zfs-import-rpool.service" ]; + before = [ "sysroot.mount" ]; unitConfig.DefaultDependencies = "no"; serviceConfig = { Type = "oneshot"; diff --git a/config/resolved.nix b/config/resolved.nix index c715d6a..2981d59 100644 --- a/config/resolved.nix +++ b/config/resolved.nix @@ -2,7 +2,8 @@ config, lib, ... -}: { +}: +{ services.resolved = { enable = true; dnssec = "false"; # wake me up in 20 years when DNSSEC is at least partly working @@ -20,51 +21,45 @@ }; system.nssDatabases.hosts = lib.mkMerge [ - (lib.mkBefore ["mdns_minimal [NOTFOUND=return]"]) - (lib.mkAfter ["mdns"]) + (lib.mkBefore [ "mdns_minimal [NOTFOUND=return]" ]) + (lib.mkAfter [ "mdns" ]) ]; # Open port 5353 for any interfaces that have MulticastDNS enabled - networking.nftables.firewall = let - # Determine all networks that have MulticastDNS enabled - networksWithMulticast = - lib.filter - (n: config.systemd.network.networks.${n}.networkConfig.MulticastDNS or false) - (lib.attrNames config.systemd.network.networks); + networking.nftables.firewall = + let + # Determine all networks that have MulticastDNS enabled + networksWithMulticast = lib.filter ( + n: config.systemd.network.networks.${n}.networkConfig.MulticastDNS or false + ) (lib.attrNames config.systemd.network.networks); - # Determine all known mac addresses and the corresponding link name - # based on the renameInterfacesByMac option. - knownMacs = - lib.mapAttrs' - (k: v: lib.nameValuePair v k) - config.networking.renameInterfacesByMac; - # A helper that returns the link name for the given mac address, - # or null if it doesn't exist or the given mac was null. - linkNameFor = mac: - if mac == null - then null - else knownMacs.${mac} or null; + # Determine all known mac addresses and the corresponding link name + # based on the renameInterfacesByMac option. + knownMacs = lib.mapAttrs' (k: v: lib.nameValuePair v k) config.networking.renameInterfacesByMac; + # A helper that returns the link name for the given mac address, + # or null if it doesn't exist or the given mac was null. + linkNameFor = mac: if mac == null then null else knownMacs.${mac} or null; - # Calls the given function for each network that has MulticastDNS enabled, - # and collects all non-null values. - mapNetworks = f: lib.filter (v: v != null) (map f networksWithMulticast); + # Calls the given function for each network that has MulticastDNS enabled, + # and collects all non-null values. + mapNetworks = f: lib.filter (v: v != null) (map f networksWithMulticast); - # All interfaces on which MulticastDNS is used - mdnsInterfaces = lib.unique ( - # For each network that is matched by MAC, lookup the link name - # and if map the definition name to the link name. - mapNetworks (x: linkNameFor (config.systemd.network.networks.${x}.matchConfig.MACAddress or null)) - # For each network that is matched by name, map the definition - # name to the link name. - ++ mapNetworks (x: config.systemd.network.networks.${x}.matchConfig.Name or null) - ); - in - lib.mkIf (mdnsInterfaces != []) { + # All interfaces on which MulticastDNS is used + mdnsInterfaces = lib.unique ( + # For each network that is matched by MAC, lookup the link name + # and if map the definition name to the link name. + mapNetworks (x: linkNameFor (config.systemd.network.networks.${x}.matchConfig.MACAddress or null)) + # For each network that is matched by name, map the definition + # name to the link name. + ++ mapNetworks (x: config.systemd.network.networks.${x}.matchConfig.Name or null) + ); + in + lib.mkIf (mdnsInterfaces != [ ]) { zones.mdns.interfaces = mdnsInterfaces; rules.mdns-to-local = { - from = ["mdns"]; - to = ["local"]; - allowedUDPPorts = [5353]; + from = [ "mdns" ]; + to = [ "local" ]; + allowedUDPPorts = [ 5353 ]; }; }; } diff --git a/config/secrets.nix b/config/secrets.nix index 0bbe181..689436d 100644 --- a/config/secrets.nix +++ b/config/secrets.nix @@ -3,17 +3,18 @@ inputs, lib, ... -}: { +}: +{ # Define local repo secrets - repo.secretFiles = let - local = config.node.secretsDir + "/local.nix.age"; - in - lib.optionalAttrs (lib.pathExists local) {inherit local;}; + repo.secretFiles = + let + local = config.node.secretsDir + "/local.nix.age"; + in + lib.optionalAttrs (lib.pathExists local) { inherit local; }; # Setup secret rekeying parameters age.rekey = { - inherit - (inputs.self.secretsConfig) + inherit (inputs.self.secretsConfig) masterIdentities extraEncryptionPubkeys ; @@ -24,39 +25,46 @@ localStorageDir = inputs.self.outPath + "/secrets/rekeyed/${config.node.name}"; }; - age.generators.basic-auth = { - pkgs, - lib, - decrypt, - deps, - ... - }: - lib.flip lib.concatMapStrings deps ({ - name, - host, - file, - }: '' - echo " -> Aggregating "${lib.escapeShellArg host}":"${lib.escapeShellArg name}"" >&2 - ${decrypt} ${lib.escapeShellArg file} \ - | ${pkgs.apacheHttpd}/bin/htpasswd -niBC 12 ${lib.escapeShellArg host}"+"${lib.escapeShellArg name} \ - || die "Failure while aggregating basic auth hashes" - ''); + age.generators.basic-auth = + { + pkgs, + lib, + decrypt, + deps, + ... + }: + lib.flip lib.concatMapStrings deps ( + { + name, + host, + file, + }: + '' + echo " -> Aggregating "${lib.escapeShellArg host}":"${lib.escapeShellArg name}"" >&2 + ${decrypt} ${lib.escapeShellArg file} \ + | ${pkgs.apacheHttpd}/bin/htpasswd -niBC 12 ${lib.escapeShellArg host}"+"${lib.escapeShellArg name} \ + || die "Failure while aggregating basic auth hashes" + '' + ); - age.generators.argon2id = { - pkgs, - lib, - decrypt, - deps, - ... - }: let - dep = builtins.head deps; - in '' - echo " -> Deriving argon2id hash from "${lib.escapeShellArg dep.host}":"${lib.escapeShellArg dep.name}"" >&2 - ${decrypt} ${lib.escapeShellArg dep.file} \ - | tr -d '\n' \ - | ${pkgs.libargon2}/bin/argon2 "$(${pkgs.openssl}/bin/openssl rand -base64 16)" -id -e \ - || die "Failure while generating argon2id hash" - ''; + age.generators.argon2id = + { + pkgs, + lib, + decrypt, + deps, + ... + }: + let + dep = builtins.head deps; + in + '' + echo " -> Deriving argon2id hash from "${lib.escapeShellArg dep.host}":"${lib.escapeShellArg dep.name}"" >&2 + ${decrypt} ${lib.escapeShellArg dep.file} \ + | tr -d '\n' \ + | ${pkgs.libargon2}/bin/argon2 "$(${pkgs.openssl}/bin/openssl rand -base64 16)" -id -e \ + || die "Failure while generating argon2id hash" + ''; # Just before switching, remove the agenix directory if it exists. # This can happen when a secret is used in the initrd because it will @@ -65,8 +73,8 @@ # to create a link called /run/agenix. Agenix should probably fail in this case, # but doesn't and instead puts the generation link into the existing directory. # TODO See https://github.com/ryantm/agenix/pull/187. - system.activationScripts = lib.mkIf (config.age.secrets != {}) { + system.activationScripts = lib.mkIf (config.age.secrets != { }) { removeAgenixLink.text = "[[ ! -L /run/agenix ]] && [[ -d /run/agenix ]] && rm -rf /run/agenix"; - agenixNewGeneration.deps = ["removeAgenixLink"]; + agenixNewGeneration.deps = [ "removeAgenixLink" ]; }; } diff --git a/config/ssh.nix b/config/ssh.nix index 5e1eb3f..70916e9 100644 --- a/config/ssh.nix +++ b/config/ssh.nix @@ -3,7 +3,8 @@ lib, pkgs, ... -}: { +}: +{ config = lib.mkMerge [ { services.openssh = { @@ -12,7 +13,7 @@ # because we rely on ssh key generation for agenix. So we need # the service to start eagerly startWhenNeeded = lib.mkForce false; - authorizedKeysFiles = lib.mkForce ["/etc/ssh/authorized_keys.d/%u"]; + authorizedKeysFiles = lib.mkForce [ "/etc/ssh/authorized_keys.d/%u" ]; sftpServerExecutable = "internal-sftp"; settings = { PasswordAuthentication = false; @@ -35,7 +36,7 @@ boot.initrd.network.ssh = { enable = true; port = 4; - hostKeys = [config.age.secrets.initrd_host_ed25519_key.path]; + hostKeys = [ config.age.secrets.initrd_host_ed25519_key.path ]; }; # Make sure that there is always a valid initrd hostkey available that can be installed into @@ -49,9 +50,12 @@ [[ -e ${config.age.secrets.initrd_host_ed25519_key.path} ]] \ || ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f ${config.age.secrets.initrd_host_ed25519_key.path} ''; - deps = ["agenixInstall" "users"]; + deps = [ + "agenixInstall" + "users" + ]; }; - system.activationScripts.agenixChown.deps = ["agenixEnsureInitrdHostkey"]; + system.activationScripts.agenixChown.deps = [ "agenixEnsureInitrdHostkey" ]; }) ]; } diff --git a/config/system.nix b/config/system.nix index 66f4d58..6ed0f30 100644 --- a/config/system.nix +++ b/config/system.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ documentation.nixos.enable = false; # Disable sudo which is entirely unnecessary. @@ -10,7 +11,7 @@ console.keyMap = "de-latin1-nodeadkeys"; # Install the kitty terminfo package for all systems. - environment.systemPackages = [pkgs.kitty.terminfo]; + environment.systemPackages = [ pkgs.kitty.terminfo ]; # And a reasonable inputrc please environment.etc."inputrc".source = ./inputrc; diff --git a/config/topology.nix b/config/topology.nix index df939d5..5a44c41 100644 --- a/config/topology.nix +++ b/config/topology.nix @@ -1,3 +1,4 @@ -{config, ...}: { +{ config, ... }: +{ topology.id = config.node.name; } diff --git a/config/users.nix b/config/users.nix index 1b95399..a2d5e9f 100644 --- a/config/users.nix +++ b/config/users.nix @@ -1,42 +1,44 @@ { users.mutableUsers = false; - users.deterministicIds = let - uidGid = id: { - uid = id; - gid = id; + users.deterministicIds = + let + uidGid = id: { + uid = id; + gid = id; + }; + in + { + systemd-oom = uidGid 999; + systemd-coredump = uidGid 998; + sshd = uidGid 997; + nscd = uidGid 996; + polkituser = uidGid 995; + microvm = uidGid 994; + promtail = uidGid 993; + grafana = uidGid 992; + acme = uidGid 991; + kanidm = uidGid 990; + loki = uidGid 989; + vaultwarden = uidGid 988; + oauth2-proxy = uidGid 987; + influxdb2 = uidGid 986; + telegraf = uidGid 985; + rtkit = uidGid 984; + git = uidGid 983; + redis-paperless = uidGid 982; + nixseparatedebuginfod = uidGid 981; + msr = uidGid 980; + fwupd-refresh = uidGid 979; + radicale = uidGid 978; + podman = uidGid 977; + maddy = uidGid 976; + minecraft = uidGid 975; + stalwart-mail = uidGid 974; + netbird-home = uidGid 973; + gamemode = uidGid 972; + plausible = uidGid 971; + actual = uidGid 970; + flatpak = uidGid 969; }; - in { - systemd-oom = uidGid 999; - systemd-coredump = uidGid 998; - sshd = uidGid 997; - nscd = uidGid 996; - polkituser = uidGid 995; - microvm = uidGid 994; - promtail = uidGid 993; - grafana = uidGid 992; - acme = uidGid 991; - kanidm = uidGid 990; - loki = uidGid 989; - vaultwarden = uidGid 988; - oauth2-proxy = uidGid 987; - influxdb2 = uidGid 986; - telegraf = uidGid 985; - rtkit = uidGid 984; - git = uidGid 983; - redis-paperless = uidGid 982; - nixseparatedebuginfod = uidGid 981; - msr = uidGid 980; - fwupd-refresh = uidGid 979; - radicale = uidGid 978; - podman = uidGid 977; - maddy = uidGid 976; - minecraft = uidGid 975; - stalwart-mail = uidGid 974; - netbird-home = uidGid 973; - gamemode = uidGid 972; - plausible = uidGid 971; - actual = uidGid 970; - flatpak = uidGid 969; - }; } diff --git a/globals.nix b/globals.nix index b225c17..668470f 100644 --- a/globals.nix +++ b/globals.nix @@ -3,14 +3,18 @@ config, lib, ... -}: let +}: +let inherit (config) globals; # Try to access the extra builtin we loaded via nix-plugins. # Throw an error if that doesn't exist. - rageImportEncrypted = assert lib.assertMsg (builtins ? extraBuiltins.rageImportEncrypted) "The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?"; + rageImportEncrypted = + assert lib.assertMsg (builtins ? extraBuiltins.rageImportEncrypted) + "The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?"; builtins.extraBuiltins.rageImportEncrypted; -in { +in +{ imports = [ (rageImportEncrypted inputs.self.secretsConfig.masterIdentities ./secrets/global.nix.age) ]; diff --git a/hosts/envoy/acme.nix b/hosts/envoy/acme.nix index c91985f..a978619 100644 --- a/hosts/envoy/acme.nix +++ b/hosts/envoy/acme.nix @@ -1,6 +1,8 @@ -{config, ...}: let +{ config, ... }: +let inherit (config.repo.secrets.local) acme; -in { +in +{ age.secrets.acme-cloudflare-dns-token = { rekeyFile = ./secrets/acme-cloudflare-dns-token.age; mode = "440"; @@ -22,7 +24,10 @@ in { }; dnsProvider = "cloudflare"; dnsPropagationCheck = true; - reloadServices = ["nginx" "stalwart-mail"]; + reloadServices = [ + "nginx" + "stalwart-mail" + ]; }; inherit (acme) certs wildcardDomains; }; diff --git a/hosts/envoy/default.nix b/hosts/envoy/default.nix index 0b722ba..d3e1358 100644 --- a/hosts/envoy/default.nix +++ b/hosts/envoy/default.nix @@ -2,7 +2,8 @@ globals, nodes, ... -}: { +}: +{ imports = [ ../../config ../../config/hardware/hetzner-cloud.nix @@ -18,7 +19,7 @@ nixpkgs.hostPlatform = "x86_64-linux"; boot.mode = "bios"; - users.groups.acme.members = ["nginx"]; + users.groups.acme.members = [ "nginx" ]; services.nginx.enable = true; services.nginx.recommendedSetup = true; @@ -28,7 +29,9 @@ }; # Connect safely via wireguard to skip authentication - networking.hosts.${nodes.sentinel.config.wireguard.proxy-sentinel.ipv4} = [globals.services.influxdb.domain]; + networking.hosts.${nodes.sentinel.config.wireguard.proxy-sentinel.ipv4} = [ + globals.services.influxdb.domain + ]; meta.telegraf = { enable = true; scrapeSensors = false; diff --git a/hosts/envoy/fs.nix b/hosts/envoy/fs.nix index 96bf92f..49f9abb 100644 --- a/hosts/envoy/fs.nix +++ b/hosts/envoy/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { main = { @@ -21,9 +23,9 @@ in { }; }; zpool = { - rpool = lib.disko.zfs.mkZpool {datasets = lib.disko.zfs.impermanenceZfsDatasets;}; + rpool = lib.disko.zfs.mkZpool { datasets = lib.disko.zfs.impermanenceZfsDatasets; }; }; }; - boot.loader.grub.devices = ["/dev/disk/by-id/${disks.main}"]; + boot.loader.grub.devices = [ "/dev/disk/by-id/${disks.main}" ]; } diff --git a/hosts/envoy/idmail.nix b/hosts/envoy/idmail.nix index 33f68b5..df2dd32 100644 --- a/hosts/envoy/idmail.nix +++ b/hosts/envoy/idmail.nix @@ -4,7 +4,8 @@ globals, lib, ... -}: let +}: +let primaryDomain = globals.mail.primary; idmailDomain = "alias.${primaryDomain}"; @@ -14,12 +15,13 @@ }; mkArgon2id = secret: { - generator.dependencies = [config.age.secrets.${secret}]; + generator.dependencies = [ config.age.secrets.${secret} ]; generator.script = "argon2id"; mode = "440"; group = "stalwart-mail"; }; -in { +in +{ environment.persistence."/persist".directories = [ { directory = config.services.idmail.dataDir; @@ -56,17 +58,19 @@ in { admin = true; password_hash = "%{file:${config.age.secrets.idmail-user-hash_admin.path}}%"; }; - domains = lib.flip lib.mapAttrs globals.mail.domains (_domain: domainCfg: { - owner = "admin"; - catch_all = "catch-all@${primaryDomain}"; - inherit (domainCfg) public; - }); + domains = lib.flip lib.mapAttrs globals.mail.domains ( + _domain: domainCfg: { + owner = "admin"; + catch_all = "catch-all@${primaryDomain}"; + inherit (domainCfg) public; + } + ); mailboxes = lib.flip lib.mapAttrs' globals.mail.domains ( _domain: _domainCfg: - lib.nameValuePair "catch-all@${primaryDomain}" { - password_hash = "%{file:${config.age.secrets.idmail-mailbox-hash_catch-all.path}}%"; - owner = "admin"; - } + lib.nameValuePair "catch-all@${primaryDomain}" { + password_hash = "%{file:${config.age.secrets.idmail-mailbox-hash_catch-all.path}}%"; + owner = "admin"; + } ); # XXX: create mailboxes for git@ vaultwarden@ and simultaneously alias them to the catch all for a send only mail. }; @@ -75,7 +79,7 @@ in { services.nginx = { upstreams.idmail = { - servers."127.0.0.1:3000" = {}; + servers."127.0.0.1:3000" = { }; extraConfig = '' zone idmail 64k; keepalive 2; diff --git a/hosts/envoy/net.nix b/hosts/envoy/net.nix index 7d752a6..ac1ead0 100644 --- a/hosts/envoy/net.nix +++ b/hosts/envoy/net.nix @@ -3,12 +3,14 @@ globals, lib, ... -}: let +}: +let icfg = config.repo.secrets.local.networking.interfaces.wan; -in { +in +{ networking.hostId = config.repo.secrets.local.networking.hostId; networking.domain = globals.mail.primary; - networking.hosts."127.0.0.1" = ["mail.${globals.mail.primary}"]; + networking.hosts."127.0.0.1" = [ "mail.${globals.mail.primary}" ]; globals.monitoring.ping.envoy = { hostv4 = lib.net.cidr.ip icfg.hostCidrv4; @@ -18,7 +20,9 @@ in { boot.initrd.systemd.network = { enable = true; - networks = {inherit (config.systemd.network.networks) "10-wan";}; + networks = { + inherit (config.systemd.network.networks) "10-wan"; + }; }; systemd.network.networks = { @@ -27,9 +31,9 @@ in { icfg.hostCidrv4 icfg.hostCidrv6 ]; - gateway = ["fe80::1"]; + gateway = [ "fe80::1" ]; routes = [ - {Destination = "172.31.1.1";} + { Destination = "172.31.1.1"; } { Gateway = "172.31.1.1"; GatewayOnLink = true; @@ -41,7 +45,7 @@ in { }; }; - networking.nftables.firewall.zones.untrusted.interfaces = ["wan"]; + networking.nftables.firewall.zones.untrusted.interfaces = [ "wan" ]; # Allow accessing influx wireguard.proxy-sentinel.client.via = "sentinel"; diff --git a/hosts/envoy/stalwart-mail.nix b/hosts/envoy/stalwart-mail.nix index af1f016..12f31ff 100644 --- a/hosts/envoy/stalwart-mail.nix +++ b/hosts/envoy/stalwart-mail.nix @@ -4,12 +4,14 @@ lib, pkgs, ... -}: let +}: +let primaryDomain = globals.mail.primary; stalwartDomain = "mail.${primaryDomain}"; dataDir = "/var/lib/stalwart-mail"; mailBackupDir = "/var/cache/mail-backup"; -in { +in +{ environment.persistence."/persist".directories = [ { directory = dataDir; @@ -25,13 +27,13 @@ in { }; age.secrets.stalwart-admin-hash = { - generator.dependencies = [config.age.secrets.stalwart-admin-pw]; + generator.dependencies = [ config.age.secrets.stalwart-admin-pw ]; generator.script = "argon2id"; mode = "440"; group = "stalwart-mail"; }; - users.groups.acme.members = ["stalwart-mail"]; + users.groups.acme.members = [ "stalwart-mail" ]; networking.firewall.allowedTCPPorts = [ 25 # smtp @@ -51,23 +53,24 @@ in { services.stalwart-mail = { enable = true; - settings = let - case = field: check: value: data: { - "if" = field; - ${check} = value; - "then" = data; - }; - ifthen = field: data: { - "if" = field; - "then" = data; - }; - otherwise = value: {"else" = value;}; - is-smtp = case "listener" "eq" "smtp"; - is-authenticated = data: { - "if" = "!is_empty(authenticated_as)"; - "then" = data; - }; - in + settings = + let + case = field: check: value: data: { + "if" = field; + ${check} = value; + "then" = data; + }; + ifthen = field: data: { + "if" = field; + "then" = data; + }; + otherwise = value: { "else" = value; }; + is-smtp = case "listener" "eq" "smtp"; + is-authenticated = data: { + "if" = "!is_empty(authenticated_as)"; + "then" = data; + }; + in lib.mkForce { config.local-keys = [ "store.*" @@ -111,49 +114,190 @@ in { store.idmail = { type = "sqlite"; path = "${config.services.idmail.dataDir}/idmail.db"; - query = let - # Remove comments from SQL and make it single-line - toSingleLineSql = sql: - lib.concatStringsSep " " ( - lib.forEach (lib.flatten (lib.split "\n" sql)) ( - line: lib.optionalString (builtins.match "^[[:space:]]*--.*" line == null) line + query = + let + # Remove comments from SQL and make it single-line + toSingleLineSql = + sql: + lib.concatStringsSep " " ( + lib.forEach (lib.flatten (lib.split "\n" sql)) ( + line: lib.optionalString (builtins.match "^[[:space:]]*--.*" line == null) line + ) + ); + in + { + # "SELECT name, type, secret, description, quota FROM accounts WHERE name = ?1 AND active = true"; + name = toSingleLineSql '' + SELECT + m.address AS name, + 'individual' AS type, + m.password_hash AS secret, + m.address AS description, + 0 AS quota + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + ''; + # "SELECT member_of FROM group_members WHERE name = ?1"; + members = ""; + # "SELECT name FROM emails WHERE address = ?1"; + recipients = toSingleLineSql '' + -- It is important that we return only one value here, but in theory three UNIONed + -- queries are guaranteed to be distinct. This is because a mailbox address + -- and alias address can never be the same, their cross-table uniqueness is guaranteed on insert. + -- The catch-all union can also only return something if @domain.tld is given as a parameter, + -- which is invalid for aliases and mailboxes. + -- + -- Nonetheless, it may be beneficial to allow an alias to override an existing mailbox, + -- so we can have send-only mailboxes which have their incoming mail redirected somewhere else. + -- Therefore, we make sure to order the query by (aliases -> mailboxes -> catch all) and only return the + -- highest priority one. + SELECT name FROM ( + -- Select the target of a matching alias (if any) + -- but make sure that all related parts are active. + SELECT a.target AS name, 1 AS rowOrder + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.address = ?1 + AND a.active = true + AND d.active = true + -- Select the primary mailbox address if it matches and + -- all related parts are active. + UNION + SELECT m.address AS name, 2 AS rowOrder + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + -- Finally, select any catch_all address that would catch this. + -- Again make sure everything is active. + UNION + SELECT d.catch_all AS name, 3 AS rowOrder + FROM domains AS d + JOIN mailboxes AS m ON d.catch_all = m.address + JOIN users AS u ON m.owner = u.username + WHERE ?1 = ('@' || d.domain) + AND d.active = true + AND m.active = true + AND u.active = true + ORDER BY rowOrder, name ASC + LIMIT 1 ) - ); - in { - # "SELECT name, type, secret, description, quota FROM accounts WHERE name = ?1 AND active = true"; - name = toSingleLineSql '' - SELECT - m.address AS name, - 'individual' AS type, - m.password_hash AS secret, - m.address AS description, - 0 AS quota - FROM mailboxes AS m - JOIN domains AS d ON m.domain = d.domain - JOIN users AS u ON m.owner = u.username - WHERE m.address = ?1 - AND m.active = true - AND d.active = true - AND u.active = true - ''; - # "SELECT member_of FROM group_members WHERE name = ?1"; - members = ""; - # "SELECT name FROM emails WHERE address = ?1"; - recipients = toSingleLineSql '' - -- It is important that we return only one value here, but in theory three UNIONed - -- queries are guaranteed to be distinct. This is because a mailbox address - -- and alias address can never be the same, their cross-table uniqueness is guaranteed on insert. - -- The catch-all union can also only return something if @domain.tld is given as a parameter, - -- which is invalid for aliases and mailboxes. - -- - -- Nonetheless, it may be beneficial to allow an alias to override an existing mailbox, - -- so we can have send-only mailboxes which have their incoming mail redirected somewhere else. - -- Therefore, we make sure to order the query by (aliases -> mailboxes -> catch all) and only return the - -- highest priority one. - SELECT name FROM ( - -- Select the target of a matching alias (if any) - -- but make sure that all related parts are active. - SELECT a.target AS name, 1 AS rowOrder + ''; + # "SELECT address FROM emails WHERE name = ?1 AND type != 'list' ORDER BY type DESC, address ASC"; + emails = toSingleLineSql '' + -- Return first the primary address, then any aliases. + SELECT address FROM ( + -- Select primary address, if active + SELECT m.address AS address, 1 AS rowOrder + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + -- Select any active aliases + UNION + SELECT a.address AS address, 2 AS rowOrder + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.target = ?1 + AND a.active = true + AND d.active = true + -- Select the catch-all marker, if we are the target. + UNION + -- Order 2 is correct, it counts as an alias + SELECT ('@' || d.domain) AS address, 2 AS rowOrder + FROM domains AS d + JOIN mailboxes AS m ON d.catch_all = m.address + JOIN users AS u ON m.owner = u.username + WHERE d.catch_all = ?1 + AND d.active = true + AND m.active = true + AND u.active = true + ORDER BY rowOrder, address ASC + ) + ''; + # "SELECT address FROM emails WHERE address LIKE '%' || ?1 || '%' AND type = 'primary' ORDER BY address LIMIT 5"; + verify = toSingleLineSql '' + SELECT m.address AS address + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address LIKE '%' || ?1 || '%' + AND m.active = true + AND d.active = true + AND u.active = true + UNION + SELECT a.address AS address + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.address LIKE '%' || ?1 || '%' + AND a.active = true + AND d.active = true + ORDER BY address + LIMIT 5 + ''; + # "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ?1 AND l.type = 'list' ORDER BY p.address LIMIT 50"; + # XXX: We don't actually expand, but return the same address if it exists since we don't support mailing lists + expand = toSingleLineSql '' + SELECT m.address AS address + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + UNION + SELECT a.address AS address FROM aliases AS a JOIN domains AS d ON a.domain = d.domain JOIN ( @@ -172,154 +316,16 @@ in { WHERE a.address = ?1 AND a.active = true AND d.active = true - -- Select the primary mailbox address if it matches and - -- all related parts are active. - UNION - SELECT m.address AS name, 2 AS rowOrder - FROM mailboxes AS m - JOIN domains AS d ON m.domain = d.domain - JOIN users AS u ON m.owner = u.username - WHERE m.address = ?1 - AND m.active = true - AND d.active = true - AND u.active = true - -- Finally, select any catch_all address that would catch this. - -- Again make sure everything is active. - UNION - SELECT d.catch_all AS name, 3 AS rowOrder - FROM domains AS d - JOIN mailboxes AS m ON d.catch_all = m.address - JOIN users AS u ON m.owner = u.username - WHERE ?1 = ('@' || d.domain) - AND d.active = true - AND m.active = true - AND u.active = true - ORDER BY rowOrder, name ASC - LIMIT 1 - ) - ''; - # "SELECT address FROM emails WHERE name = ?1 AND type != 'list' ORDER BY type DESC, address ASC"; - emails = toSingleLineSql '' - -- Return first the primary address, then any aliases. - SELECT address FROM ( - -- Select primary address, if active - SELECT m.address AS address, 1 AS rowOrder - FROM mailboxes AS m - JOIN domains AS d ON m.domain = d.domain - JOIN users AS u ON m.owner = u.username - WHERE m.address = ?1 - AND m.active = true - AND d.active = true - AND u.active = true - -- Select any active aliases - UNION - SELECT a.address AS address, 2 AS rowOrder - FROM aliases AS a - JOIN domains AS d ON a.domain = d.domain - JOIN ( - -- To check whether the owner is active we need to make a subquery - -- because the owner could be a user or mailbox - SELECT username - FROM users - WHERE active = true - UNION - SELECT m.address AS username - FROM mailboxes AS m - JOIN users AS u ON m.owner = u.username - WHERE m.active = true - AND u.active = true - ) AS u ON a.owner = u.username - WHERE a.target = ?1 - AND a.active = true - AND d.active = true - -- Select the catch-all marker, if we are the target. - UNION - -- Order 2 is correct, it counts as an alias - SELECT ('@' || d.domain) AS address, 2 AS rowOrder - FROM domains AS d - JOIN mailboxes AS m ON d.catch_all = m.address - JOIN users AS u ON m.owner = u.username - WHERE d.catch_all = ?1 - AND d.active = true - AND m.active = true - AND u.active = true - ORDER BY rowOrder, address ASC - ) - ''; - # "SELECT address FROM emails WHERE address LIKE '%' || ?1 || '%' AND type = 'primary' ORDER BY address LIMIT 5"; - verify = toSingleLineSql '' - SELECT m.address AS address - FROM mailboxes AS m - JOIN domains AS d ON m.domain = d.domain - JOIN users AS u ON m.owner = u.username - WHERE m.address LIKE '%' || ?1 || '%' - AND m.active = true - AND d.active = true - AND u.active = true - UNION - SELECT a.address AS address - FROM aliases AS a - JOIN domains AS d ON a.domain = d.domain - JOIN ( - -- To check whether the owner is active we need to make a subquery - -- because the owner could be a user or mailbox - SELECT username - FROM users - WHERE active = true - UNION - SELECT m.address AS username - FROM mailboxes AS m - JOIN users AS u ON m.owner = u.username - WHERE m.active = true - AND u.active = true - ) AS u ON a.owner = u.username - WHERE a.address LIKE '%' || ?1 || '%' - AND a.active = true - AND d.active = true - ORDER BY address - LIMIT 5 - ''; - # "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ?1 AND l.type = 'list' ORDER BY p.address LIMIT 50"; - # XXX: We don't actually expand, but return the same address if it exists since we don't support mailing lists - expand = toSingleLineSql '' - SELECT m.address AS address - FROM mailboxes AS m - JOIN domains AS d ON m.domain = d.domain - JOIN users AS u ON m.owner = u.username - WHERE m.address = ?1 - AND m.active = true - AND d.active = true - AND u.active = true - UNION - SELECT a.address AS address - FROM aliases AS a - JOIN domains AS d ON a.domain = d.domain - JOIN ( - -- To check whether the owner is active we need to make a subquery - -- because the owner could be a user or mailbox - SELECT username - FROM users - WHERE active = true - UNION - SELECT m.address AS username - FROM mailboxes AS m - JOIN users AS u ON m.owner = u.username - WHERE m.active = true - AND u.active = true - ) AS u ON a.owner = u.username - WHERE a.address = ?1 - AND a.active = true - AND d.active = true - ORDER BY address - LIMIT 50 - ''; - # "SELECT 1 FROM emails WHERE address LIKE '%@' || ?1 LIMIT 1"; - domains = toSingleLineSql '' - SELECT domain - FROM domains - WHERE domain = ?1 - ''; - }; + ORDER BY address + LIMIT 50 + ''; + # "SELECT 1 FROM emails WHERE address LIKE '%@' || ?1 LIMIT 1"; + domains = toSingleLineSql '' + SELECT domain + FROM domains + WHERE domain = ?1 + ''; + }; }; storage = { @@ -428,7 +434,13 @@ in { private-key = "%{file:/var/lib/stalwart-mail/dkim/ed25519-${domain}.key}%"; inherit domain; selector = "ed_default"; - headers = ["From" "To" "Date" "Subject" "Message-ID"]; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; algorithm = "ed25519-sha256"; canonicalization = "relaxed/relaxed"; set-body-length = false; @@ -438,7 +450,13 @@ in { private-key = "%{file:/var/lib/stalwart-mail/dkim/rsa-${domain}.key}%"; inherit domain; selector = "rsa_default"; - headers = ["From" "To" "Date" "Subject" "Message-ID"]; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; algorithm = "rsa-sha256"; canonicalization = "relaxed/relaxed"; set-body-length = false; @@ -496,7 +514,7 @@ in { services.nginx = { upstreams.stalwart = { - servers."127.0.0.1:8080" = {}; + servers."127.0.0.1:8080" = { }; extraConfig = '' zone stalwart 64k; keepalive 2; @@ -516,52 +534,60 @@ in { }; }; } - // lib.genAttrs ["autoconfig.${primaryDomain}" "autodiscover.${primaryDomain}" "mta-sts.${primaryDomain}"] (_: { - forceSSL = true; - useACMEWildcardHost = true; - locations."/".proxyPass = "http://stalwart"; - }); + // lib.genAttrs + [ + "autoconfig.${primaryDomain}" + "autodiscover.${primaryDomain}" + "mta-sts.${primaryDomain}" + ] + (_: { + forceSSL = true; + useACMEWildcardHost = true; + locations."/".proxyPass = "http://stalwart"; + }); }; - systemd.services.stalwart-mail = let - cfg = config.services.stalwart-mail; - configFormat = pkgs.formats.toml {}; - configFile = configFormat.generate "stalwart-mail.toml" cfg.settings; - in { - preStart = lib.mkAfter ( - '' - cat ${configFile} > /run/stalwart-mail/config.toml - cat ${config.age.secrets.stalwart-admin-hash.path} \ - | tr -d '\n' > /run/stalwart-mail/admin-hash + systemd.services.stalwart-mail = + let + cfg = config.services.stalwart-mail; + configFormat = pkgs.formats.toml { }; + configFile = configFormat.generate "stalwart-mail.toml" cfg.settings; + in + { + preStart = lib.mkAfter ( + '' + cat ${configFile} > /run/stalwart-mail/config.toml + cat ${config.age.secrets.stalwart-admin-hash.path} \ + | tr -d '\n' > /run/stalwart-mail/admin-hash - mkdir -p /var/lib/stalwart-mail/dkim - '' - # Generate DKIM keys if necessary - + lib.concatLines ( - lib.forEach (builtins.attrNames globals.mail.domains) (domain: '' - if [[ ! -e /var/lib/stalwart-mail/dkim/rsa-${domain}.key ]]; then - echo "Generating DKIM key for ${domain} (rsa)" - ${lib.getExe pkgs.openssl} genrsa -traditional -out /var/lib/stalwart-mail/dkim/rsa-${domain}.key 2048 - fi - if [[ ! -e /var/lib/stalwart-mail/dkim/ed25519-${domain}.key ]]; then - echo "Generating DKIM key for ${domain} (ed25519)" - ${lib.getExe pkgs.openssl} genpkey -algorithm ed25519 -out /var/lib/stalwart-mail/dkim/ed25519-${domain}.key - fi - '') - ) - ); + mkdir -p /var/lib/stalwart-mail/dkim + '' + # Generate DKIM keys if necessary + + lib.concatLines ( + lib.forEach (builtins.attrNames globals.mail.domains) (domain: '' + if [[ ! -e /var/lib/stalwart-mail/dkim/rsa-${domain}.key ]]; then + echo "Generating DKIM key for ${domain} (rsa)" + ${lib.getExe pkgs.openssl} genrsa -traditional -out /var/lib/stalwart-mail/dkim/rsa-${domain}.key 2048 + fi + if [[ ! -e /var/lib/stalwart-mail/dkim/ed25519-${domain}.key ]]; then + echo "Generating DKIM key for ${domain} (ed25519)" + ${lib.getExe pkgs.openssl} genpkey -algorithm ed25519 -out /var/lib/stalwart-mail/dkim/ed25519-${domain}.key + fi + '') + ) + ); - serviceConfig = { - RuntimeDirectory = "stalwart-mail"; - ReadWritePaths = [config.services.idmail.dataDir]; - ExecStart = lib.mkForce [ - "" - "${cfg.package}/bin/stalwart-mail --config=/run/stalwart-mail/config.toml" - ]; - RestartSec = "60"; # Retry every minute - CacheDirectory = lib.trace "remove stalwart cache soon, it's upstream" "stalwart-mail"; + serviceConfig = { + RuntimeDirectory = "stalwart-mail"; + ReadWritePaths = [ config.services.idmail.dataDir ]; + ExecStart = lib.mkForce [ + "" + "${cfg.package}/bin/stalwart-mail --config=/run/stalwart-mail/config.toml" + ]; + RestartSec = "60"; # Retry every minute + CacheDirectory = lib.trace "remove stalwart cache soon, it's upstream" "stalwart-mail"; + }; }; - }; systemd.services.backup-mail = { description = "Mail backup"; @@ -575,15 +601,17 @@ in { Type = "oneshot"; User = "stalwart-mail"; Group = "stalwart-mail"; - ExecStart = lib.getExe (pkgs.writeShellApplication { - name = "backup-mail"; - runtimeInputs = [pkgs.sqlite]; - text = '' - sqlite3 "$STALWART_DATA/database.sqlite3" ".backup '$BACKUP_DIR/database.sqlite3'" - sqlite3 "$IDMAIL_DATA/database.sqlite3" ".backup '$BACKUP_DIR/idmail.db'" - cp -r "$STALWART_DATA/dkim" "$BACKUP_DIR/" - ''; - }); + ExecStart = lib.getExe ( + pkgs.writeShellApplication { + name = "backup-mail"; + runtimeInputs = [ pkgs.sqlite ]; + text = '' + sqlite3 "$STALWART_DATA/database.sqlite3" ".backup '$BACKUP_DIR/database.sqlite3'" + sqlite3 "$IDMAIL_DATA/database.sqlite3" ".backup '$BACKUP_DIR/idmail.db'" + cp -r "$STALWART_DATA/dkim" "$BACKUP_DIR/" + ''; + } + ); ReadWritePaths = [ dataDir config.services.idmail.dataDir @@ -591,8 +619,8 @@ in { ]; Restart = "no"; }; - requiredBy = ["restic-backups-storage-box-dusk.service"]; - before = ["restic-backups-storage-box-dusk.service"]; + requiredBy = [ "restic-backups-storage-box-dusk.service" ]; + before = [ "restic-backups-storage-box-dusk.service" ]; }; # Needed so we don't run out of tmpfs space for large backups. @@ -608,6 +636,6 @@ in { backups.storageBoxes.dusk = { subuser = "stalwart"; - paths = [mailBackupDir]; + paths = [ mailBackupDir ]; }; } diff --git a/hosts/kroma/default.nix b/hosts/kroma/default.nix index 0a7eb7d..6434177 100644 --- a/hosts/kroma/default.nix +++ b/hosts/kroma/default.nix @@ -33,9 +33,16 @@ nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.config.cudaSupport = true; boot.mode = "efi"; - boot.kernelModules = ["nvidia_uvm"]; # FIXME: For some reason this doesn't load automatically for me, causing CUDA_ERROR_UNKNOWN (999) issues when trying to cuInit - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod"]; - boot.binfmt.emulatedSystems = ["aarch64-linux"]; + boot.kernelModules = [ "nvidia_uvm" ]; # FIXME: For some reason this doesn't load automatically for me, causing CUDA_ERROR_UNKNOWN (999) issues when trying to cuInit + boot.initrd.availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usbhid" + "usb_storage" + "sd_mod" + ]; + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; } // lib.optionalAttrs (!minimal) { # TODO goodbye once -sk keys. diff --git a/hosts/kroma/fs.nix b/hosts/kroma/fs.nix index 2895476..ca356e5 100644 --- a/hosts/kroma/fs.nix +++ b/hosts/kroma/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { m2-ssd = { @@ -21,7 +23,7 @@ in { }; }; zpool = { - rpool = lib.disko.zfs.mkZpool {datasets = lib.disko.zfs.impermanenceZfsDatasets;}; + rpool = lib.disko.zfs.mkZpool { datasets = lib.disko.zfs.impermanenceZfsDatasets; }; }; }; } diff --git a/hosts/kroma/net.nix b/hosts/kroma/net.nix index 5e144bb..853fc0c 100644 --- a/hosts/kroma/net.nix +++ b/hosts/kroma/net.nix @@ -1,11 +1,14 @@ -{config, ...}: { +{ config, ... }: +{ networking = { inherit (config.repo.secrets.local.networking) hostId; }; boot.initrd.systemd.network = { enable = true; - networks = {inherit (config.systemd.network.networks) "10-lan1";}; + networks = { + inherit (config.systemd.network.networks) "10-lan1"; + }; }; systemd.network.networks = { @@ -32,6 +35,9 @@ }; networking.nftables.firewall = { - zones.untrusted.interfaces = ["lan1" "wlan1"]; + zones.untrusted.interfaces = [ + "lan1" + "wlan1" + ]; }; } diff --git a/hosts/nom/default.nix b/hosts/nom/default.nix index bbdd3ce..3448ce3 100644 --- a/hosts/nom/default.nix +++ b/hosts/nom/default.nix @@ -2,7 +2,8 @@ inputs, pkgs, ... -}: { +}: +{ imports = [ inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-pc-laptop @@ -29,11 +30,18 @@ nixpkgs.hostPlatform = "x86_64-linux"; boot.mode = "efi"; - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod"]; + boot.initrd.availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usbhid" + "usb_storage" + "sd_mod" + ]; console = { font = "ter-v28n"; - packages = [pkgs.terminus_font]; + packages = [ pkgs.terminus_font ]; }; # FIXME: fuck optional modules and make this more adjustable via settings diff --git a/hosts/nom/fs.nix b/hosts/nom/fs.nix index 2db46e8..6bdcb3d 100644 --- a/hosts/nom/fs.nix +++ b/hosts/nom/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { m2-ssd = { @@ -30,7 +32,7 @@ in { }; }; zpool = { - rpool = lib.disko.zfs.mkZpool {datasets = lib.disko.zfs.impermanenceZfsDatasets;}; + rpool = lib.disko.zfs.mkZpool { datasets = lib.disko.zfs.impermanenceZfsDatasets; }; }; }; } diff --git a/hosts/nom/net.nix b/hosts/nom/net.nix index cb52ffd..fb645a3 100644 --- a/hosts/nom/net.nix +++ b/hosts/nom/net.nix @@ -1,4 +1,5 @@ -{config, ...}: { +{ config, ... }: +{ networking = { inherit (config.repo.secrets.local.networking) hostId; wireless.iwd.enable = true; @@ -6,7 +7,9 @@ boot.initrd.systemd.network = { enable = true; - networks = {inherit (config.systemd.network.networks) "10-lan1";}; + networks = { + inherit (config.systemd.network.networks) "10-lan1"; + }; }; systemd.network.networks = { @@ -33,6 +36,9 @@ }; networking.nftables.firewall = { - zones.untrusted.interfaces = ["lan1" "wlan1"]; + zones.untrusted.interfaces = [ + "lan1" + "wlan1" + ]; }; } diff --git a/hosts/sentinel/acme.nix b/hosts/sentinel/acme.nix index f818396..6427083 100644 --- a/hosts/sentinel/acme.nix +++ b/hosts/sentinel/acme.nix @@ -1,6 +1,8 @@ -{config, ...}: let +{ config, ... }: +let inherit (config.repo.secrets.local) acme; -in { +in +{ age.secrets.acme-cloudflare-dns-token = { rekeyFile = ./secrets/acme-cloudflare-dns-token.age; mode = "440"; @@ -22,7 +24,7 @@ in { }; dnsProvider = "cloudflare"; dnsPropagationCheck = true; - reloadServices = ["nginx"]; + reloadServices = [ "nginx" ]; }; inherit (acme) certs wildcardDomains; }; diff --git a/hosts/sentinel/blog.nix b/hosts/sentinel/blog.nix index 935199f..e92f8c0 100644 --- a/hosts/sentinel/blog.nix +++ b/hosts/sentinel/blog.nix @@ -1,4 +1,5 @@ -{globals, ...}: { +{ globals, ... }: +{ environment.persistence."/persist".directories = [ { directory = "/var/lib/blog"; diff --git a/hosts/sentinel/coturn.nix b/hosts/sentinel/coturn.nix index f266807..50ea114 100644 --- a/hosts/sentinel/coturn.nix +++ b/hosts/sentinel/coturn.nix @@ -4,9 +4,9 @@ lib, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) getExe mkAfter mkForce @@ -14,7 +14,8 @@ hostDomain = globals.domains.me; coturnDomain = "coturn.${hostDomain}"; -in { +in +{ age.secrets.coturn-password-netbird = { generator.script = "alnum"; group = "turnserver"; @@ -58,23 +59,25 @@ in { pkey = "@pkey@"; }; - systemd.services.coturn = let - certsDir = config.security.acme.certs.${hostDomain}.directory; - in { - preStart = mkAfter '' - ${getExe pkgs.replace-secret} @password@ ${config.age.secrets.coturn-password-netbird.path} /run/coturn/turnserver.cfg - ${getExe pkgs.replace-secret} @cert@ <(echo "$CREDENTIALS_DIRECTORY/cert.pem") /run/coturn/turnserver.cfg - ${getExe pkgs.replace-secret} @pkey@ <(echo "$CREDENTIALS_DIRECTORY/pkey.pem") /run/coturn/turnserver.cfg - ''; - serviceConfig = { - LoadCredential = [ - "cert.pem:${certsDir}/fullchain.pem" - "pkey.pem:${certsDir}/key.pem" - ]; - Restart = mkForce "always"; - RestartSec = "60"; # Retry every minute + systemd.services.coturn = + let + certsDir = config.security.acme.certs.${hostDomain}.directory; + in + { + preStart = mkAfter '' + ${getExe pkgs.replace-secret} @password@ ${config.age.secrets.coturn-password-netbird.path} /run/coturn/turnserver.cfg + ${getExe pkgs.replace-secret} @cert@ <(echo "$CREDENTIALS_DIRECTORY/cert.pem") /run/coturn/turnserver.cfg + ${getExe pkgs.replace-secret} @pkey@ <(echo "$CREDENTIALS_DIRECTORY/pkey.pem") /run/coturn/turnserver.cfg + ''; + serviceConfig = { + LoadCredential = [ + "cert.pem:${certsDir}/fullchain.pem" + "pkey.pem:${certsDir}/key.pem" + ]; + Restart = mkForce "always"; + RestartSec = "60"; # Retry every minute + }; }; - }; security.acme.certs.${hostDomain}.postRun = '' systemctl restart coturn.service diff --git a/hosts/sentinel/default.nix b/hosts/sentinel/default.nix index 135ff4f..d016e0b 100644 --- a/hosts/sentinel/default.nix +++ b/hosts/sentinel/default.nix @@ -2,7 +2,8 @@ config, globals, ... -}: { +}: +{ imports = [ ../../config ../../config/hardware/hetzner-cloud.nix @@ -21,9 +22,12 @@ nixpkgs.hostPlatform = "x86_64-linux"; boot.mode = "bios"; - wireguard.proxy-sentinel.firewallRuleForAll.allowedTCPPorts = [80 443]; + wireguard.proxy-sentinel.firewallRuleForAll.allowedTCPPorts = [ + 80 + 443 + ]; - users.groups.acme.members = ["nginx"]; + users.groups.acme.members = [ "nginx" ]; services.nginx.enable = true; services.nginx.recommendedSetup = true; @@ -33,7 +37,7 @@ }; # Connect safely via wireguard to skip authentication - networking.hosts.${config.wireguard.proxy-sentinel.ipv4} = [globals.services.influxdb.domain]; + networking.hosts.${config.wireguard.proxy-sentinel.ipv4} = [ globals.services.influxdb.domain ]; meta.telegraf = { enable = true; scrapeSensors = false; @@ -45,6 +49,6 @@ }; # This node shall monitor the infrastructure - availableMonitoringNetworks = ["internet"]; + availableMonitoringNetworks = [ "internet" ]; }; } diff --git a/hosts/sentinel/fs.nix b/hosts/sentinel/fs.nix index 96bf92f..49f9abb 100644 --- a/hosts/sentinel/fs.nix +++ b/hosts/sentinel/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { main = { @@ -21,9 +23,9 @@ in { }; }; zpool = { - rpool = lib.disko.zfs.mkZpool {datasets = lib.disko.zfs.impermanenceZfsDatasets;}; + rpool = lib.disko.zfs.mkZpool { datasets = lib.disko.zfs.impermanenceZfsDatasets; }; }; }; - boot.loader.grub.devices = ["/dev/disk/by-id/${disks.main}"]; + boot.loader.grub.devices = [ "/dev/disk/by-id/${disks.main}" ]; } diff --git a/hosts/sentinel/net.nix b/hosts/sentinel/net.nix index 2078375..73e53a6 100644 --- a/hosts/sentinel/net.nix +++ b/hosts/sentinel/net.nix @@ -3,9 +3,11 @@ globals, lib, ... -}: let +}: +let icfg = config.repo.secrets.local.networking.interfaces.wan; -in { +in +{ networking.hostId = config.repo.secrets.local.networking.hostId; networking.domain = globals.domains.me; @@ -20,7 +22,9 @@ in { boot.initrd.systemd.network = { enable = true; - networks = {inherit (config.systemd.network.networks) "10-wan";}; + networks = { + inherit (config.systemd.network.networks) "10-wan"; + }; }; systemd.network.networks = { @@ -29,9 +33,9 @@ in { icfg.hostCidrv4 icfg.hostCidrv6 ]; - gateway = ["fe80::1"]; + gateway = [ "fe80::1" ]; routes = [ - {Destination = "172.31.1.1";} + { Destination = "172.31.1.1"; } { Gateway = "172.31.1.1"; GatewayOnLink = true; @@ -43,16 +47,19 @@ in { }; }; - networking.nftables.firewall.zones.untrusted.interfaces = ["wan"]; + networking.nftables.firewall.zones.untrusted.interfaces = [ "wan" ]; networking.nftables.chains.forward.dnat = { - after = ["conntrack"]; - rules = ["ct status dnat accept"]; + after = [ "conntrack" ]; + rules = [ "ct status dnat accept" ]; }; wireguard.proxy-sentinel.server = { host = config.networking.fqdn; port = 51443; - reservedAddresses = ["10.43.0.0/24" "fd00:43::/120"]; + reservedAddresses = [ + "10.43.0.0/24" + "fd00:43::/120" + ]; openFirewall = true; }; } diff --git a/hosts/sentinel/oauth2.nix b/hosts/sentinel/oauth2.nix index 8d8a346..df7adfb 100644 --- a/hosts/sentinel/oauth2.nix +++ b/hosts/sentinel/oauth2.nix @@ -3,7 +3,8 @@ globals, nodes, ... -}: { +}: +{ meta.oauth2-proxy = { enable = true; cookieDomain = globals.domains.me; @@ -23,36 +24,40 @@ generator.dependencies = [ nodes.ward-kanidm.config.age.secrets.kanidm-oauth2-web-sentinel ]; - generator.script = { - lib, - decrypt, - deps, - ... - }: '' - echo -n "OAUTH2_PROXY_CLIENT_SECRET=" - ${decrypt} ${lib.escapeShellArg (lib.head deps).file} - ''; + generator.script = + { + lib, + decrypt, + deps, + ... + }: + '' + echo -n "OAUTH2_PROXY_CLIENT_SECRET=" + ${decrypt} ${lib.escapeShellArg (lib.head deps).file} + ''; mode = "440"; group = "oauth2-proxy"; }; - services.oauth2-proxy = let - clientId = "web-sentinel"; - in { - provider = "oidc"; - scope = "openid email"; - loginURL = "https://${globals.services.kanidm.domain}/ui/oauth2"; - redeemURL = "https://${globals.services.kanidm.domain}/oauth2/token"; - validateURL = "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}/userinfo"; - clientID = clientId; - email.domains = ["*"]; + services.oauth2-proxy = + let + clientId = "web-sentinel"; + in + { + provider = "oidc"; + scope = "openid email"; + loginURL = "https://${globals.services.kanidm.domain}/ui/oauth2"; + redeemURL = "https://${globals.services.kanidm.domain}/oauth2/token"; + validateURL = "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}/userinfo"; + clientID = clientId; + email.domains = [ "*" ]; - extraConfig = { - oidc-issuer-url = "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}"; - provider-display-name = "Kanidm"; - #skip-provider-button = true; + extraConfig = { + oidc-issuer-url = "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}"; + provider-display-name = "Kanidm"; + #skip-provider-button = true; + }; }; - }; systemd.services.oauth2-proxy.serviceConfig.EnvironmentFile = [ config.age.secrets.oauth2-cookie-secret.path diff --git a/hosts/sentinel/plausible.nix b/hosts/sentinel/plausible.nix index f4ff469..5b2a768 100644 --- a/hosts/sentinel/plausible.nix +++ b/hosts/sentinel/plausible.nix @@ -3,9 +3,11 @@ lib, globals, ... -}: let +}: +let plausibleDomain = "analytics.${globals.domains.me}"; -in { +in +{ age.secrets.plausible-secret = { generator.script = args: "${args.pkgs.openssl}/bin/openssl rand -base64 64"; mode = "440"; @@ -61,7 +63,7 @@ in { services.nginx = { upstreams.plausible = { - servers."127.0.0.1:${toString config.services.plausible.server.port}" = {}; + servers."127.0.0.1:${toString config.services.plausible.server.port}" = { }; extraConfig = '' zone plausible 64k; keepalive 2; @@ -75,7 +77,7 @@ in { forceSSL = true; useACMEWildcardHost = true; oauth2.enable = true; - oauth2.allowedGroups = ["access_analytics"]; + oauth2.allowedGroups = [ "access_analytics" ]; locations."/".proxyPass = "http://plausible"; locations."= /js/script.js" = { proxyPass = "http://plausible"; @@ -111,7 +113,7 @@ in { }; }; - users.groups.plausible = {}; + users.groups.plausible = { }; users.users.plausible = { group = "plausible"; isSystemUser = true; diff --git a/hosts/sentinel/postgresql.nix b/hosts/sentinel/postgresql.nix index 6fabc4d..18fd15c 100644 --- a/hosts/sentinel/postgresql.nix +++ b/hosts/sentinel/postgresql.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ services.postgresql = { enable = true; package = pkgs.postgresql_16_jit; diff --git a/hosts/sire/default.nix b/hosts/sire/default.nix index a8e12b2..c734122 100644 --- a/hosts/sire/default.nix +++ b/hosts/sire/default.nix @@ -6,7 +6,8 @@ nodes, minimal, ... -}: { +}: +{ imports = [ inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-pc-ssd @@ -24,7 +25,15 @@ nixpkgs.hostPlatform = "x86_64-linux"; boot.mode = "efi"; - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "e1000e" "alx"]; + boot.initrd.availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usbhid" + "usb_storage" + "e1000e" + "alx" + ]; systemd.units."dev-tpmrm0.device".enable = false; # https://github.com/systemd/systemd/issues/33412 meta.promtail = { @@ -33,7 +42,9 @@ }; # Connect safely via wireguard to skip authentication - networking.hosts.${nodes.sentinel.config.wireguard.proxy-sentinel.ipv4} = [globals.services.influxdb.domain]; + networking.hosts.${nodes.sentinel.config.wireguard.proxy-sentinel.ipv4} = [ + globals.services.influxdb.domain + ]; meta.telegraf = { enable = true; influxdb2 = { @@ -44,52 +55,54 @@ }; }; - guests = let - mkGuest = guestName: { - enableStorageDataset ? false, - enableBunkerDataset ? false, - enablePaperlessDataset ? false, - ... - }: { - autostart = true; - zfs."/state" = { - # TODO make one option out of that? and split into two readonly options automatically? - pool = "rpool"; - dataset = "local/guests/${guestName}"; - }; - zfs."/persist" = { - pool = "rpool"; - dataset = "safe/guests/${guestName}"; - }; - zfs."/storage" = lib.mkIf enableStorageDataset { - pool = "storage"; - dataset = "safe/guests/${guestName}"; - }; - zfs."/bunker" = lib.mkIf enableBunkerDataset { - pool = "storage"; - dataset = "bunker/guests/${guestName}"; - }; - zfs."/paperless" = lib.mkIf enablePaperlessDataset { - pool = "storage"; - dataset = "bunker/paperless"; - }; - modules = [ - ../../config - ./guests/common.nix - ./guests/${guestName}.nix + guests = + let + mkGuest = + guestName: { - node.secretsDir = ./secrets/${guestName}; - networking.nftables.firewall = { - zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; + enableStorageDataset ? false, + enableBunkerDataset ? false, + enablePaperlessDataset ? false, + ... + }: + { + autostart = true; + zfs."/state" = { + # TODO make one option out of that? and split into two readonly options automatically? + pool = "rpool"; + dataset = "local/guests/${guestName}"; }; - } - ]; - }; + zfs."/persist" = { + pool = "rpool"; + dataset = "safe/guests/${guestName}"; + }; + zfs."/storage" = lib.mkIf enableStorageDataset { + pool = "storage"; + dataset = "safe/guests/${guestName}"; + }; + zfs."/bunker" = lib.mkIf enableBunkerDataset { + pool = "storage"; + dataset = "bunker/guests/${guestName}"; + }; + zfs."/paperless" = lib.mkIf enablePaperlessDataset { + pool = "storage"; + dataset = "bunker/paperless"; + }; + modules = [ + ../../config + ./guests/common.nix + ./guests/${guestName}.nix + { + node.secretsDir = ./secrets/${guestName}; + networking.nftables.firewall = { + zones.untrusted.interfaces = [ config.guests.${guestName}.networking.mainLinkName ]; + }; + } + ]; + }; - mkMicrovm = guestName: opts: { - ${guestName} = - mkGuest guestName opts - // { + mkMicrovm = guestName: opts: { + ${guestName} = mkGuest guestName opts // { backend = "microvm"; microvm = { system = "x86_64-linux"; @@ -102,13 +115,11 @@ inherit inputs minimal; }; }; - }; + }; - # deadnix: skip - mkContainer = guestName: opts: { - ${guestName} = - mkGuest guestName opts - // { + # deadnix: skip + mkContainer = guestName: opts: { + ${guestName} = mkGuest guestName opts // { backend = "container"; container.macvlan = "lan"; extraSpecialArgs = { @@ -117,27 +128,27 @@ inherit inputs minimal; }; }; - }; - in + }; + in lib.mkIf (!minimal) ( - {} - // mkMicrovm "actual" {} + { } + // mkMicrovm "actual" { } // mkMicrovm "samba" { enableStorageDataset = true; enableBunkerDataset = true; enablePaperlessDataset = true; } - // mkMicrovm "grafana" {} - // mkMicrovm "influxdb" {} - // mkMicrovm "loki" {} + // mkMicrovm "grafana" { } + // mkMicrovm "influxdb" { } + // mkMicrovm "loki" { } // mkMicrovm "paperless" { enablePaperlessDataset = true; } // mkMicrovm "immich" { enableStorageDataset = true; } - // mkMicrovm "ai" {} - // mkMicrovm "minecraft" {} + // mkMicrovm "ai" { } + // mkMicrovm "minecraft" { } #// mkMicrovm "firefly" {} #// mkMicrovm "fasten-health" {} ); diff --git a/hosts/sire/fs.nix b/hosts/sire/fs.nix index 061a608..c6e14ca 100644 --- a/hosts/sire/fs.nix +++ b/hosts/sire/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { @@ -33,11 +35,9 @@ in { zpool = { rpool = lib.disko.zfs.mkZpool { mode = "mirror"; - datasets = - lib.disko.zfs.impermanenceZfsDatasets - // { - "safe/guests" = lib.disko.zfs.unmountable; - }; + datasets = lib.disko.zfs.impermanenceZfsDatasets // { + "safe/guests" = lib.disko.zfs.unmountable; + }; }; storage = lib.disko.zfs.mkZpool { mode = "raidz"; @@ -48,7 +48,7 @@ in { }; }; - boot.initrd.systemd.services."zfs-import-storage".after = ["cryptsetup.target"]; + boot.initrd.systemd.services."zfs-import-storage".after = [ "cryptsetup.target" ]; services.zrepl = { enable = true; diff --git a/hosts/sire/guests/actual.nix b/hosts/sire/guests/actual.nix index 817c2f3..0bd3fdd 100644 --- a/hosts/sire/guests/actual.nix +++ b/hosts/sire/guests/actual.nix @@ -3,12 +3,14 @@ globals, nodes, ... -}: let +}: +let actualDomain = "finance.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.actual.settings.port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ config.services.actual.settings.port ]; }; environment.persistence."/persist".directories = [ @@ -22,7 +24,7 @@ in { services.actual = { enable = true; - settings.trustedProxies = [nodes.sentinel.config.wireguard.proxy-sentinel.ipv4]; + settings.trustedProxies = [ nodes.sentinel.config.wireguard.proxy-sentinel.ipv4 ]; }; globals.services.actual.domain = actualDomain; @@ -35,7 +37,8 @@ in { nodes.sentinel = { services.nginx = { upstreams.actual = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.actual.settings.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.actual.settings.port}" = + { }; extraConfig = '' zone actual 64k; keepalive 2; diff --git a/hosts/sire/guests/ai.nix b/hosts/sire/guests/ai.nix index dc7fee9..dfcf083 100644 --- a/hosts/sire/guests/ai.nix +++ b/hosts/sire/guests/ai.nix @@ -2,18 +2,20 @@ config, globals, ... -}: let +}: +let openWebuiDomain = "chat.${globals.domains.me}"; -in { +in +{ microvm.mem = 1024 * 16; microvm.vcpu = 20; wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.open-webui.port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ config.services.open-webui.port ]; }; - networking.firewall.allowedTCPPorts = [config.services.ollama.port]; + networking.firewall.allowedTCPPorts = [ config.services.ollama.port ]; environment.persistence."/state".directories = [ { @@ -64,7 +66,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.open-webui = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.open-webui.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.open-webui.port}" = { }; extraConfig = '' zone open-webui 64k; keepalive 2; @@ -79,7 +81,7 @@ in { useACMEWildcardHost = true; oauth2 = { enable = true; - allowedGroups = ["access_openwebui"]; + allowedGroups = [ "access_openwebui" ]; X-Email = "\${upstream_http_x_auth_request_preferred_username}@${globals.domains.personal}"; }; extraConfig = '' diff --git a/hosts/sire/guests/common.nix b/hosts/sire/guests/common.nix index 6a1aeca..323491a 100644 --- a/hosts/sire/guests/common.nix +++ b/hosts/sire/guests/common.nix @@ -4,10 +4,12 @@ lib, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; -in { +in +{ meta.promtail = { enable = true; proxy = "sentinel"; @@ -15,10 +17,11 @@ in { # Connect safely via wireguard to skip http authentication networking.hosts.${ - if config.wireguard ? proxy-home - then wardWebProxyCfg.wireguard.proxy-home.ipv4 - else sentinelCfg.wireguard.proxy-sentinel.ipv4 - } = [globals.services.influxdb.domain]; + if config.wireguard ? proxy-home then + wardWebProxyCfg.wireguard.proxy-home.ipv4 + else + sentinelCfg.wireguard.proxy-sentinel.ipv4 + } = [ globals.services.influxdb.domain ]; meta.telegraf = lib.mkIf (!config.boot.isContainer) { enable = true; diff --git a/hosts/sire/guests/grafana.nix b/hosts/sire/guests/grafana.nix index 0f049dc..cc7779e 100644 --- a/hosts/sire/guests/grafana.nix +++ b/hosts/sire/guests/grafana.nix @@ -4,18 +4,24 @@ nodes, pkgs, ... -}: let +}: +let wardWebProxyCfg = nodes.ward-web-proxy.config; grafanaDomain = "grafana.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.grafana.settings.server.http_port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ + config.services.grafana.settings.server.http_port + ]; }; wireguard.proxy-home = { client.via = "ward"; - firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [config.services.grafana.settings.server.http_port]; + firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ + config.services.grafana.settings.server.http_port + ]; }; age.secrets.grafana-secret-key = { @@ -32,14 +38,14 @@ in { age.secrets.grafana-influxdb-token-machines = { generator.script = "alnum"; - generator.tags = ["influxdb"]; + generator.tags = [ "influxdb" ]; mode = "440"; group = "grafana"; }; age.secrets.grafana-influxdb-token-home = { generator.script = "alnum"; - generator.tags = ["influxdb"]; + generator.tags = [ "influxdb" ]; mode = "440"; group = "grafana"; }; @@ -60,9 +66,10 @@ in { }; services.influxdb2.provision.organizations.machines.auths."grafana machines:telegraf (${config.node.name})" = { - readBuckets = ["telegraf"]; - writeBuckets = ["telegraf"]; - tokenFile = nodes.sire-influxdb.config.age.secrets."grafana-influxdb-token-machines-${config.node.name}".path; + readBuckets = [ "telegraf" ]; + writeBuckets = [ "telegraf" ]; + tokenFile = + nodes.sire-influxdb.config.age.secrets."grafana-influxdb-token-machines-${config.node.name}".path; }; age.secrets."grafana-influxdb-token-home-${config.node.name}" = { @@ -72,9 +79,10 @@ in { }; services.influxdb2.provision.organizations.home.auths."grafana home:home_assistant (${config.node.name})" = { - readBuckets = ["home_assistant"]; - writeBuckets = ["home_assistant"]; - tokenFile = nodes.sire-influxdb.config.age.secrets."grafana-influxdb-token-home-${config.node.name}".path; + readBuckets = [ "home_assistant" ]; + writeBuckets = [ "home_assistant" ]; + tokenFile = + nodes.sire-influxdb.config.age.secrets."grafana-influxdb-token-home-${config.node.name}".path; }; }; @@ -92,7 +100,8 @@ in { services.nginx = { upstreams.grafana = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.grafana.settings.server.http_port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.grafana.settings.server.http_port}" = + { }; extraConfig = '' zone grafana 64k; keepalive 2; @@ -116,7 +125,8 @@ in { nodes.ward-web-proxy = { services.nginx = { upstreams.grafana = { - servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.grafana.settings.server.http_port}" = {}; + servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.grafana.settings.server.http_port}" = + { }; extraConfig = '' zone grafana 64k; keepalive 2; diff --git a/hosts/sire/guests/immich.nix b/hosts/sire/guests/immich.nix index 0687955..cc2b9dc 100644 --- a/hosts/sire/guests/immich.nix +++ b/hosts/sire/guests/immich.nix @@ -4,7 +4,8 @@ nodes, pkgs, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; immichDomain = "immich.${globals.domains.me}"; @@ -127,12 +128,13 @@ serviceConfig = { serviceConfig.Restart = "always"; - after = ["podman-network-immich-default.service"]; - requires = ["podman-network-immich-default.service"]; - partOf = ["podman-compose-immich-root.target"]; - wantedBy = ["podman-compose-immich-root.target"]; + after = [ "podman-network-immich-default.service" ]; + requires = [ "podman-network-immich-default.service" ]; + partOf = [ "podman-compose-immich-root.target" ]; + wantedBy = [ "podman-compose-immich-root.target" ]; }; -in { +in +{ microvm.mem = 1024 * 12; microvm.vcpu = 16; @@ -155,7 +157,7 @@ in { system.activationScripts.agenixRooterDerivedSecrets = { # Run after agenix has generated secrets - deps = ["agenix"]; + deps = [ "agenix" ]; text = '' immichClientSecret=$(< ${config.age.secrets.immich-oauth2-client-secret.path}) ${pkgs.jq}/bin/jq --arg immichClientSecret "$immichClientSecret" '.oauth.clientSecret = $immichClientSecret' ${configFile} > ${processedConfigFile} @@ -165,14 +167,14 @@ in { wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [2283]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ 2283 ]; }; wireguard.proxy-home = { client.via = "ward"; - firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [2283]; + firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ 2283 ]; }; networking.nftables.chains.forward.into-immich-container = { - after = ["conntrack"]; + after = [ "conntrack" ]; rules = [ "iifname proxy-sentinel ip saddr ${sentinelCfg.wireguard.proxy-sentinel.ipv4} tcp dport 2283 accept" "iifname proxy-home ip saddr ${wardWebProxyCfg.wireguard.proxy-home.ipv4} tcp dport 2283 accept" @@ -190,7 +192,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.immich = { - servers."${config.wireguard.proxy-sentinel.ipv4}:2283" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:2283" = { }; extraConfig = '' zone immich 64k; keepalive 2; @@ -222,7 +224,7 @@ in { nodes.ward-web-proxy = { services.nginx = { upstreams.immich = { - servers."${config.wireguard.proxy-home.ipv4}:2283" = {}; + servers."${config.wireguard.proxy-home.ipv4}:2283" = { }; extraConfig = '' zone immich 64k; keepalive 2; @@ -238,8 +240,7 @@ in { locations."/" = { proxyPass = "http://immich"; proxyWebsockets = true; - extraConfig = '' - ''; + extraConfig = ''''; }; extraConfig = '' client_max_body_size 50G; @@ -348,18 +349,16 @@ in { "--ip=${ipImmichServer}" ]; }; - systemd.services."podman-immich_server" = - serviceConfig - // { - unitConfig.UpheldBy = [ - "podman-immich_postgres.service" - "podman-immich_redis.service" - ]; - }; + systemd.services."podman-immich_server" = serviceConfig // { + unitConfig.UpheldBy = [ + "podman-immich_postgres.service" + "podman-immich_redis.service" + ]; + }; # Networks systemd.services."podman-network-immich-default" = { - path = [pkgs.podman]; + path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; @@ -368,8 +367,8 @@ in { script = '' podman network inspect immich-default || podman network create immich-default --opt isolate=true --subnet=10.89.0.0/24 --disable-dns ''; - partOf = ["podman-compose-immich-root.target"]; - wantedBy = ["podman-compose-immich-root.target"]; + partOf = [ "podman-compose-immich-root.target" ]; + wantedBy = [ "podman-compose-immich-root.target" ]; }; # Root service @@ -379,6 +378,6 @@ in { unitConfig = { Description = "Root target generated by compose2nix."; }; - wantedBy = ["multi-user.target"]; + wantedBy = [ "multi-user.target" ]; }; } diff --git a/hosts/sire/guests/influxdb.nix b/hosts/sire/guests/influxdb.nix index 643c1b3..929be04 100644 --- a/hosts/sire/guests/influxdb.nix +++ b/hosts/sire/guests/influxdb.nix @@ -5,20 +5,22 @@ nodes, pkgs, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardCfg = nodes.ward.config; influxdbDomain = "influxdb.${globals.domains.me}"; influxdbPort = 8086; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [influxdbPort]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ influxdbPort ]; }; wireguard.proxy-home = { client.via = "ward"; - firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [influxdbPort]; + firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ influxdbPort ]; }; age.secrets.github-access-token = { @@ -28,7 +30,9 @@ in { }; meta.telegraf.secrets."@GITHUB_ACCESS_TOKEN@" = config.age.secrets.github-access-token.path; - services.telegraf.extraConfig.outputs.influxdb_v2.urls = lib.mkForce ["http://localhost:${toString influxdbPort}"]; + services.telegraf.extraConfig.outputs.influxdb_v2.urls = lib.mkForce [ + "http://localhost:${toString influxdbPort}" + ]; services.telegraf.extraConfig.inputs = { github = { @@ -51,7 +55,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.influxdb = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString influxdbPort}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString influxdbPort}" = { }; extraConfig = '' zone influxdb 64k; keepalive 2; @@ -61,35 +65,39 @@ in { expectedBodyRegex = "InfluxDB"; }; }; - virtualHosts.${influxdbDomain} = let - accessRules = '' - ${lib.concatMapStrings (ip: "allow ${ip};\n") sentinelCfg.wireguard.proxy-sentinel.server.reservedAddresses} - deny all; - ''; - in { - forceSSL = true; - useACMEWildcardHost = true; - locations."/" = { - proxyPass = "http://influxdb"; - proxyWebsockets = true; - extraConfig = accessRules; - }; - locations."/api/v2/write" = { - proxyPass = "http://influxdb/api/v2/write"; - proxyWebsockets = true; - extraConfig = '' - ${accessRules} - access_log off; + virtualHosts.${influxdbDomain} = + let + accessRules = '' + ${lib.concatMapStrings ( + ip: "allow ${ip};\n" + ) sentinelCfg.wireguard.proxy-sentinel.server.reservedAddresses} + deny all; ''; + in + { + forceSSL = true; + useACMEWildcardHost = true; + locations."/" = { + proxyPass = "http://influxdb"; + proxyWebsockets = true; + extraConfig = accessRules; + }; + locations."/api/v2/write" = { + proxyPass = "http://influxdb/api/v2/write"; + proxyWebsockets = true; + extraConfig = '' + ${accessRules} + access_log off; + ''; + }; }; - }; }; }; nodes.ward-web-proxy = { services.nginx = { upstreams.influxdb = { - servers."${config.wireguard.proxy-home.ipv4}:${toString influxdbPort}" = {}; + servers."${config.wireguard.proxy-home.ipv4}:${toString influxdbPort}" = { }; extraConfig = '' zone influxdb 64k; keepalive 2; @@ -99,28 +107,30 @@ in { expectedBodyRegex = "InfluxDB"; }; }; - virtualHosts.${influxdbDomain} = let - accessRules = '' - ${lib.concatMapStrings (ip: "allow ${ip};\n") wardCfg.wireguard.proxy-home.server.reservedAddresses} - deny all; - ''; - in { - forceSSL = true; - useACMEWildcardHost = true; - locations."/" = { - proxyPass = "http://influxdb"; - proxyWebsockets = true; - extraConfig = accessRules; - }; - locations."/api/v2/write" = { - proxyPass = "http://influxdb/api/v2/write"; - proxyWebsockets = true; - extraConfig = '' - ${accessRules} - access_log off; + virtualHosts.${influxdbDomain} = + let + accessRules = '' + ${lib.concatMapStrings (ip: "allow ${ip};\n") wardCfg.wireguard.proxy-home.server.reservedAddresses} + deny all; ''; + in + { + forceSSL = true; + useACMEWildcardHost = true; + locations."/" = { + proxyPass = "http://influxdb"; + proxyWebsockets = true; + extraConfig = accessRules; + }; + locations."/api/v2/write" = { + proxyPass = "http://influxdb/api/v2/write"; + proxyWebsockets = true; + extraConfig = '' + ${accessRules} + access_log off; + ''; + }; }; - }; }; }; @@ -166,12 +176,12 @@ in { passwordFile = config.age.secrets.influxdb-admin-password.path; tokenFile = config.age.secrets.influxdb-admin-token.path; }; - organizations.machines.buckets.telegraf = {}; - organizations.home.buckets.home_assistant = {}; + organizations.machines.buckets.telegraf = { }; + organizations.home.buckets.home_assistant = { }; }; }; - environment.systemPackages = [pkgs.influxdb2-cli]; + environment.systemPackages = [ pkgs.influxdb2-cli ]; systemd.services.grafana.serviceConfig.RestartSec = "60"; # Retry every minute } diff --git a/hosts/sire/guests/loki.nix b/hosts/sire/guests/loki.nix index 7702125..9925626 100644 --- a/hosts/sire/guests/loki.nix +++ b/hosts/sire/guests/loki.nix @@ -3,19 +3,25 @@ globals, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; lokiDomain = "loki.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ + config.services.loki.configuration.server.http_listen_port + ]; }; wireguard.proxy-home = { client.via = "ward"; - firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port]; + firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ + config.services.loki.configuration.server.http_listen_port + ]; }; globals.services.loki.domain = lokiDomain; @@ -29,7 +35,8 @@ in { services.nginx = { upstreams.loki = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.loki.configuration.server.http_listen_port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.loki.configuration.server.http_listen_port}" = + { }; extraConfig = '' zone loki 64k; keepalive 2; @@ -76,7 +83,8 @@ in { services.nginx = { upstreams.loki = { - servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.loki.configuration.server.http_listen_port}" = {}; + servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.loki.configuration.server.http_listen_port}" = + { }; extraConfig = '' zone loki 64k; keepalive 2; @@ -124,74 +132,76 @@ in { ]; topology.self.services.loki.info = "https://" + lokiDomain; - services.loki = let - lokiDir = "/var/lib/loki"; - in { - enable = true; - configuration = { - analytics.reporting_enabled = false; - auth_enabled = false; + services.loki = + let + lokiDir = "/var/lib/loki"; + in + { + enable = true; + configuration = { + analytics.reporting_enabled = false; + auth_enabled = false; - server = { - http_listen_address = "0.0.0.0"; - http_listen_port = 3100; - log_level = "warn"; - }; - - ingester = { - lifecycler = { - address = "127.0.0.1"; - ring = { - kvstore.store = "inmemory"; - replication_factor = 1; - }; - final_sleep = "0s"; + server = { + http_listen_address = "0.0.0.0"; + http_listen_port = 3100; + log_level = "warn"; }; - chunk_idle_period = "5m"; - chunk_retain_period = "30s"; - }; - schema_config.configs = [ - { - from = "2023-06-01"; - store = "tsdb"; - object_store = "filesystem"; - schema = "v13"; - index = { - prefix = "index_"; - period = "24h"; + ingester = { + lifecycler = { + address = "127.0.0.1"; + ring = { + kvstore.store = "inmemory"; + replication_factor = 1; + }; + final_sleep = "0s"; }; - } - ]; - - storage_config = { - tsdb_shipper = { - active_index_directory = "${lokiDir}/tsdb-index"; - cache_location = "${lokiDir}/tsdb-cache"; - cache_ttl = "24h"; + chunk_idle_period = "5m"; + chunk_retain_period = "30s"; }; - filesystem.directory = "${lokiDir}/chunks"; - }; - # Do not accept new logs that are ingressed when they are actually already old. - limits_config = { - reject_old_samples = true; - reject_old_samples_max_age = "168h"; - allow_structured_metadata = false; - }; + schema_config.configs = [ + { + from = "2023-06-01"; + store = "tsdb"; + object_store = "filesystem"; + schema = "v13"; + index = { + prefix = "index_"; + period = "24h"; + }; + } + ]; - # Do not delete old logs automatically - table_manager = { - retention_deletes_enabled = false; - retention_period = "0s"; - }; + storage_config = { + tsdb_shipper = { + active_index_directory = "${lokiDir}/tsdb-index"; + cache_location = "${lokiDir}/tsdb-cache"; + cache_ttl = "24h"; + }; + filesystem.directory = "${lokiDir}/chunks"; + }; - compactor = { - working_directory = lokiDir; - compactor_ring.kvstore.store = "inmemory"; + # Do not accept new logs that are ingressed when they are actually already old. + limits_config = { + reject_old_samples = true; + reject_old_samples_max_age = "168h"; + allow_structured_metadata = false; + }; + + # Do not delete old logs automatically + table_manager = { + retention_deletes_enabled = false; + retention_period = "0s"; + }; + + compactor = { + working_directory = lokiDir; + compactor_ring.kvstore.store = "inmemory"; + }; }; }; - }; systemd.services.loki.serviceConfig.RestartSec = "60"; # Retry every minute } diff --git a/hosts/sire/guests/minecraft.nix b/hosts/sire/guests/minecraft.nix index 1bcd49c..d53b0d9 100644 --- a/hosts/sire/guests/minecraft.nix +++ b/hosts/sire/guests/minecraft.nix @@ -5,7 +5,8 @@ lib, pkgs, ... -}: let +}: +let inherit (lib) getExe; minecraftDomain = "mc.${globals.domains.me}"; @@ -13,7 +14,7 @@ minecraft-attach = pkgs.writeShellApplication { name = "minecraft-attach"; - runtimeInputs = [pkgs.tmux]; + runtimeInputs = [ pkgs.tmux ]; text = '' shopt -s nullglob @@ -40,9 +41,7 @@ }; helper-functions = - /* - bash - */ + # bash '' ################################################################ # General helper functions @@ -171,7 +170,7 @@ server-backup-script = pkgs.writeShellApplication { name = "minecraft-backup"; - runtimeInputs = [pkgs.rdiff-backup]; + runtimeInputs = [ pkgs.rdiff-backup ]; text = '' BACKUP_LOG_FILE="logs/backup.log" BACKUP_TO="backups" @@ -199,7 +198,10 @@ server-start-script = pkgs.writeShellApplication { name = "minecraft-server-start"; - runtimeInputs = [pkgs.procps pkgs.gnugrep]; + runtimeInputs = [ + pkgs.procps + pkgs.gnugrep + ]; text = '' cd ${dataDir}/server @@ -252,7 +254,11 @@ server-update-script = pkgs.writeShellApplication { name = "minecraft-server-update"; - runtimeInputs = [pkgs.wget pkgs.curl pkgs.jq]; + runtimeInputs = [ + pkgs.wget + pkgs.curl + pkgs.jq + ]; text = '' cd ${dataDir}/server || exit 1 ${helper-functions} @@ -285,7 +291,11 @@ proxy-update-script = pkgs.writeShellApplication { name = "minecraft-proxy-update"; - runtimeInputs = [pkgs.wget pkgs.curl pkgs.jq]; + runtimeInputs = [ + pkgs.wget + pkgs.curl + pkgs.jq + ]; text = '' cd ${dataDir}/proxy || exit 1 ${helper-functions} @@ -313,8 +323,8 @@ User = "minecraft"; # Hardening - AmbientCapabilities = ["CAP_KILL"]; - CapabilityBoundingSet = ["CAP_KILL"]; + AmbientCapabilities = [ "CAP_KILL" ]; + CapabilityBoundingSet = [ "CAP_KILL" ]; LockPersonality = true; NoNewPrivileges = true; PrivateDevices = true; @@ -335,16 +345,21 @@ SystemCallArchitectures = "native"; UMask = "0027"; }; -in { +in +{ microvm.mem = 1024 * 24; microvm.vcpu = 16; wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [80 25565 25566]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ + 80 + 25565 + 25566 + ]; }; - users.groups.minecraft.members = ["nginx"]; + users.groups.minecraft.members = [ "nginx" ]; users.users.minecraft = { description = "Minecraft server service user"; home = dataDir; @@ -379,7 +394,7 @@ in { # - 25565,25566 (wan) -> 25565,25566 (proxy-sentinel) networking.nftables.chains = { postrouting.to-minecraft = { - after = ["hook"]; + after = [ "hook" ]; rules = [ "iifname wan ip daddr ${config.wireguard.proxy-sentinel.ipv4} tcp dport 25565 masquerade random" "iifname wan ip6 daddr ${config.wireguard.proxy-sentinel.ipv6} tcp dport 25565 masquerade random" @@ -388,7 +403,7 @@ in { ]; }; prerouting.to-minecraft = { - after = ["hook"]; + after = [ "hook" ]; rules = [ "iifname wan tcp dport 25565 dnat ip to ${config.wireguard.proxy-sentinel.ipv4}" "iifname wan tcp dport 25565 dnat ip6 to ${config.wireguard.proxy-sentinel.ipv6}" @@ -400,7 +415,7 @@ in { services.nginx = { upstreams.minecraft = { - servers."${config.wireguard.proxy-sentinel.ipv4}:80" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:80" = { }; extraConfig = '' zone minecraft 64k; keepalive 2; @@ -422,8 +437,8 @@ in { systemd.services.minecraft-server = { description = "Minecraft Server Service"; - wantedBy = ["multi-user.target"]; - after = ["network.target"]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; path = [ # for infocmp pkgs.ncurses @@ -431,18 +446,19 @@ in { pkgs.libwebp ]; - serviceConfig = - commonServiceConfig - // { - Type = "forking"; - ExecStart = ''${getExe pkgs.tmux} -S /run/minecraft-server/tmux set -g default-shell ${getExe pkgs.bashInteractive} ";" new-session -d "${getExe pkgs.python3} ${./minecraft/server-loop.py} --block control/start.block ./start.sh :POST: ./backup.sh"''; - ExecStop = "${getExe pkgs.tmux} -S /run/minecraft-server/tmux kill-server"; + serviceConfig = commonServiceConfig // { + Type = "forking"; + ExecStart = ''${getExe pkgs.tmux} -S /run/minecraft-server/tmux set -g default-shell ${getExe pkgs.bashInteractive} ";" new-session -d "${getExe pkgs.python3} ${./minecraft/server-loop.py} --block control/start.block ./start.sh :POST: ./backup.sh"''; + ExecStop = "${getExe pkgs.tmux} -S /run/minecraft-server/tmux kill-server"; - WorkingDirectory = "${dataDir}/server"; - RuntimeDirectory = "minecraft-server"; - ReadWritePaths = ["${dataDir}/server" "${dataDir}/web"]; - ReadOnlyPaths = "${dataDir}/proxy"; - }; + WorkingDirectory = "${dataDir}/server"; + RuntimeDirectory = "minecraft-server"; + ReadWritePaths = [ + "${dataDir}/server" + "${dataDir}/web" + ]; + ReadOnlyPaths = "${dataDir}/proxy"; + }; preStart = '' ln -sfT ${getExe server-start-script} start.sh @@ -470,21 +486,22 @@ in { systemd.services.minecraft-proxy = { description = "Minecraft Proxy Service"; - wantedBy = ["multi-user.target"]; - after = ["network.target"]; - path = [pkgs.ncurses]; # for infocmp + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pkgs.ncurses ]; # for infocmp - serviceConfig = - commonServiceConfig - // { - Type = "forking"; - ExecStart = ''${getExe pkgs.tmux} -S /run/minecraft-proxy/tmux set -g default-shell ${getExe pkgs.bashInteractive} ";" new-session -d "${getExe pkgs.python3} ${./minecraft/server-loop.py} ./start.sh"''; - ExecStop = "${getExe pkgs.tmux} -S /run/minecraft-proxy/tmux kill-server"; + serviceConfig = commonServiceConfig // { + Type = "forking"; + ExecStart = ''${getExe pkgs.tmux} -S /run/minecraft-proxy/tmux set -g default-shell ${getExe pkgs.bashInteractive} ";" new-session -d "${getExe pkgs.python3} ${./minecraft/server-loop.py} ./start.sh"''; + ExecStop = "${getExe pkgs.tmux} -S /run/minecraft-proxy/tmux kill-server"; - WorkingDirectory = "${dataDir}/proxy"; - RuntimeDirectory = "minecraft-proxy"; - ReadWritePaths = ["${dataDir}/proxy" "${dataDir}/server/control"]; - }; + WorkingDirectory = "${dataDir}/proxy"; + RuntimeDirectory = "minecraft-proxy"; + ReadWritePaths = [ + "${dataDir}/proxy" + "${dataDir}/server/control" + ]; + }; preStart = '' ln -sfT ${getExe proxy-start-script} start.sh diff --git a/hosts/sire/guests/paperless.nix b/hosts/sire/guests/paperless.nix index 20c25de..512d5bf 100644 --- a/hosts/sire/guests/paperless.nix +++ b/hosts/sire/guests/paperless.nix @@ -5,23 +5,25 @@ nodes, pkgs, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; paperlessDomain = "paperless.${globals.domains.me}"; paperlessBackupDir = "/var/cache/paperless-backup"; -in { +in +{ microvm.mem = 1024 * 9; microvm.vcpu = 8; wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.paperless.port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ config.services.paperless.port ]; }; wireguard.proxy-home = { client.via = "ward"; - firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [config.services.paperless.port]; + firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ config.services.paperless.port ]; }; globals.services.paperless.domain = paperlessDomain; @@ -34,7 +36,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.paperless = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.paperless.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.paperless.port}" = { }; extraConfig = '' zone paperless 64k; keepalive 2; @@ -62,7 +64,7 @@ in { nodes.ward-web-proxy = { services.nginx = { upstreams.paperless = { - servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.paperless.port}" = {}; + servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.paperless.port}" = { }; extraConfig = '' zone paperless 64k; keepalive 2; @@ -191,20 +193,22 @@ in { ) ''; - systemd.services.paperless-backup = let - cfg = config.systemd.services.paperless-consumer; - in { - description = "Paperless documents backup"; - serviceConfig = lib.recursiveUpdate cfg.serviceConfig { - ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}"; - ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [paperlessBackupDir]; - Restart = "no"; - Type = "oneshot"; + systemd.services.paperless-backup = + let + cfg = config.systemd.services.paperless-consumer; + in + { + description = "Paperless documents backup"; + serviceConfig = lib.recursiveUpdate cfg.serviceConfig { + ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}"; + ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [ paperlessBackupDir ]; + Restart = "no"; + Type = "oneshot"; + }; + inherit (cfg) environment; + requiredBy = [ "restic-backups-storage-box-dusk.service" ]; + before = [ "restic-backups-storage-box-dusk.service" ]; }; - inherit (cfg) environment; - requiredBy = ["restic-backups-storage-box-dusk.service"]; - before = ["restic-backups-storage-box-dusk.service"]; - }; # Needed so we don't run out of tmpfs space for large backups. # Technically this could be cleared each boot but whatever. @@ -219,6 +223,6 @@ in { backups.storageBoxes.dusk = { subuser = "paperless"; - paths = [paperlessBackupDir]; + paths = [ paperlessBackupDir ]; }; } diff --git a/hosts/sire/guests/samba.nix b/hosts/sire/guests/samba.nix index 1f66a01..8ea12c2 100644 --- a/hosts/sire/guests/samba.nix +++ b/hosts/sire/guests/samba.nix @@ -3,7 +3,8 @@ globals, lib, ... -}: let +}: +let smbUsers = config.repo.secrets.local.samba.users; smbGroups = config.repo.secrets.local.samba.groups; @@ -19,19 +20,22 @@ }; mkShare = id: path: cfg: { - ${id} = - { - inherit path; - public = "no"; - writable = "yes"; - "create mask" = "0740"; - "directory mask" = "0750"; - "acl allow execute always" = "yes"; - } - // cfg; + ${id} = { + inherit path; + public = "no"; + writable = "yes"; + "create mask" = "0740"; + "directory mask" = "0750"; + "acl allow execute always" = "yes"; + } // cfg; }; - mkGroupShares = group: {enableBunker ? false, ...}: + mkGroupShares = + group: + { + enableBunker ? false, + ... + }: [ (mkShare group "/shares/groups/${group}" { "valid users" = "@${group}"; @@ -47,11 +51,13 @@ } ); - mkUserShares = user: { - enableBunker ? false, - enablePaperless ? false, - ... - }: + mkUserShares = + user: + { + enableBunker ? false, + enablePaperless ? false, + ... + }: [ (mkShare user "/shares/users/${user}" { "valid users" = user; @@ -69,7 +75,8 @@ "force group" = "paperless"; } ); -in { +in +{ # For influxdb communication channel wireguard.proxy-home.client.via = "ward"; @@ -81,7 +88,7 @@ in { services.openssh = { # You really have to hate them. Thanks Brother ADS-4300N. settings = { - Macs = ["hmac-sha2-512"]; + Macs = [ "hmac-sha2-512" ]; HostkeyAlgorithms = "+ssh-rsa"; PubkeyAcceptedAlgorithms = "+ssh-rsa"; }; @@ -115,18 +122,22 @@ in { ] ++ lib.flatten ( lib.flip lib.mapAttrsToList smbUsers ( - name: {enableBunker ? false, ...}: - [(mkPersistent "/storage" "/shares/users/${name}" name)] - ++ lib.optional enableBunker ( - mkPersistent "/bunker" "/shares/users/${name}-bunker" name - ) + name: + { + enableBunker ? false, + ... + }: + [ (mkPersistent "/storage" "/shares/users/${name}" name) ] + ++ lib.optional enableBunker (mkPersistent "/bunker" "/shares/users/${name}-bunker" name) ) ++ lib.flip lib.mapAttrsToList smbGroups ( - name: {enableBunker ? false, ...}: - [(mkPersistent "/storage" "/shares/groups/${name}" name)] - ++ lib.optional enableBunker ( - mkPersistent "/bunker" "/shares/groups/${name}-bunker" name - ) + name: + { + enableBunker ? false, + ... + }: + [ (mkPersistent "/storage" "/shares/groups/${name}" name) ] + ++ lib.optional enableBunker (mkPersistent "/bunker" "/shares/groups/${name}-bunker" name) ) ) ); @@ -153,7 +164,8 @@ in { # Service Switch capability found in most modern C libraries, to arbitrary # applications via PAM and ntlm_auth and to Samba itself. winbindd.enable = false; - settings = lib.mkMerge ([ + settings = lib.mkMerge ( + [ { global = { # Show the server host name in the printer comment box in print manager @@ -215,9 +227,9 @@ in { } ] ++ lib.flatten ( - lib.mapAttrsToList mkUserShares smbUsers - ++ lib.mapAttrsToList mkGroupShares smbGroups - )); + lib.mapAttrsToList mkUserShares smbUsers ++ lib.mapAttrsToList mkGroupShares smbGroups + ) + ); }; systemd.tmpfiles.settings = lib.mkMerge ( @@ -255,8 +267,9 @@ in { ] # For each paperless share, make sure the necessary sub-folders for that user are created # at boot so we can bind-mount them into the shares. - ++ lib.flatten (lib.flip lib.mapAttrsToList smbUsers ( - user: userCfg: + ++ lib.flatten ( + lib.flip lib.mapAttrsToList smbUsers ( + user: userCfg: lib.optional (userCfg.enablePaperless or false) { "10-smb-paperless" = { "/shares/users/${user}-paperless".d = { @@ -293,7 +306,8 @@ in { }; }; } - )) + ) + ) ); # For each paperless share, bind-mount create the necessary folders using tmpfiles. @@ -306,43 +320,50 @@ in { ] ++ lib.flip lib.mapAttrsToList smbUsers ( user: userCfg: - lib.optionalAttrs (userCfg.enablePaperless or false) { - "/shares/users/${user}-paperless/consume" = { - fsType = "none"; - options = ["bind"]; - device = "/paperless/consume/${user}"; - }; - "/shares/users/${user}-paperless/documents" = { - fsType = "none"; - options = ["bind" "ro"]; - device = "/paperless/media/documents/archive/${user}"; - }; - "/shares/users/${user}-paperless/originals" = { - fsType = "none"; - options = ["bind" "ro"]; - device = "/paperless/media/documents/originals/${user}"; - }; - } + lib.optionalAttrs (userCfg.enablePaperless or false) { + "/shares/users/${user}-paperless/consume" = { + fsType = "none"; + options = [ "bind" ]; + device = "/paperless/consume/${user}"; + }; + "/shares/users/${user}-paperless/documents" = { + fsType = "none"; + options = [ + "bind" + "ro" + ]; + device = "/paperless/media/documents/archive/${user}"; + }; + "/shares/users/${user}-paperless/originals" = { + fsType = "none"; + options = [ + "bind" + "ro" + ]; + device = "/paperless/media/documents/originals/${user}"; + }; + } ) ); - users.users = let - mkUser = name: id: groups: { - isNormalUser = true; - uid = id; - group = name; - extraGroups = groups; - createHome = false; - home = "/var/empty"; - useDefaultShell = false; - autoSubUidGidRange = false; - }; - in + users.users = + let + mkUser = name: id: groups: { + isNormalUser = true; + uid = id; + group = name; + extraGroups = groups; + createHome = false; + home = "/var/empty"; + useDefaultShell = false; + autoSubUidGidRange = false; + }; + in lib.mkMerge [ ( - {} + { } // lib.mapAttrs (name: cfg: mkUser name cfg.id cfg.groups) smbUsers - // lib.mapAttrs (name: cfg: mkUser name cfg.id []) smbGroups + // lib.mapAttrs (name: cfg: mkUser name cfg.id [ ]) smbGroups ) { scanner.openssh.authorizedKeys.keys = [ @@ -357,14 +378,12 @@ in { } ]; - users.groups = - { - paperless.gid = config.ids.gids.paperless; - } - // lib.mapAttrs (_: cfg: {gid = cfg.id;}) (smbUsers // smbGroups); + users.groups = { + paperless.gid = config.ids.gids.paperless; + } // lib.mapAttrs (_: cfg: { gid = cfg.id; }) (smbUsers // smbGroups); backups.storageBoxes.dusk = { subuser = "samba"; - paths = ["/bunker"]; + paths = [ "/bunker" ]; }; } diff --git a/hosts/sire/net.nix b/hosts/sire/net.nix index 5bf6969..1659799 100644 --- a/hosts/sire/net.nix +++ b/hosts/sire/net.nix @@ -3,7 +3,8 @@ globals, lib, ... -}: { +}: +{ networking.hostId = config.repo.secrets.local.networking.hostId; globals.monitoring.ping.sire = { @@ -15,8 +16,8 @@ boot.initrd.systemd.network = { enable = true; networks."10-lan" = { - address = [globals.net.home-lan.hosts.sire.cidrv4]; - gateway = [globals.net.home-lan.hosts.ward.ipv4]; + address = [ globals.net.home-lan.hosts.sire.cidrv4 ]; + gateway = [ globals.net.home-lan.hosts.ward.ipv4 ]; matchConfig.MACAddress = config.repo.secrets.local.networking.interfaces.lan.mac; networkConfig = { IPv6PrivacyExtensions = "yes"; @@ -53,8 +54,8 @@ ''; }; "20-lan-self" = { - address = [globals.net.home-lan.hosts.sire.cidrv4]; - gateway = [globals.net.home-lan.hosts.ward.ipv4]; + address = [ globals.net.home-lan.hosts.sire.cidrv4 ]; + gateway = [ globals.net.home-lan.hosts.ward.ipv4 ]; matchConfig.Name = "lan-self"; networkConfig = { IPv6PrivacyExtensions = "yes"; @@ -71,7 +72,7 @@ }; networking.nftables.firewall = { - zones.untrusted.interfaces = ["lan-self"]; + zones.untrusted.interfaces = [ "lan-self" ]; }; # Allow accessing influx diff --git a/hosts/ward/default.nix b/hosts/ward/default.nix index fe71b86..bb119d6 100644 --- a/hosts/ward/default.nix +++ b/hosts/ward/default.nix @@ -6,7 +6,8 @@ minimal, nodes, ... -}: { +}: +{ imports = [ inputs.nixos-hardware.nixosModules.common-cpu-intel inputs.nixos-hardware.nixosModules.common-pc-ssd @@ -26,7 +27,16 @@ nixpkgs.hostPlatform = "x86_64-linux"; boot.mode = "efi"; - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" "sdhci_pci" "r8169"]; + boot.initrd.availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usbhid" + "usb_storage" + "sd_mod" + "sdhci_pci" + "r8169" + ]; meta.promtail = { enable = true; @@ -34,7 +44,9 @@ }; # Connect safely via wireguard to skip authentication - networking.hosts.${nodes.ward-web-proxy.config.wireguard.proxy-home.ipv4} = [globals.services.influxdb.domain]; + networking.hosts.${nodes.ward-web-proxy.config.wireguard.proxy-home.ipv4} = [ + globals.services.influxdb.domain + ]; meta.telegraf = { enable = true; influxdb2 = { @@ -48,34 +60,33 @@ # TODO track my github stats # services.telegraf.extraConfig.inputs.github = {}; - guests = let - mkGuest = guestName: { - autostart = true; - zfs."/state" = { - pool = "rpool"; - dataset = "local/guests/${guestName}"; + guests = + let + mkGuest = guestName: { + autostart = true; + zfs."/state" = { + pool = "rpool"; + dataset = "local/guests/${guestName}"; + }; + zfs."/persist" = { + pool = "rpool"; + dataset = "safe/guests/${guestName}"; + }; + modules = [ + ../../config + ./guests/common.nix + ./guests/${guestName}.nix + { + node.secretsDir = ./secrets/${guestName}; + networking.nftables.firewall = { + zones.untrusted.interfaces = [ config.guests.${guestName}.networking.mainLinkName ]; + }; + } + ]; }; - zfs."/persist" = { - pool = "rpool"; - dataset = "safe/guests/${guestName}"; - }; - modules = [ - ../../config - ./guests/common.nix - ./guests/${guestName}.nix - { - node.secretsDir = ./secrets/${guestName}; - networking.nftables.firewall = { - zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; - }; - } - ]; - }; - mkMicrovm = guestName: { - ${guestName} = - mkGuest guestName - // { + mkMicrovm = guestName: { + ${guestName} = mkGuest guestName // { backend = "microvm"; microvm = { system = "x86_64-linux"; @@ -88,13 +99,11 @@ inherit inputs minimal; }; }; - }; + }; - # deadnix: skip - mkContainer = guestName: { - ${guestName} = - mkGuest guestName - // { + # deadnix: skip + mkContainer = guestName: { + ${guestName} = mkGuest guestName // { backend = "container"; container.macvlan = "lan"; extraSpecialArgs = { @@ -103,10 +112,10 @@ inherit inputs minimal; }; }; - }; - in + }; + in lib.mkIf (!minimal) ( - {} + { } // mkMicrovm "adguardhome" // mkMicrovm "forgejo" // mkMicrovm "home-gateway" diff --git a/hosts/ward/fs.nix b/hosts/ward/fs.nix index df6b226..830e899 100644 --- a/hosts/ward/fs.nix +++ b/hosts/ward/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { m2-ssd = { @@ -22,11 +24,9 @@ in { }; zpool = { rpool = lib.disko.zfs.mkZpool { - datasets = - lib.disko.zfs.impermanenceZfsDatasets - // { - "safe/guests" = lib.disko.zfs.unmountable; - }; + datasets = lib.disko.zfs.impermanenceZfsDatasets // { + "safe/guests" = lib.disko.zfs.unmountable; + }; }; }; }; diff --git a/hosts/ward/guests/adguardhome.nix b/hosts/ward/guests/adguardhome.nix index c62abd9..0a36bf6 100644 --- a/hosts/ward/guests/adguardhome.nix +++ b/hosts/ward/guests/adguardhome.nix @@ -4,12 +4,14 @@ lib, pkgs, ... -}: let +}: +let adguardhomeDomain = "adguardhome.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.adguardhome.port]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ config.services.adguardhome.port ]; }; globals.services.adguardhome.domain = adguardhomeDomain; @@ -22,7 +24,8 @@ in { nodes.sentinel = { services.nginx = { upstreams.adguardhome = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.adguardhome.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.adguardhome.port}" = + { }; extraConfig = '' zone adguardhome 64k; keepalive 2; @@ -36,7 +39,7 @@ in { forceSSL = true; useACMEWildcardHost = true; oauth2.enable = true; - oauth2.allowedGroups = ["access_adguardhome"]; + oauth2.allowedGroups = [ "access_adguardhome" ]; locations."/" = { proxyPass = "http://adguardhome"; proxyWebsockets = true; @@ -53,8 +56,8 @@ in { ]; networking.firewall = { - allowedTCPPorts = [53]; - allowedUDPPorts = [53]; + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; }; topology.self.services.adguardhome.info = "https://" + adguardhomeDomain; @@ -92,19 +95,22 @@ in { } ] # Use the local mirror-proxy for some services (not necessary, just for speed) - ++ map (domain: { - inherit domain; - answer = globals.net.home-lan.hosts.ward-web-proxy.ipv4; - }) [ - # FIXME: dont hardcode, filter global service domains by internal state - globals.services.grafana.domain - globals.services.immich.domain - globals.services.influxdb.domain - globals.services.loki.domain - globals.services.paperless.domain - "home.${globals.domains.me}" - "fritzbox.${globals.domains.me}" - ]; + ++ + map + (domain: { + inherit domain; + answer = globals.net.home-lan.hosts.ward-web-proxy.ipv4; + }) + [ + # FIXME: dont hardcode, filter global service domains by internal state + globals.services.grafana.domain + globals.services.immich.domain + globals.services.influxdb.domain + globals.services.loki.domain + globals.services.paperless.domain + "home.${globals.domains.me}" + "fritzbox.${globals.domains.me}" + ]; filters = [ { name = "AdGuard DNS filter"; diff --git a/hosts/ward/guests/common.nix b/hosts/ward/guests/common.nix index 6a1aeca..323491a 100644 --- a/hosts/ward/guests/common.nix +++ b/hosts/ward/guests/common.nix @@ -4,10 +4,12 @@ lib, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; -in { +in +{ meta.promtail = { enable = true; proxy = "sentinel"; @@ -15,10 +17,11 @@ in { # Connect safely via wireguard to skip http authentication networking.hosts.${ - if config.wireguard ? proxy-home - then wardWebProxyCfg.wireguard.proxy-home.ipv4 - else sentinelCfg.wireguard.proxy-sentinel.ipv4 - } = [globals.services.influxdb.domain]; + if config.wireguard ? proxy-home then + wardWebProxyCfg.wireguard.proxy-home.ipv4 + else + sentinelCfg.wireguard.proxy-sentinel.ipv4 + } = [ globals.services.influxdb.domain ]; meta.telegraf = lib.mkIf (!config.boot.isContainer) { enable = true; diff --git a/hosts/ward/guests/forgejo.nix b/hosts/ward/guests/forgejo.nix index cef3be9..f89c661 100644 --- a/hosts/ward/guests/forgejo.nix +++ b/hosts/ward/guests/forgejo.nix @@ -5,15 +5,20 @@ nodes, pkgs, ... -}: let +}: +let forgejoDomain = "git.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.forgejo.settings.server.HTTP_PORT]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ + config.services.forgejo.settings.server.HTTP_PORT + ]; }; - age.secrets.forgejo-mailer-password.rekeyFile = config.node.secretsDir + "/forgejo-mailer-password.age"; + age.secrets.forgejo-mailer-password.rekeyFile = + config.node.secretsDir + "/forgejo-mailer-password.age"; # Mirror the original oauth2 secret age.secrets.forgejo-oauth2-client-secret = { @@ -35,14 +40,14 @@ in { # - 9922 (wan) -> 22 (proxy-sentinel) networking.nftables.chains = { postrouting.to-forgejo = { - after = ["hook"]; + after = [ "hook" ]; rules = [ "iifname wan ip daddr ${config.wireguard.proxy-sentinel.ipv4} tcp dport 22 masquerade random" "iifname wan ip6 daddr ${config.wireguard.proxy-sentinel.ipv6} tcp dport 22 masquerade random" ]; }; prerouting.to-forgejo = { - after = ["hook"]; + after = [ "hook" ]; rules = [ "iifname wan tcp dport 9922 dnat ip to ${config.wireguard.proxy-sentinel.ipv4}:22" "iifname wan tcp dport 9922 dnat ip6 to ${config.wireguard.proxy-sentinel.ipv6}:22" @@ -52,7 +57,8 @@ in { services.nginx = { upstreams.forgejo = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.forgejo.settings.server.HTTP_PORT}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.forgejo.settings.server.HTTP_PORT}" = + { }; extraConfig = '' zone forgejo 64k; keepalive 2; @@ -83,7 +89,7 @@ in { }; }; - users.groups.git = {}; + users.groups.git = { }; users.users.git = { isSystemUser = true; useDefaultShell = true; @@ -188,22 +194,49 @@ in { systemd.services.forgejo = { serviceConfig.RestartSec = "60"; # Retry every minute - preStart = let - exe = lib.getExe config.services.forgejo.package; - providerName = "kanidm"; - clientId = "forgejo"; - args = lib.escapeShellArgs (lib.concatLists [ - ["--name" providerName] - ["--provider" "openidConnect"] - ["--key" clientId] - ["--auto-discover-url" "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}/.well-known/openid-configuration"] - ["--scopes" "email"] - ["--scopes" "profile"] - ["--group-claim-name" "groups"] - ["--admin-group" "admin"] - ["--skip-local-2fa"] - ]); - in + preStart = + let + exe = lib.getExe config.services.forgejo.package; + providerName = "kanidm"; + clientId = "forgejo"; + args = lib.escapeShellArgs ( + lib.concatLists [ + [ + "--name" + providerName + ] + [ + "--provider" + "openidConnect" + ] + [ + "--key" + clientId + ] + [ + "--auto-discover-url" + "https://${globals.services.kanidm.domain}/oauth2/openid/${clientId}/.well-known/openid-configuration" + ] + [ + "--scopes" + "email" + ] + [ + "--scopes" + "profile" + ] + [ + "--group-claim-name" + "groups" + ] + [ + "--admin-group" + "admin" + ] + [ "--skip-local-2fa" ] + ] + ); + in lib.mkAfter '' provider_id=$(${exe} admin auth list | ${pkgs.gnugrep}/bin/grep -w '${providerName}' | cut -f1) SECRET="$(< ${config.age.secrets.forgejo-oauth2-client-secret.path})" diff --git a/hosts/ward/guests/home-gateway.nix b/hosts/ward/guests/home-gateway.nix index 30a7011..005bfd9 100644 --- a/hosts/ward/guests/home-gateway.nix +++ b/hosts/ward/guests/home-gateway.nix @@ -1,11 +1,12 @@ -{globals, ...}: { +{ globals, ... }: +{ # Forwarding required to masquerade netbird network boot.kernel.sysctl."net.ipv4.ip_forward" = 1; wireguard.proxy-home.client.via = "ward"; networking.nftables.chains.forward.from-netbird = { - after = ["conntrack"]; + after = [ "conntrack" ]; rules = [ "iifname wt-home oifname lan accept" ]; diff --git a/hosts/ward/guests/kanidm.nix b/hosts/ward/guests/kanidm.nix index a91d4fe..036df82 100644 --- a/hosts/ward/guests/kanidm.nix +++ b/hosts/ward/guests/kanidm.nix @@ -3,7 +3,8 @@ globals, pkgs, ... -}: let +}: +let kanidmDomain = "auth.${globals.domains.me}"; kanidmPort = 8300; @@ -12,10 +13,11 @@ mode = "440"; group = "kanidm"; }; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [kanidmPort]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ kanidmPort ]; }; age.secrets."kanidm-self-signed.crt" = { @@ -50,7 +52,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.kanidm = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString kanidmPort}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString kanidmPort}" = { }; extraConfig = '' zone kanidm 64k; keepalive 2; @@ -112,7 +114,7 @@ in { inherit (globals.kanidm) persons; # Immich - groups."immich.access" = {}; + groups."immich.access" = { }; systems.oauth2.immich = { displayName = "Immich"; originUrl = "https://${globals.services.immich.domain}/"; @@ -123,11 +125,15 @@ in { allowInsecureClientDisablePkce = true; # XXX: RS256 is used instead of ES256 so additionally we need legacy crypto enableLegacyCrypto = true; - scopeMaps."immich.access" = ["openid" "email" "profile"]; + scopeMaps."immich.access" = [ + "openid" + "email" + "profile" + ]; }; # Netbird - groups."netbird.access" = {}; + groups."netbird.access" = { }; systems.oauth2.netbird = { public = true; displayName = "Netbird"; @@ -136,78 +142,97 @@ in { preferShortUsername = true; enableLocalhostRedirects = true; enableLegacyCrypto = true; - scopeMaps."netbird.access" = ["openid" "email" "profile"]; + scopeMaps."netbird.access" = [ + "openid" + "email" + "profile" + ]; }; # Paperless - groups."paperless.access" = {}; + groups."paperless.access" = { }; systems.oauth2.paperless = { displayName = "Paperless"; originUrl = "https://${globals.services.paperless.domain}/"; originLanding = "https://${globals.services.paperless.domain}/"; basicSecretFile = config.age.secrets.kanidm-oauth2-paperless.path; preferShortUsername = true; - scopeMaps."paperless.access" = ["openid" "email" "profile"]; + scopeMaps."paperless.access" = [ + "openid" + "email" + "profile" + ]; }; # Grafana - groups."grafana.access" = {}; - groups."grafana.editors" = {}; - groups."grafana.admins" = {}; - groups."grafana.server-admins" = {}; + groups."grafana.access" = { }; + groups."grafana.editors" = { }; + groups."grafana.admins" = { }; + groups."grafana.server-admins" = { }; systems.oauth2.grafana = { displayName = "Grafana"; originUrl = "https://${globals.services.grafana.domain}/"; originLanding = "https://${globals.services.grafana.domain}/"; basicSecretFile = config.age.secrets.kanidm-oauth2-grafana.path; preferShortUsername = true; - scopeMaps."grafana.access" = ["openid" "email" "profile"]; + scopeMaps."grafana.access" = [ + "openid" + "email" + "profile" + ]; claimMaps.groups = { joinType = "array"; valuesByGroup = { - "grafana.editors" = ["editor"]; - "grafana.admins" = ["admin"]; - "grafana.server-admins" = ["server_admin"]; + "grafana.editors" = [ "editor" ]; + "grafana.admins" = [ "admin" ]; + "grafana.server-admins" = [ "server_admin" ]; }; }; }; # Forgejo - groups."forgejo.access" = {}; - groups."forgejo.admins" = {}; + groups."forgejo.access" = { }; + groups."forgejo.admins" = { }; systems.oauth2.forgejo = { displayName = "Forgejo"; originUrl = "https://${globals.services.forgejo.domain}/"; originLanding = "https://${globals.services.forgejo.domain}/"; basicSecretFile = config.age.secrets.kanidm-oauth2-forgejo.path; - scopeMaps."forgejo.access" = ["openid" "email" "profile"]; + scopeMaps."forgejo.access" = [ + "openid" + "email" + "profile" + ]; # XXX: PKCE is currently not supported by gitea/forgejo, # see https://github.com/go-gitea/gitea/issues/21376. allowInsecureClientDisablePkce = true; preferShortUsername = true; claimMaps.groups = { joinType = "array"; - valuesByGroup."forgejo.admins" = ["admin"]; + valuesByGroup."forgejo.admins" = [ "admin" ]; }; }; # Web Sentinel - groups."web-sentinel.access" = {}; - groups."web-sentinel.adguardhome" = {}; - groups."web-sentinel.openwebui" = {}; - groups."web-sentinel.analytics" = {}; + groups."web-sentinel.access" = { }; + groups."web-sentinel.adguardhome" = { }; + groups."web-sentinel.openwebui" = { }; + groups."web-sentinel.analytics" = { }; systems.oauth2.web-sentinel = { displayName = "Web Sentinel"; originUrl = "https://oauth2.${globals.domains.me}/"; originLanding = "https://oauth2.${globals.domains.me}/"; basicSecretFile = config.age.secrets.kanidm-oauth2-web-sentinel.path; preferShortUsername = true; - scopeMaps."web-sentinel.access" = ["openid" "email"]; + scopeMaps."web-sentinel.access" = [ + "openid" + "email" + ]; claimMaps.groups = { joinType = "array"; - valuesByGroup."web-sentinel.adguardhome" = ["access_adguardhome"]; - valuesByGroup."web-sentinel.openwebui" = ["access_openwebui"]; - valuesByGroup."web-sentinel.analytics" = ["access_analytics"]; + valuesByGroup."web-sentinel.adguardhome" = [ "access_adguardhome" ]; + valuesByGroup."web-sentinel.openwebui" = [ "access_openwebui" ]; + valuesByGroup."web-sentinel.analytics" = [ "access_analytics" ]; }; }; }; diff --git a/hosts/ward/guests/netbird.nix b/hosts/ward/guests/netbird.nix index 9ed45b9..755b406 100644 --- a/hosts/ward/guests/netbird.nix +++ b/hosts/ward/guests/netbird.nix @@ -4,10 +4,12 @@ lib, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; netbirdDomain = "netbird.${globals.domains.me}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; firewallRuleForNode.sentinel.allowedTCPPorts = [ @@ -26,9 +28,11 @@ in { }; age.secrets.netbird-data-store-encryption-key = { - generator.script = {pkgs, ...}: '' - ${lib.getExe pkgs.openssl} rand -base64 32 - ''; + generator.script = + { pkgs, ... }: + '' + ${lib.getExe pkgs.openssl} rand -base64 32 + ''; }; environment.persistence."/persist".directories = [ @@ -88,7 +92,8 @@ in { nodes.sentinel = { services.nginx = { upstreams.netbird-mgmt = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${builtins.toString config.services.netbird.server.management.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${builtins.toString config.services.netbird.server.management.port}" = + { }; extraConfig = '' zone netbird 64k; keepalive 5; @@ -102,7 +107,8 @@ in { }; upstreams.netbird-signal = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${builtins.toString config.services.netbird.server.signal.port}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${builtins.toString config.services.netbird.server.signal.port}" = + { }; extraConfig = '' zone netbird 64k; keepalive 5; diff --git a/hosts/ward/guests/radicale.nix b/hosts/ward/guests/radicale.nix index ce6ead4..8705740 100644 --- a/hosts/ward/guests/radicale.nix +++ b/hosts/ward/guests/radicale.nix @@ -2,12 +2,14 @@ config, globals, ... -}: let +}: +let radicaleDomain = "radicale.${globals.domains.personal}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [8000]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ 8000 ]; }; globals.services.radicale.domain = radicaleDomain; @@ -20,7 +22,7 @@ in { nodes.sentinel = { services.nginx = { upstreams.radicale = { - servers."${config.wireguard.proxy-sentinel.ipv4}:8000" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:8000" = { }; extraConfig = '' zone radicale 64k; keepalive 2; @@ -61,7 +63,10 @@ in { enable = true; settings = { server = { - hosts = ["0.0.0.0:8000" "[::]:8000"]; + hosts = [ + "0.0.0.0:8000" + "[::]:8000" + ]; }; auth = { type = "htpasswd"; @@ -95,6 +100,6 @@ in { backups.storageBoxes.dusk = { subuser = "radicale"; - paths = ["/var/lib/radicale"]; + paths = [ "/var/lib/radicale" ]; }; } diff --git a/hosts/ward/guests/vaultwarden.nix b/hosts/ward/guests/vaultwarden.nix index 6f53c5e..25453c6 100644 --- a/hosts/ward/guests/vaultwarden.nix +++ b/hosts/ward/guests/vaultwarden.nix @@ -3,12 +3,14 @@ globals, lib, ... -}: let +}: +let vaultwardenDomain = "pw.${globals.domains.personal}"; -in { +in +{ wireguard.proxy-sentinel = { client.via = "sentinel"; - firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.vaultwarden.config.rocketPort]; + firewallRuleForNode.sentinel.allowedTCPPorts = [ config.services.vaultwarden.config.rocketPort ]; }; age.secrets.vaultwarden-env = { @@ -36,7 +38,8 @@ in { nodes.sentinel = { services.nginx = { upstreams.vaultwarden = { - servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.vaultwarden.config.rocketPort}" = {}; + servers."${config.wireguard.proxy-sentinel.ipv4}:${toString config.services.vaultwarden.config.rocketPort}" = + { }; extraConfig = '' zone vaultwarden 64k; keepalive 2; @@ -110,6 +113,6 @@ in { backups.storageBoxes.dusk = { subuser = "vaultwarden"; - paths = [config.services.vaultwarden.backupDir]; + paths = [ config.services.vaultwarden.backupDir ]; }; } diff --git a/hosts/ward/guests/web-proxy.nix b/hosts/ward/guests/web-proxy.nix index 339187b..23a2580 100644 --- a/hosts/ward/guests/web-proxy.nix +++ b/hosts/ward/guests/web-proxy.nix @@ -2,19 +2,28 @@ config, globals, ... -}: let +}: +let inherit (config.repo.secrets.local) acme; fritzboxDomain = "fritzbox.${globals.domains.me}"; -in { +in +{ microvm.mem = 1024 * 4; # Need more /tmp space so nginx can store intermediary files wireguard.proxy-home = { client.via = "ward"; - firewallRuleForAll.allowedTCPPorts = [80 443]; + firewallRuleForAll.allowedTCPPorts = [ + 80 + 443 + ]; }; # This node shall monitor the infrastructure - meta.telegraf.availableMonitoringNetworks = ["internet" "home-wan" "home-lan"]; + meta.telegraf.availableMonitoringNetworks = [ + "internet" + "home-wan" + "home-lan" + ]; age.secrets.acme-cloudflare-dns-token = { rekeyFile = config.node.secretsDir + "/acme-cloudflare-dns-token.age"; @@ -37,14 +46,14 @@ in { }; dnsProvider = "cloudflare"; dnsPropagationCheck = true; - reloadServices = ["nginx"]; + reloadServices = [ "nginx" ]; }; inherit (acme) certs wildcardDomains; }; services.nginx = { upstreams.fritzbox = { - servers.${globals.net.home-wan.hosts.fritzbox.ipv4} = {}; + servers.${globals.net.home-wan.hosts.fritzbox.ipv4} = { }; extraConfig = '' zone grafana 64k; keepalive 2; @@ -68,7 +77,7 @@ in { }; }; - users.groups.acme.members = ["nginx"]; + users.groups.acme.members = [ "nginx" ]; services.nginx.enable = true; services.nginx.recommendedSetup = true; } diff --git a/hosts/ward/kea.nix b/hosts/ward/kea.nix index d12d553..f8a31c7 100644 --- a/hosts/ward/kea.nix +++ b/hosts/ward/kea.nix @@ -4,9 +4,11 @@ utils, nodes, ... -}: let +}: +let inherit (lib) net; -in { +in +{ environment.persistence."/persist".directories = [ { directory = "/var/lib/private/kea"; @@ -28,7 +30,7 @@ in { renew-timer = 3600; interfaces-config = { # XXX: BUG: why does this bind other macvtaps? - interfaces = ["lan-self"]; + interfaces = [ "lan-self" ]; service-sockets-max-retries = -1; }; option-data = [ @@ -43,7 +45,11 @@ in { interface = "lan-self"; subnet = globals.net.home-lan.cidrv4; pools = [ - {pool = "${net.cidr.host 20 globals.net.home-lan.cidrv4} - ${net.cidr.host (-6) globals.net.home-lan.cidrv4}";} + { + pool = "${net.cidr.host 20 globals.net.home-lan.cidrv4} - ${ + net.cidr.host (-6) globals.net.home-lan.cidrv4 + }"; + } ]; option-data = [ { @@ -79,5 +85,7 @@ in { }; }; - systemd.services.kea-dhcp4-server.after = ["sys-subsystem-net-devices-${utils.escapeSystemdPath "lan-self"}.device"]; + systemd.services.kea-dhcp4-server.after = [ + "sys-subsystem-net-devices-${utils.escapeSystemdPath "lan-self"}.device" + ]; } diff --git a/hosts/ward/net.nix b/hosts/ward/net.nix index f07f49e..2caabac 100644 --- a/hosts/ward/net.nix +++ b/hosts/ward/net.nix @@ -3,7 +3,8 @@ globals, lib, ... -}: { +}: +{ boot.kernel.sysctl."net.ipv4.ip_forward" = 1; networking.hostId = config.repo.secrets.local.networking.hostId; @@ -17,8 +18,8 @@ enable = true; networks = { "10-wan" = { - address = [globals.net.home-wan.hosts.ward.cidrv4]; - gateway = [globals.net.home-wan.hosts.fritzbox.ipv4]; + address = [ globals.net.home-wan.hosts.ward.cidrv4 ]; + gateway = [ globals.net.home-wan.hosts.fritzbox.ipv4 ]; matchConfig.MACAddress = config.repo.secrets.local.networking.interfaces.wan.mac; networkConfig.IPv6PrivacyExtensions = "yes"; linkConfig.RequiredForOnline = "routable"; @@ -70,8 +71,8 @@ #dhcpV4Config.UseDNS = false; #dhcpV6Config.UseDNS = false; #ipv6AcceptRAConfig.UseDNS = false; - address = [globals.net.home-wan.hosts.ward.cidrv4]; - gateway = [globals.net.home-wan.hosts.fritzbox.ipv4]; + address = [ globals.net.home-wan.hosts.ward.cidrv4 ]; + gateway = [ globals.net.home-wan.hosts.fritzbox.ipv4 ]; matchConfig.MACAddress = config.repo.secrets.local.networking.interfaces.wan.mac; networkConfig.IPv6PrivacyExtensions = "yes"; dhcpV6Config.PrefixDelegationHint = "::/64"; @@ -99,7 +100,7 @@ dhcpPrefixDelegationConfig.Token = "::ff"; # Announce a static prefix ipv6Prefixes = [ - {Prefix = globals.net.home-lan.cidrv6;} + { Prefix = globals.net.home-lan.cidrv6; } ]; # Delegate prefix dhcpPrefixDelegationConfig = { @@ -123,39 +124,45 @@ }; networking.nftables.firewall = { - snippets.nnf-icmp.ipv6Types = ["mld-listener-query" "nd-router-solicit"]; + snippets.nnf-icmp.ipv6Types = [ + "mld-listener-query" + "nd-router-solicit" + ]; zones = { - untrusted.interfaces = ["wan"]; - lan.interfaces = ["lan-self"]; - proxy-home.interfaces = ["proxy-home"]; + untrusted.interfaces = [ "wan" ]; + lan.interfaces = [ "lan-self" ]; + proxy-home.interfaces = [ "proxy-home" ]; }; rules = { masquerade = { - from = ["lan"]; - to = ["untrusted"]; + from = [ "lan" ]; + to = [ "untrusted" ]; masquerade = true; }; outbound = { - from = ["lan"]; - to = ["lan" "untrusted"]; + from = [ "lan" ]; + to = [ + "lan" + "untrusted" + ]; late = true; # Only accept after any rejects have been processed verdict = "accept"; }; lan-to-local = { - from = ["lan"]; - to = ["local"]; + from = [ "lan" ]; + to = [ "local" ]; - allowedUDPPorts = [config.wireguard.proxy-home.server.port]; + allowedUDPPorts = [ config.wireguard.proxy-home.server.port ]; }; # Forward traffic between participants forward-proxy-home-vpn-traffic = { - from = ["proxy-home"]; - to = ["proxy-home"]; + from = [ "proxy-home" ]; + to = [ "proxy-home" ]; verdict = "accept"; }; diff --git a/hosts/zackbiene/default.nix b/hosts/zackbiene/default.nix index e5668db..d0aa7e6 100644 --- a/hosts/zackbiene/default.nix +++ b/hosts/zackbiene/default.nix @@ -4,10 +4,12 @@ lib, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; wardWebProxyCfg = nodes.ward-web-proxy.config; -in { +in +{ imports = [ ../../config ../../config/hardware/odroid-n2plus.nix @@ -38,10 +40,11 @@ in { # Connect safely via wireguard to skip http authentication networking.hosts.${ - if config.wireguard ? proxy-home - then wardWebProxyCfg.wireguard.proxy-home.ipv4 - else sentinelCfg.wireguard.proxy-sentinel.ipv4 - } = [globals.services.influxdb.domain]; + if config.wireguard ? proxy-home then + wardWebProxyCfg.wireguard.proxy-home.ipv4 + else + sentinelCfg.wireguard.proxy-sentinel.ipv4 + } = [ globals.services.influxdb.domain ]; meta.telegraf = { enable = true; diff --git a/hosts/zackbiene/esphome.nix b/hosts/zackbiene/esphome.nix index d7e29c9..9a85925 100644 --- a/hosts/zackbiene/esphome.nix +++ b/hosts/zackbiene/esphome.nix @@ -2,10 +2,12 @@ config, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; esphomeDomain = "esphome.${sentinelCfg.repo.secrets.global.domains.personal}"; -in { +in +{ environment.persistence."/persist".directories = [ { directory = "/var/lib/private/esphome"; @@ -25,13 +27,13 @@ in { # "esphome.home.${personalDomain}" #]; systemd.services.nginx = { - serviceConfig.SupplementaryGroups = ["esphome"]; - requires = ["esphome.service"]; + serviceConfig.SupplementaryGroups = [ "esphome" ]; + requires = [ "esphome.service" ]; }; services.nginx = { upstreams."esphome" = { - servers."unix:/run/esphome/esphome.sock" = {}; + servers."unix:/run/esphome/esphome.sock" = { }; extraConfig = '' zone esphome 64k; keepalive 2; diff --git a/hosts/zackbiene/fs.nix b/hosts/zackbiene/fs.nix index 4ac3c48..25d546c 100644 --- a/hosts/zackbiene/fs.nix +++ b/hosts/zackbiene/fs.nix @@ -2,9 +2,11 @@ config, lib, ... -}: let +}: +let inherit (config.repo.secrets.local) disks; -in { +in +{ disko.devices = { disk = { mmc = { @@ -21,7 +23,7 @@ in { }; }; zpool = { - rpool = lib.disko.zfs.mkZpool {datasets = lib.disko.zfs.impermanenceZfsDatasets;}; + rpool = lib.disko.zfs.mkZpool { datasets = lib.disko.zfs.impermanenceZfsDatasets; }; }; }; } diff --git a/hosts/zackbiene/home-assistant.nix b/hosts/zackbiene/home-assistant.nix index f23b5db..4319dc5 100644 --- a/hosts/zackbiene/home-assistant.nix +++ b/hosts/zackbiene/home-assistant.nix @@ -5,10 +5,12 @@ nodes, pkgs, ... -}: let +}: +let homeDomain = "home.${globals.domains.me}"; fritzboxDomain = "fritzbox.${globals.domains.me}"; -in { +in +{ wireguard.proxy-home.firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [ config.services.home-assistant.config.http.server_port ]; @@ -39,10 +41,10 @@ in { ]; config = { http = { - server_host = ["0.0.0.0"]; + server_host = [ "0.0.0.0" ]; server_port = 8123; use_x_forwarded_for = true; - trusted_proxies = [nodes.ward-web-proxy.config.wireguard.proxy-home.ipv4]; + trusted_proxies = [ nodes.ward-web-proxy.config.wireguard.proxy-home.ipv4 ]; }; homeassistant = { @@ -61,26 +63,26 @@ in { #### only selected components from default_config #### - assist_pipeline = {}; - backup = {}; - bluetooth = {}; - config = {}; + assist_pipeline = { }; + backup = { }; + bluetooth = { }; + config = { }; #cloud = {}; #conversation = {}; - dhcp = {}; - energy = {}; - history = {}; - homeassistant_alerts = {}; - logbook = {}; + dhcp = { }; + energy = { }; + history = { }; + homeassistant_alerts = { }; + logbook = { }; #media_source = {}; - mobile_app = {}; - my = {}; - ssdp = {}; - stream = {}; - sun = {}; + mobile_app = { }; + my = { }; + ssdp = { }; + stream = { }; + sun = { }; #usb = {}; - webhook = {}; - zeroconf = {}; + webhook = { }; + zeroconf = { }; ### Components not from default_config @@ -100,8 +102,8 @@ in { bucket = "home_assistant"; }; }; - extraPackages = python3Packages: - with python3Packages; [ + extraPackages = + python3Packages: with python3Packages; [ psycopg2 gtts ]; @@ -122,7 +124,9 @@ in { # We don't use -i because it would require chown with is a @privileged syscall INFLUXDB_TOKEN="$(cat ${config.age.secrets.hass-influxdb-token.path})" \ ${lib.getExe pkgs.yq-go} '.influxdb_token = strenv(INFLUXDB_TOKEN)' \ - ${config.age.secrets."home-assistant-secrets.yaml".path} > ${config.services.home-assistant.configDir}/secrets.yaml + ${ + config.age.secrets."home-assistant-secrets.yaml".path + } > ${config.services.home-assistant.configDir}/secrets.yaml touch -a ${config.services.home-assistant.configDir}/{automations,scenes,scripts,manual}.yaml ''; @@ -143,19 +147,20 @@ in { }; services.influxdb2.provision.organizations.home.auths."home-assistant (${config.node.name})" = { - readBuckets = ["home_assistant"]; - writeBuckets = ["home_assistant"]; + readBuckets = [ "home_assistant" ]; + writeBuckets = [ "home_assistant" ]; tokenFile = nodes.sire-influxdb.config.age.secrets."hass-influxdb-token-${config.node.name}".path; }; }; # Connect to fritzbox via https proxy (to ensure valid cert) - networking.hosts.${globals.net.home-lan.hosts.ward-web-proxy.ipv4} = [fritzboxDomain]; + networking.hosts.${globals.net.home-lan.hosts.ward-web-proxy.ipv4} = [ fritzboxDomain ]; nodes.ward-web-proxy = { services.nginx = { upstreams."home-assistant" = { - servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.home-assistant.config.http.server_port}" = {}; + servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.home-assistant.config.http.server_port}" = + { }; extraConfig = '' zone home-assistant 64k; keepalive 2; diff --git a/hosts/zackbiene/hostapd.nix b/hosts/zackbiene/hostapd.nix index ccbeef0..f242254 100644 --- a/hosts/zackbiene/hostapd.nix +++ b/hosts/zackbiene/hostapd.nix @@ -1,4 +1,5 @@ -{config, ...}: { +{ config, ... }: +{ # Associates a mandatory and unique password to each client # TODO: autogenerate? via secret generators and derived secrets? age.secrets.wifi-clients.rekeyFile = ./secrets/wifi-clients.age; @@ -11,7 +12,16 @@ band = "2g"; countryCode = "DE"; channel = 13; # Automatic Channel Selection (ACS) is unfortunately not implemented for mt7612u. - wifi4.capabilities = ["LDPC" "HT40+" "HT40-" "GF" "SHORT-GI-20" "SHORT-GI-40" "TX-STBC" "RX-STBC1"]; + wifi4.capabilities = [ + "LDPC" + "HT40+" + "HT40-" + "GF" + "SHORT-GI-20" + "SHORT-GI-40" + "TX-STBC" + "RX-STBC1" + ]; networks.wlan1 = { inherit (config.repo.secrets.local.hostapd) ssid; macAcl = "allow"; diff --git a/hosts/zackbiene/kea.nix b/hosts/zackbiene/kea.nix index 89047bd..3b1fe6b 100644 --- a/hosts/zackbiene/kea.nix +++ b/hosts/zackbiene/kea.nix @@ -2,10 +2,12 @@ lib, utils, ... -}: let +}: +let inherit (lib) net; iotCidrv4 = "10.0.90.0/24"; # FIXME: make all subnet allocations accessible via global.net or smth -in { +in +{ environment.persistence."/persist".directories = [ { directory = "/var/lib/private/kea"; @@ -24,7 +26,7 @@ in { valid-lifetime = 86400; renew-timer = 3600; interfaces-config = { - interfaces = ["wlan1"]; + interfaces = [ "wlan1" ]; service-sockets-max-retries = -1; }; subnet4 = [ @@ -33,7 +35,7 @@ in { interface = "wlan1"; subnet = iotCidrv4; pools = [ - {pool = "${net.cidr.host 20 iotCidrv4} - ${net.cidr.host (-6) iotCidrv4}";} + { pool = "${net.cidr.host 20 iotCidrv4} - ${net.cidr.host (-6) iotCidrv4}"; } ]; option-data = [ { @@ -46,5 +48,7 @@ in { }; }; - systemd.services.kea-dhcp4-server.after = ["sys-subsystem-net-devices-${utils.escapeSystemdPath "wlan1"}.device"]; + systemd.services.kea-dhcp4-server.after = [ + "sys-subsystem-net-devices-${utils.escapeSystemdPath "wlan1"}.device" + ]; } diff --git a/hosts/zackbiene/mosquitto.nix b/hosts/zackbiene/mosquitto.nix index a400dac..fa82b3b 100644 --- a/hosts/zackbiene/mosquitto.nix +++ b/hosts/zackbiene/mosquitto.nix @@ -1,4 +1,5 @@ -{config, ...}: { +{ config, ... }: +{ age.secrets.mosquitto-pw-zigbee2mqtt = { rekeyFile = ./secrets/mosquitto-pw-zigbee2mqtt.age; mode = "440"; @@ -17,15 +18,15 @@ persistence = true; listeners = [ { - acl = ["pattern readwrite #"]; + acl = [ "pattern readwrite #" ]; users = { zigbee2mqtt = { passwordFile = config.age.secrets.mosquitto-pw-zigbee2mqtt.path; - acl = ["readwrite #"]; + acl = [ "readwrite #" ]; }; home_assistant = { passwordFile = config.age.secrets.mosquitto-pw-home_assistant.path; - acl = ["readwrite #"]; + acl = [ "readwrite #" ]; }; }; settings.allow_anonymous = false; diff --git a/hosts/zackbiene/net.nix b/hosts/zackbiene/net.nix index 6dbd5d2..7a89d37 100644 --- a/hosts/zackbiene/net.nix +++ b/hosts/zackbiene/net.nix @@ -3,10 +3,12 @@ globals, lib, ... -}: let +}: +let iotCidrv4 = "10.90.0.0/24"; iotCidrv6 = "fd00:90::/64"; -in { +in +{ networking.hostId = config.repo.secrets.local.networking.hostId; globals.monitoring.ping.zackbiene = { @@ -19,7 +21,9 @@ in { boot.initrd.systemd.network = { enable = true; - networks = {inherit (config.systemd.network.networks) "10-lan1";}; + networks = { + inherit (config.systemd.network.networks) "10-lan1"; + }; }; systemd.network.networks = { @@ -49,36 +53,39 @@ in { }; # Announce a static prefix ipv6Prefixes = [ - {Prefix = iotCidrv6;} + { Prefix = iotCidrv6; } ]; linkConfig.RequiredForOnline = "no"; }; }; networking.nftables.firewall = { - snippets.nnf-icmp.ipv6Types = ["mld-listener-query" "nd-router-solicit"]; + snippets.nnf-icmp.ipv6Types = [ + "mld-listener-query" + "nd-router-solicit" + ]; zones = { - untrusted.interfaces = ["lan1"]; - lan-interface.interfaces = ["lan1"]; + untrusted.interfaces = [ "lan1" ]; + lan-interface.interfaces = [ "lan1" ]; lan = { parent = "lan-interface"; - ipv4Addresses = [globals.net.home-lan.cidrv4]; - ipv6Addresses = [globals.net.home-lan.cidrv6]; + ipv4Addresses = [ globals.net.home-lan.cidrv4 ]; + ipv6Addresses = [ globals.net.home-lan.cidrv6 ]; }; - iot.interfaces = ["wlan1"]; + iot.interfaces = [ "wlan1" ]; }; rules = { masquerade-iot = { - from = ["lan"]; - to = ["iot"]; + from = [ "lan" ]; + to = [ "iot" ]; masquerade = true; }; outbound = { - from = ["lan"]; - to = ["iot"]; + from = [ "lan" ]; + to = [ "iot" ]; late = true; # Only accept after any rejects have been processed verdict = "accept"; }; diff --git a/hosts/zackbiene/zigbee2mqtt.nix b/hosts/zackbiene/zigbee2mqtt.nix index 8fcf245..8f9a7ef 100644 --- a/hosts/zackbiene/zigbee2mqtt.nix +++ b/hosts/zackbiene/zigbee2mqtt.nix @@ -2,10 +2,12 @@ config, nodes, ... -}: let +}: +let sentinelCfg = nodes.sentinel.config; zigbeeDomain = "zigbee.${sentinelCfg.repo.secrets.global.domains.personal}"; -in { +in +{ age.secrets."mosquitto-pw-zigbee2mqtt.yaml" = { rekeyFile = ./secrets/mosquitto-pw-zigbee2mqtt.yaml.age; mode = "440"; @@ -39,7 +41,7 @@ in { services.nginx = { upstreams."zigbee2mqtt" = { - servers."localhost:8072" = {}; + servers."localhost:8072" = { }; extraConfig = '' zone zigbee2mqtt 64k; keepalive 2; diff --git a/modules/acme-wildcard.nix b/modules/acme-wildcard.nix index 318bf28..a1ce51a 100644 --- a/modules/acme-wildcard.nix +++ b/modules/acme-wildcard.nix @@ -2,9 +2,9 @@ config, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) assertMsg elem filter @@ -16,10 +16,11 @@ removeSuffix types ; -in { +in +{ options.security.acme.wildcardDomains = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; description = '' List of domains to which a wilcard certificate exists under the same name in `certs`. All of these certs will automatically have `*.` appended to `extraDomainNames`. @@ -27,32 +28,36 @@ in { }; options.services.nginx.virtualHosts = mkOption { - type = types.attrsOf (types.submodule (submod: { - options.useACMEWildcardHost = mkOption { - type = types.bool; - default = false; - description = ''Automatically set useACMEHost with the correct wildcard domain for the virtualHosts's main domain.''; - }; - config = let - # This retrieves all matching wildcard certs that would include the corresponding domain. - # If no such domain is found then an assertion is triggered. - domain = submod.config._module.args.name; - matchingCerts = - if elem domain config.security.acme.wildcardDomains - then [domain] - else - filter - (x: !hasInfix "." (removeSuffix ".${x}" domain)) - config.security.acme.wildcardDomains; - in - mkIf submod.config.useACMEWildcardHost { - useACMEHost = assert assertMsg (matchingCerts != []) "No wildcard certificate was defined that matches ${domain}"; - head matchingCerts; + type = types.attrsOf ( + types.submodule (submod: { + options.useACMEWildcardHost = mkOption { + type = types.bool; + default = false; + description = ''Automatically set useACMEHost with the correct wildcard domain for the virtualHosts's main domain.''; }; - })); + config = + let + # This retrieves all matching wildcard certs that would include the corresponding domain. + # If no such domain is found then an assertion is triggered. + domain = submod.config._module.args.name; + matchingCerts = + if elem domain config.security.acme.wildcardDomains then + [ domain ] + else + filter (x: !hasInfix "." (removeSuffix ".${x}" domain)) config.security.acme.wildcardDomains; + in + mkIf submod.config.useACMEWildcardHost { + useACMEHost = + assert assertMsg ( + matchingCerts != [ ] + ) "No wildcard certificate was defined that matches ${domain}"; + head matchingCerts; + }; + }) + ); }; config.security.acme.certs = genAttrs config.security.acme.wildcardDomains (domain: { - extraDomainNames = ["*.${domain}"]; + extraDomainNames = [ "*.${domain}" ]; }); } diff --git a/modules/actual.nix b/modules/actual.nix index aac4907..8f16fd3 100644 --- a/modules/actual.nix +++ b/modules/actual.nix @@ -3,9 +3,9 @@ pkgs, config, ... -}: let - inherit - (lib) +}: +let + inherit (lib) getExe mkEnableOption mkIf @@ -18,11 +18,12 @@ configFile = formatType.generate "config.json" cfg.settings; dataDir = "/var/lib/actual"; - formatType = pkgs.formats.json {}; -in { + formatType = pkgs.formats.json { }; +in +{ options.services.actual = { enable = mkEnableOption "actual, a privacy focused app for managing your finances"; - package = mkPackageOption pkgs "actual-server" {}; + package = mkPackageOption pkgs "actual-server" { }; user = mkOption { type = types.str; @@ -59,7 +60,7 @@ in { }; settings = mkOption { - default = {}; + default = { }; type = types.submodule { freeformType = formatType.type; @@ -87,10 +88,10 @@ in { }; config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.settings.port]; + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ]; users.groups = mkIf (cfg.group == "actual") { - ${cfg.group} = {}; + ${cfg.group} = { }; }; users.users = mkIf (cfg.user == "actual") { @@ -103,7 +104,7 @@ in { systemd.services.actual = { description = "Actual server, a local-first personal finance app"; - after = ["network.target"]; + after = [ "network.target" ]; environment.ACTUAL_CONFIG_PATH = configFile; serviceConfig = { ExecStart = getExe cfg.package; @@ -146,7 +147,7 @@ in { ]; UMask = "0077"; }; - wantedBy = ["multi-user.target"]; + wantedBy = [ "multi-user.target" ]; }; }; } diff --git a/modules/backups.nix b/modules/backups.nix index 78ed858..d37b103 100644 --- a/modules/backups.nix +++ b/modules/backups.nix @@ -3,9 +3,9 @@ globals, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) attrValues flip mkIf @@ -13,46 +13,51 @@ mkOption types ; -in { +in +{ options.backups.storageBoxes = mkOption { description = "Backups to Hetzner Storage Boxes using restic"; - default = {}; - type = types.attrsOf (types.submodule (submod: { - options = { - name = mkOption { - description = "The name of the storage box to backup to. The box must be defined in the globals. Defaults to the attribute name."; - default = submod.config._module.args.name; - type = types.str; - }; + default = { }; + type = types.attrsOf ( + types.submodule (submod: { + options = { + name = mkOption { + description = "The name of the storage box to backup to. The box must be defined in the globals. Defaults to the attribute name."; + default = submod.config._module.args.name; + type = types.str; + }; - subuser = mkOption { - description = "The name of the storage box subuser as defined in the globals, mapping this user to a subuser id."; - type = types.str; - }; + subuser = mkOption { + description = "The name of the storage box subuser as defined in the globals, mapping this user to a subuser id."; + type = types.str; + }; - paths = mkOption { - description = "The paths to backup."; - type = types.listOf types.path; + paths = mkOption { + description = "The paths to backup."; + type = types.listOf types.path; + }; }; - }; - })); + }) + ); }; - config = mkIf (config.backups.storageBoxes != {}) { + config = mkIf (config.backups.storageBoxes != { }) { age.secrets.restic-encryption-password.generator.script = "alnum"; age.secrets.restic-ssh-privkey.generator.script = "ssh-ed25519"; - services.restic.backups = mkMerge (flip map (attrValues config.backups.storageBoxes) - (boxCfg: { + services.restic.backups = mkMerge ( + flip map (attrValues config.backups.storageBoxes) (boxCfg: { "storage-box-${boxCfg.name}" = { - hetznerStorageBox = let - box = globals.hetzner.storageboxes.${boxCfg.name}; - in { - enable = true; - inherit (box) mainUser; - inherit (box.users.${boxCfg.subuser}) subUid path; - sshAgeSecret = "restic-ssh-privkey"; - }; + hetznerStorageBox = + let + box = globals.hetzner.storageboxes.${boxCfg.name}; + in + { + enable = true; + inherit (box) mainUser; + inherit (box.users.${boxCfg.subuser}) subUid path; + sshAgeSecret = "restic-ssh-privkey"; + }; # A) We need to backup stuff from other users, so run as root. # B) We also need to be root because the ssh key will only @@ -76,6 +81,7 @@ in { # "--keep-yearly 75" # ]; }; - })); + }) + ); }; } diff --git a/modules/deterministic-ids.nix b/modules/deterministic-ids.nix index e8c67db..6c23f32 100644 --- a/modules/deterministic-ids.nix +++ b/modules/deterministic-ids.nix @@ -2,9 +2,9 @@ lib, config, ... -}: let - inherit - (lib) +}: +let + inherit (lib) concatLists flip mapAttrsToList @@ -15,65 +15,86 @@ ; cfg = config.users.deterministicIds; -in { +in +{ options = { users.deterministicIds = mkOption { - default = {}; + default = { }; description = '' Maps a user or group name to its expected uid/gid values. If a user/group is used on the system without specifying a uid/gid, this module will assign the corresponding ids defined here, or show an error if the definition is missing. ''; - type = types.attrsOf (types.submodule { - options = { - uid = mkOption { - type = types.nullOr types.int; - default = null; - description = "The uid to assign if it is missing in `users.users.`."; + type = types.attrsOf ( + types.submodule { + options = { + uid = mkOption { + type = types.nullOr types.int; + default = null; + description = "The uid to assign if it is missing in `users.users.`."; + }; + gid = mkOption { + type = types.nullOr types.int; + default = null; + description = "The gid to assign if it is missing in `users.groups.`."; + }; }; - gid = mkOption { - type = types.nullOr types.int; - default = null; - description = "The gid to assign if it is missing in `users.groups.`."; - }; - }; - }); + } + ); }; users.users = mkOption { - type = types.attrsOf (types.submodule ({name, ...}: { - config.uid = let - deterministicUid = cfg.${name}.uid or null; - in - mkIf (deterministicUid != null) (mkDefault deterministicUid); - })); + type = types.attrsOf ( + types.submodule ( + { name, ... }: + { + config.uid = + let + deterministicUid = cfg.${name}.uid or null; + in + mkIf (deterministicUid != null) (mkDefault deterministicUid); + } + ) + ); }; users.groups = mkOption { - type = types.attrsOf (types.submodule ({name, ...}: { - config.gid = let - deterministicGid = cfg.${name}.gid or null; - in - mkIf (deterministicGid != null) (mkDefault deterministicGid); - })); + type = types.attrsOf ( + types.submodule ( + { name, ... }: + { + config.gid = + let + deterministicGid = cfg.${name}.gid or null; + in + mkIf (deterministicGid != null) (mkDefault deterministicGid); + } + ) + ); }; }; config = { assertions = - concatLists (flip mapAttrsToList config.users.users (name: user: [ - { - assertion = user.uid != null; - message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`"; + concatLists ( + flip mapAttrsToList config.users.users ( + name: user: [ + { + assertion = user.uid != null; + message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`"; + } + { + assertion = !user.autoSubUidGidRange; + message = "non-deterministic subUids/subGids detected for: ${name}"; + } + ] + ) + ) + ++ flip mapAttrsToList config.users.groups ( + name: group: { + assertion = group.gid != null; + message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`"; } - { - assertion = !user.autoSubUidGidRange; - message = "non-deterministic subUids/subGids detected for: ${name}"; - } - ])) - ++ flip mapAttrsToList config.users.groups (name: group: { - assertion = group.gid != null; - message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`"; - }); + ); }; } diff --git a/modules/distributed-config.nix b/modules/distributed-config.nix index 8e2b401..3cb0779 100644 --- a/modules/distributed-config.nix +++ b/modules/distributed-config.nix @@ -3,9 +3,9 @@ lib, nodes, ... -}: let - inherit - (lib) +}: +let + inherit (lib) attrNames concatMap concatStringsSep @@ -21,16 +21,20 @@ ; nodeName = config.node.name; - mkForwardedOption = path: + mkForwardedOption = + path: mkOption { type = mkOptionType { name = "Same type that the receiving option `${concatStringsSep "." path}` normally accepts."; - merge = _loc: defs: - builtins.filter - (x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) - (map (x: x.value) defs); + merge = + _loc: defs: + builtins.filter (x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) ( + map (x: x.value) defs + ); + }; + default = { + _type = "__distributed_config_empty"; }; - default = {_type = "__distributed_config_empty";}; description = '' Anything specified here will be forwarded to `${concatStringsSep "." path}` on the given node. Forwarding happens as-is to the raw values, @@ -39,31 +43,69 @@ }; forwardedOptions = [ - ["age" "secrets"] - ["networking" "nftables" "chains"] - ["services" "nginx" "upstreams"] - ["services" "nginx" "virtualHosts"] - ["services" "influxdb2" "provision" "organizations"] - ["services" "kanidm" "provision" "groups"] - ["services" "kanidm" "provision" "systems" "oauth2"] + [ + "age" + "secrets" + ] + [ + "networking" + "nftables" + "chains" + ] + [ + "services" + "nginx" + "upstreams" + ] + [ + "services" + "nginx" + "virtualHosts" + ] + [ + "services" + "influxdb2" + "provision" + "organizations" + ] + [ + "services" + "kanidm" + "provision" + "groups" + ] + [ + "services" + "kanidm" + "provision" + "systems" + "oauth2" + ] ]; - attrsForEachOption = f: foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) {} forwardedOptions; -in { + attrsForEachOption = + f: foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) { } forwardedOptions; +in +{ options.nodes = mkOption { description = "Options forwareded to the given node."; - default = {}; - type = types.attrsOf (types.submodule { - options = attrsForEachOption mkForwardedOption; - }); + default = { }; + type = types.attrsOf ( + types.submodule { + options = attrsForEachOption mkForwardedOption; + } + ); }; - config = let - getConfig = path: otherNode: let - cfg = nodes.${otherNode}.config.nodes.${nodeName} or null; + config = + let + getConfig = + path: otherNode: + let + cfg = nodes.${otherNode}.config.nodes.${nodeName} or null; + in + optionals (cfg != null) (getAttrFromPath path cfg); + mergeConfigFromOthers = path: mkMerge (concatMap (getConfig path) (attrNames nodes)); in - optionals (cfg != null) (getAttrFromPath path cfg); - mergeConfigFromOthers = path: mkMerge (concatMap (getConfig path) (attrNames nodes)); - in attrsForEachOption mergeConfigFromOthers; } diff --git a/modules/globals.nix b/modules/globals.nix index ea02557..c3b24d0 100644 --- a/modules/globals.nix +++ b/modules/globals.nix @@ -2,9 +2,9 @@ lib, options, ... -}: let - inherit - (lib) +}: +let + inherit (lib) mkOption types ; @@ -15,10 +15,11 @@ description = "The network to which this endpoint is associated."; }; }; -in { +in +{ options = { globals = mkOption { - default = {}; + default = { }; type = types.submodule { options = { root = { @@ -40,91 +41,100 @@ in { }; net = mkOption { - type = types.attrsOf (types.submodule (netSubmod: { - options = { - cidrv4 = mkOption { - type = types.nullOr types.net.cidrv4; - description = "The CIDRv4 of this network"; - default = null; + type = types.attrsOf ( + types.submodule (netSubmod: { + options = { + cidrv4 = mkOption { + type = types.nullOr types.net.cidrv4; + description = "The CIDRv4 of this network"; + default = null; + }; + + cidrv6 = mkOption { + type = types.nullOr types.net.cidrv6; + description = "The CIDRv6 of this network"; + default = null; + }; + + hosts = mkOption { + type = types.attrsOf ( + types.submodule (hostSubmod: { + options = { + id = mkOption { + type = types.int; + description = "The id of this host in the network"; + }; + + ipv4 = mkOption { + type = types.nullOr types.net.ipv4; + description = "The IPv4 of this host"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4; + }; + + ipv6 = mkOption { + type = types.nullOr types.net.ipv6; + description = "The IPv6 of this host"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6; + }; + + cidrv4 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv4 of this host including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4; + }; + + cidrv6 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv6 of this host including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6; + }; + }; + }) + ); + }; }; - - cidrv6 = mkOption { - type = types.nullOr types.net.cidrv6; - description = "The CIDRv6 of this network"; - default = null; - }; - - hosts = mkOption { - type = types.attrsOf (types.submodule (hostSubmod: { - options = { - id = mkOption { - type = types.int; - description = "The id of this host in the network"; - }; - - ipv4 = mkOption { - type = types.nullOr types.net.ipv4; - description = "The IPv4 of this host"; - readOnly = true; - default = - if netSubmod.config.cidrv4 == null - then null - else lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4; - }; - - ipv6 = mkOption { - type = types.nullOr types.net.ipv6; - description = "The IPv6 of this host"; - readOnly = true; - default = - if netSubmod.config.cidrv6 == null - then null - else lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6; - }; - - cidrv4 = mkOption { - type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part - description = "The IPv4 of this host including CIDR mask"; - readOnly = true; - default = - if netSubmod.config.cidrv4 == null - then null - else lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4; - }; - - cidrv6 = mkOption { - type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part - description = "The IPv6 of this host including CIDR mask"; - readOnly = true; - default = - if netSubmod.config.cidrv6 == null - then null - else lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6; - }; - }; - })); - }; - }; - })); + }) + ); }; services = mkOption { - type = types.attrsOf (types.submodule { - options = { - domain = mkOption { - type = types.str; - description = "The domain under which this service can be reached"; + type = types.attrsOf ( + types.submodule { + options = { + domain = mkOption { + type = types.str; + description = "The domain under which this service can be reached"; + }; }; - }; - }); + } + ); }; monitoring = { ping = mkOption { - type = types.attrsOf (types.submodule { - options = - defaultOptions - // { + type = types.attrsOf ( + types.submodule { + options = defaultOptions // { hostv4 = mkOption { type = types.nullOr types.str; description = "The IP/hostname to ping via ipv4."; @@ -137,14 +147,14 @@ in { default = null; }; }; - }); + } + ); }; http = mkOption { - type = types.attrsOf (types.submodule { - options = - defaultOptions - // { + type = types.attrsOf ( + types.submodule { + options = defaultOptions // { url = mkOption { type = types.either (types.listOf types.str) types.str; description = "The url to connect to."; @@ -168,14 +178,14 @@ in { default = false; }; }; - }); + } + ); }; dns = mkOption { - type = types.attrsOf (types.submodule { - options = - defaultOptions - // { + type = types.attrsOf ( + types.submodule { + options = defaultOptions // { server = mkOption { type = types.str; description = "The DNS server to query."; @@ -192,14 +202,14 @@ in { default = "A"; }; }; - }); + } + ); }; tcp = mkOption { - type = types.attrsOf (types.submodule { - options = - defaultOptions - // { + type = types.attrsOf ( + types.submodule { + options = defaultOptions // { host = mkOption { type = types.str; description = "The IP/hostname to connect to."; @@ -210,22 +220,25 @@ in { description = "The port to connect to."; }; }; - }); + } + ); }; }; mail = { domains = mkOption { - default = {}; + default = { }; description = "All domains on which we receive mail."; - type = types.attrsOf (types.submodule { - options = { - public = mkOption { - type = types.bool; - description = "Whether the domain should be available for use by any user"; + type = types.attrsOf ( + types.submodule { + options = { + public = mkOption { + type = types.bool; + description = "Whether the domain should be available for use by any user"; + }; }; - }; - }); + } + ); }; primary = mkOption { @@ -247,72 +260,78 @@ in { }; macs = mkOption { - default = {}; + default = { }; type = types.attrsOf types.str; description = "Known MAC addresses for external devices."; }; hetzner.storageboxes = mkOption { - default = {}; + default = { }; description = "Storage box configurations."; - type = types.attrsOf (types.submodule { - options = { - mainUser = mkOption { - type = types.str; - description = "Main username for the storagebox"; - }; + type = types.attrsOf ( + types.submodule { + options = { + mainUser = mkOption { + type = types.str; + description = "Main username for the storagebox"; + }; - users = mkOption { - default = {}; - description = "Subuser configurations."; - type = types.attrsOf (types.submodule { - options = { - subUid = mkOption { - type = types.int; - description = "The subuser id"; - }; + users = mkOption { + default = { }; + description = "Subuser configurations."; + type = types.attrsOf ( + types.submodule { + options = { + subUid = mkOption { + type = types.int; + description = "The subuser id"; + }; - path = mkOption { - type = types.str; - description = "The home path for this subuser (i.e. backup destination)"; - }; - }; - }); + path = mkOption { + type = types.str; + description = "The home path for this subuser (i.e. backup destination)"; + }; + }; + } + ); + }; }; - }; - }); + } + ); }; # Mirror of the kanidm.persons option. kanidm.persons = mkOption { description = "Provisioning of kanidm persons"; - default = {}; - type = types.attrsOf (types.submodule { - options = { - displayName = mkOption { - description = "Display name"; - type = types.str; - }; + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + displayName = mkOption { + description = "Display name"; + type = types.str; + }; - legalName = mkOption { - description = "Full legal name"; - type = types.nullOr types.str; - default = null; - }; + legalName = mkOption { + description = "Full legal name"; + type = types.nullOr types.str; + default = null; + }; - mailAddresses = mkOption { - description = "Mail addresses. First given address is considered the primary address."; - type = types.listOf types.str; - default = []; - }; + mailAddresses = mkOption { + description = "Mail addresses. First given address is considered the primary address."; + type = types.listOf types.str; + default = [ ]; + }; - groups = mkOption { - description = "List of groups this person should belong to."; - type = types.listOf types.str; - default = []; + groups = mkOption { + description = "List of groups this person should belong to."; + type = types.listOf types.str; + default = [ ]; + }; }; - }; - }); + } + ); }; }; }; diff --git a/modules/meta.nix b/modules/meta.nix index 46c62a4..bbb2528 100644 --- a/modules/meta.nix +++ b/modules/meta.nix @@ -1,10 +1,11 @@ -{lib, ...}: let - inherit - (lib) +{ lib, ... }: +let + inherit (lib) mkOption types ; -in { +in +{ options.node.secretsDir = mkOption { description = "Path to the secrets directory for this node."; type = types.path; diff --git a/modules/netbird-client.nix b/modules/netbird-client.nix index 79ddfe4..d3b24d4 100644 --- a/modules/netbird-client.nix +++ b/modules/netbird-client.nix @@ -3,9 +3,9 @@ lib, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) attrValues concatLists concatStringsSep @@ -32,8 +32,7 @@ versionOlder ; - inherit - (lib.types) + inherit (lib.types) attrsOf bool enum @@ -54,7 +53,8 @@ hardenedClients = filterAttrs (_: client: client.hardened) cfg.clients; toHardenedClientList = fn: map fn (attrValues hardenedClients); toHardenedClientAttrs = fn: mapAttrs' (_: fn) hardenedClients; -in { +in +{ meta.maintainers = with maintainers; [ misuzu thubrecht @@ -62,8 +62,11 @@ in { ]; imports = [ - (mkRemovedOptionModule ["services" "netbird" "tunnels"] - "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`") + (mkRemovedOptionModule [ + "services" + "netbird" + "tunnels" + ] "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`") ]; options.services.netbird = { @@ -85,17 +88,17 @@ in { ``` ''; }; - package = mkPackageOption pkgs "netbird" {}; + package = mkPackageOption pkgs "netbird" { }; ui.enable = mkOption { type = bool; - default = config.services.displayManager.sessionPackages != []; + default = config.services.displayManager.sessionPackages != [ ]; defaultText = literalExpression ''config.services.displayManager.sessionPackages != [ ]''; description = '' Controls presence `netbird-ui` wrappers, defaults to presence of graphical sessions. ''; }; - ui.package = mkPackageOption pkgs "netbird-ui" {}; + ui.package = mkPackageOption pkgs "netbird-ui" { }; clients = mkOption { type = attrsOf ( @@ -104,7 +107,8 @@ in { name, config, ... - }: { + }: + { options = { port = mkOption { type = port; @@ -211,18 +215,21 @@ in { wrapper = mkOption { type = package; internal = true; - default = let - makeWrapperArgs = concatLists ( - mapAttrsToList - (key: value: ["--set-default" key value]) - config.environment - ); - in + default = + let + makeWrapperArgs = concatLists ( + mapAttrsToList (key: value: [ + "--set-default" + key + value + ]) config.environment + ); + in pkgs.stdenv.mkDerivation { name = "${cfg.package.name}-wrapper-${name}"; meta.mainProgram = "netbird-${name}"; - nativeBuildInputs = with pkgs; [makeWrapper]; - phases = ["installPhase"]; + nativeBuildInputs = with pkgs; [ makeWrapper ]; + phases = [ "installPhase" ]; installPhase = concatStringsSep "\n" [ '' mkdir -p "$out/bin" @@ -246,7 +253,7 @@ in { # see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82 config = mkOption { - inherit (pkgs.formats.json {}) type; + inherit (pkgs.formats.json { }) type; defaultText = literalExpression '' { DisableAutoConnect = !config.autoStart; @@ -292,7 +299,7 @@ in { } ) ); - default = {}; + default = { }; description = '' Attribute set of Netbird client daemons, by default each one will: @@ -327,7 +334,8 @@ in { let name = "wt0"; client = cfg.clients."${name}"; - in { + in + { services.netbird.clients."${name}" = { port = mkDefault 51820; name = mkDefault "netbird"; @@ -336,36 +344,39 @@ in { }; environment.systemPackages = [ - (lib.hiPrio (pkgs.runCommand "${name}-as-default" {} '' - mkdir -p "$out/bin" - for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do - ln -s "${client.wrapper}/bin/$binary-${name}" "$out/bin/$binary" - done - '')) + (lib.hiPrio ( + pkgs.runCommand "${name}-as-default" { } '' + mkdir -p "$out/bin" + for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do + ln -s "${client.wrapper}/bin/$binary-${name}" "$out/bin/$binary" + done + '' + )) ]; } )) { - boot.extraModulePackages = - optional - (cfg.clients != {} && (versionOlder kernel.version "5.6")) - kernelPackages.wireguard; + boot.extraModulePackages = optional ( + cfg.clients != { } && (versionOlder kernel.version "5.6") + ) kernelPackages.wireguard; - environment.systemPackages = - toClientList (client: client.wrapper) - # omitted due to https://github.com/netbirdio/netbird/issues/1562 - #++ optional (cfg.clients != { }) cfg.package - # omitted due to https://github.com/netbirdio/netbird/issues/1581 - #++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package - ; + environment.systemPackages = toClientList (client: client.wrapper) + # omitted due to https://github.com/netbirdio/netbird/issues/1562 + #++ optional (cfg.clients != { }) cfg.package + # omitted due to https://github.com/netbirdio/netbird/issues/1581 + #++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package + ; networking.dhcpcd.denyInterfaces = toClientList (client: client.interface); networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}"); - networking.firewall.allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port)); + networking.firewall.allowedUDPPorts = concatLists ( + toClientList (client: optional client.openFirewall client.port) + ); - systemd.network.networks = mkIf config.networking.useNetworkd (toClientAttrs ( - client: + systemd.network.networks = mkIf config.networking.useNetworkd ( + toClientAttrs ( + client: nameValuePair "50-netbird-${client.interface}" { matchConfig = { Name = client.interface; @@ -375,22 +386,26 @@ in { ActivationPolicy = "manual"; }; } - )); + ) + ); - environment.etc = toClientAttrs (client: + environment.etc = toClientAttrs ( + client: nameValuePair "${client.name}/config.d/50-nixos.json" { text = builtins.toJSON client.config; mode = "0444"; - }); + } + ); - systemd.services = toClientAttrs (client: + systemd.services = toClientAttrs ( + client: nameValuePair client.name { description = "A WireGuard-based mesh network that connects your devices into a single private network"; - documentation = ["https://netbird.io/docs/"]; + documentation = [ "https://netbird.io/docs/" ]; - after = ["network.target"]; - wantedBy = ["multi-user.target"]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; path = optional (!config.services.resolved.enable) pkgs.openresolv; @@ -413,52 +428,59 @@ in { }; stopIfChanged = false; - }); + } + ); } # Hardening section - (mkIf (hardenedClients != {}) { - users.groups = toHardenedClientAttrs (client: nameValuePair client.name {}); - users.users = toHardenedClientAttrs (client: + (mkIf (hardenedClients != { }) { + users.groups = toHardenedClientAttrs (client: nameValuePair client.name { }); + users.users = toHardenedClientAttrs ( + client: nameValuePair client.name { isSystemUser = true; home = "/var/lib/${client.name}"; group = client.name; - }); + } + ); - systemd.services = toHardenedClientAttrs (client: - nameValuePair client.name (mkIf client.hardened { - serviceConfig = { - RuntimeDirectoryMode = "0750"; + systemd.services = toHardenedClientAttrs ( + client: + nameValuePair client.name ( + mkIf client.hardened { + serviceConfig = { + RuntimeDirectoryMode = "0750"; - User = client.name; - Group = client.name; + User = client.name; + Group = client.name; - # settings implied by DynamicUser=true, without actully using it, - # see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser= - RemoveIPC = true; - PrivateTmp = true; - ProtectSystem = "strict"; - ProtectHome = "yes"; + # settings implied by DynamicUser=true, without actully using it, + # see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser= + RemoveIPC = true; + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = "yes"; - AmbientCapabilities = - [ - # see https://man7.org/linux/man-pages/man7/capabilities.7.html - # see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker - # - # seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE - # CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required, - # see https://github.com/netbirdio/netbird/pull/1513 + AmbientCapabilities = + [ + # see https://man7.org/linux/man-pages/man7/capabilities.7.html + # see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker + # + # seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE + # CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required, + # see https://github.com/netbirdio/netbird/pull/1513 - # failed creating tunnel interface wt-priv: [operation not permitted - "CAP_NET_ADMIN" - # failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted - "CAP_NET_RAW" - ] - # required for eBPF filter, used to be subset of CAP_SYS_ADMIN - ++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF" - ++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN"; - }; - })); + # failed creating tunnel interface wt-priv: [operation not permitted + "CAP_NET_ADMIN" + # failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted + "CAP_NET_RAW" + ] + # required for eBPF filter, used to be subset of CAP_SYS_ADMIN + ++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF" + ++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN"; + }; + } + ) + ); # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43 security.polkit.extraConfig = mkIf config.services.resolved.enable '' @@ -478,13 +500,23 @@ in { }) # migration & temporary fixups section { - systemd.services = toClientAttrs (client: + systemd.services = toClientAttrs ( + client: nameValuePair client.name { preStart = '' set -eEuo pipefail ${optionalString (client.logLevel == "trace" || client.logLevel == "debug") "set -x"} - PATH="${makeBinPath (with pkgs; [coreutils jq diffutils])}:$PATH" + PATH="${ + makeBinPath ( + with pkgs; + [ + coreutils + jq + diffutils + ] + ) + }:$PATH" export ${toShellVars client.environment} # merge /etc/${client.name}/config.d' into "$NB_CONFIG" @@ -507,7 +539,8 @@ in { fi } ''; - }); + } + ); } ]; } diff --git a/modules/nginx-upstream-monitoring.nix b/modules/nginx-upstream-monitoring.nix index eb3dd53..219c7ec 100644 --- a/modules/nginx-upstream-monitoring.nix +++ b/modules/nginx-upstream-monitoring.nix @@ -2,9 +2,9 @@ config, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) attrNames filterAttrs flip @@ -13,69 +13,71 @@ nameValuePair types ; -in { +in +{ options.services.nginx.upstreams = mkOption { - type = types.attrsOf (types.submodule { - options.monitoring = { - enable = mkOption { - type = types.bool; - description = "Whether to add a global monitoring entry for this upstream"; - default = false; - }; + type = types.attrsOf ( + types.submodule { + options.monitoring = { + enable = mkOption { + type = types.bool; + description = "Whether to add a global monitoring entry for this upstream"; + default = false; + }; - path = mkOption { - type = types.str; - description = "The path to query."; - default = ""; - }; + path = mkOption { + type = types.str; + description = "The path to query."; + default = ""; + }; - expectedStatus = mkOption { - type = types.int; - default = 200; - description = "The HTTP status code to expect."; - }; + expectedStatus = mkOption { + type = types.int; + default = 200; + description = "The HTTP status code to expect."; + }; - expectedBodyRegex = mkOption { - type = types.nullOr types.str; - description = "A regex pattern to expect in the body."; - default = null; - }; + expectedBodyRegex = mkOption { + type = types.nullOr types.str; + description = "A regex pattern to expect in the body."; + default = null; + }; - useHttps = mkOption { - type = types.bool; - description = "Whether to use https to connect to this upstream when monitoring"; - default = false; - }; + useHttps = mkOption { + type = types.bool; + description = "Whether to use https to connect to this upstream when monitoring"; + default = false; + }; - skipTlsVerification = mkOption { - type = types.bool; - description = "Skip tls verification when using https."; - default = false; + skipTlsVerification = mkOption { + type = types.bool; + description = "Skip tls verification when using https."; + default = false; + }; }; - }; - }); + } + ); }; - config = let - monitoredUpstreams = filterAttrs (_: x: x.monitoring.enable) config.services.nginx.upstreams; - in { - globals.monitoring.http = flip mapAttrs' monitoredUpstreams ( - upstreamName: upstream: let - schema = - if upstream.monitoring.useHttps - then "https" - else "http"; - in + config = + let + monitoredUpstreams = filterAttrs (_: x: x.monitoring.enable) config.services.nginx.upstreams; + in + { + globals.monitoring.http = flip mapAttrs' monitoredUpstreams ( + upstreamName: upstream: + let + schema = if upstream.monitoring.useHttps then "https" else "http"; + in nameValuePair "${config.node.name}-upstream-${upstreamName}" { url = map (server: "${schema}://${server}${upstream.monitoring.path}") (attrNames upstream.servers); network = "local-${config.node.name}"; - inherit - (upstream.monitoring) + inherit (upstream.monitoring) expectedBodyRegex expectedStatus skipTlsVerification ; } - ); - }; + ); + }; } diff --git a/modules/oauth2-proxy.nix b/modules/oauth2-proxy.nix index 5c6156d..ebf5453 100644 --- a/modules/oauth2-proxy.nix +++ b/modules/oauth2-proxy.nix @@ -2,9 +2,9 @@ lib, config, ... -}: let - inherit - (lib) +}: +let + inherit (lib) concatStringsSep mkDefault mkEnableOption @@ -15,7 +15,8 @@ ; cfg = config.meta.oauth2-proxy; -in { +in +{ options.meta.oauth2-proxy = { enable = mkEnableOption "oauth2 proxy"; @@ -31,85 +32,93 @@ in { }; options.services.nginx.virtualHosts = mkOption { - type = types.attrsOf (types.submodule ({config, ...}: { - options.oauth2 = { - enable = mkEnableOption "access protection of this resource using oauth2-proxy."; - allowedGroups = mkOption { - type = types.listOf types.str; - default = []; - description = '' - A list of groups that are allowed to access this resource, or the - empty list to allow any authenticated client. - ''; - }; - X-User = mkOption { - type = types.str; - default = "$upstream_http_x_auth_request_preferred_username"; - description = "The variable to set as X-User"; - }; - X-Email = mkOption { - type = types.str; - default = "$upstream_http_x_auth_request_email"; - description = "The variable to set as X-User"; - }; - }; - options.locations = mkOption { - type = types.attrsOf (types.submodule (locationSubmod: { - options.setOauth2Headers = mkOption { - type = types.bool; - default = true; - description = "Whether to add oauth2 specific headers to this location. Only takes effect is oauth2 is actually enabled on the parent vhost."; + type = types.attrsOf ( + types.submodule ( + { config, ... }: + { + options.oauth2 = { + enable = mkEnableOption "access protection of this resource using oauth2-proxy."; + allowedGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + A list of groups that are allowed to access this resource, or the + empty list to allow any authenticated client. + ''; + }; + X-User = mkOption { + type = types.str; + default = "$upstream_http_x_auth_request_preferred_username"; + description = "The variable to set as X-User"; + }; + X-Email = mkOption { + type = types.str; + default = "$upstream_http_x_auth_request_email"; + description = "The variable to set as X-User"; + }; }; - config = mkIf (config.oauth2.enable && locationSubmod.config.setOauth2Headers) { + options.locations = mkOption { + type = types.attrsOf ( + types.submodule (locationSubmod: { + options.setOauth2Headers = mkOption { + type = types.bool; + default = true; + description = "Whether to add oauth2 specific headers to this location. Only takes effect is oauth2 is actually enabled on the parent vhost."; + }; + config = mkIf (config.oauth2.enable && locationSubmod.config.setOauth2Headers) { + extraConfig = '' + proxy_set_header X-User $user; + proxy_set_header X-Email $email; + add_header Set-Cookie $auth_cookie; + ''; + }; + }) + ); + }; + config = mkIf config.oauth2.enable { extraConfig = '' - proxy_set_header X-User $user; - proxy_set_header X-Email $email; - add_header Set-Cookie $auth_cookie; + auth_request /oauth2/auth; + error_page 401 = @redirectToAuth2ProxyLogin; + + # set variables that can be used in locations..extraConfig + # pass information via X-User and X-Email headers to backend, + # requires running with --set-xauthrequest flag + auth_request_set $user ${config.oauth2.X-User}; + auth_request_set $email ${config.oauth2.X-Email}; + # if you enabled --cookie-refresh, this is needed for it to work with auth_request + auth_request_set $auth_cookie $upstream_http_set_cookie; ''; + + locations."@redirectToAuth2ProxyLogin" = { + # FIXME: allow referring to another node for the portaldomain + setOauth2Headers = false; + return = "307 https://${cfg.portalDomain}/oauth2/start?rd=$scheme://$host$request_uri"; + extraConfig = '' + auth_request off; + ''; + }; + + locations."= /oauth2/auth" = { + setOauth2Headers = false; + proxyPass = + "http://oauth2-proxy/oauth2/auth" + + optionalString ( + config.oauth2.allowedGroups != [ ] + ) "?allowed_groups=${concatStringsSep "," config.oauth2.allowedGroups}"; + extraConfig = '' + auth_request off; + internal; + + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; }; - })); - }; - config = mkIf config.oauth2.enable { - extraConfig = '' - auth_request /oauth2/auth; - error_page 401 = @redirectToAuth2ProxyLogin; - - # set variables that can be used in locations..extraConfig - # pass information via X-User and X-Email headers to backend, - # requires running with --set-xauthrequest flag - auth_request_set $user ${config.oauth2.X-User}; - auth_request_set $email ${config.oauth2.X-Email}; - # if you enabled --cookie-refresh, this is needed for it to work with auth_request - auth_request_set $auth_cookie $upstream_http_set_cookie; - ''; - - locations."@redirectToAuth2ProxyLogin" = { - # FIXME: allow referring to another node for the portaldomain - setOauth2Headers = false; - return = "307 https://${cfg.portalDomain}/oauth2/start?rd=$scheme://$host$request_uri"; - extraConfig = '' - auth_request off; - ''; - }; - - locations."= /oauth2/auth" = { - setOauth2Headers = false; - proxyPass = - "http://oauth2-proxy/oauth2/auth" - + optionalString (config.oauth2.allowedGroups != []) - "?allowed_groups=${concatStringsSep "," config.oauth2.allowedGroups}"; - extraConfig = '' - auth_request off; - internal; - - proxy_set_header X-Scheme $scheme; - # nginx auth_request includes headers but not body - proxy_set_header Content-Length ""; - proxy_pass_request_body off; - ''; - }; - }; - })); + } + ) + ); }; config = mkIf cfg.enable { @@ -149,11 +158,11 @@ in { RestartSec = "60"; # Retry every minute }; - users.groups.oauth2-proxy.members = ["nginx"]; + users.groups.oauth2-proxy.members = [ "nginx" ]; services.nginx = { upstreams.oauth2-proxy = { - servers."unix:/run/oauth2-proxy/oauth2-proxy.sock" = {}; + servers."unix:/run/oauth2-proxy/oauth2-proxy.sock" = { }; extraConfig = '' zone oauth2-proxy 64k; keepalive 2; diff --git a/modules/promtail.nix b/modules/promtail.nix index 6a59ebf..34484e0 100644 --- a/modules/promtail.nix +++ b/modules/promtail.nix @@ -5,9 +5,9 @@ nodes, globals, ... -}: let - inherit - (lib) +}: +let + inherit (lib) mkEnableOption mkIf mkOption @@ -15,7 +15,8 @@ ; cfg = config.meta.promtail; -in { +in +{ options.meta.promtail = { enable = mkEnableOption "promtail to push logs to a loki instance."; # TODO: FIXME: this should not be named proxy. get domain from globals and name this secretAggregatorNode or smth. @@ -114,15 +115,15 @@ in { ]; relabel_configs = [ { - source_labels = ["__journal__hostname"]; + source_labels = [ "__journal__hostname" ]; target_label = "host"; } { - source_labels = ["__journal_priority"]; + source_labels = [ "__journal_priority" ]; target_label = "priority"; } { - source_labels = ["__journal_priority_keyword"]; + source_labels = [ "__journal_priority_keyword" ]; target_label = "level"; } #{ @@ -130,15 +131,15 @@ in { # target_label = "unit"; #} { - source_labels = ["__journal__systemd_user_unit"]; + source_labels = [ "__journal__systemd_user_unit" ]; target_label = "user_unit"; } { - source_labels = ["__journal__boot_id"]; + source_labels = [ "__journal__boot_id" ]; target_label = "boot_id"; } { - source_labels = ["__journal__comm"]; + source_labels = [ "__journal__comm" ]; target_label = "command"; } ]; diff --git a/modules/secrets.nix b/modules/secrets.nix index 40ddb4f..2adc82d 100644 --- a/modules/secrets.nix +++ b/modules/secrets.nix @@ -3,9 +3,9 @@ inputs, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) assertMsg literalExpression mapAttrs @@ -16,29 +16,31 @@ # If the given expression is a bare set, it will be wrapped in a function, # so that the imported file can always be applied to the inputs, similar to # how modules can be functions or sets. - constSet = x: - if builtins.isAttrs x - then (_: x) - else x; + constSet = x: if builtins.isAttrs x then (_: x) else x; # Try to access the extra builtin we loaded via nix-plugins. # Throw an error if that doesn't exist. - rageImportEncrypted = assert assertMsg (builtins ? extraBuiltins.rageImportEncrypted) "The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?"; + rageImportEncrypted = + assert assertMsg (builtins ? extraBuiltins.rageImportEncrypted) + "The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?"; builtins.extraBuiltins.rageImportEncrypted; # This "imports" an encrypted .nix.age file by evaluating the decrypted content. - importEncrypted = path: + importEncrypted = + path: constSet ( - if builtins.pathExists path - then rageImportEncrypted inputs.self.secretsConfig.masterIdentities path - else {} + if builtins.pathExists path then + rageImportEncrypted inputs.self.secretsConfig.masterIdentities path + else + { } ); cfg = config.repo; -in { +in +{ options.repo = { secretFiles = mkOption { - default = {}; + default = { }; type = types.attrsOf types.path; example = literalExpression "{ local = ./secrets.nix.age; }"; description = '' diff --git a/modules/telegraf.nix b/modules/telegraf.nix index 6c17898..57e97cc 100644 --- a/modules/telegraf.nix +++ b/modules/telegraf.nix @@ -6,9 +6,9 @@ nodes, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) concatLists elem flip @@ -26,8 +26,9 @@ ; cfg = config.meta.telegraf; - mkIfNotEmpty = xs: mkIf (xs != []) xs; -in { + mkIfNotEmpty = xs: mkIf (xs != [ ]) xs; +in +{ options.meta.telegraf = { enable = mkEnableOption "telegraf to push metrics to influx."; @@ -39,7 +40,7 @@ in { secrets = mkOption { type = types.attrsOf types.path; - default = {}; + default = { }; example = { "@INFLUX_TOKEN@" = "/run/agenix/influx-token"; }; @@ -48,7 +49,7 @@ in { availableMonitoringNetworks = mkOption { type = types.listOf types.str; - example = ["internet"]; + example = [ "internet" ]; description = '' Any of the global monitoring definitions which has a network from this list will automatically be monitored via telegraf. Set this to any networks that @@ -88,7 +89,7 @@ in { config = mkIf (!minimal && cfg.enable) { # Monitor anything that can only be monitored from this node - meta.telegraf.availableMonitoringNetworks = ["local-${config.node.name}"]; + meta.telegraf.availableMonitoringNetworks = [ "local-${config.node.name}" ]; assertions = [ { @@ -106,9 +107,10 @@ in { }; services.influxdb2.provision.organizations.machines.auths."telegraf (${config.node.name})" = { - readBuckets = ["telegraf"]; - writeBuckets = ["telegraf"]; - tokenFile = nodes.${cfg.influxdb2.node}.config.age.secrets."telegraf-influxdb-token-${config.node.name}".path; + readBuckets = [ "telegraf" ]; + writeBuckets = [ "telegraf" ]; + tokenFile = + nodes.${cfg.influxdb2.node}.config.age.secrets."telegraf-influxdb-token-${config.node.name}".path; }; }; @@ -121,28 +123,32 @@ in { meta.telegraf.secrets."@INFLUX_TOKEN@" = config.age.secrets.telegraf-influxdb-token.path; security.elewrap.telegraf-sensors = mkIf cfg.scrapeSensors { - command = ["${pkgs.lm_sensors}/bin/sensors" "-A" "-u"]; + command = [ + "${pkgs.lm_sensors}/bin/sensors" + "-A" + "-u" + ]; targetUser = "root"; - allowedUsers = ["telegraf"]; + allowedUsers = [ "telegraf" ]; }; security.elewrap.telegraf-nvme = mkIf config.services.smartd.enable { - command = ["${pkgs.nvme-cli}/bin/nvme"]; + command = [ "${pkgs.nvme-cli}/bin/nvme" ]; targetUser = "root"; - allowedUsers = ["telegraf"]; + allowedUsers = [ "telegraf" ]; passArguments = true; }; security.elewrap.telegraf-smartctl = mkIf config.services.smartd.enable { - command = ["${pkgs.smartmontools}/bin/smartctl"]; + command = [ "${pkgs.smartmontools}/bin/smartctl" ]; targetUser = "root"; - allowedUsers = ["telegraf"]; + allowedUsers = [ "telegraf" ]; passArguments = true; }; services.telegraf = { enable = true; - environmentFiles = ["/dev/null"]; # Needed so the config file is copied to /run/telegraf + environmentFiles = [ "/dev/null" ]; # Needed so the config file is copied to /run/telegraf extraConfig = { agent = { interval = "10s"; @@ -158,112 +164,135 @@ in { }; outputs = { influxdb_v2 = { - urls = ["https://${cfg.influxdb2.domain}"]; + urls = [ "https://${cfg.influxdb2.domain}" ]; token = "@INFLUX_TOKEN@"; inherit (cfg.influxdb2) organization bucket; }; }; inputs = { - conntrack = {}; - cpu = {}; - disk = {}; - diskio = {}; - internal = {}; - interrupts = {}; - kernel = {}; - kernel_vmstat = {}; - linux_sysctl_fs = {}; - mem = {}; + conntrack = { }; + cpu = { }; + disk = { }; + diskio = { }; + internal = { }; + interrupts = { }; + kernel = { }; + kernel_vmstat = { }; + linux_sysctl_fs = { }; + mem = { }; net = { ignore_protocol_stats = true; }; - netstat = {}; - nstat = {}; - processes = {}; - swap = {}; - system = {}; + netstat = { }; + nstat = { }; + processes = { }; + swap = { }; + system = { }; systemd_units = { unittype = "service"; }; - temp = {}; - wireguard = {}; + temp = { }; + wireguard = { }; - ping = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.ping ( - name: pingCfg: - optionals (elem pingCfg.network cfg.availableMonitoringNetworks) ( - concatLists (forEach ["hostv4" "hostv6"] ( - attr: - optional (pingCfg.${attr} != null) { - interval = "1m"; - method = "native"; - urls = [pingCfg.${attr}]; - ipv4 = attr == "hostv4"; - ipv6 = attr == "hostv6"; - tags = { - inherit name; - inherit (pingCfg) network; - ip_version = - if attr == "hostv4" - then "v4" - else "v6"; - }; - fieldinclude = [ - "percent_packet_loss" - "average_response_ms" - ]; - } - )) + ping = mkIfNotEmpty ( + concatLists ( + flip mapAttrsToList globals.monitoring.ping ( + name: pingCfg: + optionals (elem pingCfg.network cfg.availableMonitoringNetworks) ( + concatLists ( + forEach + [ + "hostv4" + "hostv6" + ] + ( + attr: + optional (pingCfg.${attr} != null) { + interval = "1m"; + method = "native"; + urls = [ pingCfg.${attr} ]; + ipv4 = attr == "hostv4"; + ipv6 = attr == "hostv6"; + tags = { + inherit name; + inherit (pingCfg) network; + ip_version = if attr == "hostv4" then "v4" else "v6"; + }; + fieldinclude = [ + "percent_packet_loss" + "average_response_ms" + ]; + } + ) + ) + ) ) - ))); + ) + ); - http_response = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.http ( - name: httpCfg: - optional (elem httpCfg.network cfg.availableMonitoringNetworks) { - interval = "1m"; - urls = toList httpCfg.url; - method = "GET"; - response_status_code = httpCfg.expectedStatus; - response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex; - insecure_skip_verify = httpCfg.skipTlsVerification; - follow_redirects = true; - tags = { - inherit name; - inherit (httpCfg) network; - }; - } - ))); + http_response = mkIfNotEmpty ( + concatLists ( + flip mapAttrsToList globals.monitoring.http ( + name: httpCfg: + optional (elem httpCfg.network cfg.availableMonitoringNetworks) { + interval = "1m"; + urls = toList httpCfg.url; + method = "GET"; + response_status_code = httpCfg.expectedStatus; + response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex; + insecure_skip_verify = httpCfg.skipTlsVerification; + follow_redirects = true; + tags = { + inherit name; + inherit (httpCfg) network; + }; + } + ) + ) + ); - dns_query = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.dns ( - name: dnsCfg: - optional (elem dnsCfg.network cfg.availableMonitoringNetworks) { - interval = "1m"; - servers = [dnsCfg.server]; - domains = [dnsCfg.domain]; - record_type = dnsCfg.record-type; - tags = { - inherit name; - inherit (dnsCfg) network; - }; - } - ))); + dns_query = mkIfNotEmpty ( + concatLists ( + flip mapAttrsToList globals.monitoring.dns ( + name: dnsCfg: + optional (elem dnsCfg.network cfg.availableMonitoringNetworks) { + interval = "1m"; + servers = [ dnsCfg.server ]; + domains = [ dnsCfg.domain ]; + record_type = dnsCfg.record-type; + tags = { + inherit name; + inherit (dnsCfg) network; + }; + } + ) + ) + ); - net_response = mkIfNotEmpty (concatLists (flip mapAttrsToList globals.monitoring.tcp ( - name: tcpCfg: - optional (elem tcpCfg.network cfg.availableMonitoringNetworks) { - interval = "1m"; - address = "${tcpCfg.host}:${toString tcpCfg.port}"; - protocol = "tcp"; - tags = { - inherit name; - inherit (tcpCfg) network; - }; - fieldexclude = ["result_type" "string_found"]; - } - ))); + net_response = mkIfNotEmpty ( + concatLists ( + flip mapAttrsToList globals.monitoring.tcp ( + name: tcpCfg: + optional (elem tcpCfg.network cfg.availableMonitoringNetworks) { + interval = "1m"; + address = "${tcpCfg.host}:${toString tcpCfg.port}"; + protocol = "tcp"; + tags = { + inherit name; + inherit (tcpCfg) network; + }; + fieldexclude = [ + "result_type" + "string_found" + ]; + } + ) + ) + ); } // optionalAttrs config.services.smartd.enable { - sensors = {}; + sensors = { }; smart = { attributes = true; path_nvme = config.security.elewrap.telegraf-nvme.path; @@ -272,16 +301,19 @@ in { }; } // optionalAttrs config.services.nginx.enable { - nginx.urls = ["http://localhost/nginx_status"]; + nginx.urls = [ "http://localhost/nginx_status" ]; } // optionalAttrs (config.networking.wireless.enable || config.networking.wireless.iwd.enable) { - wireless = {}; + wireless = { }; }; }; }; services.nginx.virtualHosts = mkIf config.services.nginx.enable { - localhost.listenAddresses = ["127.0.0.1" "[::1]"]; + localhost.listenAddresses = [ + "127.0.0.1" + "[::1]" + ]; localhost.locations."= /nginx_status".extraConfig = '' allow 127.0.0.0/8; allow ::1; @@ -303,13 +335,14 @@ in { systemd.services.telegraf = { path = [ # Make sensors refer to the correct wrapper - (mkIf cfg.scrapeSensors - (pkgs.writeShellScriptBin "sensors" config.security.elewrap.telegraf-sensors.path)) + (mkIf cfg.scrapeSensors ( + pkgs.writeShellScriptBin "sensors" config.security.elewrap.telegraf-sensors.path + )) ]; serviceConfig = { ExecStartPre = mkAfter [ - ( - pkgs.writeShellScript "pre-start-token" (lib.concatLines ( + (pkgs.writeShellScript "pre-start-token" ( + lib.concatLines ( lib.flip lib.mapAttrsToList config.meta.telegraf.secrets ( key: secret: '' ${lib.getExe pkgs.replace-secret} \ @@ -318,11 +351,11 @@ in { /var/run/telegraf/config.toml '' ) - )) - ) + ) + )) ]; # For wireguard statistics - AmbientCapabilities = ["CAP_NET_ADMIN"]; + AmbientCapabilities = [ "CAP_NET_ADMIN" ]; RestartSec = "60"; # Retry every minute }; }; diff --git a/nix/agenix-rekey.nix b/nix/agenix-rekey.nix index 36bbe56..ee6e08b 100644 --- a/nix/agenix-rekey.nix +++ b/nix/agenix-rekey.nix @@ -2,7 +2,8 @@ inputs, self, ... -}: { +}: +{ imports = [ inputs.agenix-rekey.flakeModule ]; @@ -11,27 +12,29 @@ # The identities that are used to rekey agenix secrets and to # decrypt all repository-wide secrets. secretsConfig = { - masterIdentities = [../secrets/yk1-nix-rage.pub]; - extraEncryptionPubkeys = [../secrets/backup.pub]; + masterIdentities = [ ../secrets/yk1-nix-rage.pub ]; + extraEncryptionPubkeys = [ ../secrets/backup.pub ]; }; }; - perSystem = {config, ...}: { - agenix-rekey.nixosConfigurations = self.nodes; - devshells.default = { - commands = [ - { - inherit (config.agenix-rekey) package; - help = "Edit, generate and rekey secrets"; - } - ]; - env = [ - { - # Always add files to git after agenix rekey and agenix generate. - name = "AGENIX_REKEY_ADD_TO_GIT"; - value = "true"; - } - ]; + perSystem = + { config, ... }: + { + agenix-rekey.nixosConfigurations = self.nodes; + devshells.default = { + commands = [ + { + inherit (config.agenix-rekey) package; + help = "Edit, generate and rekey secrets"; + } + ]; + env = [ + { + # Always add files to git after agenix rekey and agenix generate. + name = "AGENIX_REKEY_ADD_TO_GIT"; + value = "true"; + } + ]; + }; }; - }; } diff --git a/nix/extra-builtins.nix b/nix/extra-builtins.nix index 4889cb1..74752f7 100644 --- a/nix/extra-builtins.nix +++ b/nix/extra-builtins.nix @@ -14,19 +14,32 @@ # ''; # } # ``` -{exec, ...}: let +{ exec, ... }: +let assertMsg = pred: msg: pred || builtins.throw msg; - hasSuffix = suffix: content: let - lenContent = builtins.stringLength content; - lenSuffix = builtins.stringLength suffix; - in + hasSuffix = + suffix: content: + let + lenContent = builtins.stringLength content; + lenSuffix = builtins.stringLength suffix; + in lenContent >= lenSuffix && builtins.substring (lenContent - lenSuffix) lenContent content == suffix; -in { +in +{ # Instead of calling rage directly here, we call a wrapper script that will cache the output # in a predictable path in /tmp, which allows us to only require the password for each encrypted # file once. - rageImportEncrypted = identities: nixFile: - assert assertMsg (builtins.isPath nixFile) "The file to decrypt must be given as a path to prevent impurity."; - assert assertMsg (hasSuffix ".nix.age" nixFile) "The content of the decrypted file must be a nix expression and should therefore end in .nix.age"; - exec ([./rage-decrypt-and-cache.sh nixFile] ++ identities); + rageImportEncrypted = + identities: nixFile: + assert assertMsg (builtins.isPath nixFile) + "The file to decrypt must be given as a path to prevent impurity."; + assert assertMsg (hasSuffix ".nix.age" nixFile) + "The content of the decrypted file must be a nix expression and should therefore end in .nix.age"; + exec ( + [ + ./rage-decrypt-and-cache.sh + nixFile + ] + ++ identities + ); } diff --git a/nix/globals.nix b/nix/globals.nix index 6a38bb1..9b7cc9b 100644 --- a/nix/globals.nix +++ b/nix/globals.nix @@ -1,46 +1,54 @@ -{inputs, ...}: { - flake = { - config, - lib, - ... - }: { - globals = let - globalsSystem = lib.evalModules { - prefix = ["globals"]; - specialArgs = { - inherit (inputs.self.pkgs.x86_64-linux) lib; - inherit inputs; +{ inputs, ... }: +{ + flake = + { + config, + lib, + ... + }: + { + globals = + let + globalsSystem = lib.evalModules { + prefix = [ "globals" ]; + specialArgs = { + inherit (inputs.self.pkgs.x86_64-linux) lib; + inherit inputs; + }; + modules = [ + ../modules/globals.nix + ../globals.nix + ( + { lib, ... }: + { + globals = lib.mkMerge ( + lib.concatLists ( + lib.flip lib.mapAttrsToList config.nodes ( + name: cfg: + builtins.addErrorContext "while aggregating globals from nixosConfigurations.${name} into flake-level globals:" cfg.config._globalsDefs + ) + ) + ); + } + ) + ]; + }; + in + { + # Make sure the keys of this attrset are trivially evaluatable to avoid infinite recursion, + # therefore we inherit relevant attributes from the config. + inherit (globalsSystem.config.globals) + domains + hetzner + kanidm + macs + mail + monitoring + myuser + net + root + services + ; }; - modules = [ - ../modules/globals.nix - ../globals.nix - ({lib, ...}: { - globals = lib.mkMerge ( - lib.concatLists (lib.flip lib.mapAttrsToList config.nodes ( - name: cfg: - builtins.addErrorContext "while aggregating globals from nixosConfigurations.${name} into flake-level globals:" - cfg.config._globalsDefs - )) - ); - }) - ]; - }; - in { - # Make sure the keys of this attrset are trivially evaluatable to avoid infinite recursion, - # therefore we inherit relevant attributes from the config. - inherit - (globalsSystem.config.globals) - domains - hetzner - kanidm - macs - mail - monitoring - myuser - net - root - services - ; }; - }; } diff --git a/nix/hosts.nix b/nix/hosts.nix index c5b9361..1055a02 100644 --- a/nix/hosts.nix +++ b/nix/hosts.nix @@ -1,37 +1,40 @@ -{inputs, ...}: { - flake = { - config, - lib, - ... - }: let - inherit - (lib) - concatMapAttrs - filterAttrs - flip - genAttrs - mapAttrs - mapAttrs' - nameValuePair - ; +{ inputs, ... }: +{ + flake = + { + config, + lib, + ... + }: + let + inherit (lib) + concatMapAttrs + filterAttrs + flip + genAttrs + mapAttrs + mapAttrs' + nameValuePair + ; - # Creates a new nixosSystem with the correct specialArgs, pkgs and name definition - mkHost = {minimal}: name: let - pkgs = config.pkgs.x86_64-linux; # FIXME: NOOOOOOOOOOOOOOOOOOOOOOO - in - inputs.nixpkgs.lib.nixosSystem { - specialArgs = { - # Use the correct instance lib that has our overlays - inherit (pkgs) lib; - inherit (config) nodes globals; - inherit inputs minimal; - }; - modules = [ - { - nixpkgs.config.allowUnfree = true; - nixpkgs.overlays = - (import ../pkgs/default.nix inputs) - ++ [ + # Creates a new nixosSystem with the correct specialArgs, pkgs and name definition + mkHost = + { minimal }: + name: + let + pkgs = config.pkgs.x86_64-linux; # FIXME: NOOOOOOOOOOOOOOOOOOOOOOO + in + inputs.nixpkgs.lib.nixosSystem { + specialArgs = { + # Use the correct instance lib that has our overlays + inherit (pkgs) lib; + inherit (config) nodes globals; + inherit inputs minimal; + }; + modules = [ + { + nixpkgs.config.allowUnfree = true; + nixpkgs.overlays = (import ../pkgs/default.nix inputs) ++ [ inputs.idmail.overlays.default # inputs.nixos-cosmic.overlays.default inputs.nix-topology.overlays.default @@ -40,43 +43,51 @@ inputs.wired-notify.overlays.default ]; - node.name = name; - node.secretsDir = ../hosts/${name}/secrets; - } - ../hosts/${name} - ]; - }; + node.name = name; + node.secretsDir = ../hosts/${name}/secrets; + } + ../hosts/${name} + ]; + }; - # Get all folders in hosts/ - hosts = builtins.attrNames (filterAttrs (_: type: type == "directory") (builtins.readDir ../hosts)); - in { - nixosConfigurations = genAttrs hosts (mkHost {minimal = false;}); - nixosConfigurationsMinimal = genAttrs hosts (mkHost {minimal = true;}); + # Get all folders in hosts/ + hosts = builtins.attrNames (filterAttrs (_: type: type == "directory") (builtins.readDir ../hosts)); + in + { + nixosConfigurations = genAttrs hosts (mkHost { + minimal = false; + }); + nixosConfigurationsMinimal = genAttrs hosts (mkHost { + minimal = true; + }); - # True NixOS nodes can define additional guest nodes that are built - # together with it. We collect all defined guests from each node here - # to allow accessing any node via the unified attribute `nodes`. - guestConfigs = flip concatMapAttrs config.nixosConfigurations (_: node: - flip mapAttrs' (node.config.guests or {}) ( - guestName: guestDef: + # True NixOS nodes can define additional guest nodes that are built + # together with it. We collect all defined guests from each node here + # to allow accessing any node via the unified attribute `nodes`. + guestConfigs = flip concatMapAttrs config.nixosConfigurations ( + _: node: + flip mapAttrs' (node.config.guests or { }) ( + guestName: guestDef: nameValuePair guestDef.nodeName ( - if guestDef.backend == "microvm" - then node.config.microvm.vms.${guestName}.config - else node.config.containers.${guestName}.nixosConfiguration + if guestDef.backend == "microvm" then + node.config.microvm.vms.${guestName}.config + else + node.config.containers.${guestName}.nixosConfiguration ) - )); + ) + ); - # All nixosSystem instanciations are collected here, so that we can refer - # to any system via nodes. - nodes = config.nixosConfigurations // config.guestConfigs; - # Add a shorthand to easily target toplevel derivations - "@" = mapAttrs (_: v: v.config.system.build.toplevel) config.nodes; + # All nixosSystem instanciations are collected here, so that we can refer + # to any system via nodes. + nodes = config.nixosConfigurations // config.guestConfigs; + # Add a shorthand to easily target toplevel derivations + "@" = mapAttrs (_: v: v.config.system.build.toplevel) config.nodes; - # Pre-evaluate the wireguard network information to avoid recalculating it - # for every host and every location it is used. - wireguardEvalCache = config.pkgs.x86_64-linux.lib.wireguard.createEvalCache inputs [ - "proxy-sentinel" - "proxy-home" - ]; - }; + # Pre-evaluate the wireguard network information to avoid recalculating it + # for every host and every location it is used. + wireguardEvalCache = config.pkgs.x86_64-linux.lib.wireguard.createEvalCache inputs [ + "proxy-sentinel" + "proxy-home" + ]; + }; } diff --git a/nix/installer-configuration.nix b/nix/installer-configuration.nix index 89c6bee..903fabc 100644 --- a/nix/installer-configuration.nix +++ b/nix/installer-configuration.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ system.stateVersion = "24.11"; nix.extraOptions = '' experimental-features = nix-command flakes @@ -10,7 +11,9 @@ users.users.root = { password = "nixos"; - openssh.authorizedKeys.keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA5Uq+CDy5Pmt3If5M6d8K/Q7HArU6sZ7sgoj3T521Wm"]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA5Uq+CDy5Pmt3If5M6d8K/Q7HArU6sZ7sgoj3T521Wm" + ]; }; environment = { diff --git a/nix/iso.nix b/nix/iso.nix index c6d7fec..b42f992 100644 --- a/nix/iso.nix +++ b/nix/iso.nix @@ -1,24 +1,27 @@ -{inputs, ...}: { - perSystem = { - pkgs, - system, - ... - }: { - # For each major system, we provide a customized installer image that - # has ssh and some other convenience stuff preconfigured. - # Not strictly necessary for new setups. - packages.live-iso = inputs.nixos-generators.nixosGenerate { - inherit pkgs; - modules = [ - ./installer-configuration.nix - ../config/ssh.nix - ]; - format = - { - x86_64-linux = "install-iso"; - aarch64-linux = "sd-aarch64-installer"; - } - .${system}; +{ inputs, ... }: +{ + perSystem = + { + pkgs, + system, + ... + }: + { + # For each major system, we provide a customized installer image that + # has ssh and some other convenience stuff preconfigured. + # Not strictly necessary for new setups. + packages.live-iso = inputs.nixos-generators.nixosGenerate { + inherit pkgs; + modules = [ + ./installer-configuration.nix + ../config/ssh.nix + ]; + format = + { + x86_64-linux = "install-iso"; + aarch64-linux = "sd-aarch64-installer"; + } + .${system}; + }; }; - }; } diff --git a/nix/pkgs.nix b/nix/pkgs.nix index ba1feb0..eff9ead 100644 --- a/nix/pkgs.nix +++ b/nix/pkgs.nix @@ -1,4 +1,5 @@ -{inputs, ...}: { +{ inputs, ... }: +{ imports = [ ( { @@ -6,33 +7,33 @@ flake-parts-lib, ... }: - flake-parts-lib.mkTransposedPerSystemModule { - name = "pkgs"; - file = ./pkgs.nix; - option = lib.mkOption { - type = lib.types.unspecified; - }; - } + flake-parts-lib.mkTransposedPerSystemModule { + name = "pkgs"; + file = ./pkgs.nix; + option = lib.mkOption { + type = lib.types.unspecified; + }; + } ) ]; - perSystem = { - pkgs, - system, - ... - }: { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; - config.allowUnfree = true; - overlays = - (import ../pkgs/default.nix inputs) - ++ [ + perSystem = + { + pkgs, + system, + ... + }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + overlays = (import ../pkgs/default.nix inputs) ++ [ inputs.nix-topology.overlays.default # inputs.nixos-cosmic.overlays.default inputs.nixos-extra-modules.overlays.default ]; - }; + }; - inherit pkgs; - }; + inherit pkgs; + }; } diff --git a/nix/storage-box.nix b/nix/storage-box.nix index e1be2fd..51090d6 100644 --- a/nix/storage-box.nix +++ b/nix/storage-box.nix @@ -1,9 +1,14 @@ -{inputs, ...}: { - perSystem = {pkgs, ...}: { - apps.setupHetznerStorageBoxes = import (inputs.nixos-extra-modules + "/apps/setup-hetzner-storage-boxes.nix") { - inherit pkgs; - nixosConfigurations = inputs.self.nodes; - decryptIdentity = builtins.head inputs.self.secretsConfig.masterIdentities; +{ inputs, ... }: +{ + perSystem = + { pkgs, ... }: + { + apps.setupHetznerStorageBoxes = + import (inputs.nixos-extra-modules + "/apps/setup-hetzner-storage-boxes.nix") + { + inherit pkgs; + nixosConfigurations = inputs.self.nodes; + decryptIdentity = builtins.head inputs.self.secretsConfig.masterIdentities; + }; }; - }; } diff --git a/pkgs/actual-server.nix b/pkgs/actual-server.nix index e1905ef..01bf0b7 100644 --- a/pkgs/actual-server.nix +++ b/pkgs/actual-server.nix @@ -8,7 +8,8 @@ gitMinimal, nodejs, yarn, -}: let +}: +let version = "24.10.1"; src = fetchFromGitHub { owner = "actualbudget"; @@ -30,9 +31,20 @@ ]; SUPPORTED_ARCHITECTURES = builtins.toJSON { - os = ["darwin" "linux"]; - cpu = ["arm" "arm64" "ia32" "x64"]; - libc = ["glibc" "musl"]; + os = [ + "darwin" + "linux" + ]; + cpu = [ + "arm" + "arm64" + "ia32" + "x64" + ]; + libc = [ + "glibc" + "musl" + ]; }; buildPhase = '' @@ -54,38 +66,41 @@ outputHash = "sha256-eNpOS21pkamugoYVhzsEnstxeVN/J06yDZcshfr0Ek4="; }; in - stdenv.mkDerivation { - pname = "actual-server"; - inherit version src; +stdenv.mkDerivation { + pname = "actual-server"; + inherit version src; - nativeBuildInputs = [ - makeWrapper - yarn + nativeBuildInputs = [ + makeWrapper + yarn + ]; + + installPhase = '' + runHook preInstall + + mkdir -p $out/{bin,lib,lib/actual} + cp -r ${offlineCache}/node_modules/ $out/lib/actual + cp -r ./ $out/lib/actual + + makeWrapper ${lib.getExe nodejs} "$out/bin/actual-server" \ + --add-flags "$out/lib/actual/app.js" \ + --set NODE_PATH "$out/node_modules" + + runHook postInstall + ''; + + passthru = { + inherit offlineCache; + }; + + meta = with lib; { + description = "A super fast privacy-focused app for managing your finances"; + homepage = "https://actualbudget.com/"; + license = licenses.mit; + mainProgram = "actual-server"; + maintainers = with maintainers; [ + oddlama + patrickdag ]; - - installPhase = '' - runHook preInstall - - mkdir -p $out/{bin,lib,lib/actual} - cp -r ${offlineCache}/node_modules/ $out/lib/actual - cp -r ./ $out/lib/actual - - makeWrapper ${lib.getExe nodejs} "$out/bin/actual-server" \ - --add-flags "$out/lib/actual/app.js" \ - --set NODE_PATH "$out/node_modules" - - runHook postInstall - ''; - - passthru = { - inherit offlineCache; - }; - - meta = with lib; { - description = "A super fast privacy-focused app for managing your finances"; - homepage = "https://actualbudget.com/"; - license = licenses.mit; - mainProgram = "actual-server"; - maintainers = with maintainers; [oddlama patrickdag]; - }; - } + }; +} diff --git a/pkgs/awakened-poe-trade.nix b/pkgs/awakened-poe-trade.nix index b408888..53b54ab 100644 --- a/pkgs/awakened-poe-trade.nix +++ b/pkgs/awakened-poe-trade.nix @@ -1,7 +1,8 @@ { pkgs, fetchurl, -}: let +}: +let name = "awakened-poe-trade"; version = "3.24.10002"; description = "Path of Exile trading app for price checking"; @@ -18,17 +19,17 @@ ''; file = "Awakened-PoE-Trade-${version}.AppImage"; in - pkgs.appimageTools.wrapType2 { - name = "awakened-poe-trade"; - src = fetchurl { - url = "https://github.com/SnosMe/awakened-poe-trade/releases/download/v${version}/${file}"; - hash = "sha256-ieRBYrtpB8GgnDDy+fDuwamix5syRH3NG5jE5UoGg5A="; - }; +pkgs.appimageTools.wrapType2 { + name = "awakened-poe-trade"; + src = fetchurl { + url = "https://github.com/SnosMe/awakened-poe-trade/releases/download/v${version}/${file}"; + hash = "sha256-ieRBYrtpB8GgnDDy+fDuwamix5syRH3NG5jE5UoGg5A="; + }; - extraInstallCommands = '' - mkdir -p $out/share/applications - cp ${./TransferOrb.png} $out/share/applications/awakened-poe-trade.png - cp ${desktopEntry} $out/share/applications/${name}.desktop - substituteInPlace $out/share/applications/awakened-poe-trade.desktop --replace /share/ $out/share/ - ''; - } + extraInstallCommands = '' + mkdir -p $out/share/applications + cp ${./TransferOrb.png} $out/share/applications/awakened-poe-trade.png + cp ${desktopEntry} $out/share/applications/${name}.desktop + substituteInPlace $out/share/applications/awakened-poe-trade.desktop --replace /share/ $out/share/ + ''; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index f117c10..3383ddb 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,14 +1,14 @@ _inputs: [ (import ./scripts) (_final: prev: { - deploy = prev.callPackage ./deploy.nix {}; - git-fuzzy = prev.callPackage ./git-fuzzy {}; - awakened-poe-trade = prev.callPackage ./awakened-poe-trade.nix {}; - segoe-ui-ttf = prev.callPackage ./segoe-ui-ttf.nix {}; - zsh-histdb-skim = prev.callPackage ./zsh-skim-histdb.nix {}; - actual-server = prev.callPackage ./actual-server.nix {}; + deploy = prev.callPackage ./deploy.nix { }; + git-fuzzy = prev.callPackage ./git-fuzzy { }; + awakened-poe-trade = prev.callPackage ./awakened-poe-trade.nix { }; + segoe-ui-ttf = prev.callPackage ./segoe-ui-ttf.nix { }; + zsh-histdb-skim = prev.callPackage ./zsh-skim-histdb.nix { }; + actual-server = prev.callPackage ./actual-server.nix { }; neovim-clean = prev.neovim-unwrapped.overrideAttrs (old: { - nativeBuildInputs = (old.nativeBuildInputs or []) ++ [prev.makeWrapper]; + nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ prev.makeWrapper ]; postInstall = (old.postInstall or "") + '' @@ -22,10 +22,8 @@ _inputs: [ # }) # ]; - formats = - prev.formats - // { - ron = import ./ron.nix {inherit (prev) lib pkgs;}; - }; + formats = prev.formats // { + ron = import ./ron.nix { inherit (prev) lib pkgs; }; + }; }) ] diff --git a/pkgs/deploy.nix b/pkgs/deploy.nix index 1717c4d..a609849 100644 --- a/pkgs/deploy.nix +++ b/pkgs/deploy.nix @@ -1,7 +1,8 @@ { bc, writeShellApplication, -}: let +}: +let deploy = writeShellApplication { name = "deploy"; text = '' @@ -116,4 +117,4 @@ ''; }; in - deploy +deploy diff --git a/pkgs/git-fuzzy/default.nix b/pkgs/git-fuzzy/default.nix index b299313..f64698e 100644 --- a/pkgs/git-fuzzy/default.nix +++ b/pkgs/git-fuzzy/default.nix @@ -6,64 +6,73 @@ gitAndTools, bc, bat, - extraPackages ? [], -}: let - binPath = lib.makeBinPath ([gitAndTools.hub gitAndTools.delta bc bat] ++ extraPackages); + extraPackages ? [ ], +}: +let + binPath = lib.makeBinPath ( + [ + gitAndTools.hub + gitAndTools.delta + bc + bat + ] + ++ extraPackages + ); in - stdenvNoCC.mkDerivation { - pname = "git-fuzzy"; - version = "unstable-2023-09-18"; - src = fetchFromGitHub { - owner = "bigH"; - repo = "git-fuzzy"; - rev = "fb02ba3522e19ae1c69be80e2a58561fe2416155"; - hash = "sha256-Eo2TCx3w3SppoXi8RZu8EC1NhLOnL39bFliHDc2YsyM="; - }; +stdenvNoCC.mkDerivation { + pname = "git-fuzzy"; + version = "unstable-2023-09-18"; + src = fetchFromGitHub { + owner = "bigH"; + repo = "git-fuzzy"; + rev = "fb02ba3522e19ae1c69be80e2a58561fe2416155"; + hash = "sha256-Eo2TCx3w3SppoXi8RZu8EC1NhLOnL39bFliHDc2YsyM="; + }; - patches = [ - ./0001-load-git-config.patch - ]; + patches = [ + ./0001-load-git-config.patch + ]; - postPatch = '' - for GF_key in $(grep -ohr -- 'GF_[A-Z0-9_]*' lib | sort -u); do - key=''${GF_key#"GF_"} - key=''${key//_/-} - key=''${key,,} - cat >> lib/load-configs-from-git.sh <> lib/load-configs-from-git.sh <> lib/load-configs-from-git.sh <> lib/load-configs-from-git.sh <\0\0\0\0\x10\0\0\0\0\0\0\0\x3\0\0\0\x4\0\0\0\x5\0\0\0\x6\0\0\0\x12\0\0\0\xf\0\0\0\x13\0\0\0\a\0\0\0\t\0\0\0\x10\0\0\0\n\0\0\0\v\0\0\0\x17\0\0\0\f\0\0\0\x11)''; diff --git a/users/myuser/graphical/games/bottles.nix b/users/myuser/graphical/games/bottles.nix index 5ec34bb..d91de44 100644 --- a/users/myuser/graphical/games/bottles.nix +++ b/users/myuser/graphical/games/bottles.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ # XXX: to enable dark mode, no idea why it isn't detected by default. # dconf write /com/usebottles/bottles/dark-theme true diff --git a/users/myuser/graphical/games/minecraft.nix b/users/myuser/graphical/games/minecraft.nix index 4eb6393..2107b69 100644 --- a/users/myuser/graphical/games/minecraft.nix +++ b/users/myuser/graphical/games/minecraft.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ # XXX: Do NOT enable "Use discrete GPU" when running on nvidia by default. # Otherwise the xorg server will crash big time (no logs). After some gdb # debugging the culprit seems to be the x11 nvidia driver (nvidia_drv.so), diff --git a/users/myuser/graphical/games/poe.nix b/users/myuser/graphical/games/poe.nix index 4f696e4..02bbdb9 100644 --- a/users/myuser/graphical/games/poe.nix +++ b/users/myuser/graphical/games/poe.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ home.persistence."/state".directories = [ ".config/awakened-poe-trade" ]; diff --git a/users/myuser/graphical/gpu-screen-recorder.nix b/users/myuser/graphical/gpu-screen-recorder.nix index af3b948..3413e30 100644 --- a/users/myuser/graphical/gpu-screen-recorder.nix +++ b/users/myuser/graphical/gpu-screen-recorder.nix @@ -3,7 +3,8 @@ pkgs, nixosConfig, ... -}: let +}: +let save-replay = pkgs.writeShellApplication { name = "gpu-screen-recorder-save-replay"; runtimeInputs = [ @@ -34,7 +35,7 @@ on-stop-service = pkgs.writeShellApplication { name = "gpu-screen-recorder-stop-service"; - runtimeInputs = [pkgs.libnotify]; + runtimeInputs = [ pkgs.libnotify ]; text = '' if [[ "$SERVICE_RESULT" == "success" ]]; then notify-send '🎥 GPU Screen Recorder' '🔴 Replay service stopped!' \ @@ -85,7 +86,8 @@ -o "$GSR_OUTPUTDIR" ''; }; -in { +in +{ lib.gpu-screen-recorder = { inherit save-replay; }; diff --git a/users/myuser/graphical/hyprland.nix b/users/myuser/graphical/hyprland.nix index d1f3dae..0f9a0ac 100644 --- a/users/myuser/graphical/hyprland.nix +++ b/users/myuser/graphical/hyprland.nix @@ -7,9 +7,9 @@ nixosConfig, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) concatMap elem flip @@ -20,7 +20,8 @@ ; rofi-drun = "rofi -show drun -theme ~/.config/rofi/launchers/type-1/style-10.rasi"; -in { +in +{ home.packages = with pkgs; [ wl-clipboard ]; @@ -94,12 +95,10 @@ in { "SUPER + SHIFT,comma,movetoworkspacesilent,-1" "SUPER + SHIFT,period,movetoworkspacesilent,+1" ] - ++ flip concatMap (map toString (lib.lists.range 1 9)) ( - x: [ - "SUPER,${x},workspace,${x}" - "SUPER + SHIFT,${x},movetoworkspacesilent,${x}" - ] - ); + ++ flip concatMap (map toString (lib.lists.range 1 9)) (x: [ + "SUPER,${x},workspace,${x}" + "SUPER + SHIFT,${x},movetoworkspacesilent,${x}" + ]); bindm = [ # mouse movements @@ -200,10 +199,12 @@ in { ]; }) (mkIf (nixosConfig.node.name == "nom") { - monitor = [ - ]; - workspace = [ - ]; + monitor = + [ + ]; + workspace = + [ + ]; }) ]; diff --git a/users/myuser/graphical/i3-per-workspace-layout/src/main.rs b/users/myuser/graphical/i3-per-workspace-layout/src/main.rs index 33c16c9..b4b5758 100644 --- a/users/myuser/graphical/i3-per-workspace-layout/src/main.rs +++ b/users/myuser/graphical/i3-per-workspace-layout/src/main.rs @@ -139,7 +139,7 @@ fn cmd_toggle_layout(config: &Config, workspace: &Node) -> Option { // Don't do anything if the layout is already correct if &con.layout == desired_layout { - return None + return None; } con = &con.nodes[0]; diff --git a/users/myuser/graphical/i3.nix b/users/myuser/graphical/i3.nix index f030339..b7d1dff 100644 --- a/users/myuser/graphical/i3.nix +++ b/users/myuser/graphical/i3.nix @@ -4,9 +4,9 @@ nixosConfig, pkgs, ... -}: let - inherit - (lib) +}: +let + inherit (lib) escapeShellArg getExe mapAttrs' @@ -26,7 +26,7 @@ meta = with lib; { description = "A helper utility to allow assigning a layout to each workspace in i3"; license = licenses.mit; - maintainers = with maintainers; [oddlama]; + maintainers = with maintainers; [ oddlama ]; mainProgram = "i3-per-workspace-layout"; }; }; @@ -46,11 +46,12 @@ meta = with lib; { description = "Better focus navigation for sway and i3"; license = licenses.mit; - maintainers = with maintainers; [oddlama]; + maintainers = with maintainers; [ oddlama ]; mainProgram = "sway-overfocus"; }; }; -in { +in +{ xsession.numlock.enable = true; xsession.windowManager.i3 = { enable = true; @@ -148,107 +149,126 @@ in { window.titlebar = false; floating.criteria = [ - {class = "^Pavucontrol$";} + { class = "^Pavucontrol$"; } #{class = "^awakened-poe-trade$";} ]; assigns = { "1" = [ - {class = "^firefox$";} + { class = "^firefox$"; } ]; "5" = [ - {class = "^bottles$";} - {class = "^steam$";} - {class = "^prismlauncher$";} + { class = "^bottles$"; } + { class = "^steam$"; } + { class = "^prismlauncher$"; } ]; "7" = [ - {class = "^obsidian$";} - {class = "^discord$";} - {class = "^Signal$";} - {class = "^TelegramDesktop$";} + { class = "^obsidian$"; } + { class = "^discord$"; } + { class = "^Signal$"; } + { class = "^TelegramDesktop$"; } ]; "8" = [ - {class = "^Spotify$";} + { class = "^Spotify$"; } ]; }; # TODO eww -> bars = [ ]; workspaceOutputAssign = { - kroma = let - monitorMain = "DP-2"; - monitorLeft = "DP-4"; - in - map (x: { - workspace = x; - output = monitorMain; - }) ["1" "2" "3" "4" "5" "6"] - ++ map (x: { - workspace = x; - output = monitorLeft; - }) ["7" "8" "9"]; + kroma = + let + monitorMain = "DP-2"; + monitorLeft = "DP-4"; + in + map + (x: { + workspace = x; + output = monitorMain; + }) + [ + "1" + "2" + "3" + "4" + "5" + "6" + ] + ++ + map + (x: { + workspace = x; + output = monitorLeft; + }) + [ + "7" + "8" + "9" + ]; } - .${nixosConfig.node.name} - or []; + .${nixosConfig.node.name} or [ ]; - startup = let - configLayouts = (pkgs.formats.toml {}).generate "per-workspace-layouts.toml" { - force = true; - layouts = { - "1" = "tabbed"; - "5" = "tabbed"; - "7" = "tabbed"; - "8" = "tabbed"; - "9" = "tabbed"; + startup = + let + configLayouts = (pkgs.formats.toml { }).generate "per-workspace-layouts.toml" { + force = true; + layouts = { + "1" = "tabbed"; + "5" = "tabbed"; + "7" = "tabbed"; + "8" = "tabbed"; + "9" = "tabbed"; + }; }; - }; - in [ - { - command = "${getExe i3-per-workspace-layout} --config ${configLayouts}"; - always = false; - notification = false; - } - ]; + in + [ + { + command = "${getExe i3-per-workspace-layout} --config ${configLayouts}"; + always = false; + notification = false; + } + ]; }; }; systemd.user.services = { - wired.Install.WantedBy = lib.mkForce ["i3-session.target"]; - flameshot.Install.WantedBy = lib.mkForce ["i3-session.target"]; + wired.Install.WantedBy = lib.mkForce [ "i3-session.target" ]; + flameshot.Install.WantedBy = lib.mkForce [ "i3-session.target" ]; }; programs.autorandr.enable = true; programs.autorandr.profiles = { - kroma = let - monitorMain = "DP-2"; - monitorLeft = "DP-4"; - in { - main = { - config = { - ${monitorLeft} = { - enable = true; - mode = "3840x2160"; - rate = "60.00"; - position = "0x0"; + kroma = + let + monitorMain = "DP-2"; + monitorLeft = "DP-4"; + in + { + main = { + config = { + ${monitorLeft} = { + enable = true; + mode = "3840x2160"; + rate = "60.00"; + position = "0x0"; + }; + ${monitorMain} = { + enable = true; + primary = true; + mode = "3840x2160"; + rate = "144.00"; + position = "3840x0"; + }; }; - ${monitorMain} = { - enable = true; - primary = true; - mode = "3840x2160"; - rate = "144.00"; - position = "3840x0"; + fingerprint = { + ${monitorMain} = "00ffffffffffff001e6d9a5b078e0a000b1f0104b53c2278f919c1ae5044af260e5054210800d1c061404540314001010101010101014dd000a0f0703e803020350058542100001a000000fd0c3090505086010a202020202020000000fc003237474e3935300a2020202020000000ff003131314e5447594c423731390a02e602032d7123090707830100004410040301e2006ae305c000e60605017360216d1a0000020b309000047321602900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f47012790300030128d8060284ff0e9f002f801f006f08910002000400404f0104ff0e9f002f801f006f086200020004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d90"; + ${monitorLeft} = "00ffffffffffff001e6d095b39790700081a0104b53c22789f3035a7554ea3260f50542108007140818081c0a9c0d1c08100010101014dd000a0f0703e803020650c58542100001a286800a0f0703e800890650c58542100001a000000fd00283d878738010a202020202020000000fc004c4720556c7472612048440a2001850203117144900403012309070783010000023a801871382d40582c450058542100001e565e00a0a0a029503020350058542100001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8"; }; }; - fingerprint = { - ${monitorMain} = "00ffffffffffff001e6d9a5b078e0a000b1f0104b53c2278f919c1ae5044af260e5054210800d1c061404540314001010101010101014dd000a0f0703e803020350058542100001a000000fd0c3090505086010a202020202020000000fc003237474e3935300a2020202020000000ff003131314e5447594c423731390a02e602032d7123090707830100004410040301e2006ae305c000e60605017360216d1a0000020b309000047321602900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f47012790300030128d8060284ff0e9f002f801f006f08910002000400404f0104ff0e9f002f801f006f086200020004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d90"; - ${monitorLeft} = "00ffffffffffff001e6d095b39790700081a0104b53c22789f3035a7554ea3260f50542108007140818081c0a9c0d1c08100010101014dd000a0f0703e803020650c58542100001a286800a0f0703e800890650c58542100001a000000fd00283d878738010a202020202020000000fc004c4720556c7472612048440a2001850203117144900403012309070783010000023a801871382d40582c450058542100001e565e00a0a0a029503020350058542100001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8"; - }; }; - }; } - .${nixosConfig.node.name} - or {}; + .${nixosConfig.node.name} or { }; home.sessionVariables = { # Make gtk apps bigger diff --git a/users/myuser/graphical/kitty.nix b/users/myuser/graphical/kitty.nix index c1f827e..f5d8f0f 100644 --- a/users/myuser/graphical/kitty.nix +++ b/users/myuser/graphical/kitty.nix @@ -3,7 +3,8 @@ lib, pkgs, ... -}: { +}: +{ home.sessionVariables = { TERMINFO_DIRS = "${pkgs.kitty.terminfo.outPath}/share/terminfo"; }; diff --git a/users/myuser/graphical/rofi.nix b/users/myuser/graphical/rofi.nix index bcbcc30..5ba880d 100644 --- a/users/myuser/graphical/rofi.nix +++ b/users/myuser/graphical/rofi.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ programs.rofi = { enable = true; package = pkgs.rofi-wayland; @@ -7,26 +8,26 @@ }; }; - home.file = let - rofi-themes = pkgs.fetchFromGitHub { - owner = "adi1090x"; - repo = "rofi"; - rev = "3a28753b0a8fb666f4bd0394ac4b0e785577afa2"; - hash = "sha256-G3sAyIZbq1sOJxf+NBlXMOtTMiBCn6Sat8PHryxRS0w="; + home.file = + let + rofi-themes = pkgs.fetchFromGitHub { + owner = "adi1090x"; + repo = "rofi"; + rev = "3a28753b0a8fb666f4bd0394ac4b0e785577afa2"; + hash = "sha256-G3sAyIZbq1sOJxf+NBlXMOtTMiBCn6Sat8PHryxRS0w="; + }; + in + { + ".config/rofi/colors" = { + source = "${rofi-themes}/files/colors"; + recursive = true; + }; + ".config/rofi/launchers/type-1/style-10.rasi".source = "${rofi-themes}/files/launchers/type-1/style-10.rasi"; + ".config/rofi/launchers/type-1/shared/colors.rasi".text = + # css + '' + @import "~/.config/rofi/colors/onedark.rasi" + ''; + ".config/rofi/launchers/type-1/shared/fonts.rasi".text = ""; }; - in { - ".config/rofi/colors" = { - source = "${rofi-themes}/files/colors"; - recursive = true; - }; - ".config/rofi/launchers/type-1/style-10.rasi".source = "${rofi-themes}/files/launchers/type-1/style-10.rasi"; - ".config/rofi/launchers/type-1/shared/colors.rasi".text = - /* - css - */ - '' - @import "~/.config/rofi/colors/onedark.rasi" - ''; - ".config/rofi/launchers/type-1/shared/fonts.rasi".text = ""; - }; } diff --git a/users/myuser/graphical/signal.nix b/users/myuser/graphical/signal.nix index 2d93824..cbac437 100644 --- a/users/myuser/graphical/signal.nix +++ b/users/myuser/graphical/signal.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ home.packages = with pkgs; [ signal-desktop ]; diff --git a/users/myuser/graphical/swaync.nix b/users/myuser/graphical/swaync.nix index 2778fe0..3602df8 100644 --- a/users/myuser/graphical/swaync.nix +++ b/users/myuser/graphical/swaync.nix @@ -2,7 +2,8 @@ config, lib, ... -}: { +}: +{ services.swaync = { enable = true; settings = { diff --git a/users/myuser/graphical/swww.nix b/users/myuser/graphical/swww.nix index 89de0fa..5646a64 100644 --- a/users/myuser/graphical/swww.nix +++ b/users/myuser/graphical/swww.nix @@ -2,7 +2,8 @@ lib, pkgs, ... -}: let +}: +let swww-update-wallpaper = pkgs.writeShellApplication { name = "swww-update-wallpaper"; runtimeInputs = [ @@ -20,15 +21,16 @@ --transition-duration 1.5 ''; }; -in { +in +{ systemd.user = { services = { swww = { - Install.WantedBy = ["graphical-session.target"]; + Install.WantedBy = [ "graphical-session.target" ]; Unit = { Description = "Wayland wallpaper daemon"; - PartOf = ["graphical-session.target"]; - After = ["graphical-session.target"]; + PartOf = [ "graphical-session.target" ]; + After = [ "graphical-session.target" ]; }; Service = { ExecStart = "${pkgs.swww}/bin/swww-daemon"; @@ -36,7 +38,7 @@ in { }; }; swww-update-wallpaper = { - Install.WantedBy = ["default.target"]; + Install.WantedBy = [ "default.target" ]; Unit.Description = "Update the wallpaper"; Service = { Type = "oneshot"; @@ -47,7 +49,7 @@ in { }; }; timers.swww-update-wallpaper = { - Install.WantedBy = ["timers.target"]; + Install.WantedBy = [ "timers.target" ]; Unit.Description = "Periodically switch to a new wallpaper"; Timer.OnCalendar = "*:0/5"; # Every 5 minutes }; diff --git a/users/myuser/graphical/theme.nix b/users/myuser/graphical/theme.nix index 919a5dd..0e58007 100644 --- a/users/myuser/graphical/theme.nix +++ b/users/myuser/graphical/theme.nix @@ -3,7 +3,8 @@ nixosConfig, pkgs, ... -}: { +}: +{ xresources.properties = { "Xft.hinting" = true; "Xft.antialias" = true; @@ -13,29 +14,31 @@ "Xft.rgba" = "rgb"; }; - gtk = let - gtk34extraConfig = { - gtk-application-prefer-dark-theme = 1; - gtk-cursor-theme-size = 32; - gtk-enable-animations = true; - gtk-xft-antialias = 1; - gtk-xft-dpi = 160; # XXX: delete for wayland? - gtk-xft-hinting = 1; - gtk-xft-hintstyle = "hintfull"; - gtk-xft-rgba = "rgb"; - }; - in { - enable = true; + gtk = + let + gtk34extraConfig = { + gtk-application-prefer-dark-theme = 1; + gtk-cursor-theme-size = 32; + gtk-enable-animations = true; + gtk-xft-antialias = 1; + gtk-xft-dpi = 160; # XXX: delete for wayland? + gtk-xft-hinting = 1; + gtk-xft-hintstyle = "hintfull"; + gtk-xft-rgba = "rgb"; + }; + in + { + enable = true; - iconTheme = { - name = "WhiteSur-dark"; - package = pkgs.whitesur-icon-theme; - }; + iconTheme = { + name = "WhiteSur-dark"; + package = pkgs.whitesur-icon-theme; + }; - gtk2.extraConfig = "gtk-application-prefer-dark-theme = true"; - gtk3.extraConfig = gtk34extraConfig; - gtk4.extraConfig = gtk34extraConfig; - }; + gtk2.extraConfig = "gtk-application-prefer-dark-theme = true"; + gtk3.extraConfig = gtk34extraConfig; + gtk4.extraConfig = gtk34extraConfig; + }; home.sessionVariables.GTK_THEME = config.gtk.theme.name; diff --git a/users/myuser/graphical/thunderbird.nix b/users/myuser/graphical/thunderbird.nix index 5faf4d1..fd9da79 100644 --- a/users/myuser/graphical/thunderbird.nix +++ b/users/myuser/graphical/thunderbird.nix @@ -2,16 +2,19 @@ config, lib, ... -}: { - accounts.email.accounts = lib.flip lib.mapAttrs' config.userSecrets.accounts.email (_n: v: +}: +{ + accounts.email.accounts = lib.flip lib.mapAttrs' config.userSecrets.accounts.email ( + _n: v: lib.nameValuePair v.address ( lib.recursiveUpdate v { thunderbird = { enable = true; - profiles = ["personal"]; + profiles = [ "personal" ]; }; } - )); + ) + ); programs.thunderbird = { enable = true; @@ -81,8 +84,8 @@ ]; xdg.mimeApps.defaultApplications = { - "x-scheme-handler/mailto" = ["thunderbird.desktop"]; - "x-scheme-handler/mid" = ["thunderbird.desktop"]; - "message/rfc822" = ["thunderbird.desktop"]; + "x-scheme-handler/mailto" = [ "thunderbird.desktop" ]; + "x-scheme-handler/mid" = [ "thunderbird.desktop" ]; + "message/rfc822" = [ "thunderbird.desktop" ]; }; } diff --git a/users/myuser/graphical/ts3.nix b/users/myuser/graphical/ts3.nix index 88e9c72..210f730 100644 --- a/users/myuser/graphical/ts3.nix +++ b/users/myuser/graphical/ts3.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: let +{ pkgs, ... }: +let ts3 = pkgs.writeShellApplication { name = "teamspeak3"; runtimeInputs = [ @@ -9,8 +10,9 @@ exec ts3client ''; }; -in { - home.packages = [ts3]; +in +{ + home.packages = [ ts3 ]; home.persistence."/persist".directories = [ ".config/teamspeak3" ]; diff --git a/users/myuser/graphical/waybar.nix b/users/myuser/graphical/waybar.nix index 06d53d3..e7b4af8 100644 --- a/users/myuser/graphical/waybar.nix +++ b/users/myuser/graphical/waybar.nix @@ -2,7 +2,8 @@ lib, pkgs, ... -}: { +}: +{ programs.waybar = { enable = true; systemd.enable = true; @@ -121,7 +122,17 @@ battery = { interval = 2; format = "{icon} {capacity}%"; - format-icons = ["" "" "" "" "" "" "" "" ""]; + format-icons = [ + "" + "" + "" + "" + "" + "" + "" + "" + "" + ]; states = { warning = 25; critical = 15; @@ -154,7 +165,10 @@ wireplumber = { format = "{icon} {volume}%"; format-muted = " {volume}%"; - format-icons = ["" ""]; + format-icons = [ + "" + "" + ]; on-click = "${pkgs.hyprland}/bin/hyprctl dispatch exec \"[float;pin;move 80% 50%;size 20% 50%;noborder]\" ${lib.getExe pkgs.pwvucontrol}"; on-click-middle = "${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 100%"; on-click-right = "${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; @@ -247,7 +261,7 @@ }; systemd.user.services.waybar = { - Unit.After = ["graphical-session.target"]; - Service.Slice = ["app-graphical.slice"]; + Unit.After = [ "graphical-session.target" ]; + Service.Slice = [ "app-graphical.slice" ]; }; } diff --git a/users/myuser/graphical/wired-notify.nix b/users/myuser/graphical/wired-notify.nix index 7bedc7b..acb2d25 100644 --- a/users/myuser/graphical/wired-notify.nix +++ b/users/myuser/graphical/wired-notify.nix @@ -3,380 +3,431 @@ lib, pkgs, ... -}: { +}: +{ systemd.user.services.wired.Unit.ConditionEnvironment = "DISPLAY"; services.wired = { enable = true; - config = let - inherit (builtins) floor; + config = + let + inherit (builtins) floor; - format = pkgs.formats.ron {}; - inherit (format.lib) mkLiteral struct; + format = pkgs.formats.ron { }; + inherit (format.lib) mkLiteral struct; - colors = lib.mapAttrs (_: Color) config.lib.stylix.colors.withHashtag; - inherit (config.stylix) fonts; + colors = lib.mapAttrs (_: Color) config.lib.stylix.colors.withHashtag; + inherit (config.stylix) fonts; - # A global scaling factor to apply to all notifications. - globalScale = 1.2; + # A global scaling factor to apply to all notifications. + globalScale = 1.2; - # Ron format shorthands and helpers - unnamedStruct = struct ""; - Color = hex: struct "Color" {inherit hex;}; - Hook = struct "Hook"; - Vec2 = x: y: struct "Vec2" {inherit x y;}; - ShortcutsConfig = struct "ShortcutsConfig"; - And = struct "And"; - Or = struct "Or"; - Not = struct "Not"; + # Ron format shorthands and helpers + unnamedStruct = struct ""; + Color = hex: struct "Color" { inherit hex; }; + Hook = struct "Hook"; + Vec2 = x: y: struct "Vec2" { inherit x y; }; + ShortcutsConfig = struct "ShortcutsConfig"; + And = struct "And"; + Or = struct "Or"; + Not = struct "Not"; - mkVec2 = x: y: Vec2 (globalScale * x) (globalScale * y); + mkVec2 = x: y: Vec2 (globalScale * x) (globalScale * y); - mkHook = parent_anchor: self_anchor: - Hook { - parent_anchor = mkLiteral parent_anchor; - self_anchor = mkLiteral self_anchor; - }; - - mkPaddingLrBt = left: right: bottom: top: - struct "Padding" { - left = globalScale * left; - right = globalScale * right; - bottom = globalScale * bottom; - top = globalScale * top; - }; - - mkDimensionsWH = minw: maxw: minh: maxh: - unnamedStruct { - width = unnamedStruct { - min = floor (globalScale * minw); - max = floor (globalScale * maxw); + mkHook = + parent_anchor: self_anchor: + Hook { + parent_anchor = mkLiteral parent_anchor; + self_anchor = mkLiteral self_anchor; }; - height = unnamedStruct { - min = floor (globalScale * minh); - max = floor (globalScale * maxh); + + mkPaddingLrBt = + left: right: bottom: top: + struct "Padding" { + left = globalScale * left; + right = globalScale * right; + bottom = globalScale * bottom; + top = globalScale * top; }; - }; - mkTopBar = name: extra: - map (x: extra // x) [ - { - name = "${name}_app_image"; - parent = "${name}_root"; - hook = mkHook "TL" "TL"; - offset = mkVec2 0 0; - params = struct "ImageBlock" (unnamedStruct { - filter_mode = mkLiteral "Lanczos3"; - image_type = mkLiteral "App"; - min_height = floor (globalScale * 24); - min_width = floor (globalScale * 24); - padding = mkPaddingLrBt 16 (-8) 6 12; - rounding = globalScale * 12.0; - scale_height = floor (globalScale * 24); - scale_width = floor (globalScale * 24); - }); - } - { - name = "${name}_app_name"; - parent = "${name}_app_image"; - hook = mkHook "TR" "TL"; - offset = mkVec2 0 0; - params = struct "TextBlock" (unnamedStruct { - color = colors.base05; - dimensions = mkDimensionsWH 350 350 28 28; - ellipsize = mkLiteral "End"; - font = "${fonts.monospace.name} ${toString (globalScale * 14)}"; - padding = mkPaddingLrBt 16 0 0 12; - text = "%n"; - }); - } - ]; + mkDimensionsWH = + minw: maxw: minh: maxh: + unnamedStruct { + width = unnamedStruct { + min = floor (globalScale * minw); + max = floor (globalScale * maxw); + }; + height = unnamedStruct { + min = floor (globalScale * minh); + max = floor (globalScale * maxh); + }; + }; - mkBody = name: ident: yOffset: summaryRightPadding: extra: let - maxWFull = 580 - 12; - maxWImg = maxWFull - 128 - 12; - in - map (x: extra // x) ( - [ + mkTopBar = + name: extra: + map (x: extra // x) [ { - name = "${name}_${ident}_hint"; + name = "${name}_app_image"; parent = "${name}_root"; hook = mkHook "TL" "TL"; - offset = mkVec2 0 yOffset; + offset = mkVec2 0 0; params = struct "ImageBlock" (unnamedStruct { filter_mode = mkLiteral "Lanczos3"; - image_type = mkLiteral "Hint"; - padding = mkPaddingLrBt 12 0 12 12; - rounding = globalScale * 9; - scale_height = floor (globalScale * 128); - scale_width = floor (globalScale * 128); + image_type = mkLiteral "App"; + min_height = floor (globalScale * 24); + min_width = floor (globalScale * 24); + padding = mkPaddingLrBt 16 (-8) 6 12; + rounding = globalScale * 12.0; + scale_height = floor (globalScale * 24); + scale_width = floor (globalScale * 24); }); } { - name = "${name}_${ident}_summary"; - parent = "${name}_${ident}_hint"; + name = "${name}_app_name"; + parent = "${name}_app_image"; hook = mkHook "TR" "TL"; offset = mkVec2 0 0; params = struct "TextBlock" (unnamedStruct { - text = "%s"; - font = "${fonts.sansSerif.name} Bold ${toString (globalScale * 16)}"; + color = colors.base05; + dimensions = mkDimensionsWH 350 350 28 28; ellipsize = mkLiteral "End"; - color = colors.base06; - padding = mkPaddingLrBt 16 16 12 8; - dimensions = mkDimensionsWH (maxWFull - summaryRightPadding) (maxWFull - summaryRightPadding) 0 30; - dimensions_image_hint = mkDimensionsWH (maxWImg - summaryRightPadding) (maxWImg - summaryRightPadding) 0 30; - dimensions_image_both = mkDimensionsWH (maxWImg - summaryRightPadding) (maxWImg - summaryRightPadding) 0 30; + font = "${fonts.monospace.name} ${toString (globalScale * 14)}"; + padding = mkPaddingLrBt 16 0 0 12; + text = "%n"; }); } - { - name = "${name}_${ident}_body"; - parent = "${name}_${ident}_summary"; - hook = mkHook "BL" "TL"; - offset = mkVec2 0 0; - render_criteria = [ - (And [ - (Or extra.render_criteria) - (mkLiteral "Body") - ]) - ]; - params = struct "TextBlock" (unnamedStruct { - text = "%b"; - font = "${fonts.sansSerif.name} ${toString (globalScale * 16)}"; - ellipsize = mkLiteral "End"; - color = colors.base06; - padding = mkPaddingLrBt 16 16 12 (-4); - dimensions = mkDimensionsWH maxWFull maxWFull 0 88; - dimensions_image_hint = mkDimensionsWH maxWImg maxWImg 0 88; - dimensions_image_both = mkDimensionsWH maxWImg maxWImg 0 88; - }); - } - ] - # We unfortunately cannot move these out of mkBody, because the depend - # on the specific name for the parent, which cannot be changed dynamically. - # So each call to mkBody creates these progress bars which only differ in - # their parent :/ - ++ (mkProgress name "${ident}_hint" (-4) { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (mkLiteral "Progress") - (mkLiteral "HintImage") - ])) - ]; - }) - ++ (mkProgress name "${ident}_body" 0 { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (mkLiteral "Progress") - (mkLiteral "Body") - (Not (mkLiteral "HintImage")) - ])) - ]; - }) - ++ (mkProgress name "${ident}_summary" 0 { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (mkLiteral "Progress") - (mkLiteral "Summary") - (Not (Or [ - (mkLiteral "Body") - (mkLiteral "HintImage") - ])) - ])) - ]; - }) - # Each mkProgress includes a button bar. But if no progress is included in a notification, - # those won't be rendered, so we have to define bars for non-progress notifications. - # (And yes, we need 3 because we cannot have duplicate names or dynamic parents) - ++ (mkButtonBar name "${ident}_hint" { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (Not (mkLiteral "Progress")) - (mkLiteral "HintImage") - ])) - ]; - }) - ++ (mkButtonBar name "${ident}_body" { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (Not (mkLiteral "Progress")) - (mkLiteral "Body") - (Not (mkLiteral "HintImage")) - ])) - ]; - }) - ++ (mkButtonBar name "${ident}_summary" { - render_criteria = [ - (And (extra.render_criteria - ++ [ - (Not (mkLiteral "Progress")) - (mkLiteral "Summary") - (Not (Or [ - (mkLiteral "Body") - (mkLiteral "HintImage") - ])) - ])) - ]; - }) - ); + ]; - mkProgress = name: parent: yOffset: extra: - map (x: extra // x) ( - [ - { - name = "${name}_progress_${parent}_text"; - parent = "${name}_${parent}"; - hook = mkHook "BL" "TL"; - offset = mkVec2 0 0; - params = struct "TextBlock" (unnamedStruct { - text = "%p%"; - font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; - align = mkLiteral "Right"; - ellipsize = mkLiteral "End"; - color = colors.base06; - padding = mkPaddingLrBt 12 16 12 yOffset; - dimensions = mkDimensionsWH 48 48 24 24; - }); - } - { - name = "${name}_progress_${parent}_bar"; - parent = "${name}_${parent}"; - hook = mkHook "BL" "TL"; - offset = mkVec2 0 0; - params = struct "ProgressBlock" (unnamedStruct { - width = globalScale * 524; - height = globalScale * 12; - border_width = 0.0; - border_rounding = globalScale * 4; - border_color = colors.base03; - background_color = colors.base03; - fill_color = colors.base0D; - fill_rounding = globalScale * 4; - padding = mkPaddingLrBt 68 16 12 (yOffset + 8); - }); - } - ] - ++ (mkButtonBar name "progress_${parent}_text" extra) - ); - - mkButtonBar = name: parent: extra: - map (x: extra // x) (lib.flatten [ - { - name = "${name}_button_bar_for_${parent}"; - parent = "${name}_${parent}"; - hook = mkHook "BL" "TL"; - offset = mkVec2 0 0; - render_criteria = [ - (And (extra.render_criteria - ++ [ - (Or [ - (struct "ActionOther" 0) - (struct "ActionOther" 1) - (struct "ActionOther" 2) - (struct "ActionOther" 3) - (struct "ActionOther" 4) - (struct "ActionOther" 5) - ]) - ])) - ]; - params = struct "TextBlock" (unnamedStruct { - text = ""; - font = "${fonts.monospace.name} 1"; - color = colors.base06; - padding = mkPaddingLrBt 0 0 0 0; - dimensions = mkDimensionsWH 568 568 44 44; - }); - } - (lib.flip map [0 1 2 3 4 5] ( - i: - lib.optionalAttrs (i == 0) { - parent = "${name}_button_bar_for_${parent}"; + mkBody = + name: ident: yOffset: summaryRightPadding: extra: + let + maxWFull = 580 - 12; + maxWImg = maxWFull - 128 - 12; + in + map (x: extra // x) ( + [ + { + name = "${name}_${ident}_hint"; + parent = "${name}_root"; hook = mkHook "TL" "TL"; - offset = mkVec2 16 0; - } - // lib.optionalAttrs (i != 0) { - parent = "${name}_action_${toString (i - 1)}_for_${parent}"; - hook = mkHook "TR" "TL"; - offset = mkVec2 8 0; - } - // { - name = "${name}_action_${toString i}_for_${parent}"; - render_criteria = [ - ( - And (extra.render_criteria - ++ [ - (struct "ActionOther" i) - ]) - ) - ]; - params = struct "ButtonBlock" (unnamedStruct { - text = "%a"; - font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; - ellipsize = mkLiteral "End"; - action = struct "OtherAction" i; - text_color = colors.base06; - text_color_hovered = colors.base06; - background_color = colors.base01; - background_color_hovered = colors.base02; - border_color = colors.base04; - border_color_hovered = colors.base0F; - border_rounding = globalScale * 0; - border_width = globalScale * 2; - padding = mkPaddingLrBt 8 8 4 4; - # Technically distribute like below, but we'll just allow more even - # if it breaks when having > 4 max length buttons, because it probably - # never happens and looks a lot better this way. - dimensions = mkDimensionsWH 32 144 24 24; - # dimensions = mkDimensionsWH 32 (( - # 568 /* available width */ - # - 2 * 16 /* padding lr */ - # - (/* count actions */ 6 - 1) * 8 /* padding between */ - # ) / /* count actions */ 6) 24 24; + offset = mkVec2 0 yOffset; + params = struct "ImageBlock" (unnamedStruct { + filter_mode = mkLiteral "Lanczos3"; + image_type = mkLiteral "Hint"; + padding = mkPaddingLrBt 12 0 12 12; + rounding = globalScale * 9; + scale_height = floor (globalScale * 128); + scale_width = floor (globalScale * 128); }); } - )) - ]); + { + name = "${name}_${ident}_summary"; + parent = "${name}_${ident}_hint"; + hook = mkHook "TR" "TL"; + offset = mkVec2 0 0; + params = struct "TextBlock" (unnamedStruct { + text = "%s"; + font = "${fonts.sansSerif.name} Bold ${toString (globalScale * 16)}"; + ellipsize = mkLiteral "End"; + color = colors.base06; + padding = mkPaddingLrBt 16 16 12 8; + dimensions = mkDimensionsWH (maxWFull - summaryRightPadding) (maxWFull - summaryRightPadding) 0 30; + dimensions_image_hint = mkDimensionsWH (maxWImg - summaryRightPadding) ( + maxWImg - summaryRightPadding + ) 0 30; + dimensions_image_both = mkDimensionsWH (maxWImg - summaryRightPadding) ( + maxWImg - summaryRightPadding + ) 0 30; + }); + } + { + name = "${name}_${ident}_body"; + parent = "${name}_${ident}_summary"; + hook = mkHook "BL" "TL"; + offset = mkVec2 0 0; + render_criteria = [ + (And [ + (Or extra.render_criteria) + (mkLiteral "Body") + ]) + ]; + params = struct "TextBlock" (unnamedStruct { + text = "%b"; + font = "${fonts.sansSerif.name} ${toString (globalScale * 16)}"; + ellipsize = mkLiteral "End"; + color = colors.base06; + padding = mkPaddingLrBt 16 16 12 (-4); + dimensions = mkDimensionsWH maxWFull maxWFull 0 88; + dimensions_image_hint = mkDimensionsWH maxWImg maxWImg 0 88; + dimensions_image_both = mkDimensionsWH maxWImg maxWImg 0 88; + }); + } + ] + # We unfortunately cannot move these out of mkBody, because the depend + # on the specific name for the parent, which cannot be changed dynamically. + # So each call to mkBody creates these progress bars which only differ in + # their parent :/ + ++ (mkProgress name "${ident}_hint" (-4) { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (mkLiteral "Progress") + (mkLiteral "HintImage") + ] + )) + ]; + }) + ++ (mkProgress name "${ident}_body" 0 { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (mkLiteral "Progress") + (mkLiteral "Body") + (Not (mkLiteral "HintImage")) + ] + )) + ]; + }) + ++ (mkProgress name "${ident}_summary" 0 { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (mkLiteral "Progress") + (mkLiteral "Summary") + (Not (Or [ + (mkLiteral "Body") + (mkLiteral "HintImage") + ])) + ] + )) + ]; + }) + # Each mkProgress includes a button bar. But if no progress is included in a notification, + # those won't be rendered, so we have to define bars for non-progress notifications. + # (And yes, we need 3 because we cannot have duplicate names or dynamic parents) + ++ (mkButtonBar name "${ident}_hint" { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (Not (mkLiteral "Progress")) + (mkLiteral "HintImage") + ] + )) + ]; + }) + ++ (mkButtonBar name "${ident}_body" { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (Not (mkLiteral "Progress")) + (mkLiteral "Body") + (Not (mkLiteral "HintImage")) + ] + )) + ]; + }) + ++ (mkButtonBar name "${ident}_summary" { + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (Not (mkLiteral "Progress")) + (mkLiteral "Summary") + (Not (Or [ + (mkLiteral "Body") + (mkLiteral "HintImage") + ])) + ] + )) + ]; + }) + ); - mkIndicatorValue = name: ident: parent: extra: textParamsExtra: progressParamsExtra: - map (x: extra // x) (lib.flatten [ - { - name = "${name}_${ident}_value_text"; - parent = "${name}_${parent}"; - hook = mkHook "BM" "TM"; - offset = mkVec2 0 0; - params = struct "TextBlock" (unnamedStruct ({ - text = "%p%"; - font = "${fonts.monospace.name} Bold ${toString (globalScale * 18)}"; - align = mkLiteral "Center"; - color = colors.base06; - padding = mkPaddingLrBt 0 0 12 0; - dimensions = mkDimensionsWH 64 64 32 32; + mkProgress = + name: parent: yOffset: extra: + map (x: extra // x) ( + [ + { + name = "${name}_progress_${parent}_text"; + parent = "${name}_${parent}"; + hook = mkHook "BL" "TL"; + offset = mkVec2 0 0; + params = struct "TextBlock" (unnamedStruct { + text = "%p%"; + font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; + align = mkLiteral "Right"; + ellipsize = mkLiteral "End"; + color = colors.base06; + padding = mkPaddingLrBt 12 16 12 yOffset; + dimensions = mkDimensionsWH 48 48 24 24; + }); } - // textParamsExtra)); - } - { - name = "${name}_${ident}_value_bar"; - parent = "${name}_${ident}_value_text"; - hook = mkHook "BM" "TM"; - offset = mkVec2 0 0; - params = struct "ProgressBlock" (unnamedStruct ({ - width = globalScale * 0.90 * 384; - height = globalScale * 16; - border_width = 0.0; - border_rounding = globalScale * 6; - border_color = colors.base03; - background_color = colors.base03; - fill_color = colors.base0F; - fill_rounding = globalScale * 6; - padding = mkPaddingLrBt 0 0 0 0; + { + name = "${name}_progress_${parent}_bar"; + parent = "${name}_${parent}"; + hook = mkHook "BL" "TL"; + offset = mkVec2 0 0; + params = struct "ProgressBlock" (unnamedStruct { + width = globalScale * 524; + height = globalScale * 12; + border_width = 0.0; + border_rounding = globalScale * 4; + border_color = colors.base03; + background_color = colors.base03; + fill_color = colors.base0D; + fill_rounding = globalScale * 4; + padding = mkPaddingLrBt 68 16 12 (yOffset + 8); + }); } - // progressParamsExtra)); - } - ]); - in + ] + ++ (mkButtonBar name "progress_${parent}_text" extra) + ); + + mkButtonBar = + name: parent: extra: + map (x: extra // x) ( + lib.flatten [ + { + name = "${name}_button_bar_for_${parent}"; + parent = "${name}_${parent}"; + hook = mkHook "BL" "TL"; + offset = mkVec2 0 0; + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (Or [ + (struct "ActionOther" 0) + (struct "ActionOther" 1) + (struct "ActionOther" 2) + (struct "ActionOther" 3) + (struct "ActionOther" 4) + (struct "ActionOther" 5) + ]) + ] + )) + ]; + params = struct "TextBlock" (unnamedStruct { + text = ""; + font = "${fonts.monospace.name} 1"; + color = colors.base06; + padding = mkPaddingLrBt 0 0 0 0; + dimensions = mkDimensionsWH 568 568 44 44; + }); + } + (lib.flip map + [ + 0 + 1 + 2 + 3 + 4 + 5 + ] + ( + i: + lib.optionalAttrs (i == 0) { + parent = "${name}_button_bar_for_${parent}"; + hook = mkHook "TL" "TL"; + offset = mkVec2 16 0; + } + // lib.optionalAttrs (i != 0) { + parent = "${name}_action_${toString (i - 1)}_for_${parent}"; + hook = mkHook "TR" "TL"; + offset = mkVec2 8 0; + } + // { + name = "${name}_action_${toString i}_for_${parent}"; + render_criteria = [ + (And ( + extra.render_criteria + ++ [ + (struct "ActionOther" i) + ] + )) + ]; + params = struct "ButtonBlock" (unnamedStruct { + text = "%a"; + font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; + ellipsize = mkLiteral "End"; + action = struct "OtherAction" i; + text_color = colors.base06; + text_color_hovered = colors.base06; + background_color = colors.base01; + background_color_hovered = colors.base02; + border_color = colors.base04; + border_color_hovered = colors.base0F; + border_rounding = globalScale * 0; + border_width = globalScale * 2; + padding = mkPaddingLrBt 8 8 4 4; + # Technically distribute like below, but we'll just allow more even + # if it breaks when having > 4 max length buttons, because it probably + # never happens and looks a lot better this way. + dimensions = mkDimensionsWH 32 144 24 24; + # dimensions = mkDimensionsWH 32 (( + # 568 /* available width */ + # - 2 * 16 /* padding lr */ + # - (/* count actions */ 6 - 1) * 8 /* padding between */ + # ) / /* count actions */ 6) 24 24; + }); + } + ) + ) + ] + ); + + mkIndicatorValue = + name: ident: parent: extra: textParamsExtra: progressParamsExtra: + map (x: extra // x) ( + lib.flatten [ + { + name = "${name}_${ident}_value_text"; + parent = "${name}_${parent}"; + hook = mkHook "BM" "TM"; + offset = mkVec2 0 0; + params = struct "TextBlock" ( + unnamedStruct ( + { + text = "%p%"; + font = "${fonts.monospace.name} Bold ${toString (globalScale * 18)}"; + align = mkLiteral "Center"; + color = colors.base06; + padding = mkPaddingLrBt 0 0 12 0; + dimensions = mkDimensionsWH 64 64 32 32; + } + // textParamsExtra + ) + ); + } + { + name = "${name}_${ident}_value_bar"; + parent = "${name}_${ident}_value_text"; + hook = mkHook "BM" "TM"; + offset = mkVec2 0 0; + params = struct "ProgressBlock" ( + unnamedStruct ( + { + width = globalScale * 0.9 * 384; + height = globalScale * 16; + border_width = 0.0; + border_rounding = globalScale * 6; + border_color = colors.base03; + background_color = colors.base03; + fill_color = colors.base0F; + fill_rounding = globalScale * 6; + padding = mkPaddingLrBt 0 0 0 0; + } + // progressParamsExtra + ) + ); + } + ] + ); + in format.generate "wired.ron" (unnamedStruct { max_notifications = 10; timeout = 10000; @@ -396,211 +447,220 @@ notification_action1 = 3; # middle click }; - layout_blocks = let - criterionHasTopBar = And [ - (mkLiteral "AppImage") - (Not (Or [ - (struct "AppName" "") - (struct "AppName" "notify-send") - ])) - ]; - in - map unnamedStruct (lib.flatten [ - # Root block for normal notifications - { - name = "general_root"; - parent = ""; - hook = mkHook "TR" "TR"; - offset = Vec2 (-50) 50; # Vec2 instead of mkVec2 to not apply scaling. - render_criteria = [ - (Not (Or [ - (struct "Tag" "indicator") - ])) - ]; - params = struct "NotificationBlock" (unnamedStruct { - monitor = 1; - border_width = globalScale * 2; - border_rounding = globalScale * 0; - background_color = colors.base00; - border_color = colors.base04; - border_color_low = colors.base04; - border_color_critical = colors.base08; - border_color_paused = colors.base09; - gap = mkVec2 0 8; - notification_hook = mkHook "BR" "TR"; - }); - } - # Dummy text that enforces minimum window size - { - name = "general_size"; - parent = "general_root"; - hook = mkHook "TL" "TL"; - offset = Vec2 0 0; - params = struct "TextBlock" (unnamedStruct { - text = ""; - font = "${fonts.monospace.name} 1"; - color = colors.base06; - padding = mkPaddingLrBt 0 0 0 0; - dimensions = mkDimensionsWH 600 600 1 1; - }); - } - # Time is always shown in the top right corner. - { - name = "general_time"; - parent = "general_root"; - hook = mkHook "TL" "TL"; - offset = mkVec2 (600 - 100) 0; - params = struct "TextBlock" (unnamedStruct { - color = colors.base05; - dimensions = mkDimensionsWH 100 100 28 28; - ellipsize = mkLiteral "End"; - font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; - padding = mkPaddingLrBt 0 16 4 12; - text = "%t(%a %H:%M)"; - }); - } - # Top bar for app image, name and time, but only - # if there is an app name or image. - (mkTopBar "general" { - render_criteria = [criterionHasTopBar]; - }) - # if no top bar present: A body with no offset and a summary padding to the right (to not overlay the time) - (mkBody "general" "notop" 0 (16 + 100) { - render_criteria = [(Not criterionHasTopBar)]; - }) - # if top bar present: A body with matching y offset and no summary padding to the right - (mkBody "general" "withtop" 36 0 { - render_criteria = [criterionHasTopBar]; - }) - - # Root block for brightness/volume indicators - { - name = "indicator_root"; - parent = ""; - hook = mkHook "MM" "MM"; - offset = Vec2 0 0; # Vec2 instead of mkVec2 to not apply scaling. - render_criteria = [ - (And [ - (struct "Tag" "indicator") - ]) - ]; - params = struct "NotificationBlock" (unnamedStruct { - monitor = 1; - border_width = globalScale * 2; - border_rounding = globalScale * 0; - background_color = colors.base00; - border_color = colors.base04; - border_color_low = colors.base04; - border_color_critical = colors.base08; - border_color_paused = colors.base09; - gap = mkVec2 0 0; - notification_hook = mkHook "MM" "MM"; - }); - } - # Dummy text that enforces minimum window size - { - name = "indicator_size"; - parent = "indicator_root"; - hook = mkHook "TL" "TL"; - offset = Vec2 0 0; - params = struct "TextBlock" (unnamedStruct { - text = ""; - font = "${fonts.monospace.name} 1"; - color = colors.base06; - padding = mkPaddingLrBt 0 0 0 0; - dimensions = mkDimensionsWH 384 384 384 384; - }); - } - { - name = "indicator_summary"; - parent = "indicator_size"; - hook = mkHook "TM" "TM"; - offset = mkVec2 0 0; - params = struct "TextBlock" (unnamedStruct { - text = "%s"; - font = "${fonts.sansSerif.name} Bold ${toString (globalScale * 18)}"; - align = mkLiteral "Center"; - color = colors.base06; - padding = mkPaddingLrBt 0 0 8 8; - dimensions = mkDimensionsWH 300 300 32 32; - }); - } - { - name = "indicator_hint"; - parent = "indicator_summary"; - hook = mkHook "BM" "TM"; - offset = mkVec2 0 0; - params = struct "ImageBlock" (unnamedStruct { - filter_mode = mkLiteral "Lanczos3"; - image_type = mkLiteral "Hint"; - min_height = floor (globalScale * 180); - min_width = floor (globalScale * 180); - padding = mkPaddingLrBt 0 0 (35 + 8) 35; - rounding = globalScale * 9; - scale_height = floor (globalScale * 180); - scale_width = floor (globalScale * 180); - }); - } - ( - mkIndicatorValue "indicator" "anything" "hint" { + layout_blocks = + let + criterionHasTopBar = And [ + (mkLiteral "AppImage") + (Not (Or [ + (struct "AppName" "") + (struct "AppName" "notify-send") + ])) + ]; + in + map unnamedStruct ( + lib.flatten [ + # Root block for normal notifications + { + name = "general_root"; + parent = ""; + hook = mkHook "TR" "TR"; + offset = Vec2 (-50) 50; # Vec2 instead of mkVec2 to not apply scaling. render_criteria = [ (Not (Or [ - (struct "Note" "brightness") - (struct "Note" "volume") - (struct "Note" "volume-overdrive") + (struct "Tag" "indicator") ])) ]; + params = struct "NotificationBlock" (unnamedStruct { + monitor = 1; + border_width = globalScale * 2; + border_rounding = globalScale * 0; + background_color = colors.base00; + border_color = colors.base04; + border_color_low = colors.base04; + border_color_critical = colors.base08; + border_color_paused = colors.base09; + gap = mkVec2 0 8; + notification_hook = mkHook "BR" "TR"; + }); } - # text extra - {} - # progress extra - {} - ) - (mkIndicatorValue "indicator" "brightness" "hint" { + # Dummy text that enforces minimum window size + { + name = "general_size"; + parent = "general_root"; + hook = mkHook "TL" "TL"; + offset = Vec2 0 0; + params = struct "TextBlock" (unnamedStruct { + text = ""; + font = "${fonts.monospace.name} 1"; + color = colors.base06; + padding = mkPaddingLrBt 0 0 0 0; + dimensions = mkDimensionsWH 600 600 1 1; + }); + } + # Time is always shown in the top right corner. + { + name = "general_time"; + parent = "general_root"; + hook = mkHook "TL" "TL"; + offset = mkVec2 (600 - 100) 0; + params = struct "TextBlock" (unnamedStruct { + color = colors.base05; + dimensions = mkDimensionsWH 100 100 28 28; + ellipsize = mkLiteral "End"; + font = "${fonts.monospace.name} Bold ${toString (globalScale * 14)}"; + padding = mkPaddingLrBt 0 16 4 12; + text = "%t(%a %H:%M)"; + }); + } + # Top bar for app image, name and time, but only + # if there is an app name or image. + (mkTopBar "general" { + render_criteria = [ criterionHasTopBar ]; + }) + # if no top bar present: A body with no offset and a summary padding to the right (to not overlay the time) + (mkBody "general" "notop" 0 (16 + 100) { + render_criteria = [ (Not criterionHasTopBar) ]; + }) + # if top bar present: A body with matching y offset and no summary padding to the right + (mkBody "general" "withtop" 36 0 { + render_criteria = [ criterionHasTopBar ]; + }) + + # Root block for brightness/volume indicators + { + name = "indicator_root"; + parent = ""; + hook = mkHook "MM" "MM"; + offset = Vec2 0 0; # Vec2 instead of mkVec2 to not apply scaling. render_criteria = [ (And [ - (struct "Note" "brightness") + (struct "Tag" "indicator") ]) ]; + params = struct "NotificationBlock" (unnamedStruct { + monitor = 1; + border_width = globalScale * 2; + border_rounding = globalScale * 0; + background_color = colors.base00; + border_color = colors.base04; + border_color_low = colors.base04; + border_color_critical = colors.base08; + border_color_paused = colors.base09; + gap = mkVec2 0 0; + notification_hook = mkHook "MM" "MM"; + }); } - # text extra - {} - # progress extra + # Dummy text that enforces minimum window size { - fill_color = colors.base0A; - }) - (mkIndicatorValue "indicator" "volume" "hint" { - render_criteria = [ - (And [ - (struct "Note" "volume") - ]) - ]; + name = "indicator_size"; + parent = "indicator_root"; + hook = mkHook "TL" "TL"; + offset = Vec2 0 0; + params = struct "TextBlock" (unnamedStruct { + text = ""; + font = "${fonts.monospace.name} 1"; + color = colors.base06; + padding = mkPaddingLrBt 0 0 0 0; + dimensions = mkDimensionsWH 384 384 384 384; + }); } - # text extra { - text = "%b"; + name = "indicator_summary"; + parent = "indicator_size"; + hook = mkHook "TM" "TM"; + offset = mkVec2 0 0; + params = struct "TextBlock" (unnamedStruct { + text = "%s"; + font = "${fonts.sansSerif.name} Bold ${toString (globalScale * 18)}"; + align = mkLiteral "Center"; + color = colors.base06; + padding = mkPaddingLrBt 0 0 8 8; + dimensions = mkDimensionsWH 300 300 32 32; + }); } - # progress extra { - fill_color = colors.base0B; - }) - (mkIndicatorValue "indicator" "volume_overdrive" "hint" { - render_criteria = [ - (And [ - (struct "Note" "volume-overdrive") - ]) - ]; + name = "indicator_hint"; + parent = "indicator_summary"; + hook = mkHook "BM" "TM"; + offset = mkVec2 0 0; + params = struct "ImageBlock" (unnamedStruct { + filter_mode = mkLiteral "Lanczos3"; + image_type = mkLiteral "Hint"; + min_height = floor (globalScale * 180); + min_width = floor (globalScale * 180); + padding = mkPaddingLrBt 0 0 (35 + 8) 35; + rounding = globalScale * 9; + scale_height = floor (globalScale * 180); + scale_width = floor (globalScale * 180); + }); } - # text extra - { - text = "%b"; - } - # progress extra - { - background_color = colors.base0B; - fill_color = colors.base0A; - }) - ]); + (mkIndicatorValue "indicator" "anything" "hint" + { + render_criteria = [ + (Not (Or [ + (struct "Note" "brightness") + (struct "Note" "volume") + (struct "Note" "volume-overdrive") + ])) + ]; + } + # text extra + { } + # progress extra + { } + ) + (mkIndicatorValue "indicator" "brightness" "hint" + { + render_criteria = [ + (And [ + (struct "Note" "brightness") + ]) + ]; + } + # text extra + { } + # progress extra + { + fill_color = colors.base0A; + } + ) + (mkIndicatorValue "indicator" "volume" "hint" + { + render_criteria = [ + (And [ + (struct "Note" "volume") + ]) + ]; + } + # text extra + { + text = "%b"; + } + # progress extra + { + fill_color = colors.base0B; + } + ) + (mkIndicatorValue "indicator" "volume_overdrive" "hint" + { + render_criteria = [ + (And [ + (struct "Note" "volume-overdrive") + ]) + ]; + } + # text extra + { + text = "%b"; + } + # progress extra + { + background_color = colors.base0B; + fill_color = colors.base0A; + } + ) + ] + ); }); }; } diff --git a/users/myuser/neovim/alpha.nix b/users/myuser/neovim/alpha.nix index 35f0fa0..878ceab 100644 --- a/users/myuser/neovim/alpha.nix +++ b/users/myuser/neovim/alpha.nix @@ -1,89 +1,87 @@ { programs.nixvim.plugins.alpha = { enable = true; - layout = let - padding = val: { - type = "padding"; - inherit val; - }; - in [ - (padding 2) - { - opts = { - hl = "Type"; - position = "center"; + layout = + let + padding = val: { + type = "padding"; + inherit val; }; - type = "text"; - val = [ - " ███╗ ██╗██╗██╗ ██╗██╗ ██╗██╗███╗ ███╗ " - " ████╗ ██║██║╚██╗██╔╝██║ ██║██║████╗ ████║ " - " ██╔██╗ ██║██║ ╚███╔╝ ██║ ██║██║██╔████╔██║ " - " ██║╚██╗██║██║ ██╔██╗ ╚██╗ ██╔╝██║██║╚██╔╝██║ " - " ██║ ╚████║██║██╔╝ ██╗ ╚████╔╝ ██║██║ ╚═╝ ██║ " - " ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ " - ]; - } - (padding 2) - { - type = "group"; - opts.spacing = 1; - val = [ - { - type = "button"; - val = " New file"; - on_press.__raw = - /* - lua - */ - "function() vim.cmd[[enew]] end"; - opts = { - shortcut = "e"; - position = "center"; - hl_shortcut = "keyword"; - align_shortcut = "right"; - width = 50; - cursor = 3; - keymap = [ - "n" - "e" - ":enew" - { - noremap = true; - silent = true; - nowait = true; - } - ]; - }; - } - { - type = "button"; - val = "󰅙 Quit Neovim"; - on_press.__raw = - /* - lua - */ - "function() vim.cmd[[qa]] end"; - opts = { - shortcut = "q"; - position = "center"; - hl_shortcut = "keyword"; - align_shortcut = "right"; - width = 50; - cursor = 3; - keymap = [ - "n" - "q" - ":qa" - { - noremap = true; - silent = true; - nowait = true; - } - ]; - }; - } - ]; - } - ]; + in + [ + (padding 2) + { + opts = { + hl = "Type"; + position = "center"; + }; + type = "text"; + val = [ + " ███╗ ██╗██╗██╗ ██╗██╗ ██╗██╗███╗ ███╗ " + " ████╗ ██║██║╚██╗██╔╝██║ ██║██║████╗ ████║ " + " ██╔██╗ ██║██║ ╚███╔╝ ██║ ██║██║██╔████╔██║ " + " ██║╚██╗██║██║ ██╔██╗ ╚██╗ ██╔╝██║██║╚██╔╝██║ " + " ██║ ╚████║██║██╔╝ ██╗ ╚████╔╝ ██║██║ ╚═╝ ██║ " + " ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ " + ]; + } + (padding 2) + { + type = "group"; + opts.spacing = 1; + val = [ + { + type = "button"; + val = " New file"; + on_press.__raw = + # lua + "function() vim.cmd[[enew]] end"; + opts = { + shortcut = "e"; + position = "center"; + hl_shortcut = "keyword"; + align_shortcut = "right"; + width = 50; + cursor = 3; + keymap = [ + "n" + "e" + ":enew" + { + noremap = true; + silent = true; + nowait = true; + } + ]; + }; + } + { + type = "button"; + val = "󰅙 Quit Neovim"; + on_press.__raw = + # lua + "function() vim.cmd[[qa]] end"; + opts = { + shortcut = "q"; + position = "center"; + hl_shortcut = "keyword"; + align_shortcut = "right"; + width = 50; + cursor = 3; + keymap = [ + "n" + "q" + ":qa" + { + noremap = true; + silent = true; + nowait = true; + } + ]; + }; + } + ]; + } + ]; }; } diff --git a/users/myuser/neovim/appearance.nix b/users/myuser/neovim/appearance.nix index 84d5a6b..245aea8 100644 --- a/users/myuser/neovim/appearance.nix +++ b/users/myuser/neovim/appearance.nix @@ -4,24 +4,42 @@ lualine = { enable = true; settings = { - extensions = ["fzf" "nvim-dap-ui" "symbols-outline" "trouble" "neo-tree" "quickfix" "fugitive"]; + extensions = [ + "fzf" + "nvim-dap-ui" + "symbols-outline" + "trouble" + "neo-tree" + "quickfix" + "fugitive" + ]; component_separators.left = null; component_separators.right = null; sections = { - lualine_a = ["mode"]; - lualine_b = ["branch" "filename"]; - lualine_c = ["diff" "diagnostics"]; - lualine_x = ["encoding" "fileformat" "filetype"]; - lualine_y = ["progress"]; - lualine_z = ["location"]; + lualine_a = [ "mode" ]; + lualine_b = [ + "branch" + "filename" + ]; + lualine_c = [ + "diff" + "diagnostics" + ]; + lualine_x = [ + "encoding" + "fileformat" + "filetype" + ]; + lualine_y = [ "progress" ]; + lualine_z = [ "location" ]; }; inactive_sections = { - lualine_a = ["filename"]; - lualine_b = []; - lualine_c = ["diagnostics"]; - lualine_x = []; - lualine_y = []; - lualine_z = ["location"]; + lualine_a = [ "filename" ]; + lualine_b = [ ]; + lualine_c = [ "diagnostics" ]; + lualine_x = [ ]; + lualine_y = [ ]; + lualine_z = [ "location" ]; }; }; }; diff --git a/users/myuser/neovim/completion.nix b/users/myuser/neovim/completion.nix index bba9eda..3a3cc01 100644 --- a/users/myuser/neovim/completion.nix +++ b/users/myuser/neovim/completion.nix @@ -25,14 +25,14 @@ enable = true; settings = { sources = [ - {name = "nvim_lsp_signature_help";} - {name = "nvim_lsp";} - {name = "nvim_lsp_document_symbol";} - {name = "path";} - {name = "treesitter";} - {name = "dap";} + { name = "nvim_lsp_signature_help"; } + { name = "nvim_lsp"; } + { name = "nvim_lsp_document_symbol"; } + { name = "path"; } + { name = "treesitter"; } + { name = "dap"; } # {name = "luasnip";} - {name = "emoji";} + { name = "emoji"; } ]; mapping = { "" = @@ -100,7 +100,11 @@ end, {"i", "s"}) ''; }; - formatting.fields = ["abbr" "kind" "menu"]; + formatting.fields = [ + "abbr" + "kind" + "menu" + ]; formatting.format = # lua '' diff --git a/users/myuser/neovim/default.nix b/users/myuser/neovim/default.nix index 035921a..e81ca4c 100644 --- a/users/myuser/neovim/default.nix +++ b/users/myuser/neovim/default.nix @@ -33,12 +33,13 @@ # Hide line numbers in terminal windows autoCmd = [ { - event = ["BufEnter" "BufWinEnter"]; - pattern = ["term://*"]; + event = [ + "BufEnter" + "BufWinEnter" + ]; + pattern = [ "term://*" ]; callback.__raw = - /* - lua - */ + # lua '' function() vim.bo.number = false @@ -46,12 +47,10 @@ ''; } { - event = ["WinEnter"]; - pattern = ["*"]; + event = [ "WinEnter" ]; + pattern = [ "*" ]; callback.__raw = - /* - lua - */ + # lua '' function() pcall(function() @@ -67,172 +66,268 @@ ]; # TODO split into files - keymaps = let - keymap = mode: key: action: desc: { - inherit action key mode; - options = { - silent = true; - inherit desc; + keymaps = + let + keymap = mode: key: action: desc: { + inherit action key mode; + options = { + silent = true; + inherit desc; + }; }; - }; - in [ - # ------------------------------------------------------------------------------------------------- - # General - # ------------------------------------------------------------------------------------------------- + in + [ + # ------------------------------------------------------------------------------------------------- + # General + # ------------------------------------------------------------------------------------------------- - # Shift + scroll with cursor locked to position - (keymap ["n" "v"] "" "" "") - (keymap ["n" "v"] "" "" "") - (keymap ["i"] "" "" "") - (keymap ["i"] "" "" "") + # Shift + scroll with cursor locked to position + (keymap [ + "n" + "v" + ] "" "" "") + (keymap [ + "n" + "v" + ] "" "" "") + (keymap [ "i" ] "" "" "") + (keymap [ "i" ] "" "" "") - # Shift + Alt + change the current window size - (keymap ["n"] "" ":resize -2" "") - (keymap ["n"] "" ":resize +2" "") - (keymap ["n"] "" ":vertical resize -2" "") - (keymap ["n"] "" ":vertical resize +2" "") + # Shift + Alt + change the current window size + (keymap [ "n" ] "" ":resize -2" "") + (keymap [ "n" ] "" ":resize +2" "") + (keymap [ "n" ] "" ":vertical resize -2" "") + (keymap [ "n" ] "" ":vertical resize +2" "") - # Allow exiting terminal mode - (keymap ["t"] "" "" "") - # Allow C-w in terminal mode - (keymap ["t"] "" "" "") + # Allow exiting terminal mode + (keymap [ "t" ] "" "" "") + # Allow C-w in terminal mode + (keymap [ "t" ] "" "" "") - # Open fixed size terminal window at the bottom - (keymap ["n"] "" ":belowright new | setlocal wfh | resize 10 | terminal" "Open Terminal") + # Open fixed size terminal window at the bottom + (keymap [ + "n" + ] "" ":belowright new | setlocal wfh | resize 10 | terminal" "Open Terminal") - # ------------------------------------------------------------------------------------------------- - # Language server - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Language server + # ------------------------------------------------------------------------------------------------- - (keymap ["n"] "gD" "lua vim.lsp.buf.declaration()" "Goto declaration") - (keymap ["n"] "gd" "lua require('telescope.builtin').lsp_definitions()" "Goto definition") - (keymap ["n"] "K" "lua vim.lsp.buf.hover()" "Hover") - (keymap ["n"] "gi" "lua require('telescope.builtin').lsp_implementations()" "Goto implementation") - (keymap ["n"] "" "lua vim.lsp.buf.signature_help()" "Signature Help") - (keymap ["n"] "wa" "lua vim.lsp.buf.add_workspace_folder()" "Add workspace folder") - (keymap ["n"] "wr" "lua vim.lsp.buf.remove_workspace_folder()" "Remove workspace folder") - (keymap ["n"] "wl" "lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))" "List workspace folders") - (keymap ["n"] "gt" "lua require('telescope.builtin').lsp_type_definitions()" "Goto type-definition") - (keymap ["n"] "r" "lua vim.lsp.buf.rename()" "Rename") - (keymap ["n"] "a" "lua vim.lsp.buf.code_action()" "Code Action") - (keymap ["n"] "gr" "lua require('telescope.builtin').lsp_references()" "References") - (keymap ["n"] "gl" "lua vim.diagnostic.open_float()" "Diagnostic float") - (keymap ["n"] "[d" "lua vim.diagnostic.goto_prev()" "Next diagnostic") - (keymap ["n"] "]d" "lua vim.diagnostic.goto_next()" "Previous diagnostic") - (keymap ["n"] "q" "lua vim.diagnostic.setloclist()" "Show diagnostic quickfix list") - (keymap ["n"] "f" "lua vim.lsp.buf.format { async = true }" "Format code") + (keymap [ "n" ] "gD" "lua vim.lsp.buf.declaration()" "Goto declaration") + (keymap [ + "n" + ] "gd" "lua require('telescope.builtin').lsp_definitions()" "Goto definition") + (keymap [ "n" ] "K" "lua vim.lsp.buf.hover()" "Hover") + (keymap [ + "n" + ] "gi" "lua require('telescope.builtin').lsp_implementations()" "Goto implementation") + (keymap [ "n" ] "" "lua vim.lsp.buf.signature_help()" "Signature Help") + (keymap [ + "n" + ] "wa" "lua vim.lsp.buf.add_workspace_folder()" "Add workspace folder") + (keymap [ + "n" + ] "wr" "lua vim.lsp.buf.remove_workspace_folder()" "Remove workspace folder") + (keymap [ "n" ] "wl" "lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))" + "List workspace folders" + ) + (keymap [ + "n" + ] "gt" "lua require('telescope.builtin').lsp_type_definitions()" "Goto type-definition") + (keymap [ "n" ] "r" "lua vim.lsp.buf.rename()" "Rename") + (keymap [ "n" ] "a" "lua vim.lsp.buf.code_action()" "Code Action") + (keymap [ "n" ] "gr" "lua require('telescope.builtin').lsp_references()" "References") + (keymap [ "n" ] "gl" "lua vim.diagnostic.open_float()" "Diagnostic float") + (keymap [ "n" ] "[d" "lua vim.diagnostic.goto_prev()" "Next diagnostic") + (keymap [ "n" ] "]d" "lua vim.diagnostic.goto_next()" "Previous diagnostic") + (keymap [ + "n" + ] "q" "lua vim.diagnostic.setloclist()" "Show diagnostic quickfix list") + (keymap [ "n" ] "f" "lua vim.lsp.buf.format { async = true }" "Format code") - # ------------------------------------------------------------------------------------------------- - # Plugin: Easy Align - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Easy Align + # ------------------------------------------------------------------------------------------------- - (keymap ["n"] "A" "(EasyAlign)" "Easy-Align") - (keymap ["v"] "A" "(EasyAlign)" "Easy-Align") + (keymap [ "n" ] "A" "(EasyAlign)" "Easy-Align") + (keymap [ "v" ] "A" "(EasyAlign)" "Easy-Align") - # ------------------------------------------------------------------------------------------------- - # Plugin: Undotree - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Undotree + # ------------------------------------------------------------------------------------------------- - (keymap ["n"] "u" ":UndotreeToggle" "Undotree") + (keymap [ "n" ] "u" ":UndotreeToggle" "Undotree") - # ------------------------------------------------------------------------------------------------- - # Plugin: Better Whitespace - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Better Whitespace + # ------------------------------------------------------------------------------------------------- - (keymap ["n"] "$" ":StripWhitespace" "Strip whitespace") + (keymap [ "n" ] "$" ":StripWhitespace" "Strip whitespace") - # ------------------------------------------------------------------------------------------------- - # Plugin: Neotree - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Neotree + # ------------------------------------------------------------------------------------------------- - # Mappings to open the tree / find the current file - (keymap ["n"] "t" ":Neotree toggle" "Filetree toggle") - (keymap ["n"] "T" ":Neotree reveal" "Filetree reveal current file") - (keymap ["n"] "G" ":Neotree float git_status" "Show git status") - (keymap ["n"] "b" ":Neotree float buffers" "Show open buffers") + # Mappings to open the tree / find the current file + (keymap [ "n" ] "t" ":Neotree toggle" "Filetree toggle") + (keymap [ "n" ] "T" ":Neotree reveal" "Filetree reveal current file") + (keymap [ "n" ] "G" ":Neotree float git_status" "Show git status") + (keymap [ "n" ] "b" ":Neotree float buffers" "Show open buffers") - # ------------------------------------------------------------------------------------------------- - # Plugin: Sandwich - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Sandwich + # ------------------------------------------------------------------------------------------------- - (keymap ["n" "v"] "m" "(operator-sandwich-add)" "Sandwich Add") - (keymap ["n" "v"] "M" "(operator-sandwich-delete)" "Sandwich Delete") - (keymap ["n" "v"] "" "(operator-sandwich-replace)" "Sandwich Replace") + (keymap [ + "n" + "v" + ] "m" "(operator-sandwich-add)" "Sandwich Add") + (keymap [ + "n" + "v" + ] "M" "(operator-sandwich-delete)" "Sandwich Delete") + (keymap [ + "n" + "v" + ] "" "(operator-sandwich-replace)" "Sandwich Replace") - # ------------------------------------------------------------------------------------------------- - # Plugin: gomove - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: gomove + # ------------------------------------------------------------------------------------------------- - #(keymap ["n"] "" "GoNSMLeft" "") - #(keymap ["n"] "" "GoNSMDown" "") - #(keymap ["n"] "" "GoNSMUp" "") - #(keymap ["n"] "" "GoNSMRight" "") + #(keymap ["n"] "" "GoNSMLeft" "") + #(keymap ["n"] "" "GoNSMDown" "") + #(keymap ["n"] "" "GoNSMUp" "") + #(keymap ["n"] "" "GoNSMRight" "") - (keymap ["x"] "" "GoVSMLeft" "") - (keymap ["x"] "" "GoVSMDown" "") - (keymap ["x"] "" "GoVSMUp" "") - (keymap ["x"] "" "GoVSMRight" "") + (keymap [ "x" ] "" "GoVSMLeft" "") + (keymap [ "x" ] "" "GoVSMDown" "") + (keymap [ "x" ] "" "GoVSMUp" "") + (keymap [ "x" ] "" "GoVSMRight" "") - #(keymap ["n"] "" "GoNSDLeft" "") - #(keymap ["n"] "" "GoNSDDown" "") - #(keymap ["n"] "" "GoNSDUp" "") - #(keymap ["n"] "" "GoNSDRight" "") + #(keymap ["n"] "" "GoNSDLeft" "") + #(keymap ["n"] "" "GoNSDDown" "") + #(keymap ["n"] "" "GoNSDUp" "") + #(keymap ["n"] "" "GoNSDRight" "") - (keymap ["x"] "" "GoVSDLeft" "") - (keymap ["x"] "" "GoVSDDown" "") - (keymap ["x"] "" "GoVSDUp" "") - (keymap ["x"] "" "GoVSDRight" "") + (keymap [ "x" ] "" "GoVSDLeft" "") + (keymap [ "x" ] "" "GoVSDDown" "") + (keymap [ "x" ] "" "GoVSDUp" "") + (keymap [ "x" ] "" "GoVSDRight" "") - # ------------------------------------------------------------------------------------------------- - # Plugin: wordmotion - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: wordmotion + # ------------------------------------------------------------------------------------------------- - (keymap ["x" "o"] "ie" "WordMotion_iw" "inside subword") + (keymap [ + "x" + "o" + ] "ie" "WordMotion_iw" "inside subword") - # ------------------------------------------------------------------------------------------------- - # Plugin: telescope - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: telescope + # ------------------------------------------------------------------------------------------------- - (keymap ["n" "v"] "" "lua require('telescope.builtin').find_files()" "Telescope find files") - (keymap ["n" "v"] "g" "lua require('telescope.builtin').live_grep()" "Telescope live grep") - (keymap ["n" "v"] "b" "lua require('telescope.builtin').buffers()" "Telescope buffers") + (keymap [ + "n" + "v" + ] "" "lua require('telescope.builtin').find_files()" "Telescope find files") + (keymap [ + "n" + "v" + ] "g" "lua require('telescope.builtin').live_grep()" "Telescope live grep") + (keymap [ + "n" + "v" + ] "b" "lua require('telescope.builtin').buffers()" "Telescope buffers") - # ------------------------------------------------------------------------------------------------- - # Plugin: textcase - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: textcase + # ------------------------------------------------------------------------------------------------- - (keymap ["n"] "C" "TextCaseOpenTelescopeQuickChange" "Change word case") + (keymap [ "n" ] "C" "TextCaseOpenTelescopeQuickChange" "Change word case") - (keymap ["n"] "cu" "lua require('textcase').current_word('to_upper_case')" "To UPPER CASE") - (keymap ["n"] "cl" "lua require('textcase').current_word('to_lower_case')" "To lower case") - (keymap ["n"] "cs" "lua require('textcase').current_word('to_snake_case')" "To snake_case") - (keymap ["n"] "cd" "lua require('textcase').current_word('to_dash_case')" "To dash-case") - (keymap ["n"] "cn" "lua require('textcase').current_word('to_constant_case')" "To CONSTANT_CASE") - (keymap ["n"] "cd" "lua require('textcase').current_word('to_dot_case')" "To dot.case") - (keymap ["n"] "ca" "lua require('textcase').current_word('to_phrase_case')" "To Phrase case") - (keymap ["n"] "cc" "lua require('textcase').current_word('to_camel_case')" "To camelCase") - (keymap ["n"] "cp" "lua require('textcase').current_word('to_pascal_case')" "To PascalCase") - (keymap ["n"] "ct" "lua require('textcase').current_word('to_title_case')" "To Title Case") - (keymap ["n"] "cf" "lua require('textcase').current_word('to_path_case')" "To path/case") + (keymap [ "n" ] "cu" "lua require('textcase').current_word('to_upper_case')" + "To UPPER CASE" + ) + (keymap [ "n" ] "cl" "lua require('textcase').current_word('to_lower_case')" + "To lower case" + ) + (keymap [ "n" ] "cs" "lua require('textcase').current_word('to_snake_case')" + "To snake_case" + ) + (keymap [ "n" ] "cd" "lua require('textcase').current_word('to_dash_case')" + "To dash-case" + ) + (keymap [ "n" ] "cn" + "lua require('textcase').current_word('to_constant_case')" + "To CONSTANT_CASE" + ) + (keymap [ + "n" + ] "cd" "lua require('textcase').current_word('to_dot_case')" "To dot.case") + (keymap [ "n" ] "ca" + "lua require('textcase').current_word('to_phrase_case')" + "To Phrase case" + ) + (keymap [ "n" ] "cc" "lua require('textcase').current_word('to_camel_case')" + "To camelCase" + ) + (keymap [ "n" ] "cp" + "lua require('textcase').current_word('to_pascal_case')" + "To PascalCase" + ) + (keymap [ "n" ] "ct" "lua require('textcase').current_word('to_title_case')" + "To Title Case" + ) + (keymap [ "n" ] "cf" "lua require('textcase').current_word('to_path_case')" + "To path/case" + ) - (keymap ["n"] "cU" "lua require('textcase').lsp_rename('to_upper_case')" "LSP Rename: To UPPER CASE") - (keymap ["n"] "cL" "lua require('textcase').lsp_rename('to_lower_case')" "LSP Rename: To lower case") - (keymap ["n"] "cS" "lua require('textcase').lsp_rename('to_snake_case')" "LSP Rename: To snake_case") - (keymap ["n"] "cD" "lua require('textcase').lsp_rename('to_dash_case')" "LSP Rename: To dash-case") - (keymap ["n"] "cN" "lua require('textcase').lsp_rename('to_constant_case')" "LSP Rename: To CONSTANT_CASE") - (keymap ["n"] "cD" "lua require('textcase').lsp_rename('to_dot_case')" "LSP Rename: To dot.case") - (keymap ["n"] "cA" "lua require('textcase').lsp_rename('to_phrase_case')" "LSP Rename: To Phrase case") - (keymap ["n"] "cC" "lua require('textcase').lsp_rename('to_camel_case')" "LSP Rename: To camelCase") - (keymap ["n"] "cP" "lua require('textcase').lsp_rename('to_pascal_case')" "LSP Rename: To PascalCase") - (keymap ["n"] "cT" "lua require('textcase').lsp_rename('to_title_case')" "LSP Rename: To Title Case") - (keymap ["n"] "cF" "lua require('textcase').lsp_rename('to_path_case')" "LSP Rename: To path/case") + (keymap [ "n" ] "cU" "lua require('textcase').lsp_rename('to_upper_case')" + "LSP Rename: To UPPER CASE" + ) + (keymap [ "n" ] "cL" "lua require('textcase').lsp_rename('to_lower_case')" + "LSP Rename: To lower case" + ) + (keymap [ "n" ] "cS" "lua require('textcase').lsp_rename('to_snake_case')" + "LSP Rename: To snake_case" + ) + (keymap [ "n" ] "cD" "lua require('textcase').lsp_rename('to_dash_case')" + "LSP Rename: To dash-case" + ) + (keymap [ "n" ] "cN" + "lua require('textcase').lsp_rename('to_constant_case')" + "LSP Rename: To CONSTANT_CASE" + ) + (keymap [ "n" ] "cD" "lua require('textcase').lsp_rename('to_dot_case')" + "LSP Rename: To dot.case" + ) + (keymap [ "n" ] "cA" "lua require('textcase').lsp_rename('to_phrase_case')" + "LSP Rename: To Phrase case" + ) + (keymap [ "n" ] "cC" "lua require('textcase').lsp_rename('to_camel_case')" + "LSP Rename: To camelCase" + ) + (keymap [ "n" ] "cP" "lua require('textcase').lsp_rename('to_pascal_case')" + "LSP Rename: To PascalCase" + ) + (keymap [ "n" ] "cT" "lua require('textcase').lsp_rename('to_title_case')" + "LSP Rename: To Title Case" + ) + (keymap [ "n" ] "cF" "lua require('textcase').lsp_rename('to_path_case')" + "LSP Rename: To path/case" + ) - # ------------------------------------------------------------------------------------------------- - # Plugin: Neogit - # ------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------- + # Plugin: Neogit + # ------------------------------------------------------------------------------------------------- - (keymap ["n" "v"] "g" "lua require('neogit').open()" "Open Neogit") - ]; + (keymap [ + "n" + "v" + ] "g" "lua require('neogit').open()" "Open Neogit") + ]; }; home.sessionVariables.EDITOR = "nvim"; diff --git a/users/myuser/neovim/globals.nix b/users/myuser/neovim/globals.nix index b941c1d..9d9c78d 100644 --- a/users/myuser/neovim/globals.nix +++ b/users/myuser/neovim/globals.nix @@ -22,8 +22,13 @@ number = true; # Show line numbers cursorline = true; # Enable cursorline, colorscheme only shows this in number column - wildmode = ["list" "full"]; # Only complete the longest common prefix and list all results - fillchars = {stlnc = "─";}; # Show separators in inactive window statuslines + wildmode = [ + "list" + "full" + ]; # Only complete the longest common prefix and list all results + fillchars = { + stlnc = "─"; + }; # Show separators in inactive window statuslines # FIXME: disabled because this really fucks everything up in the terminal. title = false; # Sets the window title diff --git a/users/myuser/neovim/languages.nix b/users/myuser/neovim/languages.nix index ed7d833..79e8068 100644 --- a/users/myuser/neovim/languages.nix +++ b/users/myuser/neovim/languages.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ programs.nixvim = { files."ftplugin/nix.lua".extraConfigLua = '' vim.opt_local.expandtab = true @@ -37,22 +38,24 @@ rustaceanvim = { enable = true; settings = { - server.default_settings.files.excludeDirs = [".direnv"]; + server.default_settings.files.excludeDirs = [ ".direnv" ]; dap.autoloadConfigurations = true; - dap.adapter = let - code-lldb = pkgs.vscode-extensions.vadimcn.vscode-lldb; - in { - executable.command = "${code-lldb}/share/vscode/extensions/vadimcn.vscode-lldb/adapter/codelldb"; - executable.args = [ - "--liblldb" - "${code-lldb}/share/vscode/extensions/vadimcn.vscode-lldb/lldb/lib/liblldb.dylib" - "--port" - "31337" - ]; - type = "server"; - port = "31337"; - host = "127.0.0.1"; - }; + dap.adapter = + let + code-lldb = pkgs.vscode-extensions.vadimcn.vscode-lldb; + in + { + executable.command = "${code-lldb}/share/vscode/extensions/vadimcn.vscode-lldb/adapter/codelldb"; + executable.args = [ + "--liblldb" + "${code-lldb}/share/vscode/extensions/vadimcn.vscode-lldb/lldb/lib/liblldb.dylib" + "--port" + "31337" + ]; + type = "server"; + port = "31337"; + host = "127.0.0.1"; + }; }; }; }; diff --git a/users/myuser/neovim/lsp.nix b/users/myuser/neovim/lsp.nix index 3740b6f..6ce3fbf 100644 --- a/users/myuser/neovim/lsp.nix +++ b/users/myuser/neovim/lsp.nix @@ -2,7 +2,8 @@ lib, pkgs, ... -}: { +}: +{ programs.nixvim.plugins = { nvim-jdtls = { enable = true; @@ -14,9 +15,7 @@ lsp = { enable = true; preConfig = - /* - lua - */ + # lua '' local lsp_symbol = function(name, icon) vim.fn.sign_define( @@ -38,7 +37,10 @@ nil_ls = { enable = true; settings = { - formatting.command = [(lib.getExe pkgs.nixfmt-rfc-style) "--quiet"]; + formatting.command = [ + (lib.getExe pkgs.nixfmt-rfc-style) + "--quiet" + ]; }; }; nixd.enable = true; diff --git a/users/myuser/neovim/misc.nix b/users/myuser/neovim/misc.nix index e8b1cfe..3f25c39 100644 --- a/users/myuser/neovim/misc.nix +++ b/users/myuser/neovim/misc.nix @@ -1,4 +1,5 @@ -{pkgs, ...}: { +{ pkgs, ... }: +{ programs.nixvim = { plugins = { notify = { @@ -24,12 +25,15 @@ # Fzf picker for arbitrary stuff telescope = { enable = true; - enabledExtensions = ["fzf" "notify" "ui-select" "textcase"]; + enabledExtensions = [ + "fzf" + "notify" + "ui-select" + "textcase" + ]; extensions.fzf-native.enable = true; settings.defaults.mappings.i."".__raw = - /* - lua - */ + # lua '' function(...) return require("telescope.actions").close(...) @@ -80,9 +84,7 @@ ]; extraConfigLuaPre = - /* - lua - */ + # lua '' vim.g.operator_sandwich_no_default_key_mappings = 1 vim.g.textobj_sandwich_no_default_key_mappings = 1 @@ -91,9 +93,7 @@ ''; extraConfigLuaPost = - /* - lua - */ + # lua '' require("window-picker").setup { hint = "floating-big-letter", diff --git a/users/myuser/neovim/neo-tree.nix b/users/myuser/neovim/neo-tree.nix index 19114ac..f2359ef 100644 --- a/users/myuser/neovim/neo-tree.nix +++ b/users/myuser/neovim/neo-tree.nix @@ -56,7 +56,7 @@ useLibuvFileWatcher = true; filteredItems = { hideDotfiles = false; - hideByName = [".git"]; + hideByName = [ ".git" ]; }; }; }; diff --git a/users/myuser/neovim/onedark.nix b/users/myuser/neovim/onedark.nix index 01771bc..8694b14 100644 --- a/users/myuser/neovim/onedark.nix +++ b/users/myuser/neovim/onedark.nix @@ -2,7 +2,8 @@ lib, pkgs, ... -}: { +}: +{ programs.nixvim = { extraPlugins = with pkgs.vimPlugins; [ # navarasu's one dark @@ -11,74 +12,74 @@ extraConfigLua = lib.mkBefore - # lua - '' - local onedark = require "onedark" - onedark.setup { - toggle_style_key = "", - colors = { - fg = "#abb2bf", - black = "#181a1f", - bg0 = "#1e222a", - bg1 = "#252931", - bg2 = "#282c34", - bg3 = "#353b45", - bg_d = "#191c21", - bg_blue = "#73b8f1", - bg_yellow = "#ebd09c", + # lua + '' + local onedark = require "onedark" + onedark.setup { + toggle_style_key = "", + colors = { + fg = "#abb2bf", + black = "#181a1f", + bg0 = "#1e222a", + bg1 = "#252931", + bg2 = "#282c34", + bg3 = "#353b45", + bg_d = "#191c21", + bg_blue = "#73b8f1", + bg_yellow = "#ebd09c", - dark_cyan = "#2b6f77", - dark_red = "#993939", - dark_yellow = "#93691d", + dark_cyan = "#2b6f77", + dark_red = "#993939", + dark_yellow = "#93691d", - grey = "#42464e", - grey_fg = "#565c64", - grey_fg2 = "#6f737b", - light_grey = "#6f737b", - baby_pink = "#de8c92", - pink = "#ff75a0", - nord_blue = "#81a1c1", - sun = "#ebcb8b", - light_purple = "#de98fd", - dark_purple = "#c882e7", - teal = "#519aba", - dark_pink = "#fca2aa", - light_blue = "#a3b8ef", - vibrant_green = "#7eca9c", + grey = "#42464e", + grey_fg = "#565c64", + grey_fg2 = "#6f737b", + light_grey = "#6f737b", + baby_pink = "#de8c92", + pink = "#ff75a0", + nord_blue = "#81a1c1", + sun = "#ebcb8b", + light_purple = "#de98fd", + dark_purple = "#c882e7", + teal = "#519aba", + dark_pink = "#fca2aa", + light_blue = "#a3b8ef", + vibrant_green = "#7eca9c", - red = "#e06c75", - orange = "#d19a66", - yellow = "#e5c07b", - green = "#98c379", - cyan = "#56b6c2", - blue = "#61afef", - purple = "#c678dd", + red = "#e06c75", + orange = "#d19a66", + yellow = "#e5c07b", + green = "#98c379", + cyan = "#56b6c2", + blue = "#61afef", + purple = "#c678dd", - diff_add = "#31392b", - diff_delete = "#382b2c", - diff_change = "#1c3448", - diff_text = "#2c5372", - }, - highlights = { - CursorLine = { bg = "$bg0" }, - FloatBorder = { fg = "$blue" }, - NeoTreeTabActive = { fg = "$fg", bg = "$bg_d" }, - NeoTreeTabInactive = { fg = "$grey", bg = "$black" }, - NeoTreeTabSeparatorActive = { fg = "$black", bg = "$black" }, - NeoTreeTabSeparatorInactive = { fg = "$black", bg = "$black" }, - NeoTreeWinSeparator = { fg = "$bg_d", bg = "$bg_d" }, - NeoTreeVertSplit = { fg = "$bg_d", bg = "$bg_d" }, - VisualMultiMono = { fg = "$purple", bg = "$diff_change" }, - VisualMultiExtend = { bg = "$diff_change" }, - VisualMultiCursor = { fg = "$purple", bg = "$diff_change" }, - VisualMultiInsert = { fg = "$blue", bg = "$diff_change" }, - }, - } - vim.g.VM_Mono_hl = "VisualMultiMono" - vim.g.VM_Extend_hl = "VisualMultiExtend" - vim.g.VM_Cursor_hl = "VisualMultiCursor" - vim.g.VM_Insert_hl = "VisualMultiInsert" - onedark.load() - ''; + diff_add = "#31392b", + diff_delete = "#382b2c", + diff_change = "#1c3448", + diff_text = "#2c5372", + }, + highlights = { + CursorLine = { bg = "$bg0" }, + FloatBorder = { fg = "$blue" }, + NeoTreeTabActive = { fg = "$fg", bg = "$bg_d" }, + NeoTreeTabInactive = { fg = "$grey", bg = "$black" }, + NeoTreeTabSeparatorActive = { fg = "$black", bg = "$black" }, + NeoTreeTabSeparatorInactive = { fg = "$black", bg = "$black" }, + NeoTreeWinSeparator = { fg = "$bg_d", bg = "$bg_d" }, + NeoTreeVertSplit = { fg = "$bg_d", bg = "$bg_d" }, + VisualMultiMono = { fg = "$purple", bg = "$diff_change" }, + VisualMultiExtend = { bg = "$diff_change" }, + VisualMultiCursor = { fg = "$purple", bg = "$diff_change" }, + VisualMultiInsert = { fg = "$blue", bg = "$diff_change" }, + }, + } + vim.g.VM_Mono_hl = "VisualMultiMono" + vim.g.VM_Extend_hl = "VisualMultiExtend" + vim.g.VM_Cursor_hl = "VisualMultiCursor" + vim.g.VM_Insert_hl = "VisualMultiInsert" + onedark.load() + ''; }; } diff --git a/users/myuser/ssh.nix b/users/myuser/ssh.nix index f3cc571..51e8fc4 100644 --- a/users/myuser/ssh.nix +++ b/users/myuser/ssh.nix @@ -1,4 +1,5 @@ -{config, ...}: { +{ config, ... }: +{ home.file.".ssh/yubikey.pub".text = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA5Uq+CDy5Pmt3If5M6d8K/Q7HArU6sZ7sgoj3T521Wm"; programs.ssh = { enable = true; diff --git a/users/root/default.nix b/users/root/default.nix index 7fc94a6..d2cf45c 100644 --- a/users/root/default.nix +++ b/users/root/default.nix @@ -3,10 +3,13 @@ globals, pkgs, ... -}: { +}: +{ users.users.root = { inherit (globals.root) hashedPassword; - openssh.authorizedKeys.keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA5Uq+CDy5Pmt3If5M6d8K/Q7HArU6sZ7sgoj3T521Wm"]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA5Uq+CDy5Pmt3If5M6d8K/Q7HArU6sZ7sgoj3T521Wm" + ]; shell = pkgs.zsh; };