From 888167afc2c160f5e4a16c092aa5d380b7a63669 Mon Sep 17 00:00:00 2001 From: mjallen18 Date: Fri, 13 Jun 2025 10:44:00 -0500 Subject: [PATCH] cleanup --- flake.lock | 92 +++---- hosts/nas/apps.nix | 67 +++++ hosts/nas/apps/actual/default.nix | 192 +++++++------ hosts/nas/apps/actual/options.nix | 37 +++ hosts/nas/apps/arrs/default.nix | 399 ++++++++++++++-------------- hosts/nas/apps/arrs/options.nix | 112 ++++++++ hosts/nas/apps/crowdsec/default.nix | 82 +++--- hosts/nas/apps/crowdsec/options.nix | 27 ++ hosts/nas/apps/gitea/default.nix | 207 ++++++++------- hosts/nas/apps/gitea/options.nix | 42 +++ hosts/nas/apps/options.nix | 46 ++++ hosts/nas/apps/traefik/default.nix | 192 ++++++------- hosts/nas/configuration.nix | 1 + hosts/nas/grafana.nix | 2 +- hosts/nas/networking.nix | 1 + hosts/nas/nix-serve.nix | 14 +- hosts/nas/sops.nix | 3 + secrets/nas-secrets.yaml | 5 +- 18 files changed, 958 insertions(+), 563 deletions(-) create mode 100644 hosts/nas/apps/actual/options.nix create mode 100644 hosts/nas/apps/arrs/options.nix create mode 100644 hosts/nas/apps/crowdsec/options.nix create mode 100644 hosts/nas/apps/gitea/options.nix create mode 100644 hosts/nas/apps/options.nix diff --git a/flake.lock b/flake.lock index fd618c1..08a009e 100755 --- a/flake.lock +++ b/flake.lock @@ -19,16 +19,16 @@ "authentik-src": { "flake": false, "locked": { - "lastModified": 1745954192, - "narHash": "sha256-QuIgeu3CN6S44/zSiaj+iIkDz2494mb1MWvD3eYYkVE=", + "lastModified": 1749043670, + "narHash": "sha256-gwHngqb23U8By7jhxFWQZOXy+vPQApJSkvr4gHI5ifQ=", "owner": "goauthentik", "repo": "authentik", - "rev": "22412729e2379d645da2ac0c0270a0ac6147945e", + "rev": "bda30c5ad5838fea36dc0a06f8580cca437f0fc0", "type": "github" }, "original": { "owner": "goauthentik", - "ref": "version/2025.4.0", + "ref": "version/2025.4.2", "repo": "authentik", "type": "github" } @@ -358,11 +358,11 @@ "flake-compat_3": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -445,11 +445,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1748821116, + "narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1", "type": "github" }, "original": { @@ -796,11 +796,11 @@ "uv2nix": "uv2nix" }, "locked": { - "lastModified": 1746874492, - "narHash": "sha256-Gm2Eb5KBxAL6y9WJj7phRMXNAZzVkKlm9Dky9WDZHtQ=", + "lastModified": 1749129962, + "narHash": "sha256-gc1l5z5dWw9a9DWsrp0ZiD+SSMsNpEwMEiRi8K5sh5c=", "owner": "nix-community", "repo": "authentik-nix", - "rev": "2ef24fac993808a1a57f367ef58ac0f5254c3489", + "rev": "271a38f7c4e2551f0674b894e2adf7cd1ddb8168", "type": "github" }, "original": { @@ -819,11 +819,11 @@ "rust-overlay": "rust-overlay_4" }, "locked": { - "lastModified": 1747308097, - "narHash": "sha256-indU9vouoMSHMuB9TTZMsXywj8N5UNOVnCwuA9xh9LM=", + "lastModified": 1749770917, + "narHash": "sha256-3jOhroFAAKg/vPmgmDnOKUGJp6GfLycUkhyMaJKZ7zg=", "owner": "lilyinstarlight", "repo": "nixos-cosmic", - "rev": "3c989494b1968ca066f5893401c9cb8e2202a8f2", + "rev": "f5d076cdc61fe2f268d624a34a3df52573620396", "type": "github" }, "original": { @@ -860,11 +860,11 @@ ] }, "locked": { - "lastModified": 1747340209, - "narHash": "sha256-tUiXrwlJoG3dzJ+fSwv1S3VPU5ODSPZJHoBmlu4t344=", + "lastModified": 1749657191, + "narHash": "sha256-QLilaHuhGxiwhgceDWESj9gFcKIdEp7+9lRqNGpN8S4=", "owner": "nix-community", "repo": "home-manager", - "rev": "098e365dd83311cc8236f83ea6be42abb49a6c76", + "rev": "faeab32528a9360e9577ff4082de2d35c6bbe1ce", "type": "github" }, "original": { @@ -954,11 +954,11 @@ }, "nas-nixos-hardware": { "locked": { - "lastModified": 1747129300, - "narHash": "sha256-L3clA5YGeYCF47ghsI7Tcex+DnaaN/BbQ4dR2wzoiKg=", + "lastModified": 1749195551, + "narHash": "sha256-W5GKQHgunda/OP9sbKENBZhMBDNu2QahoIPwnsF6CeM=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "e81fd167b33121269149c57806599045fd33eeed", + "rev": "4602f7e1d3f197b3cb540d5accf5669121629628", "type": "github" }, "original": { @@ -970,11 +970,11 @@ }, "nas-nixpkgs": { "locked": { - "lastModified": 1747179050, - "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", + "lastModified": 1749285348, + "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", + "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f", "type": "github" }, "original": { @@ -1007,11 +1007,11 @@ ] }, "locked": { - "lastModified": 1746485181, - "narHash": "sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB+qsl9BZUnRvg=", + "lastModified": 1749592509, + "narHash": "sha256-VunQzfZFA+Y6x3wYi2UE4DEQ8qKoAZZCnZPUlSoqC+A=", "owner": "Mic92", "repo": "sops-nix", - "rev": "e93ee1d900ad264d65e9701a5c6f895683433386", + "rev": "50754dfaa0e24e313c626900d44ef431f3210138", "type": "github" }, "original": { @@ -1172,11 +1172,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "lastModified": 1748740939, + "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "rev": "656a64127e9d791a334452c6b6606d17539476e2", "type": "github" }, "original": { @@ -1219,11 +1219,11 @@ }, "nixpkgs-stable_3": { "locked": { - "lastModified": 1747209494, - "narHash": "sha256-fLise+ys+bpyjuUUkbwqo5W/UyIELvRz9lPBPoB0fbM=", + "lastModified": 1749488106, + "narHash": "sha256-b9GIWdF/8jKpCC5JIMgDLZgwe8cEbty2fyTyo1eDFfI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5d736263df906c5da72ab0f372427814de2f52f8", + "rev": "8fe3e32e7f210522377c3bcff80931a3284ace6a", "type": "github" }, "original": { @@ -1689,11 +1689,11 @@ ] }, "locked": { - "lastModified": 1744599653, - "narHash": "sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU=", + "lastModified": 1748562898, + "narHash": "sha256-STk4QklrGpM3gliPKNJdBLSQvIrqRuwHI/rnYb/5rh8=", "owner": "pyproject-nix", "repo": "build-system-pkgs", - "rev": "7dba6dbc73120e15b558754c26024f6c93015dd7", + "rev": "33bd58351957bb52dd1700ea7eeefe34de06a892", "type": "github" }, "original": { @@ -1710,11 +1710,11 @@ ] }, "locked": { - "lastModified": 1746146146, - "narHash": "sha256-60+mzI2lbgn+G8F5mz+cmkDvHFn4s5oqcOna1SzYy74=", + "lastModified": 1746540146, + "narHash": "sha256-QxdHGNpbicIrw5t6U3x+ZxeY/7IEJ6lYbvsjXmcxFIM=", "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "3e9623bdd86a3c545e82b7f97cfdba5f07232d9a", + "rev": "e09c10c24ebb955125fda449939bfba664c467fd", "type": "github" }, "original": { @@ -1880,11 +1880,11 @@ ] }, "locked": { - "lastModified": 1747190175, - "narHash": "sha256-s33mQ2s5L/2nyllhRTywgECNZyCqyF4MJeM3vG/GaRo=", + "lastModified": 1749695868, + "narHash": "sha256-debjTLOyqqsYOUuUGQsAHskFXH5+Kx2t3dOo/FCoNRA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "58160be7abad81f6f8cb53120d5b88c16e01c06d", + "rev": "55f914d5228b5c8120e9e0f9698ed5b7214d09cd", "type": "github" }, "original": { @@ -2208,11 +2208,11 @@ ] }, "locked": { - "lastModified": 1746048139, - "narHash": "sha256-LdCLyiihLg6P2/mjzP0+W7RtraDSIaJJPTy6SCtW5Ag=", + "lastModified": 1748916602, + "narHash": "sha256-GiwjjmPIISDFD0uQ1DqQ+/38hZ+2z1lTKVj/TkKaWwQ=", "owner": "pyproject-nix", "repo": "uv2nix", - "rev": "680e2f8e637bc79b84268949d2f2b2f5e5f1d81c", + "rev": "a4dd471de62b27928191908f57bfcd702ec2bfc9", "type": "github" }, "original": { diff --git a/hosts/nas/apps.nix b/hosts/nas/apps.nix index c07a2a1..491a2e9 100755 --- a/hosts/nas/apps.nix +++ b/hosts/nas/apps.nix @@ -20,6 +20,73 @@ ]; nas-apps = { + actual = { + enable = true; + port = 3333; + localAddress = "10.0.3.18"; + dataDir = "/media/nas/ssd/nix-app-data/actual"; + reverseProxy = { + enable = true; + host = "actual.mjallen.dev"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + }; + }; + + arrs = { + enable = true; + localAddress = "10.0.1.51"; + downloadsDir = "/media/nas/ssd/ssd_app_data/downloads"; + incompleteDownloadsDir = "/media/nas/ssd/ssd_app_data/downloads-incomplete"; + moviesDir = "/media/nas/main/movies"; + tvDir = "/media/nas/main/tv"; + isosDir = "/media/nas/main/isos"; + radarr = { + enable = true; + port = 7878; + dataDir = "/media/nas/ssd/nix-app-data/radarr"; + }; + sonarr = { + enable = true; + port = 8989; + dataDir = "/media/nas/ssd/nix-app-data/sonarr"; + }; + sabnzbd = { + enable = true; + port = 8280; + dataDir = "/media/nas/ssd/nix-app-data/sabnzbd"; + }; + deluge = { + enable = true; + port = 8112; + }; + jackett = { + enable = true; + port = 9117; + dataDir = "/media/nas/ssd/nix-app-data/jackett"; + }; + }; + + crowdsec = { + enable = true; + port = 9898; + apiAddress = "10.0.1.18"; + apiKey = "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE"; + dataDir = "/media/nas/ssd/nix-app-data/crowdsec"; + }; + + gitea = { + enable = true; + httpPort = 3000; + sshPort = 2222; + localAddress = "10.0.4.18"; + dataDir = "/media/nas/ssd/nix-app-data/gitea"; + reverseProxy = { + enable = true; + host = "gitea.mjallen.dev"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + }; + }; + free-games-claimer.enable = true; manyfold.enable = true; diff --git a/hosts/nas/apps/actual/default.nix b/hosts/nas/apps/actual/default.nix index 30bd662..836b581 100644 --- a/hosts/nas/apps/actual/default.nix +++ b/hosts/nas/apps/actual/default.nix @@ -1,100 +1,124 @@ { config, pkgs, lib, ... }: +with lib; let - actualPort = 3333; - hostDataDir = "/media/nas/ssd/nix-app-data/actual"; + cfg = config.nas-apps.actual; dataDir = "/data"; hostAddress = "10.0.1.18"; - localAddress = "10.0.3.18"; actualUserId = config.users.users.nix-apps.uid; actualGroupId = config.users.groups.jallen-nas.gid; in { - containers.actual = { - autoStart = true; - privateNetwork = true; - hostAddress = hostAddress; - localAddress = localAddress; + imports = [ ./options.nix ]; - bindMounts = { - ${dataDir} = { - hostPath = hostDataDir; - isReadOnly = false; + config = mkIf cfg.enable { + containers.actual = { + autoStart = true; + privateNetwork = true; + hostAddress = hostAddress; + localAddress = cfg.localAddress; + + bindMounts = { + ${dataDir} = { + hostPath = cfg.dataDir; + isReadOnly = false; + }; + }; + + config = { lib, ... }: + { + services.actual = { + enable = true; + openFirewall = true; + settings = { + trustedProxies = [ hostAddress ]; + port = cfg.port; + dataDir = dataDir; + serverFiles = "${dataDir}/server-files"; + userFiles = "${dataDir}/user-files"; + }; + }; + + users.users.actual = { + isSystemUser = true; + uid = lib.mkForce actualUserId; + group = "actual"; + }; + + users.groups = { + actual = { + gid = lib.mkForce actualGroupId; + }; + }; + + # System packages + environment.systemPackages = with pkgs; [ + sqlite + ]; + + # Create and set permissions for required directories + system.activationScripts.actual-dirs = '' + mkdir -p ${dataDir} + chown -R actual:actual ${dataDir} + chmod -R 0700 ${dataDir} + ''; + + systemd.services = { + actual = { + environment.ACTUAL_CONFIG_PATH = lib.mkForce "${dataDir}/config.json"; + serviceConfig = { + ExecStart = lib.mkForce "${pkgs.actual-server}/bin/actual-server --config ${dataDir}/config.json"; + WorkingDirectory = lib.mkForce dataDir; + StateDirectory = lib.mkForce dataDir; + StateDirectoryMode = lib.mkForce 0700; + DynamicUser = lib.mkForce false; + ProtectSystem = lib.mkForce null; + }; + }; + }; + + networking = { + firewall = { + enable = true; + allowedTCPPorts = [ cfg.port ]; + }; + # Use systemd-resolved inside the container + # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 + useHostResolvConf = lib.mkForce false; + }; + + services.resolved.enable = true; + system.stateVersion = "23.11"; + }; + }; + + services.traefik.dynamicConfigOptions = lib.mkIf cfg.reverseProxy.enable { + services.actual.loadBalancer.servers = [ + { + url = "http://${cfg.localAddress}:${toString cfg.port}"; + } + ]; + routers.actual = { + entryPoints = [ "websecure" ]; + rule = "Host(`${cfg.reverseProxy.host}`)"; + service = "actual"; + middlewares = cfg.reverseProxy.middlewares; + tls.certResolver = "letsencrypt"; }; }; - config = { lib, ... }: - { - services.actual = { - enable = true; - openFirewall = true; - settings = { - trustedProxies = [ hostAddress ]; - port = actualPort; - dataDir = dataDir; - serverFiles = "${dataDir}/server-files"; - userFiles = "${dataDir}/user-files"; - }; - }; - - users.users.actual = { - isSystemUser = true; - uid = lib.mkForce actualUserId; - group = "actual"; - }; - - users.groups = { - actual = { - gid = lib.mkForce actualGroupId; - }; - }; - - # System packages - environment.systemPackages = with pkgs; [ - sqlite + networking = { + nat = { + forwardPorts = [ + { + destination = "${cfg.localAddress}:${toString cfg.port}"; + sourcePort = cfg.port; + } ]; - - # Create and set permissions for required directories - system.activationScripts.actual-dirs = '' - mkdir -p ${dataDir} - chown -R actual:actual ${dataDir} - chmod -R 0700 ${dataDir} - ''; - - systemd.services = { - actual = { - environment.ACTUAL_CONFIG_PATH = lib.mkForce "${dataDir}/config.json"; - serviceConfig = { - ExecStart = lib.mkForce "${pkgs.actual-server}/bin/actual-server --config ${dataDir}/config.json"; - WorkingDirectory = lib.mkForce dataDir; - StateDirectory = lib.mkForce dataDir; - StateDirectoryMode = lib.mkForce 0700; - DynamicUser = lib.mkForce false; - ProtectSystem = lib.mkForce null; - }; - }; - }; - - networking = { - firewall = { - enable = true; - allowedTCPPorts = [ actualPort ]; - }; - # Use systemd-resolved inside the container - # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 - useHostResolvConf = lib.mkForce false; - }; - - services.resolved.enable = true; - system.stateVersion = "23.11"; }; - }; - - networking.nat = { - forwardPorts = [ - { - destination = "${localAddress}:${toString actualPort}"; - sourcePort = actualPort; - } - ]; + firewall = { + allowedTCPPorts = [ cfg.port ]; + allowedUDPPorts = [ cfg.port ]; + }; + }; }; } diff --git a/hosts/nas/apps/actual/options.nix b/hosts/nas/apps/actual/options.nix new file mode 100644 index 0000000..6582f4c --- /dev/null +++ b/hosts/nas/apps/actual/options.nix @@ -0,0 +1,37 @@ +{ lib, ... }: +with lib; +{ + options.nas-apps.actual = { + enable = mkEnableOption "actual service"; + + port = mkOption { + type = types.int; + default = 80; + }; + + localAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + + dataDir = mkOption { + type = types.str; + default = ""; + }; + + reverseProxy = { + enable = mkOption { + type = types.bool; + default = false; + }; + host = mkOption { + type = types.str; + default = ""; + }; + middlewares = mkOption { + type = with types; listOf str; + default = [ ]; + }; + }; + }; +} diff --git a/hosts/nas/apps/arrs/default.nix b/hosts/nas/apps/arrs/default.nix index 44632f6..13f27df 100755 --- a/hosts/nas/apps/arrs/default.nix +++ b/hosts/nas/apps/arrs/default.nix @@ -1,15 +1,12 @@ { config, pkgs, + lib, ... }: - +with lib; let - radarrPort = 7878; - sonarrPort = 8989; - sabnzbdPort = 8280; - delugePort = 8112; - jackettPort = 9117; + cfg = config.nas-apps.arrs; radarrDataDir = "/var/lib/radarr"; downloadDir = "/downloads"; incompleteDir = "/downloads-incomplete"; @@ -19,210 +16,220 @@ let mediaDir = "/media"; arrUserId = config.users.users.nix-apps.uid; arrGroupId = config.users.groups.jallen-nas.gid; - radarrPkg = pkgs.unstable.radarr; - sonarrPkg = pkgs.unstable.sonarr; - delugePkg = pkgs.unstable.deluge; - jackettPkg = pkgs.unstable.jackett; - sabnzbdPkg = pkgs.unstable.sabnzbd; + radarrPkg = pkgs.radarr; + sonarrPkg = pkgs.sonarr; + delugePkg = pkgs.deluge; + jackettPkg = pkgs.jackett; + sabnzbdPkg = pkgs.sabnzbd; in { - containers.arrs = { - autoStart = true; - privateNetwork = true; - hostAddress = "10.0.1.18"; - localAddress = "10.0.1.51"; + imports = [ ./options.nix ]; - config = - { - pkgs, - lib, - ... - }: - { - nixpkgs.config.allowUnfree = true; + config = mkIf cfg.enable { + containers.arrs = { + autoStart = true; + privateNetwork = true; + hostAddress = "10.0.1.18"; + localAddress = cfg.localAddress; - # Enable radarr service - services.radarr = { - enable = true; - openFirewall = true; - user = "arrs"; - group = "media"; - dataDir = radarrDataDir; - package = radarrPkg; - }; + config = + { + pkgs, + lib, + ... + }: + { + nixpkgs.config.allowUnfree = true; - # Enable Sonarr service - services.sonarr = { - enable = true; - openFirewall = true; - user = "arrs"; - group = "media"; - dataDir = sonarrDataDir; - package = sonarrPkg; - }; - - # Enable Sabnzbd service - services.sabnzbd = { - enable = true; - openFirewall = true; - user = "arrs"; - group = "media"; - configFile = "${sabnzbdConfig}/sabnzbd.ini"; - package = sabnzbdPkg; - }; - - services.deluge = { - enable = true; - user = "arrs"; - group = "media"; - openFirewall = true; - dataDir = "/media"; - package = delugePkg; - web = { - enable = true; - port = 8112; + # Enable radarr service + services.radarr = { + enable = cfg.radarr.enable; openFirewall = true; + user = "arrs"; + group = "media"; + dataDir = cfg.radarr.dataDir; + package = radarrPkg; }; - }; - services.jackett = { - enable = true; - user = "arrs"; - group = "media"; - openFirewall = true; - package = jackettPkg; - }; - - # Create required users and groups - users.users.arrs = { - isSystemUser = true; - uid = lib.mkForce arrUserId; - group = "media"; - extraGroups = [ "downloads" ]; - }; - - users.groups = { - media = { - gid = lib.mkForce arrGroupId; + # Enable Sonarr service + services.sonarr = { + enable = cfg.sonarr.enable; + openFirewall = true; + user = "arrs"; + group = "media"; + dataDir = cfg.sonarr.dataDir; + package = sonarrPkg; }; - downloads = { }; + + # Enable Sabnzbd service + services.sabnzbd = { + enable = cfg.sabnzbd.enable; + openFirewall = true; + user = "arrs"; + group = "media"; + configFile = "${cfg.sabnzbd.dataDir}/sabnzbd.ini"; + package = sabnzbdPkg; + }; + + services.deluge = { + enable = cfg.deluge.enable; + user = "arrs"; + group = "media"; + openFirewall = true; + dataDir = "/media"; + package = delugePkg; + web = { + enable = true; + port = cfg.deluge.port; + openFirewall = true; + }; + }; + + services.jackett = { + enable = cfg.jackett.enable; + user = "arrs"; + group = "media"; + openFirewall = true; + package = jackettPkg; + }; + + # Create required users and groups + users.users.arrs = { + isSystemUser = true; + uid = lib.mkForce arrUserId; + group = "media"; + extraGroups = [ "downloads" ]; + }; + + users.groups = { + media = { + gid = lib.mkForce arrGroupId; + }; + downloads = { }; + }; + + # System packages + environment.systemPackages = with pkgs; [ + glib + sqlite + mono + mediainfo + protonvpn-cli_2 + ]; + + # Create and set permissions for required directories + system.activationScripts.arr-dirs = '' + mkdir -p ${radarrDataDir} + mkdir -p ${sonarrDataDir} + mkdir -p ${sabnzbdConfig} + mkdir -p ${downloadDir} + mkdir -p ${incompleteDir} + mkdir -p ${mediaDir} + + chown -R arrs:media ${radarrDataDir} + chown -R arrs:media ${sonarrDataDir} + chown -R arrs:media ${sabnzbdConfig} + chown -R arrs:media ${downloadDir} + chown -R arrs:media ${incompleteDir} + chown -R arrs:media ${mediaDir} + + chmod -R 775 ${radarrDataDir} + chmod -R 775 ${sonarrDataDir} + chmod -R 775 ${sabnzbdConfig} + chmod -R 775 ${downloadDir} + chmod -R 775 ${incompleteDir} + chmod -R 775 ${mediaDir} + + ''; + + networking = { + firewall = { + enable = true; + allowedTCPPorts = [ + cfg.radarr.port + cfg.sonarr.port + cfg.sabnzbd.port + ]; + }; + # Use systemd-resolved inside the container + # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 + useHostResolvConf = lib.mkForce false; + }; + + services.resolved.enable = true; + system.stateVersion = "23.11"; }; - # System packages - environment.systemPackages = with pkgs; [ - glib - sqlite - mono - mediainfo - protonvpn-cli_2 + # Bind mount directories from host + bindMounts = { + "${radarrDataDir}" = { + hostPath = cfg.radarr.dataDir; + isReadOnly = false; + }; + "${sonarrDataDir}" = { + hostPath = cfg.sonarr.dataDir; + isReadOnly = false; + }; + "${sabnzbdConfig}" = { + hostPath = cfg.sabnzbd.dataDir; + isReadOnly = false; + }; + "${downloadDir}" = { + hostPath = cfg.downloadsDir; + isReadOnly = false; + }; + "${incompleteDir}" = { + hostPath = cfg.incompleteDownloadsDir; + isReadOnly = false; + }; + "${jackettDir}" = { + hostPath = cfg.jackett.dataDir; + isReadOnly = false; + }; + "/media/movies" = { + hostPath = cfg.moviesDir; + isReadOnly = false; + }; + "/media/tv" = { + hostPath = cfg.tvDir; + isReadOnly = false; + }; + "/media/isos" = { + hostPath = cfg.isosDir; + isReadOnly = false; + }; + }; + }; + + networking = { + nat = { + forwardPorts = [ + { + destination = "${cfg.localAddress}:${toString cfg.radarr.port}"; + sourcePort = cfg.radarr.port; + } + { + destination = "${cfg.localAddress}:${toString cfg.sonarr.port}"; + sourcePort = cfg.sonarr.port; + } + { + destination = "${cfg.localAddress}:${toString cfg.sabnzbd.port}"; + sourcePort = cfg.sabnzbd.port; + } + { + destination = "${cfg.localAddress}:${toString cfg.deluge.port}"; + sourcePort = cfg.deluge.port; + } + { + destination = "${cfg.localAddress}:${toString cfg.jackett.port}"; + sourcePort = cfg.jackett.port; + } ]; - - # Create and set permissions for required directories - system.activationScripts.radarr-dirs = '' - mkdir -p ${radarrDataDir} - mkdir -p ${sonarrDataDir} - mkdir -p ${sabnzbdConfig} - mkdir -p ${downloadDir} - mkdir -p ${incompleteDir} - mkdir -p ${mediaDir} - - chown -R arrs:media ${radarrDataDir} - chown -R arrs:media ${sonarrDataDir} - chown -R arrs:media ${sabnzbdConfig} - chown -R arrs:media ${downloadDir} - chown -R arrs:media ${incompleteDir} - chown -R arrs:media ${mediaDir} - - chmod -R 775 ${radarrDataDir} - chmod -R 775 ${sonarrDataDir} - chmod -R 775 ${sabnzbdConfig} - chmod -R 775 ${downloadDir} - chmod -R 775 ${incompleteDir} - chmod -R 775 ${mediaDir} - - ''; - - networking = { - firewall = { - enable = true; - allowedTCPPorts = [ - radarrPort - sonarrPort - sabnzbdPort - ]; - }; - # Use systemd-resolved inside the container - # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 - useHostResolvConf = lib.mkForce false; - }; - - services.resolved.enable = true; - system.stateVersion = "23.11"; }; - - # Bind mount directories from host - bindMounts = { - "${radarrDataDir}" = { - hostPath = "/media/nas/ssd/nix-app-data/radarr"; - isReadOnly = false; - }; - "${sonarrDataDir}" = { - hostPath = "/media/nas/ssd/nix-app-data/sonarr"; - isReadOnly = false; - }; - "${sabnzbdConfig}" = { - hostPath = "/media/nas/ssd/nix-app-data/sabnzbd"; - isReadOnly = false; - }; - "${downloadDir}" = { - hostPath = "/media/nas/ssd/ssd_app_data/downloads"; - isReadOnly = false; - }; - "${incompleteDir}" = { - hostPath = "/media/nas/ssd/ssd_app_data/downloads-incomplete"; - isReadOnly = false; - }; - "${jackettDir}" = { - hostPath = "/media/nas/ssd/nix-app-data/jackett"; - isReadOnly = false; - }; - "/media/movies" = { - hostPath = "/media/nas/main/movies"; - isReadOnly = false; - }; - "/media/tv" = { - hostPath = "/media/nas/main/tv"; - isReadOnly = false; - }; - "/media/isos" = { - hostPath = "/media/nas/main/isos"; - isReadOnly = false; + firewall = { + allowedTCPPorts = [ cfg.radarr.port cfg.sonarr.port cfg.sabnzbd.port cfg.deluge.port cfg.jackett.port ]; + allowedUDPPorts = [ cfg.radarr.port cfg.sonarr.port cfg.sabnzbd.port cfg.deluge.port cfg.jackett.port ]; }; }; }; - - networking.nat = { - forwardPorts = [ - { - destination = "10.0.1.51:7878"; - sourcePort = radarrPort; - } - { - destination = "10.0.1.51:8989"; - sourcePort = sonarrPort; - } - { - destination = "10.0.1.51:8080"; - sourcePort = sabnzbdPort; - } - { - destination = "10.0.1.51:8112"; - sourcePort = delugePort; - } - { - destination = "10.0.1.51:9117"; - sourcePort = jackettPort; - } - ]; - }; } diff --git a/hosts/nas/apps/arrs/options.nix b/hosts/nas/apps/arrs/options.nix new file mode 100644 index 0000000..c79de31 --- /dev/null +++ b/hosts/nas/apps/arrs/options.nix @@ -0,0 +1,112 @@ +{ lib, ... }: +with lib; +{ + options.nas-apps.arrs = { + enable = mkEnableOption "arrs services"; + + radarr = { + enable = mkOption { + type = types.bool; + default = false; + }; + port = mkOption { + type = types.int; + default = 7878; + }; + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; + + sonarr = { + enable = mkOption { + type = types.bool; + default = false; + }; + port = mkOption { + type = types.int; + default = 8989; + }; + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; + + sabnzbd = { + enable = mkOption { + type = types.bool; + default = false; + }; + port = mkOption { + type = types.int; + default = 8280; + }; + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; + + deluge = { + enable = mkOption { + type = types.bool; + default = false; + }; + port = mkOption { + type = types.int; + default = 8112; + }; + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; + + jackett = { + enable = mkOption { + type = types.bool; + default = false; + }; + port = mkOption { + type = types.int; + default = 9117; + }; + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; + + localAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + + downloadsDir = mkOption { + type = types.str; + default = ""; + }; + + incompleteDownloadsDir = mkOption { + type = types.str; + default = ""; + }; + + moviesDir = mkOption { + type = types.str; + default = ""; + }; + + tvDir = mkOption { + type = types.str; + default = ""; + }; + + isosDir = mkOption { + type = types.str; + default = ""; + }; + }; +} diff --git a/hosts/nas/apps/crowdsec/default.nix b/hosts/nas/apps/crowdsec/default.nix index 7640d0e..99a165e 100755 --- a/hosts/nas/apps/crowdsec/default.nix +++ b/hosts/nas/apps/crowdsec/default.nix @@ -1,44 +1,58 @@ -{ outputs, pkgs, ... }: +{ outputs, config, lib, pkgs, ... }: +with lib; +let + cfg = config.nas-apps.crowdsec; +in { - services = { - crowdsec = let - yaml = (pkgs.formats.yaml {}).generate; - acquisitions_file = yaml "acquisitions.yaml" { - source = "journalctl"; - journalctl_filter = ["_SYSTEMD_UNIT=sshd.service"]; - labels.type = "syslog"; + imports = [ ./options.nix ]; + config = lib.mkIf cfg.enable { + services = { + crowdsec = let + yaml = (pkgs.formats.yaml {}).generate; + acquisitions_file = yaml "acquisitions.yaml" { + source = "journalctl"; + journalctl_filter = ["_SYSTEMD_UNIT=sshd.service"]; + labels.type = "syslog"; + }; + in { + enable = true; + enrollKeyFile = "${cfg.dataDir}/enroll.key"; + settings = { + crowdsec_service.acquisition_path = acquisitions_file; + api.server = { + listen_uri = "0.0.0.0:${toString cfg.port}"; + }; + }; }; - in { - enable = true; - enrollKeyFile = "/media/nas/ssd/nix-app-data/crowdsec/enroll.key"; - settings = { - crowdsec_service.acquisition_path = acquisitions_file; - api.server = { - listen_uri = "0.0.0.0:9898"; + + crowdsec-firewall-bouncer = { + enable = true; + settings = { + api_key = cfg.apiKey; + api_url = "http://${cfg.apiAddress}:${toString cfg.port}"; }; }; }; - crowdsec-firewall-bouncer = { - enable = true; - settings = { - api_key = "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE"; - api_url = "http://10.0.1.18:9898"; + systemd.services.crowdsec.serviceConfig = { + ExecStartPre = let + script = pkgs.writeScriptBin "register-bouncer" '' + #!${pkgs.runtimeShell} + set -eu + set -o pipefail + + if ! cscli bouncers list | grep -q "nas-bouncer"; then + cscli bouncers add "nas-bouncer" --key "${cfg.apiKey}" + fi + ''; + in ["${script}/bin/register-bouncer"]; + }; + + networking = { + firewall = { + allowedTCPPorts = [ cfg.port ]; + allowedUDPPorts = [ cfg.port ]; }; }; }; - - systemd.services.crowdsec.serviceConfig = { - ExecStartPre = let - script = pkgs.writeScriptBin "register-bouncer" '' - #!${pkgs.runtimeShell} - set -eu - set -o pipefail - - if ! cscli bouncers list | grep -q "nas-bouncer"; then - cscli bouncers add "nas-bouncer" --key "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE" - fi - ''; - in ["${script}/bin/register-bouncer"]; - }; } \ No newline at end of file diff --git a/hosts/nas/apps/crowdsec/options.nix b/hosts/nas/apps/crowdsec/options.nix new file mode 100644 index 0000000..ca72184 --- /dev/null +++ b/hosts/nas/apps/crowdsec/options.nix @@ -0,0 +1,27 @@ +{ lib, ... }: +with lib; +{ + options.nas-apps.crowdsec = { + enable = mkEnableOption "crowdsec service"; + + port = mkOption { + type = types.int; + default = 9898; + }; + + apiAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + + apiKey = mkOption { + type = types.str; + default = ""; + }; + + dataDir = mkOption { + type = types.str; + default = ""; + }; + }; +} diff --git a/hosts/nas/apps/gitea/default.nix b/hosts/nas/apps/gitea/default.nix index 7d6179e..7aa122c 100644 --- a/hosts/nas/apps/gitea/default.nix +++ b/hosts/nas/apps/gitea/default.nix @@ -1,109 +1,130 @@ -{ config, ... }: +{ config, lib, ... }: +with lib; let + cfg = config.nas-apps.gitea; hostAddress = "10.0.1.18"; - localAddress = "10.0.4.18"; - httpPort = 3000; - sshPort = 2222; + # localAddress = "10.0.4.18"; + # httpPort = 3000; + # sshPort = 2222; rootUrl = "https://gitea.mjallen.dev/"; - stateDir = "/media/nas/ssd/nix-app-data/gitea"; + # stateDir = "/media/nas/ssd/nix-app-data/gitea"; dataDir = "/var/lib/gitea"; secretsDir = "/run/secrets/jallen-nas/gitea"; mailerPasswordFile = config.sops.secrets."jallen-nas/gitea/mail-key".path; metricsTokenFile = config.sops.secrets."jallen-nas/gitea/metrics-key".path; in { - containers.gitea = { - autoStart = true; - privateNetwork = true; - hostAddress = hostAddress; - localAddress = localAddress; + imports = [ ./options.nix ]; + config = mkIf cfg.enable { + containers.gitea = { + autoStart = true; + privateNetwork = true; + hostAddress = hostAddress; + localAddress = cfg.localAddress; - bindMounts = { - ${dataDir} = { - hostPath = stateDir; - isReadOnly = false; + bindMounts = { + ${dataDir} = { + hostPath = cfg.dataDir; + isReadOnly = false; + }; + secrets = { + hostPath = secretsDir; + isReadOnly = true; + mountPoint = secretsDir; + }; }; - secrets = { - hostPath = secretsDir; - isReadOnly = true; - mountPoint = secretsDir; + + config = { lib, ... }: + { + services.gitea = { + enable = true; + stateDir = dataDir; + mailerPasswordFile = mailerPasswordFile; + metricsTokenFile = metricsTokenFile; + settings = { + server = { + DOMAIN = "jallen-nas"; + HTTP_ADDR = "0.0.0.0"; + HTTP_PORT = cfg.httpPort; + PROTOCOL = "http"; + ROOT_URL = rootUrl; + START_SSH_SERVER = true; + SSH_PORT = cfg.sshPort; + }; + service = { + REGISTER_EMAIL_CONFIRM = false; + ENABLE_CAPTCHA = false; + DISABLE_REGISTRATION = true; + ENABLE_OPENID_SIGNIN = false; + ENABLE_LDAP_SIGNIN = false; + ENABLE_SSH_SIGNIN = true; + ENABLE_BUILTIN_SSH_SERVER = true; + ENABLE_REVERSE_PROXY_AUTHENTICATION = true; + }; + }; + }; + + users.users.gitea = { + extraGroups = [ "keys" ]; + }; + + networking = { + firewall = { + enable = true; + allowedTCPPorts = [ cfg.httpPort cfg.sshPort ]; + }; + # Use systemd-resolved inside the container + # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 + useHostResolvConf = lib.mkForce false; + }; + + # Create and set permissions for required directories + system.activationScripts.gitea-dirs = '' + mkdir -p /var/lib/gitea + chown -R gitea:gitea /var/lib/gitea + chmod -R 775 /var/lib/gitea + mkdir -p /run/secrets/jallen-nas + chown -R gitea:gitea /run/secrets/jallen-nas + chmod -R 775 /run/secrets/jallen-nas + ''; + + services.resolved.enable = true; + system.stateVersion = "23.11"; + }; + }; + + services.traefik.dynamicConfigOptions = lib.mkIf cfg.reverseProxy.enable { + services.gitea.loadBalancer.servers = [ + { + url = "http://${cfg.localAddress}:${toString cfg.httpPort}"; + } + ]; + routers.gitea = { + entryPoints = [ "websecure" ]; + rule = "Host(`${cfg.reverseProxy.host}`)"; + service = "gitea"; + middlewares = cfg.reverseProxy.middlewares; + tls.certResolver = "letsencrypt"; }; }; - config = { lib, ... }: - { - services.gitea = { - enable = true; - stateDir = dataDir; - useWizard = false; - mailerPasswordFile = mailerPasswordFile; - metricsTokenFile = metricsTokenFile; - settings = { - server = { - DOMAIN = "jallen-nas"; - HTTP_ADDR = "0.0.0.0"; - HTTP_PORT = httpPort; - PROTOCOL = "http"; - ROOT_URL = rootUrl; - START_SSH_SERVER = true; - SSH_PORT = sshPort; - }; - service = { - REGISTER_EMAIL_CONFIRM = false; - ENABLE_CAPTCHA = false; - DISABLE_REGISTRATION = true; - ENABLE_OPENID_SIGNIN = false; - ENABLE_LDAP_SIGNIN = false; - ENABLE_SSH_SIGNIN = true; - ENABLE_BUILTIN_SSH_SERVER = true; - ENABLE_REVERSE_PROXY_AUTHENTICATION = true; - }; - }; - }; - - users.users.gitea = { - extraGroups = [ "keys" ]; - }; - - networking = { - firewall = { - enable = true; - allowedTCPPorts = [ httpPort sshPort 22 ]; - }; - # Use systemd-resolved inside the container - # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 - useHostResolvConf = lib.mkForce false; - }; - - # Create and set permissions for required directories - system.activationScripts.gitea-dirs = '' - mkdir -p /var/lib/gitea - chown -R gitea:gitea /var/lib/gitea - chmod -R 775 /var/lib/gitea - mkdir -p /run/secrets/jallen-nas - chown -R gitea:gitea /run/secrets/jallen-nas - chmod -R 775 /run/secrets/jallen-nas - ''; - - services.resolved.enable = true; - system.stateVersion = "23.11"; + networking = { + nat = { + forwardPorts = [ + { + destination = "${cfg.localAddress}:${toString cfg.httpPort}"; + sourcePort = cfg.httpPort; + } + { + destination = "${cfg.localAddress}:${toString cfg.sshPort}"; + sourcePort = cfg.sshPort; + } + ]; }; + firewall = { + allowedTCPPorts = [ cfg.httpPort cfg.sshPort ]; + allowedUDPPorts = [ cfg.httpPort cfg.sshPort ]; + }; + }; }; - - networking.nat = { - forwardPorts = [ - { - destination = "${localAddress}:${toString httpPort}"; - sourcePort = httpPort; - } - { - destination = "${localAddress}:${toString 2222}"; - sourcePort = sshPort; - } - # { - # destination = "${localAddress}:${toString 22}"; - # sourcePort = 22; - # } - ]; - }; -} \ No newline at end of file +} diff --git a/hosts/nas/apps/gitea/options.nix b/hosts/nas/apps/gitea/options.nix new file mode 100644 index 0000000..294810c --- /dev/null +++ b/hosts/nas/apps/gitea/options.nix @@ -0,0 +1,42 @@ +{ lib, ... }: +with lib; +{ + options.nas-apps.gitea = { + enable = mkEnableOption "gitea service"; + + httpPort = mkOption { + type = types.int; + default = 80; + }; + + sshPort = mkOption { + type = types.int; + default = 22; + }; + + localAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + + dataDir = mkOption { + type = types.str; + default = ""; + }; + + reverseProxy = { + enable = mkOption { + type = types.bool; + default = false; + }; + host = mkOption { + type = types.str; + default = ""; + }; + middlewares = mkOption { + type = with types; listOf str; + default = [ ]; + }; + }; + }; +} diff --git a/hosts/nas/apps/options.nix b/hosts/nas/apps/options.nix new file mode 100644 index 0000000..96394ce --- /dev/null +++ b/hosts/nas/apps/options.nix @@ -0,0 +1,46 @@ +{ lib, ... }: +let + inherit (lib) types mkOption; +in +{ + options.nas-apps = mkOption { + type = types.attrsOf (types.submodule ({ config, name, ... }: { + options = { + enable = mkOption { + type = types.bool; + default = false; + }; + + port = mkOption { + type = types.int; + default = 80; + }; + + localAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + + dataDir = mkOption { + type = types.str; + default = ""; + }; + + reverseProxy = { + enable = mkOption { + type = types.bool; + default = false; + }; + host = mkOption { + type = types.str; + default = ""; + }; + middlewares = mkOption { + type = with types; listOf str; + default = [ ]; + }; + }; + }; + })); + }; +} diff --git a/hosts/nas/apps/traefik/default.nix b/hosts/nas/apps/traefik/default.nix index 3c58f62..0584391 100755 --- a/hosts/nas/apps/traefik/default.nix +++ b/hosts/nas/apps/traefik/default.nix @@ -1,25 +1,24 @@ { config, ... }: let domain = "mjallen.dev"; + serverIp = "10.0.1.18"; # Forward services - authUrl = "http://10.0.1.18:9000/outpost.goauthentik.io"; - authentikUrl = "http://10.0.1.18:9000"; - onlyofficeUrl = "http://10.0.2.18:9980"; - cloudUrl = "http://10.0.2.18:80"; - jellyfinUrl = "http://10.0.1.18:8096"; - jellyseerrUrl = "http://10.0.1.52:5055"; - hassUrl = "http://homeassistant.local:8123"; - openWebUIUrl = "http://10.0.1.18:8888"; - paperlessUrl = "http://10.0.1.20:28981"; - cacheUrl = "http://10.0.1.18:5000"; - giteaUrl = "http://10.0.4.18:3000"; - actualUrl = "http://10.0.3.18:3333"; - lubeloggerUrl = "http://10.0.1.18:6754"; - immichUrl = "http://10.0.1.18:2283"; + authUrl = "http://${serverIp}:9000/outpost.goauthentik.io"; - # internal services - codeUrl = "http://10.0.1.18:4444"; + actualUrl = "http://${config.containers.actual.localAddress}:${toString config.containers.actual.config.services.actual.settings.port}"; + authentikUrl = "http://${serverIp}:9000"; + cacheUrl = "http://${serverIp}:${toString config.services.nix-serve.port}"; + cloudUrl = "http://${config.containers.nextcloud.localAddress}:80"; + giteaUrl = "http://${config.containers.gitea.localAddress}:${toString config.containers.gitea.config.services.gitea.settings.server.SSH_PORT}"; + hassUrl = "http://homeassistant.local:8123"; + immichUrl = "http://${serverIp}:${toString config.services.immich.port}"; + jellyfinUrl = "http://${serverIp}:8096"; + jellyseerrUrl = "http://${config.containers.jellyseerr.localAddress}:${toString config.containers.jellyseerr.config.services.jellyseerr.port}"; + lubeloggerUrl = "http://${serverIp}:6754"; + onlyofficeUrl = "http://${config.containers.nextcloud.localAddress}:${toString config.containers.nextcloud.config.services.onlyoffice.port}"; + openWebUIUrl = "http://${serverIp}:8888"; + paperlessUrl = "http://${config.containers.paperless.localAddress}:${toString config.containers.paperless.config.services.paperless.port}"; # Plugins traefikPlugins = { @@ -33,7 +32,7 @@ let }; }; - crowdsecAppsecHost = "10.0.1.18:7422"; + crowdsecAppsecHost = "${serverIp}:7422"; crowdsecLapiKeyFile = config.sops.secrets."jallen-nas/traefik/crowdsec-lapi-key".path; # Ports @@ -52,8 +51,7 @@ let # misc letsEncryptEmail = "jalle008@proton.me"; dataDir = "/media/nas/ssd/nix-app-data/traefik"; - authentikAddress = "http://10.0.1.18:9000/outpost.goauthentik.io/auth/traefik"; - group = [ config.users.users.nix-apps.group.name ]; + authentikAddress = "http://${serverIp}:9000/outpost.goauthentik.io/auth/traefik"; in { sops = { @@ -228,14 +226,25 @@ in url = authUrl; } ]; + + actual.loadBalancer.servers = [ + { + url = actualUrl; + } + ]; authentik.loadBalancer.servers = [ { url = authentikUrl; } ]; - onlyoffice.loadBalancer.servers = [ + cache.loadBalancer.servers = [ { - url = onlyofficeUrl; + url = cacheUrl; + } + ]; + chat.loadBalancer.servers = [ + { + url = openWebUIUrl; } ]; cloud.loadBalancer.servers = [ @@ -243,6 +252,21 @@ in url = cloudUrl; } ]; + gitea.loadBalancer.servers = [ + { + url = giteaUrl; + } + ]; + hass.loadBalancer.servers = [ + { + url = hassUrl; + } + ]; + immich.loadBalancer.servers = [ + { + url = immichUrl; + } + ]; jellyfin.loadBalancer.servers = [ { url = jellyfinUrl; @@ -253,51 +277,19 @@ in url = jellyseerrUrl; } ]; - hass.loadBalancer.servers = [ - { - url = hassUrl; - } - ]; - chat.loadBalancer.servers = [ - { - url = openWebUIUrl; - } - ]; - cache.loadBalancer.servers = [ - { - url = cacheUrl; - } - ]; - paperless.loadBalancer.servers = [ - { - url = paperlessUrl; - } - ]; - gitea.loadBalancer.servers = [ - { - url = giteaUrl; - } - ]; - actual.loadBalancer.servers = [ - { - url = actualUrl; - } - ]; lubelogger.loadBalancer.servers = [ { url = lubeloggerUrl; } ]; - immich.loadBalancer.servers = [ + onlyoffice.loadBalancer.servers = [ { - url = immichUrl; + url = onlyofficeUrl; } ]; - - # internal services - code.loadBalancer.servers = [ + paperless.loadBalancer.servers = [ { - url = codeUrl; + url = paperlessUrl; } ]; }; @@ -311,6 +303,14 @@ in priority = 15; tls.certResolver = "letsencrypt"; }; + + actual = { + entryPoints = [ "websecure" ]; + rule = "Host(`actual.${domain}`)"; + service = "actual"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + tls.certResolver = "letsencrypt"; + }; authentik = { entryPoints = [ "websecure" ]; rule = "Host(`authentik.${domain}`)"; @@ -318,11 +318,12 @@ in middlewares = [ "crowdsec" "whitelist-geoblock" ]; tls.certResolver = "letsencrypt"; }; - onlyoffice = { + cache = { entryPoints = [ "websecure" ]; - rule = "Host(`office.${domain}`)"; - service = "onlyoffice"; - middlewares = [ "crowdsec" "whitelist-geoblock" "onlyoffice-websocket" ]; + rule = "Host(`cache.${domain}`)"; + service = "cache"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + priority = 10; tls.certResolver = "letsencrypt"; }; cloud = { @@ -332,6 +333,28 @@ in middlewares = [ "crowdsec" "whitelist-geoblock" ]; tls.certResolver = "letsencrypt"; }; + gitea = { + entryPoints = [ "websecure" ]; + rule = "Host(`gitea.${domain}`)"; + service = "gitea"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + tls.certResolver = "letsencrypt"; + }; + hass = { + entryPoints = [ "websecure" ]; + rule = "Host(`hass.${domain}`)"; + service = "hass"; + middlewares = [ "crowdsec" "whitelist-geoblock" "authentik" ]; + priority = 10; + tls.certResolver = "letsencrypt"; + }; + immich = { + entryPoints = [ "websecure" ]; + rule = "Host(`immich.${domain}`)"; + service = "immich"; + middlewares = [ "crowdsec" "whitelist-geoblock" ]; + tls.certResolver = "letsencrypt"; + }; jellyfin = { entryPoints = [ "websecure" ]; rule = "Host(`jellyfin.${domain}`)"; @@ -346,36 +369,6 @@ in middlewares = [ "crowdsec" "whitelist-geoblock" ]; tls.certResolver = "letsencrypt"; }; - gitea = { - entryPoints = [ "websecure" ]; - rule = "Host(`gitea.${domain}`)"; - service = "gitea"; - middlewares = [ "crowdsec" "whitelist-geoblock" ]; - tls.certResolver = "letsencrypt"; - }; - actual = { - entryPoints = [ "websecure" ]; - rule = "Host(`actual.${domain}`)"; - service = "actual"; - middlewares = [ "crowdsec" "whitelist-geoblock" ]; - tls.certResolver = "letsencrypt"; - }; - hass = { - entryPoints = [ "websecure" ]; - rule = "Host(`hass.${domain}`)"; - service = "hass"; - middlewares = [ "crowdsec" "whitelist-geoblock" "authentik" ]; - priority = 10; - tls.certResolver = "letsencrypt"; - }; - cache = { - entryPoints = [ "websecure" ]; - rule = "Host(`cache.${domain}`)"; - service = "cache"; - middlewares = [ "crowdsec" "whitelist-geoblock" ]; - priority = 10; - tls.certResolver = "letsencrypt"; - }; lubelogger = { entryPoints = [ "websecure" ]; rule = "Host(`lubelogger.${domain}`)"; @@ -383,20 +376,11 @@ in middlewares = [ "crowdsec" "whitelist-geoblock" ]; tls.certResolver = "letsencrypt"; }; - immich = { + onlyoffice = { entryPoints = [ "websecure" ]; - rule = "Host(`immich.${domain}`)"; - service = "immich"; - middlewares = [ "crowdsec" "whitelist-geoblock" ]; - tls.certResolver = "letsencrypt"; - }; - - # internal services - code = { - entryPoints = [ "websecure" ]; - rule = "Host(`code.${domain}`)"; - service = "code"; - middlewares = [ "internal-ipallowlist" ]; + rule = "Host(`office.${domain}`)"; + service = "onlyoffice"; + middlewares = [ "crowdsec" "whitelist-geoblock" "onlyoffice-websocket" ]; tls.certResolver = "letsencrypt"; }; }; diff --git a/hosts/nas/configuration.nix b/hosts/nas/configuration.nix index ffca6b0..cbf0e3b 100755 --- a/hosts/nas/configuration.nix +++ b/hosts/nas/configuration.nix @@ -57,6 +57,7 @@ ''; systemPackages = with pkgs; [ + attic-client binutils cryptsetup cmake diff --git a/hosts/nas/grafana.nix b/hosts/nas/grafana.nix index c2f0ae7..6dcdbb5 100755 --- a/hosts/nas/grafana.nix +++ b/hosts/nas/grafana.nix @@ -22,7 +22,7 @@ in ]; }; libvirt = { - enable = true; + enable = false; openFirewall = true; }; nut = { diff --git a/hosts/nas/networking.nix b/hosts/nas/networking.nix index 858647f..b8496f8 100755 --- a/hosts/nas/networking.nix +++ b/hosts/nas/networking.nix @@ -21,6 +21,7 @@ let 6754 # lubelogger 2283 # immich 4444 # code-server + 9012 ]; in { diff --git a/hosts/nas/nix-serve.nix b/hosts/nas/nix-serve.nix index 90c3317..275c6f5 100755 --- a/hosts/nas/nix-serve.nix +++ b/hosts/nas/nix-serve.nix @@ -1,4 +1,4 @@ -{ pkgs, ... }: +{ config, pkgs, ... }: let nix-build-mail = pkgs.writeShellScript "echo -e \"Content-Type: text/plain\\r\\nSubject: NixOS cache rebuild failed\\r\\n\\r\\nThe nix-rebuild-cache service failed at $(date).\" | sendmail jalle008@proton.me"; in @@ -13,6 +13,14 @@ in openFirewall = true; }; + services.atticd = { + enable = true; + environmentFile = config.sops.secrets."jallen-nas/attic-key".path; + settings = { + listen = "[::]:9012"; + }; + }; + # Improved systemd service with better error handling systemd = { services = { @@ -299,8 +307,8 @@ in }; }; -# nix.settings.builders-use-substitutes = true; -# nix.distributedBuilds = true; + nix.settings.builders-use-substitutes = true; + nix.distributedBuilds = true; nix.buildMachines = [ { hostName = "pi5.local"; diff --git a/hosts/nas/sops.nix b/hosts/nas/sops.nix index 7e32dfa..cdbbaad 100755 --- a/hosts/nas/sops.nix +++ b/hosts/nas/sops.nix @@ -233,6 +233,9 @@ in path = "/etc/secureboot/keys/PK/PK.pem"; mode = "0640"; }; + "jallen-nas/attic-key" = { +# owner = "atticd"; + }; }; # ------------------------------ diff --git a/secrets/nas-secrets.yaml b/secrets/nas-secrets.yaml index 29cb7d3..65f7554 100644 --- a/secrets/nas-secrets.yaml +++ b/secrets/nas-secrets.yaml @@ -49,6 +49,7 @@ jallen-nas: system-ed25519-priv: ENC[AES256_GCM,data:tMk6T/mfo+8k5ZWMy0zZOrs2svq3ePWn3TJKxEnpiuShNV1wT1bMkIojJUqFPNwoxt1DFm3KgDpPvHp/NpffgLJ0wYenHZTYKZj4d7RtcFnCmCWH61hLwmriqxP//bK5d1WX7ynXgVzyTCPyCAr2JQNITjug5geEnpcUb9AKpa+WRTNF6T88mI/i5DMaiMeUl/PGofQjK8YzcOhYgvCZe19TCK6QGDViwe7az96h5x0q5nuhp8rwiGVso3DRvWcLUSzYcxvkyh2Z58bX2mHNUJEslYlAK0ekA9O1mj/1dJei7KD+saJGM7uvP4zrSlHzFvSx1gkxvWbQL84LZOp9XfCNTjwDi503nPb8vLpg08m2iKrAS3NEA/cifCL9h5QhlJWNEz0pA7GTERsZvKk6Ajt9E+MrFs6ixKey3IPZhLExjt5UjjWeZ9PWHTlL2805GgKD+yjqWEDLsWmcRDUlmLcd8tIi7RBmo2PjVIq1ZtDeuCQuWTHWAgoX8ijak6JsNl/8RsyK2rwKb6RTDAihuPuiQFuW+d7mfANX,iv:9CU3QJORv0CD1u9nnFDbKo9DitjQvWGUVjtLD9TkcQw=,tag:zJLPWaKrSNPJtd/PFvmf9A==,type:str] system-rsa-pub: ENC[AES256_GCM,data:9vhYfhIvBPDckZLxa5RmVWGEzRoLZ51aEkAfUpHFm6SJ2e/BS7glExFh+rw+DXX1KrERbl7W3R3TRF8YVsxEKjMXf7OH51IWSmpdHtoeT9Thkyjt9EX7uu68NqSyUaq0ndRBUBp1zCAXIuBw7JcGC0H+nxfxdscDrLoKD8bW2NfRazv5crW97wWj8ajpfeKZTH7fOopYHIWjgcWTfiKh3gC3j1WCt6WjWxfHIGrNnBrvlhpi9QOFFfBOxF7NrSLfIUm8LURtPtcIZEX0SmSq1j9azU4EU5WoI3FwFJTqTLY0HXPM0MdFg4y5EXGwzok7icAWiznC3UVhjWVTjAjGjDEaAzoGTd+KNipUmiTWZNW12ab7XMHbeR1ZJPaVzcMEB0wKMbvRSZSFT1eLcULHwzezE5rP2JTiLkADgK06kQNs908uO2NEaGdB/H7sroFah4ZthisPy33lxVinfE2MDLDABj7ORIURST8ag3bw6Jhmam3+38x80YSA3APVWHsRc3+mJhIRCXPS6vfurVY/AFMkIwH/Fxcx6lyeJHx/KutUR9doV4h91hiVHK+hstYuiA0qvKw5VahS9Ht8BosJ6T1eBC1xwiSctMUcC4dRltTjVegNnEsuvyQh1xqL1Xq49k8MyuBCoqmNrTqkxt2Dq6facxwqt4XpSMK2qHEQuXrY61oWh2z+5B8wUk/wVCYg3cAlhFOAObI8aRundOh5Pe6mdFxKW4TnuD+mgoNWsJnqfTZfBbhEJWDtxsaIpoJoSMvZmBirWBCZsDeWSyOaegFZd6hmNgyQ3exmu/aByojzJm30Gcg01WYrDZMVXBFUfkymhxTpHUSXX0dEdfd8QCQjbiH3g6rPttXYL/ZYlS++8oJwkYNYrKi6ENFORmReQpATuWVmxGHnLLV/Si5BhYjPLAc2Hum4nIBd43SL2jijUSXLE6hOrntL36Ll4ToTs7cDMHIDtopYC6ML6DS+Mek95l0=,iv:3ziWTbKw4C7noiQvrsx1+/rA5me/zZLMImR0Emjc6d8=,tag:3ncMX9pUOSB5VjdbO8HMgQ==,type:str] system-rsa-priv: ENC[AES256_GCM,data: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,iv:BnghwsMVzoXpHXJcmii1vQykVQYzqRlEbDrC1ofdBfI=,tag:6CQYbV17YcIjBmaOrO6/NQ==,type:str] + attic-key: ENC[AES256_GCM,data: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,iv:/Qwb4B4uS0aimH3WaVPh4D0iRhQneDZKiSes2eXR6Ws=,tag:WH7C6VGckzqSycEXfYLqkA==,type:str] sops: age: - recipient: age157jemphjzg6zmk373vpccuguyw6e75qnkqmz8pcnn2yue85p939swqqhy0 @@ -141,7 +142,7 @@ sops: WmFoMmlNbXUvREZESnU3ZzFMbkNGVjAKGLZWo3E9098b2kOn77U/CGQpo4/mqSXY 5+rZJuC7Iqh+VDW519Cf1go+0k05rgree3IqHXN2/KHX+pC0L6CkyA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-05-29T02:16:50Z" - mac: ENC[AES256_GCM,data:+da4p8E8QfzHZQhOyM6G6JcCFgyvLjxvJANXKz4oKHqhcJqfwWMbmNdh5Qx0xHhAN7saDpuJtqcJdafdirIDpQdEU1Mu/N2vtFG7pds0HKgF7anGaIAI4K6C871XsewhbXw+MYYA6cehPWKPanglmIFYolUHBrcFBPrct1/iasA=,iv:bS5yX7Kbbixc3nMPum2TEPJuFRlHCDqjUfTMeZU6cK4=,tag:hsv92EWzjHJaYZYWvK0kxA==,type:str] + lastmodified: "2025-06-11T03:10:55Z" + mac: ENC[AES256_GCM,data:01jMwSJK3zW0vltFj06veiAsJBBSKrN5SJuU87/sfhjrfZHWQ/fOITK4Ihz/d9/J2kA9bMJEM50C3cSBaakdYW4RDkL1P83aOsJLjp/c3WjJ8BApDdzoQzUvkjIVt7qd/jNg13c8d+ofBkeSsZ8T3COrOHCc3RgG961T4Ij9WBY=,iv:cNJUQ8ZEPKPKfUlTYD/Zlfomev3ZC/mnP90Me3ycjMc=,tag:LluaNhFpQbuql208sEyPkA==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2