diff --git a/hosts/sobotka/server.nix b/hosts/sobotka/server.nix index 70c7f750..6145850e 100644 --- a/hosts/sobotka/server.nix +++ b/hosts/sobotka/server.nix @@ -3,109 +3,253 @@ enable = true; email = "adam@cnst.dev"; domain = "cnix.dev"; + ip = "192.168.88.14"; user = "share"; group = "share"; uid = 994; gid = 993; - traefik = { - enable = true; - }; - tailscale = { - enable = true; - }; - unbound = { - enable = true; - }; - homepage-dashboard = { - enable = true; - }; - n8n = { - enable = true; - }; - bazarr = { - enable = true; - }; - prowlarr = { - enable = true; - }; - lidarr = { - enable = true; - }; - sonarr = { - enable = true; - }; - radarr = { - enable = true; - }; - jellyseerr = { - enable = true; - }; - jellyfin = { - enable = true; - }; - uptime-kuma = { - enable = true; - }; - gitea = { - enable = true; - url = "git.cnst.dev"; - cloudflared = { - tunnelId = "33e2fb8e-ecef-4d42-b845-6d15e216e448"; - credentialsFile = config.age.secrets.giteaCloudflared.path; - }; - }; - vaultwarden = { - enable = true; - url = "vault.cnst.dev"; - cloudflared = { - tunnelId = "fdd98086-6a4c-44f2-bba0-eb86b833cce5"; - credentialsFile = config.age.secrets.vaultwardenCloudflared.path; - }; - }; - www = { - enable = true; - url = "cnst.dev"; - cloudflared = { - tunnelId = "e5076186-efb7-405a-998c-6155af7fb221"; - credentialsFile = config.age.secrets.wwwCloudflared.path; - }; - }; - authentik = { - enable = true; - url = "auth.cnst.dev"; - cloudflared = { - tunnelId = "b66f9368-db9e-4302-8b48-527cda34a635"; - credentialsFile = config.age.secrets.authentikCloudflared.path; - }; - }; - nextcloud = { - enable = true; - adminpassFile = config.age.secrets.nextcloudAdminPass.path; - }; - fail2ban = { - enable = true; - apiKeyFile = config.age.secrets.cloudflareFirewallApiKey.path; - zoneId = "0027acdfb8bbe010f55b676ad8698dfb"; - }; - keepalived = { - enable = true; - interface = "enp6s0"; - }; - podman = { - enable = true; - gluetun.enable = true; - qbittorrent = { + infra = { + authentik = { enable = true; - port = 8080; + url = "auth.cnst.dev"; + port = 9000; + cloudflared = { + tunnelId = "b66f9368-db9e-4302-8b48-527cda34a635"; + credentialsFile = config.age.secrets.authentikCloudflared.path; + }; }; - slskd = { + traefik = { enable = true; }; - pihole = { + tailscale = { enable = true; - port = 8053; }; + unbound = { + enable = true; + }; + fail2ban = { + enable = true; + apiKeyFile = config.age.secrets.cloudflareFirewallApiKey.path; + zoneId = "0027acdfb8bbe010f55b676ad8698dfb"; + }; + keepalived = { + enable = true; + interface = "enp6s0"; + }; + gluetun = { + enable = true; + }; + podman = { + enable = true; + }; + www = { + enable = true; + url = "cnst.dev"; + port = 8283; + cloudflared = { + tunnelId = "e5076186-efb7-405a-998c-6155af7fb221"; + credentialsFile = config.age.secrets.wwwCloudflared.path; + }; + }; + }; + + services = { + # homepage-dashboard = { + # enable = true; + # subdomain = ""; + # port = "8082"; + # }; + # n8n = { + # enable = true; + # subdomain = "n8n"; + # port = "5678"; + # homepage = { + # name = "n8n"; + # description = "A workflow automation platform"; + # icon = "n8n.svg"; + # category = "Services"; + # }; + # }; + # bazarr = { + # enable = true; + # subdomain = "bazarr"; + # port = 6767; + # homepage = { + # name = "Bazarr"; + # description = "Subtitle manager"; + # icon = "bazarr.svg"; + # category = "Arr"; + # }; + # }; + # prowlarr = { + # enable = true; + # subdomain = "prowlarr"; + # port = 9696; + # homepage = { + # name = "prowlarr"; + # description = "PVR indexer"; + # icon = "prowlarr.svg"; + # category = "Arr"; + # }; + # }; + # flaresolverr = { + # enable = true; + # subdomain = "flaresolverr"; + # port = 8191; + # homepage = { + # name = "FlareSolverr"; + # description = "Proxy to bypass Cloudflare/DDoS-GUARD protection"; + # icon = "flaresolverr.svg"; + # category = "Arr"; + # }; + # }; + # lidarr = { + # enable = true; + # subdomain = "lidarr"; + # port = 8686; + # homepage = { + # name = "Lidarr"; + # description = "Music collection manager"; + # icon = "lidarr.svg"; + # category = "Arr"; + # }; + # }; + # sonarr = { + # enable = true; + # subdomain = "sonarr"; + # port = 8989; + # homepage = { + # name = "Sonarr"; + # description = "Internet PVR for Usenet and Torrents"; + # icon = "sonarr.svg"; + # category = "Arr"; + # }; + # }; + # radarr = { + # enable = true; + # subdomain = "radarr"; + # port = 7878; + # homepage = { + # name = "Radarr"; + # description = "Movie collection manager"; + # icon = "radarr.svg"; + # category = "Arr"; + # }; + # }; + # jellyseerr = { + # enable = true; + # subdomain = "jellyseerr"; + # port = 5055; + # homepage = { + # name = "Jellyseerr"; + # description = "Media request and discovery manager"; + # icon = "jellyserr.svg"; + # category = "Arr"; + # }; + # }; + # jellyfin = { + # enable = true; + # subdomain = "fin"; + # exposure = "tailscale"; + # port = 8096; + # homepage = { + # name = "Jellyfin"; + # description = "The Free Software Media System"; + # icon = "jellyfin.svg"; + # category = "Media"; + # }; + # }; + # uptime-kuma = { + # enable = true; + # subdomain = "uptime"; + # port = 3001; + # homepage = { + # name = "Uptime Kuma"; + # description = "Service monitoring tool"; + # icon = "uptime-kuma.svg"; + # category = "Services"; + # }; + # }; + # gitea = { + # enable = true; + # subdomain = "git"; + # exposure = "tunnel"; + # port = 5003; + # cloudflared = { + # tunnelId = "33e2fb8e-ecef-4d42-b845-6d15e216e448"; + # credentialsFile = config.age.secrets.giteaCloudflared.path; + # }; + # homepage = { + # name = "Gitea"; + # description = "Git with a cup of tea"; + # icon = "gitea.svg"; + # category = "Services"; + # }; + # }; + vaultwarden = { + enable = true; + # subdomain = "vault"; + # exposure = "tunnel"; + # port = 8222; + # cloudflared = { + # tunnelId = "fdd98086-6a4c-44f2-bba0-eb86b833cce5"; + # credentialsFile = config.age.secrets.vaultwardenCloudflared.path; + # }; + # homepage = { + # name = "Vaultwarden"; + # description = "Password manager"; + # icon = "vaultwarden.svg"; + # category = "Services"; + # }; + }; + nextcloud = { + enable = true; + subdomain = "cloud"; + exposure = "local"; + port = 8182; + adminpassFile = config.age.secrets.nextcloudAdminPass.path; + homepage = { + name = "Nextcloud"; + description = "A safe home for all your data"; + icon = "nextcloud.svg"; + category = "Services"; + }; + }; + # qbittorrent = { + # enable = true; + # subdomain = "qbt"; + # port = 8080; + # homepage = { + # name = "qBittorrent"; + # description = "Torrent client"; + # icon = "qbittorrent.svg"; + # category = "Downloads"; + # }; + # }; + # slskd = { + # enable = true; + # subdomain = "slskd"; + # port = 5030; + # homepage = { + # name = "Soulseek"; + # description = "Web-based Soulseek client"; + # icon = "slskd.svg"; + # category = "Downloads"; + # }; + # }; + # pihole = { + # enable = true; + # subdomain = "pihole"; + # port = 8053; + # homepage = { + # name = "PiHole"; + # description = "Adblocking and DNS service"; + # icon = "pi-hole.svg"; + # category = "Services"; + # path = "/admin"; + # }; + # }; }; }; } diff --git a/modules/default.nix b/modules/default.nix index f0c6f11d..d94992bb 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -123,28 +123,31 @@ server = { imports = [ ./server - ./server/fail2ban - ./server/homepage-dashboard - ./server/nextcloud - ./server/vaultwarden - ./server/bazarr - ./server/prowlarr - ./server/lidarr - ./server/radarr - ./server/sonarr - ./server/jellyseerr - ./server/jellyfin - ./server/n8n - ./server/podman - ./server/unbound - ./server/uptime-kuma - ./server/keepalived - ./server/gitea - ./server/postgres - ./server/traefik - ./server/www - ./server/authentik - ./server/tailscale + + ./server/infra/authentik + ./server/infra/fail2ban + ./server/infra/keepalived + ./server/infra/podman + ./server/infra/postgres + ./server/infra/tailscale + ./server/infra/traefik + ./server/infra/unbound + ./server/infra/www + + ./server/services/bazarr + ./server/services/flaresolverr + ./server/services/gitea + ./server/services/homepage-dashboard + ./server/services/jellyfin + ./server/services/jellyseerr + ./server/services/lidarr + ./server/services/n8n + ./server/services/nextcloud + ./server/services/prowlarr + ./server/services/radarr + ./server/services/sonarr + ./server/services/uptime-kuma + ./server/services/vaultwarden ]; }; settings = { diff --git a/modules/server/bazarr/default.nix b/modules/server/bazarr/default.nix deleted file mode 100644 index dba3f1d5..00000000 --- a/modules/server/bazarr/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "bazarr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Bazarr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Subtitle manager"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "bazarr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - user = srv.user; - group = srv.group; - }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.bazarr.loadBalancer.servers = [{url = "http://127.0.0.1:${toString config.services.${unit}.listenPort}";}]; - routers = { - bazarr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "bazarr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/default.nix b/modules/server/default.nix index 3f7362e4..f513f156 100644 --- a/modules/server/default.nix +++ b/modules/server/default.nix @@ -27,6 +27,11 @@ in { Domain name to be used to access the server services via Caddy reverse proxy ''; }; + ip = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "The local IP of the service."; + }; user = lib.mkOption { default = "share"; type = lib.types.str; @@ -62,6 +67,86 @@ in { Time zone to be used for the server services ''; }; + services = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + enable = lib.mkEnableOption "the service"; + subdomain = lib.mkOption { + type = lib.types.str; + default = ""; + description = "The subdomain for the service (e.g., 'jellyfin')"; + }; + exposure = lib.mkOption { + type = lib.types.enum ["local" "tunnel" "tailscale"]; + default = "local"; + description = "Controls where the service is exposed"; + }; + port = lib.mkOption { + type = lib.types.int; + default = 80; + description = "The port to host service on."; + }; + configDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/${name}"; + description = "Configuration directory for ${name}."; + }; + cloudflared = lib.mkOption { + type = lib.types.submodule { + options = { + credentialsFile = lib.mkOption { + type = lib.types.str; + example = "/path/to/cloudflare-credentials.json"; + # example = lib.literalExpression '' + # pkgs.writeText "cloudflare-credentials.json" ''' + # {"AccountTag":"secret","TunnelSecret":"secret","TunnelID":"secret"} + # ''' + # ''; + }; + tunnelId = lib.mkOption { + type = lib.types.str; + example = "00000000-0000-0000-0000-000000000000"; + }; + }; + }; + description = "Cloudflare tunnel configuration for this service."; + }; + homepage = lib.mkOption { + type = lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Display name on the homepage."; + }; + description = lib.mkOption { + type = lib.types.str; + default = ""; + description = "A short description for the homepage tile."; + }; + icon = lib.mkOption { + type = lib.types.str; + default = "Zervices c00l stuff"; + description = "Icon file name for the homepage tile."; + }; + category = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Homepage category grouping."; + }; + path = lib.mkOption { + type = lib.types.str; + default = ""; + example = "/admin"; + description = "Optional path suffix for homepage links (e.g. /admin)."; + }; + }; + }; + description = "Homepage metadata for this service."; + }; + }; + })); + }; }; config = lib.mkIf cfg.enable { diff --git a/modules/server/authentik/default.nix b/modules/server/infra/authentik/default.nix similarity index 84% rename from modules/server/authentik/default.nix rename to modules/server/infra/authentik/default.nix index 133e12af..f5a463fc 100644 --- a/modules/server/authentik/default.nix +++ b/modules/server/infra/authentik/default.nix @@ -1,15 +1,14 @@ { config, lib, - pkgs, self, ... }: let unit = "authentik"; - cfg = config.server.${unit}; - srv = config.server; + cfg = config.server.infra.${unit}; + srv = config.server.infra.www.domain; in { - options.server.${unit} = { + options.server.infra.${unit} = { enable = lib.mkEnableOption { description = "Enable ${unit}"; }; @@ -17,6 +16,10 @@ in { type = lib.types.str; default = "auth.${srv.www.domain}"; }; + port = lib.mkOption { + type = lib.types.port; + description = "The local port the service runs on"; + }; cloudflared = { credentialsFile = lib.mkOption { type = lib.types.str; @@ -31,21 +34,11 @@ in { example = "00000000-0000-0000-0000-000000000000"; }; }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Authentik"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "An open-source IdP for modern SSO"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "authentik.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; + homepage = { + name = "Authentik"; + description = "An open-source IdP for modern SSO"; + icon = "authentik.svg"; + category = "Services"; }; }; @@ -59,8 +52,8 @@ in { }; }; - server = { - fail2ban = lib.mkIf cfg.enable { + server.infra = { + fail2ban = { jails = { authentik = { serviceName = "authentik"; diff --git a/modules/server/fail2ban/default.nix b/modules/server/infra/fail2ban/default.nix similarity index 98% rename from modules/server/fail2ban/default.nix rename to modules/server/infra/fail2ban/default.nix index 6de50898..880c704c 100644 --- a/modules/server/fail2ban/default.nix +++ b/modules/server/infra/fail2ban/default.nix @@ -5,9 +5,9 @@ pkgs, ... }: let - cfg = config.server.fail2ban; + cfg = config.server.infra.fail2ban; in { - options.server.fail2ban = { + options.server.infra.fail2ban = { enable = lib.mkEnableOption { description = "Enable cloudflare fail2ban"; }; @@ -61,6 +61,7 @@ in { ); }; }; + config = lib.mkIf cfg.enable { services.fail2ban = { enable = true; diff --git a/modules/server/keepalived/default.nix b/modules/server/infra/keepalived/default.nix similarity index 72% rename from modules/server/keepalived/default.nix rename to modules/server/infra/keepalived/default.nix index e782527a..858db4f6 100644 --- a/modules/server/keepalived/default.nix +++ b/modules/server/infra/keepalived/default.nix @@ -3,27 +3,24 @@ config, self, ... -}: -let +}: let unit = "keepalived"; - cfg = config.server.${unit}; + cfg = config.server.infra.${unit}; - hostCfg = - hostname: - if hostname == "sobotka" then - { - ip = "192.168.88.14"; - priority = 20; - state = "MASTER"; - } - else if hostname == "ziggy" then - { - ip = "192.168.88.12"; - priority = 10; - state = "BACKUP"; - } - else - throw "No keepalived config defined for host ${hostname}"; + hostCfg = hostname: + if hostname == "sobotka" + then { + ip = "192.168.88.14"; + priority = 20; + state = "MASTER"; + } + else if hostname == "ziggy" + then { + ip = "192.168.88.12"; + priority = 10; + state = "BACKUP"; + } + else throw "No keepalived config defined for host ${hostname}"; _self = hostCfg config.networking.hostName; @@ -34,9 +31,8 @@ let # Remove self from peers peers = builtins.filter (ip: ip != _self.ip) allPeers; -in -{ - options.server.${unit} = { +in { + options.server.infra.${unit} = { enable = lib.mkEnableOption { description = "Enable ${unit}"; }; diff --git a/modules/server/infra/podman/acquis.yaml b/modules/server/infra/podman/acquis.yaml new file mode 100644 index 00000000..0ae1151e --- /dev/null +++ b/modules/server/infra/podman/acquis.yaml @@ -0,0 +1,6 @@ +--- +filenames: + - /var/log/traefik/access.log +poll_without_inotify: true +labels: + type: traefik diff --git a/modules/server/infra/podman/default.nix b/modules/server/infra/podman/default.nix new file mode 100644 index 00000000..7b5904ec --- /dev/null +++ b/modules/server/infra/podman/default.nix @@ -0,0 +1,162 @@ +{ + config, + lib, + self, + ... +}: let + infra = config.server.infra; + cfg = config.server.services; + + getPiholeSecret = hostname: + if hostname == "ziggy" + then [config.age.secrets.piholeZiggy.path] + else if hostname == "sobotka" + then [config.age.secrets.pihole.path] + else throw "Unknown hostname: ${hostname}"; +in { + options.server.infra = { + podman.enable = lib.mkEnableOption "Enables Podman"; + gluetun.enable = lib.mkEnableOption "Enables gluetun"; + }; + config = lib.mkIf infra.podman.enable { + age.secrets = { + pihole.file = "${self}/secrets/${config.networking.hostName}Pihole.age"; + slskd.file = "${self}/secrets/slskd.age"; + }; + + virtualisation = { + containers.enable = true; + podman.enable = true; + }; + + networking.firewall = lib.mkIf cfg.pihole.enable { + allowedTCPPorts = [ + 53 + 5335 + ]; + allowedUDPPorts = [ + 53 + 5335 + ]; + }; + + virtualisation.oci-containers.containers = lib.mkMerge [ + (lib.mkIf infra.gluetun.enable { + gluetun = { + image = "qmcgaw/gluetun"; + ports = [ + "8388:8388" + "58846:58846" + "8080:8080" + "5030:5030" + "5031:5031" + "50300:50300" + ]; + devices = ["/dev/net/tun:/dev/net/tun"]; + autoStart = true; + extraOptions = [ + "--cap-add=NET_ADMIN" + ]; + volumes = ["/var:/gluetun"]; + environmentFiles = [ + config.age.secrets.gluetunEnvironment.path + ]; + environment = { + DEV_MODE = "false"; + VPN_SERVICE_PROVIDER = "mullvad"; + VPN_TYPE = "wireguard"; + SERVER_CITIES = "Stockholm"; + }; + }; + }) + + (lib.mkIf cfg.qbittorrent.enable { + qbittorrent = { + image = "ghcr.io/hotio/qbittorrent:latest"; + autoStart = true; + dependsOn = ["gluetun"]; + ports = [ + "8080:8080" + "58846:58846" + ]; + extraOptions = [ + "--network=container:gluetun" + ]; + volumes = [ + "/var/lib/qbittorrent:/config:rw" + "/mnt/data/media/downloads:/downloads:rw" + ]; + environmentFiles = [ + config.age.secrets.gluetunEnvironment.path + ]; + environment = { + PUID = "994"; + PGID = "993"; + TZ = "Europe/Stockholm"; + WEBUI_PORT = "${builtins.toString cfg.qbittorrent.port}"; + }; + }; + }) + + (lib.mkIf cfg.slskd.enable { + slskd = { + image = "slskd/slskd:latest"; + autoStart = true; + dependsOn = ["gluetun"]; + ports = [ + "5030:5030" + "5031:5031" + "50300:50300" + ]; + extraOptions = [ + "--network=container:gluetun" + ]; + volumes = [ + "/var/lib/slskd:/app:rw" + "/mnt/data/media/downloads:/downloads:rw" + ]; + environmentFiles = [ + config.age.secrets.gluetunEnvironment.path + config.age.secrets.slskd.path + ]; + environment = { + TZ = "Europe/Stockholm"; + PUID = "981"; + PGID = "982"; + SLSKD_REMOTE_CONFIGURATION = "true"; + SLSKD_REMOTE_FILE_MANAGEMENT = "true"; + SLSKD_DOWNLOADS_DIR = "/downloads"; + SLSKD_UMASK = "022"; + }; + }; + }) + + (lib.mkIf cfg.pihole.enable { + pihole = { + autoStart = true; + image = "pihole/pihole:2025.08.0"; + volumes = [ + "/var/lib/pihole:/etc/pihole/" + "/var/lib/dnsmasq.d:/etc/dnsmasq.d/" + ]; + environment = { + TZ = "Europe/Stockholm"; + CUSTOM_CACHE_SIZE = "0"; + WEBTHEME = "default-darker"; + }; + environmentFiles = getPiholeSecret config.networking.hostName; + ports = [ + "53:53/tcp" + "53:53/udp" + "8053:80/tcp" + ]; + extraOptions = [ + "--cap-add=NET_ADMIN" + "--cap-add=SYS_NICE" + "--cap-add=SYS_TIME" + ]; + }; + }) + ]; + }; +} diff --git a/modules/server/postgres/default.nix b/modules/server/infra/postgres/default.nix similarity index 100% rename from modules/server/postgres/default.nix rename to modules/server/infra/postgres/default.nix diff --git a/modules/server/postgres/postgres-upgrade.nix b/modules/server/infra/postgres/postgres-upgrade.nix similarity index 94% rename from modules/server/postgres/postgres-upgrade.nix rename to modules/server/infra/postgres/postgres-upgrade.nix index addb6bd8..5171ae26 100644 --- a/modules/server/postgres/postgres-upgrade.nix +++ b/modules/server/infra/postgres/postgres-upgrade.nix @@ -7,10 +7,10 @@ }: let inherit (lib) types mkOption; - cfg = config.server.postgresql; + cfg = config.server.infra.postgresql; in { options = { - server.postgresql = { + server.infra.postgresql = { upgradeTargetPackage = mkOption { type = types.nullOr types.package; default = null; diff --git a/modules/server/postgres/postgres.nix b/modules/server/infra/postgres/postgres.nix similarity index 98% rename from modules/server/postgres/postgres.nix rename to modules/server/infra/postgres/postgres.nix index 595aa297..94abdfe8 100644 --- a/modules/server/postgres/postgres.nix +++ b/modules/server/infra/postgres/postgres.nix @@ -7,7 +7,7 @@ }: let inherit (lib) types mkOption; - cfg = config.server.postgresql; + cfg = config.server.infra.postgresql; database = {name, ...}: { options = { @@ -31,7 +31,7 @@ }; in { options = { - server.postgresql = { + server.infra.postgresql = { databases = mkOption { type = types.listOf (types.submodule database); default = []; diff --git a/modules/server/tailscale/default.nix b/modules/server/infra/tailscale/default.nix similarity index 75% rename from modules/server/tailscale/default.nix rename to modules/server/infra/tailscale/default.nix index 8ed32a8b..6750be56 100644 --- a/modules/server/tailscale/default.nix +++ b/modules/server/infra/tailscale/default.nix @@ -5,16 +5,11 @@ ... }: with lib; let - cfg = config.server.tailscale; + cfg = config.server.infra.tailscale; in { - options.server.tailscale = { + options.server.infra.tailscale = { enable = mkEnableOption "Enable tailscale server configuration"; - url = lib.mkOption { - type = lib.types.str; - default = "ts.cnst.dev"; - }; }; - config = mkIf cfg.enable { age.secrets.sobotkaTsAuth.file = "${self}/secrets/sobotkaTsAuth.age"; diff --git a/modules/server/infra/traefik/default.nix b/modules/server/infra/traefik/default.nix new file mode 100644 index 00000000..5c6e2f2c --- /dev/null +++ b/modules/server/infra/traefik/default.nix @@ -0,0 +1,192 @@ +{ + lib, + config, + pkgs, + self, + ... +}: let + inherit (lib) mkEnableOption mkIf types; + + cfg = config.server.infra.traefik; + srv = config.server; + + # Generates all Traefik routers from the central service list + generateRouters = services: + lib.mapAttrs' ( + name: service: let + domain = + if service.exposure == "tunnel" + then "cnst.dev" + else if service.exposure == "tailscale" + then "ts.cnst.dev" + else srv.domain; + in + lib.nameValuePair "${service.subdomain}" { + entryPoints = ["websecure"]; + rule = "Host(`${service.subdomain}.${domain}`)"; + service = service.subdomain; + tls.certResolver = "letsencrypt"; + } + ) (lib.filterAttrs (name: service: service.enable) services); + + # Generates all Traefik backend services + generateServices = services: + lib.mapAttrs' (name: service: + lib.nameValuePair "${service.subdomain}" { + loadBalancer.servers = [{url = "http://localhost:${toString service.port}";}]; + }) (lib.filterAttrs (name: service: service.enable) services); + + getCloudflareCredentials = hostname: + if hostname == "ziggy" + then config.age.secrets.cloudflareDnsCredentialsZiggy.path + else if hostname == "sobotka" + then config.age.secrets.cloudflareDnsCredentials.path + else throw "Unknown hostname: ${hostname}"; +in { + options.server.infra.traefik = { + enable = mkEnableOption "Enable global Traefik reverse proxy with ACME"; + }; + + config = mkIf cfg.enable { + age.secrets = { + traefikEnv = { + file = "${self}/secrets/traefikEnv.age"; + mode = "640"; + owner = "traefik"; + group = "traefik"; + }; + crowdsecApi.file = "${self}/secrets/crowdsecApi.age"; + }; + + systemd.services.traefik = { + serviceConfig = { + EnvironmentFile = [config.age.secrets.traefikEnv.path]; + }; + }; + networking.firewall.allowedTCPPorts = [80 443]; + + services = { + tailscale.permitCertUid = "traefik"; + traefik = { + enable = true; + + staticConfigOptions = { + log = { + level = "DEBUG"; + }; + + accesslog = {filepath = "/var/lib/traefik/logs/access.log";}; + + tracing = {}; + api = { + dashboard = true; + insecure = false; + }; + + certificatesResolvers = { + vpn.tailscale = {}; + letsencrypt = { + acme = { + email = "adam@cnst.dev"; + storage = "/var/lib/traefik/cert.json"; + dnsChallenge = { + provider = "cloudflare"; + resolvers = [ + "1.1.1.1:53" + "1.0.0.1:53" + ]; + }; + }; + }; + }; + + entryPoints = { + # redis = { + # address = "0.0.0.0:6381"; + # }; + # postgres = { + # address = "0.0.0.0:5433"; + # }; + web = { + address = ":80"; + forwardedHeaders.insecure = true; + http.redirections.entryPoint = { + to = "websecure"; + scheme = "https"; + permanent = true; + }; + # http.middlewares = "crowdsec@file"; + }; + + websecure = { + address = ":443"; + forwardedHeaders.insecure = true; + http.tls = { + certResolver = "letsencrypt"; + domains = [ + { + main = "cnix.dev"; + sans = ["*.cnix.dev"]; + } + { + main = "ts.cnst.dev"; + sans = ["*ts.cnst.dev"]; + } + ]; + }; + # http.middlewares = "crowdsec@file"; + }; + + experimental = { + address = ":1111"; + forwardedHeaders.insecure = true; + }; + }; + + experimental = { + # Install the Crowdsec Bouncer plugin + plugins = { + #enabled = "true"; + bouncer = { + moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"; + version = "v1.4.5"; + }; + }; + }; + }; + + dynamicConfigOptions = { + http = { + # Generate the services from your central list + services = generateServices srv.services; + + # Generate the routers and manually add the special 'api' router + routers = + (generateRouters srv.services) + // { + api = { + entryPoints = ["websecure"]; + rule = "Host(`traefik.${srv.domain}`)"; + service = "api@internal"; + tls.certResolver = "letsencrypt"; + }; + }; + # middlewares = { + # crowdsec = { + # plugin = { + # bouncer = { + # enabled = "true"; + # logLevel = "DEBUG"; + # crowdsecLapiKeyFile = config.age.secrets.crowdsecApi.path; + # crowdsecMode = "live"; + # crowdsecLapiHost = ":4223"; + # }; + # }; + # }; + # }; + }; + }; + }; + }; + }; +} diff --git a/modules/server/unbound/default.nix b/modules/server/infra/unbound/default.nix similarity index 85% rename from modules/server/unbound/default.nix rename to modules/server/infra/unbound/default.nix index fda9dd05..8072cfbe 100644 --- a/modules/server/unbound/default.nix +++ b/modules/server/infra/unbound/default.nix @@ -5,7 +5,13 @@ ... }: let unit = "unbound"; - cfg = config.server.${unit}; + cfg = config.server.infra.${unit}; + srv = config.server; + + generateLocalRecords = services: + lib.mapAttrsToList ( + name: service: "local-data: \"${service.subdomain}.${srv.domain}. A ${srv.ip}\"" + ) (lib.filterAttrs (name: service: service.enable) services); hostIp = hostname: if hostname == "ziggy" @@ -14,11 +20,12 @@ then "192.168.88.14" else throw "No IP defined for host ${hostname}"; in { - options.server.${unit} = { + options.server.infra.${unit} = { enable = lib.mkEnableOption { description = "Enable ${unit}"; }; }; + config = lib.mkIf cfg.enable { services = { # resolved.enable = lib.mkForce false; @@ -97,6 +104,10 @@ in { "255.255.255.255/32" "2001:db8::/32" ]; + local-data = generateLocalRecords srv.services; + local-data-ptr = [ + "local-data: \"traefik.${srv.domain}. A ${srv.ip}\"" + ]; }; }; }; diff --git a/modules/server/www/default.nix b/modules/server/infra/www/default.nix similarity index 91% rename from modules/server/www/default.nix rename to modules/server/infra/www/default.nix index 394f1c68..b0b9ef71 100644 --- a/modules/server/www/default.nix +++ b/modules/server/infra/www/default.nix @@ -1,15 +1,13 @@ { lib, config, - pkgs, self, ... }: let - inherit (lib) mkOption mkEnableOption mkIf types; - cfg = config.server.www; - srv = config.server; + inherit (lib) mkIf mkEnableOption mkOption types; + cfg = config.server.infra.www; in { - options.server.www = { + options.server.infra.www = { enable = mkEnableOption { description = "Enable personal website"; }; @@ -20,6 +18,12 @@ in { Public domain name to be used to access the server services via Traefik reverse proxy ''; }; + port = lib.mkOption { + type = lib.types.int; + default = 8283; + description = "The port to host webservice on."; + }; + cloudflared = { credentialsFile = lib.mkOption { type = lib.types.str; @@ -41,8 +45,8 @@ in { wwwCloudflared.file = "${self}/secrets/wwwCloudflared.age"; }; - server = { - fail2ban = lib.mkIf config.server.www.enable { + server.infra = { + fail2ban = { jails = { nginx-404 = { serviceName = "nginx"; diff --git a/modules/server/jellyfin/default.nix b/modules/server/jellyfin/default.nix deleted file mode 100644 index 3b7edb0d..00000000 --- a/modules/server/jellyfin/default.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - unit = "jellyfin"; - cfg = config.server.${unit}; - srv = config.server; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "fin.${srv.tailscale.url}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Jellyfin"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "The Free Software Media System"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "jellyfin.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Media"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - user = srv.user; - group = srv.group; - }; - environment.systemPackages = with pkgs; [ - jellyfin-ffmpeg - ]; - services.traefik = { - dynamicConfigOptions = { - http = { - services.${unit}.loadBalancer.servers = [{url = "http://localhost:8096";}]; - routers = { - jellyfinRouter = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "${unit}"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/jellyseerr/default.nix b/modules/server/jellyseerr/default.nix deleted file mode 100644 index 859c5f33..00000000 --- a/modules/server/jellyseerr/default.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "jellyseerr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - # default = "seer.${srv.tailscale.url}"; - default = "jellyseerr.${srv.domain}"; - }; - port = lib.mkOption { - type = lib.types.port; - default = 5055; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Jellyseerr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Media request and discovery manager"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "jellyseerr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - port = cfg.port; - }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.jellyseerr.loadBalancer.servers = [{url = "http://localhost:${toString cfg.port}";}]; - routers = { - jellyseerr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "${unit}"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/lidarr/default.nix b/modules/server/lidarr/default.nix deleted file mode 100644 index b80502d2..00000000 --- a/modules/server/lidarr/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "lidarr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Lidarr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Music collection manager"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "lidarr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - user = srv.user; - group = srv.group; - }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.lidarr.loadBalancer.servers = [{url = "http://127.0.0.1:8686";}]; - routers = { - lidarr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "lidarr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/n8n/default.nix b/modules/server/n8n/default.nix deleted file mode 100644 index 98209498..00000000 --- a/modules/server/n8n/default.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "n8n"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "n8n"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "A workflow automation platform"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "n8n.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; - }; - }; - config = lib.mkIf cfg.enable { - services = { - n8n = { - enable = true; - openFirewall = true; - }; - - traefik = { - dynamicConfigOptions = { - http = { - services.n8n.loadBalancer.servers = [{url = "http://127.0.0.1:5678";}]; - routers = { - n8n = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "n8n"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/podman/default.nix b/modules/server/podman/default.nix deleted file mode 100644 index 395f3aab..00000000 --- a/modules/server/podman/default.nix +++ /dev/null @@ -1,323 +0,0 @@ -{ - config, - lib, - pkgs, - self, - ... -}: let - srv = config.server; - cfg = config.server.podman; - - piholeUrl = - if config.networking.hostName == "sobotka" - then "pihole0" - else if config.networking.hostName == "ziggy" - then "pihole1" - else throw "Unknown hostname"; - - getPiholeSecret = hostname: - if hostname == "ziggy" - then [config.age.secrets.piholeZiggy.path] - else if hostname == "sobotka" - then [config.age.secrets.pihole.path] - else throw "Unknown hostname: ${hostname}"; -in { - options.server.podman = { - enable = lib.mkEnableOption "Enables Podman"; - gluetun.enable = lib.mkEnableOption "Enables gluetun"; - - qbittorrent = { - enable = lib.mkEnableOption "Enable qBittorrent"; - url = lib.mkOption { - type = lib.types.str; - default = "qbt.${srv.domain}"; - }; - port = lib.mkOption { - type = lib.types.int; - default = 8080; - description = "The port to host qBittorrent on."; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "qBittorrent"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Torrent client"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "qbittorrent.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Downloads"; - }; - }; - - slskd = { - enable = lib.mkEnableOption "Enable Soulseek"; - url = lib.mkOption { - type = lib.types.str; - default = "slskd.${srv.domain}"; - }; - port = lib.mkOption { - type = lib.types.int; - default = 5030; - description = "The port to host Soulseek webui on."; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "slskd"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Web-based Soulseek client"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "slskd.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Downloads"; - }; - }; - - pihole = { - enable = lib.mkEnableOption { - description = "Enable"; - }; - port = lib.mkOption { - type = lib.types.int; - default = 8053; - description = "The port to host PiHole on."; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${piholeUrl}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "PiHole"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Adblocking and DNS service"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "pi-hole.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; - }; - homepage.path = lib.mkOption { - type = lib.types.str; - default = "/admin"; - description = "Optional path suffix for homepage links (e.g. /admin)."; - }; - }; - }; - - config = lib.mkIf cfg.enable { - age.secrets = { - pihole.file = "${self}/secrets/${config.networking.hostName}Pihole.age"; - slskd.file = "${self}/secrets/slskd.age"; - }; - - virtualisation = { - containers.enable = true; - podman.enable = true; - }; - - networking.firewall = lib.mkIf cfg.pihole.enable { - allowedTCPPorts = [ - 53 - 5335 - ]; - allowedUDPPorts = [ - 53 - 5335 - ]; - }; - - services = { - traefik = lib.mkMerge [ - (lib.mkIf cfg.pihole.enable { - dynamicConfigOptions = { - http = { - services = { - pihole.loadBalancer.servers = [{url = "http://localhost:${toString cfg.pihole.port}";}]; - }; - routers = { - pihole = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.pihole.url}`)"; - service = "pihole"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - - (lib.mkIf cfg.qbittorrent.enable { - dynamicConfigOptions = { - http = { - services = { - qbittorrent.loadBalancer.servers = [{url = "http://localhost:${toString cfg.qbittorrent.port}";}]; - }; - routers = { - qbittorrent = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.qbittorrent.url}`)"; - service = "qbittorrent"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - - (lib.mkIf cfg.slskd.enable { - dynamicConfigOptions = { - http = { - services = { - slskd.loadBalancer.servers = [{url = "http://localhost:${toString cfg.slskd.port}";}]; - }; - routers = { - slskd = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.slskd.url}`)"; - service = "slskd"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - ]; - }; - - virtualisation.oci-containers.containers = lib.mkMerge [ - (lib.mkIf cfg.gluetun.enable { - gluetun = { - image = "qmcgaw/gluetun"; - ports = [ - "8388:8388" - "58846:58846" - "8080:8080" - "5030:5030" - "5031:5031" - "50300:50300" - ]; - devices = ["/dev/net/tun:/dev/net/tun"]; - autoStart = true; - extraOptions = [ - "--cap-add=NET_ADMIN" - ]; - volumes = ["/var:/gluetun"]; - environmentFiles = [ - config.age.secrets.gluetunEnvironment.path - ]; - environment = { - DEV_MODE = "false"; - VPN_SERVICE_PROVIDER = "mullvad"; - VPN_TYPE = "wireguard"; - SERVER_CITIES = "Stockholm"; - }; - }; - }) - - (lib.mkIf cfg.qbittorrent.enable { - qbittorrent = { - image = "ghcr.io/hotio/qbittorrent:latest"; - autoStart = true; - dependsOn = ["gluetun"]; - ports = [ - "8080:8080" - "58846:58846" - ]; - extraOptions = [ - "--network=container:gluetun" - ]; - volumes = [ - "/var/lib/qbittorrent:/config:rw" - "/mnt/data/media/downloads:/downloads:rw" - ]; - environmentFiles = [ - config.age.secrets.gluetunEnvironment.path - ]; - environment = { - PUID = "994"; - PGID = "993"; - TZ = "Europe/Stockholm"; - WEBUI_PORT = "${builtins.toString cfg.qbittorrent.port}"; - }; - }; - }) - - (lib.mkIf cfg.slskd.enable { - slskd = { - image = "slskd/slskd:latest"; - autoStart = true; - dependsOn = ["gluetun"]; - ports = [ - "5030:5030" - "5031:5031" - "50300:50300" - ]; - extraOptions = [ - "--network=container:gluetun" - ]; - volumes = [ - "/var/lib/slskd:/app:rw" - "/mnt/data/media/downloads:/downloads:rw" - ]; - environmentFiles = [ - config.age.secrets.gluetunEnvironment.path - config.age.secrets.slskd.path - ]; - environment = { - TZ = "Europe/Stockholm"; - PUID = "981"; - PGID = "982"; - SLSKD_REMOTE_CONFIGURATION = "true"; - SLSKD_REMOTE_FILE_MANAGEMENT = "true"; - SLSKD_DOWNLOADS_DIR = "/downloads"; - SLSKD_UMASK = "022"; - }; - }; - }) - - (lib.mkIf cfg.pihole.enable { - pihole = { - autoStart = true; - image = "pihole/pihole:2025.08.0"; - volumes = [ - "/var/lib/pihole:/etc/pihole/" - "/var/lib/dnsmasq.d:/etc/dnsmasq.d/" - ]; - environment = { - TZ = "Europe/Stockholm"; - CUSTOM_CACHE_SIZE = "0"; - WEBTHEME = "default-darker"; - }; - environmentFiles = getPiholeSecret config.networking.hostName; - ports = [ - "53:53/tcp" - "53:53/udp" - "8053:80/tcp" - ]; - extraOptions = [ - "--cap-add=NET_ADMIN" - "--cap-add=SYS_NICE" - "--cap-add=SYS_TIME" - ]; - }; - }) - ]; - }; -} diff --git a/modules/server/prowlarr/default.nix b/modules/server/prowlarr/default.nix deleted file mode 100644 index 681152f0..00000000 --- a/modules/server/prowlarr/default.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "prowlarr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Prowlarr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "PVR indexer"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "prowlarr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services = { - ${unit} = { - enable = true; - }; - flaresolverr = { - enable = true; - }; - - traefik = { - dynamicConfigOptions = { - http = { - services = { - prowlarr = { - loadBalancer.servers = [{url = "http://127.0.0.1:9696";}]; - }; - flaresolverr = { - loadBalancer.servers = [{url = "http://127.0.0.1:8191";}]; - }; - }; - routers = { - prowlarr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "prowlarr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - flaresolverr = { - entryPoints = ["websecure"]; - rule = "Host(`flaresolverr.${srv.domain}`)"; - service = "flaresolverr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/radarr/default.nix b/modules/server/radarr/default.nix deleted file mode 100644 index d89b8b89..00000000 --- a/modules/server/radarr/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "radarr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Radarr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Film collection manager"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "radarr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - user = srv.user; - group = srv.group; - }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.radarr.loadBalancer.servers = [{url = "http://127.0.0.1:7878";}]; - routers = { - radarr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "radarr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/services/bazarr/default.nix b/modules/server/services/bazarr/default.nix new file mode 100644 index 00000000..7c568b67 --- /dev/null +++ b/modules/server/services/bazarr/default.nix @@ -0,0 +1,17 @@ +{ + config, + lib, + ... +}: let + unit = "bazarr"; + srv = config.server; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + }; +} diff --git a/modules/server/services/flaresolverr/default.nix b/modules/server/services/flaresolverr/default.nix new file mode 100644 index 00000000..dbf9bdfc --- /dev/null +++ b/modules/server/services/flaresolverr/default.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: let + unit = "flaresolverr"; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services = { + ${unit} = { + enable = true; + }; + }; + }; +} diff --git a/modules/server/gitea/default.nix b/modules/server/services/gitea/default.nix similarity index 57% rename from modules/server/gitea/default.nix rename to modules/server/services/gitea/default.nix index 0331ef2c..e9689e9a 100644 --- a/modules/server/gitea/default.nix +++ b/modules/server/services/gitea/default.nix @@ -6,60 +6,15 @@ ... }: let unit = "gitea"; - srv = config.server; - cfg = config.server.${unit}; + cfg = config.server.services.${unit}; in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "git.${srv.domain}"; - }; - port = lib.mkOption { - type = lib.types.int; - default = 5003; - description = "The port to host Gitea on."; - }; - cloudflared = { - credentialsFile = lib.mkOption { - type = lib.types.str; - example = lib.literalExpression '' - pkgs.writeText "cloudflare-credentials.json" ''' - {"AccountTag":"secret"."TunnelSecret":"secret","TunnelID":"secret"} - ''' - ''; - }; - tunnelId = lib.mkOption { - type = lib.types.str; - example = "00000000-0000-0000-0000-000000000000"; - }; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Gitea"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Git with a cup of tea"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "gitea.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; - }; - }; config = lib.mkIf cfg.enable { age.secrets = { giteaCloudflared.file = "${self}/secrets/giteaCloudflared.age"; }; - server = { - fail2ban = lib.mkIf config.server.fail2ban.enable { + server.infra = { + fail2ban = { jails = { gitea = { serviceName = "gitea"; @@ -144,24 +99,7 @@ in { }; }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.gitea.loadBalancer.servers = [{url = "http://127.0.0.1:5003";}]; - routers = { - gitea = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "gitea"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - - server.postgresql.databases = [ + server.infra.postgresql.databases = [ { database = "gitea"; } diff --git a/modules/server/homepage-dashboard/default.nix b/modules/server/services/homepage-dashboard/default.nix similarity index 68% rename from modules/server/homepage-dashboard/default.nix rename to modules/server/services/homepage-dashboard/default.nix index ea6cbf10..a90c327d 100644 --- a/modules/server/homepage-dashboard/default.nix +++ b/modules/server/services/homepage-dashboard/default.nix @@ -5,37 +5,9 @@ ... }: let unit = "homepage-dashboard"; - cfg = config.server.homepage-dashboard; + cfg = config.server.services.homepage-dashboard; srv = config.server; in { - options.server.homepage-dashboard = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - misc = lib.mkOption { - default = []; - type = lib.types.listOf ( - lib.types.attrsOf ( - lib.types.submodule { - options = { - description = lib.mkOption { - type = lib.types.str; - }; - href = lib.mkOption { - type = lib.types.str; - }; - siteMonitor = lib.mkOption { - type = lib.types.str; - }; - icon = lib.mkOption { - type = lib.types.str; - }; - }; - } - ) - ); - }; - }; config = lib.mkIf cfg.enable { age.secrets = { homepageEnvironment = { @@ -112,31 +84,24 @@ in { "Downloads" "Services" ]; - hl = config.server; - mergedServices = hl // hl.podman; + hl = config.server.services; homepageServices = x: (lib.attrsets.filterAttrs ( - name: value: value ? homepage && value.homepage.category == x + _name: value: value ? homepage && value.homepage.category == x ) - mergedServices); + srv.services); in lib.lists.forEach homepageCategories (cat: { "${cat}" = - lib.lists.forEach - (lib.attrsets.mapAttrsToList (name: value: { - inherit name; - url = value.url; - homepage = value.homepage; - }) (homepageServices "${cat}")) + lib.lists.forEach (lib.attrsets.mapAttrsToList (name: _value: name) (homepageServices "${cat}")) (x: { - "${x.homepage.name}" = { - icon = x.homepage.icon; - description = x.homepage.description; - href = "https://${x.url}${x.homepage.path or ""}"; - siteMonitor = "https://${x.url}${x.homepage.path or ""}"; + "${hl.${x}.homepage.name}" = { + icon = hl.${x}.homepage.icon; + description = hl.${x}.homepage.description; + href = "https://${hl.${x}.url}"; + siteMonitor = "https://${hl.${x}.url}"; }; }); }) - ++ [{Misc = cfg.misc;}] ++ [ { Glances = let @@ -212,25 +177,6 @@ in { } ]; }; - - traefik = { - dynamicConfigOptions = { - http = { - services.homepage.loadBalancer.servers = [ - {url = "http://127.0.0.1:${toString config.services.${unit}.listenPort}";} - ]; - routers = { - homepage = { - entryPoints = ["websecure"]; - rule = "Host(`cnix.dev`)"; - service = "homepage"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; }; }; } diff --git a/modules/server/services/jellyfin/default.nix b/modules/server/services/jellyfin/default.nix new file mode 100644 index 00000000..76fe84d1 --- /dev/null +++ b/modules/server/services/jellyfin/default.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + pkgs, + ... +}: let + unit = "jellyfin"; + cfg = config.server.services.${unit}; + srv = config.server; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + environment.systemPackages = with pkgs; [ + jellyfin-ffmpeg + ]; + }; +} diff --git a/modules/server/services/jellyseerr/default.nix b/modules/server/services/jellyseerr/default.nix new file mode 100644 index 00000000..c175aef6 --- /dev/null +++ b/modules/server/services/jellyseerr/default.nix @@ -0,0 +1,15 @@ +{ + config, + lib, + ... +}: let + unit = "jellyseerr"; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + port = cfg.port; + }; + }; +} diff --git a/modules/server/services/lidarr/default.nix b/modules/server/services/lidarr/default.nix new file mode 100644 index 00000000..f80a2ce3 --- /dev/null +++ b/modules/server/services/lidarr/default.nix @@ -0,0 +1,17 @@ +{ + config, + lib, + ... +}: let + unit = "lidarr"; + srv = config.server; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + }; +} diff --git a/modules/server/services/n8n/default.nix b/modules/server/services/n8n/default.nix new file mode 100644 index 00000000..2e673ff6 --- /dev/null +++ b/modules/server/services/n8n/default.nix @@ -0,0 +1,17 @@ +{ + config, + lib, + ... +}: let + unit = "n8n"; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services = { + n8n = { + enable = true; + openFirewall = true; + }; + }; + }; +} diff --git a/modules/server/nextcloud/default.nix b/modules/server/services/nextcloud/default.nix similarity index 64% rename from modules/server/nextcloud/default.nix rename to modules/server/services/nextcloud/default.nix index 45f0b76a..03d0870f 100644 --- a/modules/server/nextcloud/default.nix +++ b/modules/server/services/nextcloud/default.nix @@ -6,52 +6,16 @@ ... }: let unit = "nextcloud"; - cfg = config.server.${unit}; + cfg = config.server.services.${unit}; srv = config.server; in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - adminpassFile = lib.mkOption { - type = lib.types.path; - }; - adminuser = lib.mkOption { - type = lib.types.str; - default = "cnst"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "cloud.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Nextcloud"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "A safe home for all your data"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "nextcloud.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; - }; - }; config = lib.mkIf cfg.enable { age.secrets = { nextcloudAdminPass.file = "${self}/secrets/nextcloudAdminPass.age"; nextcloudCloudflared.file = "${self}/secrets/nextcloudCloudflared.age"; }; - server.fail2ban = lib.mkIf config.server.fail2ban.enable { + server.infra.fail2ban = lib.mkIf srv.infra.fail2ban.enable { jails = { nextcloud = { serviceName = "${unit}"; @@ -107,7 +71,7 @@ in { dbhost = "/run/postgresql"; dbname = "nextcloud"; adminuser = "cnst"; - adminpassFile = cfg.adminpassFile; + adminpassFile = config.age.secrets.nextcloudAdminPass.path; }; }; @@ -126,21 +90,9 @@ in { forceSSL = false; }; }; - - traefik.dynamicConfigOptions.http = { - routers.nextcloud = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "nextcloud"; - tls.certResolver = "letsencrypt"; - }; - services.nextcloud.loadBalancer.servers = [ - {url = "http://127.0.0.1:8182";} - ]; - }; }; - server.postgresql.databases = [ + server.infra.postgresql.databases = [ { database = "nextcloud"; } diff --git a/modules/server/services/prowlarr/default.nix b/modules/server/services/prowlarr/default.nix new file mode 100644 index 00000000..e308c06e --- /dev/null +++ b/modules/server/services/prowlarr/default.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: let + unit = "prowlarr"; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services = { + ${unit} = { + enable = true; + }; + }; + }; +} diff --git a/modules/server/services/radarr/default.nix b/modules/server/services/radarr/default.nix new file mode 100644 index 00000000..1e7fc28e --- /dev/null +++ b/modules/server/services/radarr/default.nix @@ -0,0 +1,17 @@ +{ + config, + lib, + ... +}: let + unit = "radarr"; + srv = config.server; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + }; +} diff --git a/modules/server/services/sonarr/default.nix b/modules/server/services/sonarr/default.nix new file mode 100644 index 00000000..013793b8 --- /dev/null +++ b/modules/server/services/sonarr/default.nix @@ -0,0 +1,17 @@ +{ + config, + lib, + ... +}: let + unit = "sonarr"; + srv = config.server; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services.${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + }; +} diff --git a/modules/server/services/uptime-kuma/default.nix b/modules/server/services/uptime-kuma/default.nix new file mode 100644 index 00000000..2edfd40f --- /dev/null +++ b/modules/server/services/uptime-kuma/default.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: let + unit = "uptime-kuma"; + cfg = config.server.services.${unit}; +in { + config = lib.mkIf cfg.enable { + services = { + ${unit} = { + enable = true; + }; + }; + }; +} diff --git a/modules/server/vaultwarden/default.nix b/modules/server/services/vaultwarden/default.nix similarity index 53% rename from modules/server/vaultwarden/default.nix rename to modules/server/services/vaultwarden/default.nix index 67581415..6ce5e491 100644 --- a/modules/server/vaultwarden/default.nix +++ b/modules/server/services/vaultwarden/default.nix @@ -5,57 +5,37 @@ self, ... }: let - inherit (lib) mkIf mkEnableOption; - vcfg = config.services.vaultwarden.config; - cfg = config.server.vaultwarden; + unit = "vaultwarden"; + cfg = config.server.services.${unit}; + www = config.server.infra.www; in { - options = { - server.vaultwarden = { - enable = mkEnableOption "Enables vaultwarden"; - url = lib.mkOption { - type = lib.types.str; - default = "${cfg.domain}"; - }; - cloudflared = { - credentialsFile = lib.mkOption { - type = lib.types.str; - example = lib.literalExpression '' - pkgs.writeText "cloudflare-credentials.json" ''' - {"AccountTag":"secret"."TunnelSecret":"secret","TunnelID":"secret"} - ''' - ''; - }; - tunnelId = lib.mkOption { - type = lib.types.str; - example = "00000000-0000-0000-0000-000000000000"; - }; - }; - }; - }; - - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { age.secrets = { vaultwardenCloudflared.file = "${self}/secrets/vaultwardenCloudflared.age"; vaultwardenEnvironment.file = "${self}/secrets/vaultwardenEnvironment.age"; }; - server = { - fail2ban = lib.mkIf config.server.fail2ban.enable { + server.infra = { + fail2ban = { jails = { vaultwarden = { - serviceName = "vaultwarden"; + serviceName = "${unit}"; failRegex = ''^.*?Username or password is incorrect\. Try again\. IP: \. Username:.*$''; }; }; }; }; - systemd.services.backup-vaultwarden.serviceConfig = { - User = "root"; - Group = "root"; - }; - services = { + cloudflared = { + enable = true; + tunnels.${cfg.cloudflared.tunnelId} = { + credentialsFile = cfg.cloudflared.credentialsFile; + default = "http_status:404"; + ingress."${cfg.url}".service = "http://localhost:${toString cfg.port}"; + }; + }; + vaultwarden = { enable = true; environmentFile = config.age.secrets.vaultwardenEnvironment.path; @@ -63,10 +43,10 @@ in { backupDir = "/var/backup/vaultwarden"; config = { - DOMAIN = "https://${cfg.url}"; + DOMAIN = "https://vault.${www.url}"; SIGNUPS_ALLOWED = false; ROCKET_ADDRESS = "127.0.0.1"; - ROCKET_PORT = 8222; + ROCKET_PORT = cfg.port; IP_HEADER = "CF-Connecting-IP"; logLevel = "warn"; @@ -76,14 +56,10 @@ in { showPasswordHint = false; }; }; - cloudflared = { - enable = true; - tunnels.${cfg.cloudflared.tunnelId} = { - credentialsFile = cfg.cloudflared.credentialsFile; - default = "http_status:404"; - ingress."${cfg.url}".service = "http://${vcfg.ROCKET_ADDRESS}:${toString vcfg.ROCKET_PORT}"; - }; - }; + }; + systemd.services.backup-vaultwarden.serviceConfig = { + User = "root"; + Group = "root"; }; }; } diff --git a/modules/server/sonarr/default.nix b/modules/server/sonarr/default.nix deleted file mode 100644 index aa77ae3f..00000000 --- a/modules/server/sonarr/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "sonarr"; - srv = config.server; - cfg = config.server.${unit}; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/${unit}"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "${unit}.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Sonarr"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Series collection manager"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "sonarr.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Arr"; - }; - }; - config = lib.mkIf cfg.enable { - services.${unit} = { - enable = true; - user = srv.user; - group = srv.group; - }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.sonarr.loadBalancer.servers = [{url = "http://127.0.0.1:8989";}]; - routers = { - sonarr = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "sonarr"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/traefik/default.nix b/modules/server/traefik/default.nix deleted file mode 100644 index 1f0add66..00000000 --- a/modules/server/traefik/default.nix +++ /dev/null @@ -1,104 +0,0 @@ -{ - lib, - config, - pkgs, - self, - ... -}: let - inherit (lib) mkEnableOption mkIf types; - - cfg = config.server.traefik; - getCloudflareCredentials = hostname: - if hostname == "ziggy" - then config.age.secrets.cloudflareDnsCredentialsZiggy.path - else if hostname == "sobotka" - then config.age.secrets.cloudflareDnsCredentials.path - else throw "Unknown hostname: ${hostname}"; -in { - options.server.traefik = { - enable = mkEnableOption "Enable global Traefik reverse proxy with ACME"; - }; - - config = mkIf cfg.enable { - age.secrets.traefikEnv = { - file = "${self}/secrets/traefikEnv.age"; - mode = "640"; - owner = "traefik"; - group = "traefik"; - }; - - systemd.services.traefik = { - serviceConfig = { - EnvironmentFile = [config.age.secrets.traefikEnv.path]; - }; - }; - networking.firewall.allowedTCPPorts = [80 443]; - - services = { - tailscale.permitCertUid = "traefik"; - traefik = { - enable = true; - - staticConfigOptions = { - log = { - level = "DEBUG"; - }; - - tracing = {}; - api = { - dashboard = true; - }; - certificatesResolvers = { - vpn.tailscale = {}; - letsencrypt = { - acme = { - email = "adam@cnst.dev"; - storage = "/var/lib/traefik/cert.json"; - dnsChallenge = { - provider = "cloudflare"; - resolvers = [ - "1.1.1.1:53" - "1.0.0.1:53" - ]; - }; - }; - }; - }; - - entryPoints = { - redis = { - address = "0.0.0.0:6381"; - }; - postgres = { - address = "0.0.0.0:5433"; - }; - web = { - address = "0.0.0.0:80"; - http.redirections.entryPoint = { - to = "websecure"; - scheme = "https"; - permanent = true; - }; - }; - websecure = { - address = "0.0.0.0:443"; - http.tls = { - certResolver = "letsencrypt"; - domains = [ - { - main = "cnix.dev"; - sans = ["*.cnix.dev"]; - } - { - main = "ts.cnst.dev"; - sans = ["*ts.cnst.dev"]; - } - ]; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/uptime-kuma/default.nix b/modules/server/uptime-kuma/default.nix deleted file mode 100644 index 63f9aa85..00000000 --- a/modules/server/uptime-kuma/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - config, - lib, - ... -}: let - unit = "uptime-kuma"; - cfg = config.server.${unit}; - srv = config.server; -in { - options.server.${unit} = { - enable = lib.mkEnableOption { - description = "Enable ${unit}"; - }; - configDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/uptime-kuma"; - }; - url = lib.mkOption { - type = lib.types.str; - default = "uptime.${srv.domain}"; - }; - homepage.name = lib.mkOption { - type = lib.types.str; - default = "Uptime Kuma"; - }; - homepage.description = lib.mkOption { - type = lib.types.str; - default = "Service monitoring tool"; - }; - homepage.icon = lib.mkOption { - type = lib.types.str; - default = "uptime-kuma.svg"; - }; - homepage.category = lib.mkOption { - type = lib.types.str; - default = "Services"; - }; - }; - config = lib.mkIf cfg.enable { - services = { - ${unit} = { - enable = true; - }; - traefik = { - dynamicConfigOptions = { - http = { - services.uptime-kuma.loadBalancer.servers = [{url = "http://127.0.0.1:3001";}]; - routers = { - uptime-kuma = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "uptime-kuma"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/secrets/crowdsecApi.age b/secrets/crowdsecApi.age new file mode 100644 index 00000000..93aea645 --- /dev/null +++ b/secrets/crowdsecApi.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 t9iOEg lf7aPbZX2v3WGzE/KI/069DBObphqrDtjq7rhNriGl8 +Vv+Pqk6DbcE5R1A9135gVKroCex1xKsCLPETZdT3yTg +-> ssh-ed25519 KUYMFA XxtBSmCwrQCZ9G3VcCrbzTdMshTK1pjlHPYj7fke818 +9tO2EcnHPD6v3TNeuZdL+zP39SM5R5q7om5sCFDB8lg +-> ssh-ed25519 76RhUQ I6O/fYFRqYxExC9uLijZr6/kFze7uze0cIudCsl2jTo +WAwb822vVj5UtUAdE1oVJ0/q6nQbWqdx0OHuGEogO7M +-> ssh-ed25519 Jf8sqw gWBoe4HhXNw7Ih58lQ/L2vBoQfbU1ht8+ZSLUx/4TWk +xor0ieJ2UI5bK4rSlCM0dX61PVbxYE37FNry0YSmHG4 +--- Cp8b3eTb3NfjPFvBE12a2c+Yni2jW6DZUK10IaXmmvo +wxqz:.{?fAjT{J >Fē$,Q" .{_! \ No newline at end of file diff --git a/secrets/mikrotikSecret.age b/secrets/mikrotikSecret.age new file mode 100644 index 00000000..be96bb5d --- /dev/null +++ b/secrets/mikrotikSecret.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 t9iOEg MLi7IOM8QlpvlCMSmo4SwZbTwZ9pyysSbiMMuWD/dyU +cotV5TJf7oyyXIaAmu8n9Ie1rl27i8w7hsduwtQFnis +-> ssh-ed25519 KUYMFA BhFQ/RXOH8L7gl/FSabAUv28fbaod+muvTGSV3rYQSs +fWqwAkhSAmg6YB+yEtj0e83Q4XO/r+TBnMTN7vXBNqU +-> ssh-ed25519 76RhUQ b1fDfGPNdJ9c3wtr8ww0mW5K4fKJxpxxTZy/ZCECWzs +qhbvucUrv7dzOPKUmUaRs/AtXtwQfy/qp5HnaYzZ5eQ +-> ssh-ed25519 Jf8sqw 19D2ztjyxJfGQAiUOTdgWyC0ZFso/wrC9VPEkmI34U8 +PavT5O8M6Zc2Num9Hb2sY+F3UmMPqRgjUZxuvP6AhyM +--- uYOcbsL7JWoDF2mRUDLhXrbp6ssLFbQ9+a6RhAXNNPA + XPCh; |3O Z()t~qv0] 2Ѣ \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix index f8f53525..38c8a01b 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -63,6 +63,8 @@ in { "wwwCloudflared.age".publicKeys = kima ++ sobotka; "authentikCloudflared.age".publicKeys = kima ++ sobotka; "sobotkaTsAuth.age".publicKeys = kima ++ sobotka; + "mikrotikSecret.age".publicKeys = kima ++ sobotka; + "crowdsecApi.age".publicKeys = kima ++ sobotka; # Ziggy-specific "cloudflareDnsCredentialsZiggy.age".publicKeys = kima ++ ziggy;