From 15f5c2552d0e53614d04c404a6bf5a62d67826d4 Mon Sep 17 00:00:00 2001 From: Nickolaj Jepsen Date: Wed, 21 Jan 2026 00:10:58 +0100 Subject: [PATCH] refactor: split overloaded modules --- modules/desktop/default.nix | 2 +- modules/desktop/niri.nix | 243 --------------------- modules/desktop/niri/binds.nix | 132 +++++++++++ modules/desktop/niri/default.nix | 29 +++ modules/desktop/niri/outputs.nix | 42 ++++ modules/desktop/niri/settings.nix | 64 ++++++ modules/homelab/default.nix | 4 +- modules/homelab/home-assistant.nix | 224 ------------------- modules/homelab/home-assistant/default.nix | 6 + modules/homelab/home-assistant/hass.nix | 124 +++++++++++ modules/homelab/home-assistant/mqtt.nix | 104 +++++++++ modules/homelab/sso.nix | 116 ---------- modules/homelab/sso/default.nix | 6 + modules/homelab/sso/proxy.nix | 48 ++++ modules/homelab/sso/zitadel.nix | 80 +++++++ 15 files changed, 638 insertions(+), 586 deletions(-) delete mode 100644 modules/desktop/niri.nix create mode 100644 modules/desktop/niri/binds.nix create mode 100644 modules/desktop/niri/default.nix create mode 100644 modules/desktop/niri/outputs.nix create mode 100644 modules/desktop/niri/settings.nix delete mode 100644 modules/homelab/home-assistant.nix create mode 100644 modules/homelab/home-assistant/default.nix create mode 100644 modules/homelab/home-assistant/hass.nix create mode 100644 modules/homelab/home-assistant/mqtt.nix delete mode 100644 modules/homelab/sso.nix create mode 100644 modules/homelab/sso/default.nix create mode 100644 modules/homelab/sso/proxy.nix create mode 100644 modules/homelab/sso/zitadel.nix diff --git a/modules/desktop/default.nix b/modules/desktop/default.nix index 0e369cc..1418273 100644 --- a/modules/desktop/default.nix +++ b/modules/desktop/default.nix @@ -17,7 +17,7 @@ ./audio.nix ./fonts.nix ./greetd.nix - ./niri.nix + ./niri ./qt.nix ./gtk.nix ./dms/default.nix diff --git a/modules/desktop/niri.nix b/modules/desktop/niri.nix deleted file mode 100644 index 117b1f1..0000000 --- a/modules/desktop/niri.nix +++ /dev/null @@ -1,243 +0,0 @@ -{ - config, - lib, - pkgs, - inputs, - ... -}: let - c = config.fireproof.theme.colors; - hasMonitors = config.monitors != []; - primaryMonitorName = - if hasMonitors - then (builtins.head config.monitors).name or "" - else ""; -in { - config = lib.mkIf config.fireproof.desktop.windowManager.enable { - programs.xwayland.enable = true; - - xdg.portal = { - enable = true; - extraPortals = [pkgs.xdg-desktop-portal-gtk]; - config.common.default = "gnome"; - xdgOpenUsePortal = true; - }; - programs.niri = { - enable = true; - package = inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".niri-unstable; - }; - fireproof.home-manager.programs.niri.settings = { - prefer-no-csd = true; - clipboard.disable-primary = true; - spawn-at-startup = [ - {command = ["systemctl" "--user" "start" "hyprpaper"];} - ]; - xwayland-satellite = { - enable = true; - path = lib.getExe inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".xwayland-satellite-unstable; - }; - environment = { - NIXOS_OZONE_WL = "1"; - GDK_BACKEND = "wayland"; # Attempt to fix screen recording issue - }; - layout = { - gaps = 10; - focus-ring.enable = false; - insert-hint.display.color = "#${c.accent}"; - border = { - enable = true; - width = 2; - active.color = "#${c.accent}"; - inactive.color = "#${c.ui}"; - }; - tab-indicator = { - hide-when-single-tab = true; - place-within-column = true; - gap = 2; - position = "top"; - corner-radius = 8; - }; - }; - input = { - focus-follows-mouse = { - enable = true; - max-scroll-amount = "0%"; - }; - mouse.accel-profile = "flat"; - keyboard.xkb.layout = "eu"; - }; - window-rules = [ - { - clip-to-geometry = true; - geometry-corner-radius = { - top-left = 8.0; - top-right = 8.0; - bottom-left = 8.0; - bottom-right = 8.0; - }; - } - ]; - - workspaces = lib.mkIf (primaryMonitorName != "") { - "01" = { - open-on-output = primaryMonitorName; - }; - "02" = { - open-on-output = primaryMonitorName; - }; - "03" = { - open-on-output = primaryMonitorName; - }; - "04" = { - open-on-output = primaryMonitorName; - }; - "05" = { - open-on-output = primaryMonitorName; - }; - }; - - binds = { - "XF86AudioRaiseVolume" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "audio" "increment" "3"]; - }; - "XF86AudioLowerVolume" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "audio" "decrement" "3"]; - }; - "XF86AudioMute" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "audio" "mute"]; - }; - "XF86AudioMicMute" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "audio" "micmute"]; - }; - "XF86MonBrightnessUp" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "brightness" "increment" "5" ""]; - }; - "XF86MonBrightnessDown" = { - allow-when-locked = true; - action.spawn = ["dms" "ipc" "brightness" "decrement" "5" ""]; - }; - "Mod+N" = { - action.spawn = ["dms" "ipc" "notifications" "toggle"]; - hotkey-overlay.title = "Toggle Notification Center"; - }; - "Mod+V" = { - action.spawn = ["dms" "ipc" "clipboard" "toggle"]; - hotkey-overlay.title = "Toggle Clipboard Manager"; - }; - "Mod+Space" = { - action.spawn = ["dms" "ipc" "spotlight" "toggle"]; - hotkey-overlay.title = "Toggle Application Launcher"; - }; - "Mod+Semicolon" = { - action.spawn = ["dms" "ipc" "spotlight" "toggleQuery" ":"]; - hotkey-overlay.title = "Toggle Emoji Picker"; - }; - "Mod+P" = { - action.spawn = ["dms" "ipc" "powermenu" "toggle"]; - hotkey-overlay.title = "Toggle Power Menu"; - }; - - "Mod+Left".action.focus-column-or-monitor-left = {}; - "Mod+Down".action.focus-window-or-monitor-down = {}; - "Mod+Up".action.focus-window-or-monitor-up = {}; - "Mod+Right".action.focus-column-or-monitor-right = {}; - "Mod+H".action.focus-column-or-monitor-left = {}; - "Mod+J".action.focus-window-or-monitor-down = {}; - "Mod+K".action.focus-window-or-monitor-up = {}; - "Mod+L".action.focus-column-or-monitor-right = {}; - - "Mod+Shift+Left".action.move-column-left-or-to-monitor-left = {}; - "Mod+Shift+Down".action.move-window-down = {}; - "Mod+Shift+Up".action.move-window-up = {}; - "Mod+Shift+Right".action.move-column-right-or-to-monitor-right = {}; - "Mod+Shift+H".action.move-column-left-or-to-monitor-left = {}; - "Mod+Shift+J".action.move-window-down = {}; - "Mod+Shift+K".action.move-window-up = {}; - "Mod+Shift+L".action.move-column-right-or-to-monitor-right = {}; - - "Mod+Home".action.focus-column-first = {}; - "Mod+End".action.focus-column-last = {}; - "Mod+Shift+Home".action.move-column-to-first = {}; - "Mod+Shift+End".action.move-column-to-last = {}; - - "Mod+Ctrl+Left".action.focus-monitor-left = {}; - "Mod+Ctrl+Down".action.focus-monitor-down = {}; - "Mod+Ctrl+Up".action.focus-monitor-up = {}; - "Mod+Ctrl+Right".action.focus-monitor-right = {}; - "Mod+Ctrl+H".action.focus-monitor-left = {}; - "Mod+Ctrl+J".action.focus-monitor-down = {}; - "Mod+Ctrl+K".action.focus-monitor-up = {}; - "Mod+Ctrl+L".action.focus-monitor-right = {}; - - "Mod+F".action.maximize-column = {}; - "Mod+Shift+F".action.fullscreen-window = {}; - "Mod+M".action.toggle-column-tabbed-display = {}; - "Mod+A".action.toggle-overview = {}; - "Mod+S".action.toggle-window-floating = {}; - "Mod+C".action.switch-preset-column-width = {}; - - "Mod+Z".action.set-column-width = "-5%"; - "Mod+X".action.set-column-width = "+5%"; - "Mod+Ctrl+X".action.expand-column-to-available-width = {}; - "Mod+Shift+Z".action.set-window-height = "-5%"; - "Mod+Shift+X".action.set-window-height = "+5%"; - - "Mod+Shift+WheelScrollDown".action.focus-workspace-down = {}; - "Mod+Shift+WheelScrollUp".action.focus-workspace-up = {}; - "Mod+WheelScrollDown".action.focus-column-right = {}; - "Mod+WheelScrollUp".action.focus-column-left = {}; - - "Mod+WheelScrollRight".action.focus-column-right = {}; - "Mod+WheelScrollLeft".action.focus-column-left = {}; - "Mod+Shift+WheelScrollRight".action.move-column-right = {}; - "Mod+Shift+WheelScrollLeft".action.move-column-left = {}; - - "Mod+q".action.focus-workspace = "01"; - "Mod+w".action.focus-workspace = "02"; - "Mod+e".action.focus-workspace = "03"; - "Mod+r".action.focus-workspace = "04"; - "Mod+t".action.focus-workspace = "05"; - "Mod+Shift+q".action.move-column-to-workspace = "01"; - "Mod+Shift+w".action.move-column-to-workspace = "02"; - "Mod+Shift+e".action.move-column-to-workspace = "03"; - "Mod+Shift+r".action.move-column-to-workspace = "04"; - "Mod+Shift+t".action.move-column-to-workspace = "05"; - - "Mod+Comma".action.consume-or-expel-window-left = {}; - "Mod+Period".action.consume-or-expel-window-right = {}; - - "Print".action.screenshot = {}; - "Ctrl+Print".action.screenshot-screen = {}; - "Alt+Print".action.screenshot-window = {}; - - "Mod+Slash".action.show-hotkey-overlay = {}; - - "Mod+Return".action.spawn = ["ghostty"]; - "Mod+Backspace".action.close-window = {}; - }; - - outputs = lib.mkIf (config.monitors != []) ( - lib.listToAttrs (map (monitor: { - inherit (monitor) name; - value = { - inherit (monitor) position; - mode = lib.mkIf (monitor.resolution.width != null && monitor.resolution.height != null) { - inherit (monitor.resolution) width height; - refresh = monitor.refreshRateNiri; - }; - focus-at-startup = monitor.name == primaryMonitorName; - transform.rotation = - if (monitor.transform != null) - then monitor.transform * 90 - else 0; - }; - }) - config.monitors) - ); - }; - }; -} diff --git a/modules/desktop/niri/binds.nix b/modules/desktop/niri/binds.nix new file mode 100644 index 0000000..dfc1118 --- /dev/null +++ b/modules/desktop/niri/binds.nix @@ -0,0 +1,132 @@ +{ + config, + lib, + ... +}: { + config = lib.mkIf config.fireproof.desktop.windowManager.enable { + fireproof.home-manager.programs.niri.settings.binds = { + "XF86AudioRaiseVolume" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "audio" "increment" "3"]; + }; + "XF86AudioLowerVolume" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "audio" "decrement" "3"]; + }; + "XF86AudioMute" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "audio" "mute"]; + }; + "XF86AudioMicMute" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "audio" "micmute"]; + }; + "XF86MonBrightnessUp" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "brightness" "increment" "5" ""]; + }; + "XF86MonBrightnessDown" = { + allow-when-locked = true; + action.spawn = ["dms" "ipc" "brightness" "decrement" "5" ""]; + }; + "Mod+N" = { + action.spawn = ["dms" "ipc" "notifications" "toggle"]; + hotkey-overlay.title = "Toggle Notification Center"; + }; + "Mod+V" = { + action.spawn = ["dms" "ipc" "clipboard" "toggle"]; + hotkey-overlay.title = "Toggle Clipboard Manager"; + }; + "Mod+Space" = { + action.spawn = ["dms" "ipc" "spotlight" "toggle"]; + hotkey-overlay.title = "Toggle Application Launcher"; + }; + "Mod+Semicolon" = { + action.spawn = ["dms" "ipc" "spotlight" "toggleQuery" ":"]; + hotkey-overlay.title = "Toggle Emoji Picker"; + }; + "Mod+P" = { + action.spawn = ["dms" "ipc" "powermenu" "toggle"]; + hotkey-overlay.title = "Toggle Power Menu"; + }; + + "Mod+Left".action.focus-column-or-monitor-left = {}; + "Mod+Down".action.focus-window-or-monitor-down = {}; + "Mod+Up".action.focus-window-or-monitor-up = {}; + "Mod+Right".action.focus-column-or-monitor-right = {}; + "Mod+H".action.focus-column-or-monitor-left = {}; + "Mod+J".action.focus-window-or-monitor-down = {}; + "Mod+K".action.focus-window-or-monitor-up = {}; + "Mod+L".action.focus-column-or-monitor-right = {}; + + "Mod+Shift+Left".action.move-column-left-or-to-monitor-left = {}; + "Mod+Shift+Down".action.move-window-down = {}; + "Mod+Shift+Up".action.move-window-up = {}; + "Mod+Shift+Right".action.move-column-right-or-to-monitor-right = {}; + "Mod+Shift+H".action.move-column-left-or-to-monitor-left = {}; + "Mod+Shift+J".action.move-window-down = {}; + "Mod+Shift+K".action.move-window-up = {}; + "Mod+Shift+L".action.move-column-right-or-to-monitor-right = {}; + + "Mod+Home".action.focus-column-first = {}; + "Mod+End".action.focus-column-last = {}; + "Mod+Shift+Home".action.move-column-to-first = {}; + "Mod+Shift+End".action.move-column-to-last = {}; + + "Mod+Ctrl+Left".action.focus-monitor-left = {}; + "Mod+Ctrl+Down".action.focus-monitor-down = {}; + "Mod+Ctrl+Up".action.focus-monitor-up = {}; + "Mod+Ctrl+Right".action.focus-monitor-right = {}; + "Mod+Ctrl+H".action.focus-monitor-left = {}; + "Mod+Ctrl+J".action.focus-monitor-down = {}; + "Mod+Ctrl+K".action.focus-monitor-up = {}; + "Mod+Ctrl+L".action.focus-monitor-right = {}; + + "Mod+F".action.maximize-column = {}; + "Mod+Shift+F".action.fullscreen-window = {}; + "Mod+M".action.toggle-column-tabbed-display = {}; + "Mod+A".action.toggle-overview = {}; + "Mod+S".action.toggle-window-floating = {}; + "Mod+C".action.switch-preset-column-width = {}; + + "Mod+Z".action.set-column-width = "-5%"; + "Mod+X".action.set-column-width = "+5%"; + "Mod+Ctrl+X".action.expand-column-to-available-width = {}; + "Mod+Shift+Z".action.set-window-height = "-5%"; + "Mod+Shift+X".action.set-window-height = "+5%"; + + "Mod+Shift+WheelScrollDown".action.focus-workspace-down = {}; + "Mod+Shift+WheelScrollUp".action.focus-workspace-up = {}; + "Mod+WheelScrollDown".action.focus-column-right = {}; + "Mod+WheelScrollUp".action.focus-column-left = {}; + + "Mod+WheelScrollRight".action.focus-column-right = {}; + "Mod+WheelScrollLeft".action.focus-column-left = {}; + "Mod+Shift+WheelScrollRight".action.move-column-right = {}; + "Mod+Shift+WheelScrollLeft".action.move-column-left = {}; + + "Mod+q".action.focus-workspace = "01"; + "Mod+w".action.focus-workspace = "02"; + "Mod+e".action.focus-workspace = "03"; + "Mod+r".action.focus-workspace = "04"; + "Mod+t".action.focus-workspace = "05"; + "Mod+Shift+q".action.move-column-to-workspace = "01"; + "Mod+Shift+w".action.move-column-to-workspace = "02"; + "Mod+Shift+e".action.move-column-to-workspace = "03"; + "Mod+Shift+r".action.move-column-to-workspace = "04"; + "Mod+Shift+t".action.move-column-to-workspace = "05"; + + "Mod+Comma".action.consume-or-expel-window-left = {}; + "Mod+Period".action.consume-or-expel-window-right = {}; + + "Print".action.screenshot = {}; + "Ctrl+Print".action.screenshot-screen = {}; + "Alt+Print".action.screenshot-window = {}; + + "Mod+Slash".action.show-hotkey-overlay = {}; + + "Mod+Return".action.spawn = ["ghostty"]; + "Mod+Backspace".action.close-window = {}; + }; + }; +} diff --git a/modules/desktop/niri/default.nix b/modules/desktop/niri/default.nix new file mode 100644 index 0000000..18bb192 --- /dev/null +++ b/modules/desktop/niri/default.nix @@ -0,0 +1,29 @@ +{ + config, + lib, + pkgs, + inputs, + ... +}: { + imports = [ + ./settings.nix + ./binds.nix + ./outputs.nix + ]; + + config = lib.mkIf config.fireproof.desktop.windowManager.enable { + programs.xwayland.enable = true; + + xdg.portal = { + enable = true; + extraPortals = [pkgs.xdg-desktop-portal-gtk]; + config.common.default = "gnome"; + xdgOpenUsePortal = true; + }; + + programs.niri = { + enable = true; + package = inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".niri-unstable; + }; + }; +} diff --git a/modules/desktop/niri/outputs.nix b/modules/desktop/niri/outputs.nix new file mode 100644 index 0000000..98917f6 --- /dev/null +++ b/modules/desktop/niri/outputs.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + ... +}: let + hasMonitors = config.monitors != []; + primaryMonitorName = + if hasMonitors + then (builtins.head config.monitors).name or "" + else ""; +in { + config = lib.mkIf config.fireproof.desktop.windowManager.enable { + fireproof.home-manager.programs.niri.settings = { + workspaces = lib.mkIf (primaryMonitorName != "") { + "01".open-on-output = primaryMonitorName; + "02".open-on-output = primaryMonitorName; + "03".open-on-output = primaryMonitorName; + "04".open-on-output = primaryMonitorName; + "05".open-on-output = primaryMonitorName; + }; + + outputs = lib.mkIf (config.monitors != []) ( + lib.listToAttrs (map (monitor: { + inherit (monitor) name; + value = { + inherit (monitor) position; + mode = lib.mkIf (monitor.resolution.width != null && monitor.resolution.height != null) { + inherit (monitor.resolution) width height; + refresh = monitor.refreshRateNiri; + }; + focus-at-startup = monitor.name == primaryMonitorName; + transform.rotation = + if (monitor.transform != null) + then monitor.transform * 90 + else 0; + }; + }) + config.monitors) + ); + }; + }; +} diff --git a/modules/desktop/niri/settings.nix b/modules/desktop/niri/settings.nix new file mode 100644 index 0000000..4172341 --- /dev/null +++ b/modules/desktop/niri/settings.nix @@ -0,0 +1,64 @@ +{ + config, + lib, + pkgs, + inputs, + ... +}: let + c = config.fireproof.theme.colors; +in { + config = lib.mkIf config.fireproof.desktop.windowManager.enable { + fireproof.home-manager.programs.niri.settings = { + prefer-no-csd = true; + clipboard.disable-primary = true; + spawn-at-startup = [ + {command = ["systemctl" "--user" "start" "hyprpaper"];} + ]; + xwayland-satellite = { + enable = true; + path = lib.getExe inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".xwayland-satellite-unstable; + }; + environment = { + NIXOS_OZONE_WL = "1"; + GDK_BACKEND = "wayland"; + }; + layout = { + gaps = 10; + focus-ring.enable = false; + insert-hint.display.color = "#${c.accent}"; + border = { + enable = true; + width = 2; + active.color = "#${c.accent}"; + inactive.color = "#${c.ui}"; + }; + tab-indicator = { + hide-when-single-tab = true; + place-within-column = true; + gap = 2; + position = "top"; + corner-radius = 8; + }; + }; + input = { + focus-follows-mouse = { + enable = true; + max-scroll-amount = "0%"; + }; + mouse.accel-profile = "flat"; + keyboard.xkb.layout = "eu"; + }; + window-rules = [ + { + clip-to-geometry = true; + geometry-corner-radius = { + top-left = 8.0; + top-right = 8.0; + bottom-left = 8.0; + bottom-right = 8.0; + }; + } + ]; + }; + }; +} diff --git a/modules/homelab/default.nix b/modules/homelab/default.nix index 28057af..f2cbdf3 100644 --- a/modules/homelab/default.nix +++ b/modules/homelab/default.nix @@ -8,7 +8,7 @@ ./audiobookshelf.nix ./freshrss.nix ./glance.nix - ./home-assistant.nix + ./home-assistant ./jellyfin.nix ./nextcloud.nix ./nginx.nix @@ -19,7 +19,7 @@ ./restic.nix ./scrutiny.nix ./security.nix - ./sso.nix + ./sso ./vaultwarden.nix ]; } diff --git a/modules/homelab/home-assistant.nix b/modules/homelab/home-assistant.nix deleted file mode 100644 index d363afd..0000000 --- a/modules/homelab/home-assistant.nix +++ /dev/null @@ -1,224 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: -lib.mkIf config.fireproof.homelab.enable (let - mosquittoPort = 1883; - zigbee2mqttPort = 8180; - homeAssistantPort = 8123; -in { - age.secrets = { - "zigbee2mqtt-secret.yaml" = { - rekeyFile = ../../secrets/hosts/homelab/zigbee2mqtt-secret.yaml.age; - owner = "zigbee2mqtt"; - group = "zigbee2mqtt"; - }; - mosquitto-zigbee2mqtt.rekeyFile = ../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age; - mosquitto-sas.rekeyFile = ../../secrets/hosts/homelab/mosquitto-sas.age; - mosquitto-ha.rekeyFile = ../../secrets/hosts/homelab/mosquitto-ha.age; - hassSecrets = { - rekeyFile = ../../secrets/hosts/homelab/hass.yaml.age; - path = "${config.services.home-assistant.configDir}/secrets.yaml"; - mode = "400"; - owner = "hass"; - group = "hass"; - }; - }; - - networking.firewall.allowedTCPPorts = [ - mosquittoPort - ]; - - services = { - restic.backups.homelab = { - paths = [ - config.services.zigbee2mqtt.dataDir - config.services.home-assistant.configDir - ]; - exclude = [ - "/var/lib/zigbee2mqtt/log/" - ]; - }; - - oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"]; - nginx.virtualHosts = { - "zigbee.nickolaj.com" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://localhost:${toString zigbee2mqttPort}"; - proxyWebsockets = true; - }; - }; - "ha.nickolaj.com" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://localhost:${toString homeAssistantPort}"; - proxyWebsockets = true; - }; - }; - }; - - home-assistant = { - enable = true; - package = pkgs.home-assistant; - customComponents = with pkgs.home-assistant-custom-components; [ - adaptive_lighting - sleep_as_android_mqtt - (pkgs.buildHomeAssistantComponent rec { - owner = "Sian-Lee-SA"; - domain = "switch_manager"; - version = "v2.0.4b"; - src = pkgs.fetchFromGitHub { - inherit owner; - repo = "Home-Assistant-Switch-Manager"; - rev = version; - hash = "sha256-W9xO3JjnRKHk/dlXMA6y5nEJl/KsGzPvJoumGw+nohw="; - }; - }) - (pkgs.buildHomeAssistantComponent rec { - owner = "snicker"; - domain = "zwift"; - version = "v3.3.5"; - src = pkgs.fetchFromGitHub { - inherit owner; - repo = "zwift_hass"; - rev = version; - hash = "sha256-+lJ6Otp8lT+xVtjiQLSQrqT5cVinRTRPTzS+HB1AxB0="; - }; - propagatedBuildInputs = [ - (pkgs.python313.pkgs.buildPythonPackage rec { - pname = "zwift-client"; - version = "0.2.0"; - pyproject = true; - src = pkgs.fetchFromGitHub { - owner = "nickolaj-jepsen"; - repo = "zwift-client"; - rev = "882fb881f1271dc104fd0250cab4ceb6e3710a59"; - hash = "sha256-4gOlWG+QVwODlIhiNH7rhiD0rzNv2WxY2ty9o/51eHU="; - }; - doCheck = false; - propagatedBuildInputs = with pkgs.python313.pkgs; [ - hatchling - requests - protobuf - ]; - }) - ]; - }) - ]; - extraComponents = [ - "default_config" - "met" - "mqtt" - "esphome" - "google" - "spotify" - "unifi" - "upnp" - "homeassistant_hardware" - ]; - config = { - homeassistant = { - name = "Home"; - latitude = "!secret latitude"; - longitude = "!secret longitude"; - elevation = "!secret elevation"; - unit_system = "metric"; - time_zone = "Europe/Copenhagen"; - }; - frontend = { - themes = "!include_dir_merge_named themes"; - }; - http = { - server_port = homeAssistantPort; - use_x_forwarded_for = true; - trusted_proxies = [ - "127.0.0.1" - "::1" - ]; - }; - sensor = [ - { - platform = "zwift"; - username = "!secret zwift_username"; - password = "!secret zwift_password"; - } - ]; - - automation = "!include automations.yaml"; - script = "!include scripts.yaml"; - scene = "!include scenes.yaml"; - }; - }; - - mosquitto = { - enable = true; - listeners = [ - { - port = mosquittoPort; - users."zigbee2mqtt" = { - acl = ["readwrite #"]; - passwordFile = "${config.age.secrets.mosquitto-zigbee2mqtt.path}"; - }; - users."homeassistant" = { - acl = ["readwrite #"]; - passwordFile = "${config.age.secrets.mosquitto-ha.path}"; - }; - users."sleep_as_android" = { - acl = ["readwrite SleepAsAndroid"]; - passwordFile = "${config.age.secrets.mosquitto-sas.path}"; - }; - } - ]; - }; - - zigbee2mqtt = { - enable = true; - settings = { - homeassistant = { - enabled = true; - }; - mqtt = { - base_topic = "zigbee2mqtt"; - server = "mqtt://localhost:${toString mosquittoPort}"; - user = "zigbee2mqtt"; - password = "!${config.age.secrets."zigbee2mqtt-secret.yaml".path} password"; - }; - frontend = { - enabled = true; - port = zigbee2mqttPort; - }; - serial = { - port = "/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0"; - adapter = "zstack"; - }; - advanced = { - network_key = [ - 233 - 138 - 136 - 76 - 51 - 117 - 128 - 127 - 74 - 84 - 33 - 179 - 116 - 61 - 79 - 101 - ]; - channel = 25; - log_level = "debug"; - }; - }; - }; - }; -}) diff --git a/modules/homelab/home-assistant/default.nix b/modules/homelab/home-assistant/default.nix new file mode 100644 index 0000000..10d573f --- /dev/null +++ b/modules/homelab/home-assistant/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./hass.nix + ./mqtt.nix + ]; +} diff --git a/modules/homelab/home-assistant/hass.nix b/modules/homelab/home-assistant/hass.nix new file mode 100644 index 0000000..b778a3b --- /dev/null +++ b/modules/homelab/home-assistant/hass.nix @@ -0,0 +1,124 @@ +{ + pkgs, + config, + lib, + ... +}: let + homeAssistantPort = 8123; +in { + config = lib.mkIf config.fireproof.homelab.enable { + age.secrets.hassSecrets = { + rekeyFile = ../../../secrets/hosts/homelab/hass.yaml.age; + path = "${config.services.home-assistant.configDir}/secrets.yaml"; + mode = "400"; + owner = "hass"; + group = "hass"; + }; + + services.restic.backups.homelab = { + paths = [config.services.home-assistant.configDir]; + }; + + services.nginx.virtualHosts."ha.nickolaj.com" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:${toString homeAssistantPort}"; + proxyWebsockets = true; + }; + }; + + services.home-assistant = { + enable = true; + package = pkgs.home-assistant; + customComponents = with pkgs.home-assistant-custom-components; [ + adaptive_lighting + sleep_as_android_mqtt + (pkgs.buildHomeAssistantComponent rec { + owner = "Sian-Lee-SA"; + domain = "switch_manager"; + version = "v2.0.4b"; + src = pkgs.fetchFromGitHub { + inherit owner; + repo = "Home-Assistant-Switch-Manager"; + rev = version; + hash = "sha256-W9xO3JjnRKHk/dlXMA6y5nEJl/KsGzPvJoumGw+nohw="; + }; + }) + (pkgs.buildHomeAssistantComponent rec { + owner = "snicker"; + domain = "zwift"; + version = "v3.3.5"; + src = pkgs.fetchFromGitHub { + inherit owner; + repo = "zwift_hass"; + rev = version; + hash = "sha256-+lJ6Otp8lT+xVtjiQLSQrqT5cVinRTRPTzS+HB1AxB0="; + }; + propagatedBuildInputs = [ + (pkgs.python313.pkgs.buildPythonPackage { + pname = "zwift-client"; + version = "0.2.0"; + pyproject = true; + src = pkgs.fetchFromGitHub { + owner = "nickolaj-jepsen"; + repo = "zwift-client"; + rev = "882fb881f1271dc104fd0250cab4ceb6e3710a59"; + hash = "sha256-4gOlWG+QVwODlIhiNH7rhiD0rzNv2WxY2ty9o/51eHU="; + }; + doCheck = false; + propagatedBuildInputs = with pkgs.python313.pkgs; [ + hatchling + requests + protobuf + ]; + }) + ]; + }) + ]; + extraComponents = [ + "default_config" + "met" + "mqtt" + "esphome" + "google" + "spotify" + "unifi" + "upnp" + "homeassistant_hardware" + ]; + config = { + homeassistant = { + name = "Home"; + latitude = "!secret latitude"; + longitude = "!secret longitude"; + elevation = "!secret elevation"; + unit_system = "metric"; + time_zone = "Europe/Copenhagen"; + }; + frontend = { + themes = "!include_dir_merge_named themes"; + }; + http = { + server_port = homeAssistantPort; + use_x_forwarded_for = true; + trusted_proxies = [ + "127.0.0.1" + "::1" + ]; + }; + sensor = [ + { + platform = "zwift"; + username = "!secret zwift_username"; + password = "!secret zwift_password"; + } + ]; + + automation = "!include automations.yaml"; + script = "!include scripts.yaml"; + scene = "!include scenes.yaml"; + }; + }; + }; +} diff --git a/modules/homelab/home-assistant/mqtt.nix b/modules/homelab/home-assistant/mqtt.nix new file mode 100644 index 0000000..e20507f --- /dev/null +++ b/modules/homelab/home-assistant/mqtt.nix @@ -0,0 +1,104 @@ +{ + config, + lib, + ... +}: let + mosquittoPort = 1883; + zigbee2mqttPort = 8180; +in { + config = lib.mkIf config.fireproof.homelab.enable { + age.secrets = { + "zigbee2mqtt-secret.yaml" = { + rekeyFile = ../../../secrets/hosts/homelab/zigbee2mqtt-secret.yaml.age; + owner = "zigbee2mqtt"; + group = "zigbee2mqtt"; + }; + mosquitto-zigbee2mqtt.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age; + mosquitto-sas.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-sas.age; + mosquitto-ha.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-ha.age; + }; + + networking.firewall.allowedTCPPorts = [mosquittoPort]; + + services.restic.backups.homelab = { + paths = [config.services.zigbee2mqtt.dataDir]; + exclude = ["/var/lib/zigbee2mqtt/log/"]; + }; + + services.oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"]; + services.nginx.virtualHosts."zigbee.nickolaj.com" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:${toString zigbee2mqttPort}"; + proxyWebsockets = true; + }; + }; + + services.mosquitto = { + enable = true; + listeners = [ + { + port = mosquittoPort; + users."zigbee2mqtt" = { + acl = ["readwrite #"]; + passwordFile = "${config.age.secrets.mosquitto-zigbee2mqtt.path}"; + }; + users."homeassistant" = { + acl = ["readwrite #"]; + passwordFile = "${config.age.secrets.mosquitto-ha.path}"; + }; + users."sleep_as_android" = { + acl = ["readwrite SleepAsAndroid"]; + passwordFile = "${config.age.secrets.mosquitto-sas.path}"; + }; + } + ]; + }; + + services.zigbee2mqtt = { + enable = true; + settings = { + homeassistant = { + enabled = true; + }; + mqtt = { + base_topic = "zigbee2mqtt"; + server = "mqtt://localhost:${toString mosquittoPort}"; + user = "zigbee2mqtt"; + password = "!${config.age.secrets."zigbee2mqtt-secret.yaml".path} password"; + }; + frontend = { + enabled = true; + port = zigbee2mqttPort; + }; + serial = { + port = "/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0"; + adapter = "zstack"; + }; + advanced = { + network_key = [ + 233 + 138 + 136 + 76 + 51 + 117 + 128 + 127 + 74 + 84 + 33 + 179 + 116 + 61 + 79 + 101 + ]; + channel = 25; + log_level = "debug"; + }; + }; + }; + }; +} diff --git a/modules/homelab/sso.nix b/modules/homelab/sso.nix deleted file mode 100644 index 032dacd..0000000 --- a/modules/homelab/sso.nix +++ /dev/null @@ -1,116 +0,0 @@ -{ - config, - pkgsUnstable, - lib, - ... -}: -lib.mkIf config.fireproof.homelab.enable (let - port = 9190; - rootDomain = "nickolaj.com"; - zitadelDomain = "sso.${rootDomain}"; - oathproxyDomain = "oauth2-proxy.${rootDomain}"; -in { - age.secrets.zitadel-master = { - rekeyFile = ../../secrets/hosts/homelab/zitadel-master.age; - owner = config.services.zitadel.user; - }; - age.secrets.oauth2-proxy = { - rekeyFile = ../../secrets/hosts/homelab/oauth2-proxy-keyfile.age; - owner = "oauth2-proxy"; - }; - - services.nginx.virtualHosts."${zitadelDomain}" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString port}"; - extraConfig = '' - grpc_pass grpc://127.0.0.1:${toString port}; - grpc_set_header Host $host:$server_port; - ''; - }; - }; - services.nginx.virtualHosts."${oathproxyDomain}" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyWebsockets = true; - proxyPass = "http://127.0.0.1:4180"; - }; - }; - - services.postgresql = { - ensureDatabases = ["zitadel"]; - ensureUsers = [ - { - name = "zitadel"; - ensureDBOwnership = true; - ensureClauses.login = true; - } - ]; - }; - - services.zitadel = { - enable = true; - package = pkgsUnstable.zitadel; - masterKeyFile = config.age.secrets.zitadel-master.path; - settings = { - Port = port; - Database.postgres = { - Host = "/var/run/postgresql/"; - Port = 5432; - Database = "zitadel"; - User = { - Username = "zitadel"; - SSL.Mode = "disable"; - }; - Admin = { - Username = "zitadel"; - SSL.Mode = "disable"; - ExistingDatabase = "zitadel"; - }; - }; - ExternalDomain = zitadelDomain; - ExternalPort = 443; - ExternalSecure = true; - }; - steps.FirstInstance = { - InstanceName = "Fireproof Auth"; - Org = { - Name = "Fireproof Auth"; - Human = { - UserName = "nickolaj1177@gmail.com"; - FirstName = "Nickolaj"; - LastName = "Jepsen"; - Email.Verified = true; - Password = "Password1!"; - PasswordChangeRequired = true; - }; - }; - LoginPolicy.AllowRegister = false; - }; - }; - - services.oauth2-proxy = { - enable = true; - provider = "oidc"; - reverseProxy = true; - redirectURL = "https://${oathproxyDomain}/oauth2/callback"; - validateURL = "https://${zitadelDomain}/oauth2/"; - oidcIssuerUrl = "https://${zitadelDomain}:443"; - keyFile = config.age.secrets.oauth2-proxy.path; - passBasicAuth = true; - setXauthrequest = true; - nginx.domain = oathproxyDomain; - email.domains = ["*"]; - extraConfig = { - whitelist-domain = ".${rootDomain}"; - cookie-domain = ".${rootDomain}"; - }; - }; - - systemd.services.oauth2-proxy.serviceConfig = { - Restart = "always"; - RestartSec = "5s"; - }; -}) diff --git a/modules/homelab/sso/default.nix b/modules/homelab/sso/default.nix new file mode 100644 index 0000000..30602bd --- /dev/null +++ b/modules/homelab/sso/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./zitadel.nix + ./proxy.nix + ]; +} diff --git a/modules/homelab/sso/proxy.nix b/modules/homelab/sso/proxy.nix new file mode 100644 index 0000000..e4e2145 --- /dev/null +++ b/modules/homelab/sso/proxy.nix @@ -0,0 +1,48 @@ +{ + config, + lib, + ... +}: let + rootDomain = "nickolaj.com"; + zitadelDomain = "sso.${rootDomain}"; + oathproxyDomain = "oauth2-proxy.${rootDomain}"; +in { + config = lib.mkIf config.fireproof.homelab.enable { + age.secrets.oauth2-proxy = { + rekeyFile = ../../../secrets/hosts/homelab/oauth2-proxy-keyfile.age; + owner = "oauth2-proxy"; + }; + + services.nginx.virtualHosts."${oathproxyDomain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyWebsockets = true; + proxyPass = "http://127.0.0.1:4180"; + }; + }; + + services.oauth2-proxy = { + enable = true; + provider = "oidc"; + reverseProxy = true; + redirectURL = "https://${oathproxyDomain}/oauth2/callback"; + validateURL = "https://${zitadelDomain}/oauth2/"; + oidcIssuerUrl = "https://${zitadelDomain}:443"; + keyFile = config.age.secrets.oauth2-proxy.path; + passBasicAuth = true; + setXauthrequest = true; + nginx.domain = oathproxyDomain; + email.domains = ["*"]; + extraConfig = { + whitelist-domain = ".${rootDomain}"; + cookie-domain = ".${rootDomain}"; + }; + }; + + systemd.services.oauth2-proxy.serviceConfig = { + Restart = "always"; + RestartSec = "5s"; + }; + }; +} diff --git a/modules/homelab/sso/zitadel.nix b/modules/homelab/sso/zitadel.nix new file mode 100644 index 0000000..aba806a --- /dev/null +++ b/modules/homelab/sso/zitadel.nix @@ -0,0 +1,80 @@ +{ + config, + pkgsUnstable, + lib, + ... +}: let + port = 9190; + zitadelDomain = "sso.nickolaj.com"; +in { + config = lib.mkIf config.fireproof.homelab.enable { + age.secrets.zitadel-master = { + rekeyFile = ../../../secrets/hosts/homelab/zitadel-master.age; + owner = config.services.zitadel.user; + }; + + services.nginx.virtualHosts."${zitadelDomain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString port}"; + extraConfig = '' + grpc_pass grpc://127.0.0.1:${toString port}; + grpc_set_header Host $host:$server_port; + ''; + }; + }; + + services.postgresql = { + ensureDatabases = ["zitadel"]; + ensureUsers = [ + { + name = "zitadel"; + ensureDBOwnership = true; + ensureClauses.login = true; + } + ]; + }; + + services.zitadel = { + enable = true; + package = pkgsUnstable.zitadel; + masterKeyFile = config.age.secrets.zitadel-master.path; + settings = { + Port = port; + Database.postgres = { + Host = "/var/run/postgresql/"; + Port = 5432; + Database = "zitadel"; + User = { + Username = "zitadel"; + SSL.Mode = "disable"; + }; + Admin = { + Username = "zitadel"; + SSL.Mode = "disable"; + ExistingDatabase = "zitadel"; + }; + }; + ExternalDomain = zitadelDomain; + ExternalPort = 443; + ExternalSecure = true; + }; + steps.FirstInstance = { + InstanceName = "Fireproof Auth"; + Org = { + Name = "Fireproof Auth"; + Human = { + UserName = "nickolaj1177@gmail.com"; + FirstName = "Nickolaj"; + LastName = "Jepsen"; + Email.Verified = true; + Password = "Password1!"; + PasswordChangeRequired = true; + }; + }; + LoginPolicy.AllowRegister = false; + }; + }; + }; +}