diff --git a/constants.nix b/constants.nix index 4e7fcd6..aeb5aea 100644 --- a/constants.nix +++ b/constants.nix @@ -3,12 +3,15 @@ rec { intranet = "i." + domain; ca-fqdn = "ca." + intranet; hosts = { - "16ach6".ip = "100.64.0.2"; - pc.ip = "100.64.0.5"; - pixel6a.ip = "100.64.0.4"; - rx4.ip = "100.64.0.10"; - sid.ip = "100.64.0.6"; - vde.ip = "100.64.0.1"; + sid = { + ip = "100.64.0.6"; + }; + rx4 = { + ip = "100.64.0.10"; + }; + vde = { + ip = "100.64.0.1"; + }; }; services = { docs = { @@ -22,12 +25,20 @@ rec { subdomain = "f"; fqdn = subdomain + "." + domain; }; + librechat-oci = { + fqdn = "lc." + domain; + port = 3080; + }; + mailserver = rec { + subdomain = "mail"; + fqdn = subdomain + "." + domain; + }; miniflux = { fqdn = "rss." + domain; port = 8085; }; netdata = { - fqdn = "netdata." + intranet; + fqdn = "mon." + domain; port = 19999; }; open-webui-oci = { @@ -43,12 +54,8 @@ rec { port = 1200; }; vaultwarden = { - fqdn = "pw." + intranet; + fqdn = "pw." + domain; port = 8222; }; - webdav = { - fqdn = "dav." + intranet; - port = 8080; - }; }; } diff --git a/flake.lock b/flake.lock index 1e7f01f..b0a6409 100644 --- a/flake.lock +++ b/flake.lock @@ -1762,11 +1762,11 @@ }, "flake-schemas_4": { "locked": { - "lastModified": 1775244557, - "narHash": "sha256-iYXRXIX9eafJmwJFAhqT3YxvvpNRuPFSLRCSpvGh8Ic=", + "lastModified": 1776384880, + "narHash": "sha256-28Cg9HI/IwFHUm5fZyMEGCQ94L3Il6K4OfHeRf0b+Zw=", "owner": "DeterminateSystems", "repo": "flake-schemas", - "rev": "15edbeeaf77e42216dbcba8bfd907fdeabb75a2b", + "rev": "b852751d29c6e09919f25c1e10754646bdc181c9", "type": "github" }, "original": { @@ -2402,11 +2402,11 @@ ] }, "locked": { - "lastModified": 1775585728, - "narHash": "sha256-8Psjt+TWvE4thRKktJsXfR6PA/fWWsZ04DVaY6PUhr4=", + "lastModified": 1776796298, + "narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "580633fa3fe5fc0379905986543fd7495481913d", + "rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad", "type": "github" }, "original": { @@ -3013,11 +3013,11 @@ ] }, "locked": { - "lastModified": 1775425411, - "narHash": "sha256-KY6HsebJHEe5nHOWP7ur09mb0drGxYSzE3rQxy62rJo=", + "lastModified": 1777851538, + "narHash": "sha256-Gp8qwTEYNoy2yvmErVGlvLOQvrtEECCAKbonW7VJef8=", "owner": "nix-community", "repo": "home-manager", - "rev": "0d02ec1d0a05f88ef9e74b516842900c41f0f2fe", + "rev": "cc09c0f9b7eaa95c2d9827338a5eb03d32505ca5", "type": "github" }, "original": { @@ -4237,11 +4237,11 @@ ] }, "locked": { - "lastModified": 1776282709, - "narHash": "sha256-T2pc27MdV3/ejCYiv24lu7tNjPmd9t6jx+RG6RYvcRQ=", + "lastModified": 1777906128, + "narHash": "sha256-ei9g+4pCXhacUwnYuljs9v75hwfTqnfU+J6s6s8hfvc=", "owner": "nix-community", "repo": "NUR", - "rev": "55f8cf0c3aa3e144cdb158a2f1de505af0b26c54", + "rev": "c278c296b010e2a32838c8d4539a2d72c1686d4d", "type": "github" }, "original": { @@ -5081,11 +5081,11 @@ "tinted-zed": "tinted-zed_6" }, "locked": { - "lastModified": 1775935110, - "narHash": "sha256-twTHKUFXjNNsaAvX0KoaIClt+923jXDRbfCd9PC/f0o=", + "lastModified": 1777581180, + "narHash": "sha256-JcDBTZkkz68WlZKYDoD+MZG8b3dnIJXqMvyuVx3Wkdg=", "owner": "nix-community", "repo": "stylix", - "rev": "14f248ad1a7668e7858c6d9163608c208b7daf02", + "rev": "a2538cd28ae2140ffce9cee9108b8d569a9c4fed", "type": "github" }, "original": { @@ -5199,11 +5199,11 @@ "stylix": "stylix_6" }, "locked": { - "lastModified": 1777756274, - "narHash": "sha256-h753tgN4Ti929K2bsyRqt1q/nAiZtUAp1s1yL26USkY=", + "lastModified": 1779222589, + "narHash": "sha256-pFlaPXus8e+mY9C7/xQhBwux6tPk5P30K2uaN2Qluh0=", "ref": "release-25.11", - "rev": "5f6ad9ecf517ba1da78f868a0173efc427c0354c", - "revCount": 50, + "rev": "1ab817090ff5989578caefd8786e9450b37e3da5", + "revCount": 96, "type": "git", "url": "https://git.sid.ovh/sid/synix.git" }, diff --git a/flake.nix b/flake.nix index d563fab..92854d6 100644 --- a/flake.nix +++ b/flake.nix @@ -57,8 +57,8 @@ }; }; - mkNode = name: system: { - hostname = name + ".tail"; + mkNode = name: ip: system: { + hostname = ip; profiles.system = { path = inputs.deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.${name}; }; @@ -90,9 +90,9 @@ "UserKnownHostsFile=/dev/null" ]; nodes = { - rx4 = mkNode "rx4" "x86_64-linux"; - sid = mkNode "sid" "x86_64-linux"; - # vde = mkNode "vde" "x86_64-linux"; # NOTE: offline atm + rx4 = mkNode "rx4" "100.64.0.10" "x86_64-linux"; + sid = mkNode "sid" "100.64.0.6" "x86_64-linux"; + # vde = mkNode "vde" "100.64.0.1" "x86_64-linux"; # NOTE: offline atm }; }; diff --git a/hosts/rx4/secrets/secrets.yaml b/hosts/rx4/secrets/secrets.yaml index 0aa47f7..2b66560 100644 --- a/hosts/rx4/secrets/secrets.yaml +++ b/hosts/rx4/secrets/secrets.yaml @@ -13,12 +13,17 @@ syncthing: gui-pw: ENC[AES256_GCM,data:mN4rxYr5DZgvbpIkwSFIuPvviJE=,iv:Kyl3mZFOejVwEwBCKteJQpgbCosREp9C4T4JYhWz6KQ=,tag:6myk9lr/44CH/hyUPgRH0Q==,type:str] forgejo-runner: token: ENC[AES256_GCM,data:DZgi6ocpV0MplgQ6Et85vHxmkMfC4qYbLLdyRuj/4z8tJauz1w6DUQ==,iv:+SZYsv6sDn2Nc1WxhTn0dJGN9nXYZw16/HVtXJGXpHc=,tag:8Oa5mC7cUy85+lXHbRcCcg==,type:str] -webdav: - user: ENC[AES256_GCM,data:vCLx,iv:Nra/FprNfd02HpvqOb5uYK+IGRFHhNwnFXWrX71c0C0=,tag:TjbKKOKBTq31o/5MxmqIsA==,type:str] - pass: ENC[AES256_GCM,data:jfIoob6R6OhqKa2EujRzTQbvIlA=,iv:HvB088H2Z2uLCveT4YfNEdkK5VU0lBFD5FrZhx79fg0=,tag:1RnrfeUEURx0C575GTxi9A==,type:str] vaultwarden: admin-token: ENC[AES256_GCM,data:HhD0xNZ/Ep7pCOX1j6p/M/ZZ3gs=,iv:7QT71KlYz+HQYBhiRavpiXS9sNS2PoJiM/WkxM3Hk/g=,tag:SYTRWpyA2+WMSMiRM8mvew==,type:str] smtp-password: ENC[AES256_GCM,data:eQo7op5+74EID6689hL0/J1pq2s=,iv:JqrEqxabWGydRuJJ/27e1q+4YnQhTQ1bKRSsOvjQ+bE=,tag:weqnrhqK+LGEfAacBcuPUA==,type:str] +hetzner-api-key: ENC[AES256_GCM,data:casjNOXzuQDWgnSFftbBMygA8kGpGiZDqup08faWO9kfjvgOyWOXeqPd2VA1ND8yfM2LvoLYvPs6gUWtni2ldQ==,iv:p2W24uhJgBvpi3g4+cHw0/XbbTM5oYCPHreMBUR4CNs=,tag:lpwjZGoJe/91+CHX/hAkKA==,type:str] +librechat: + jwt-secret: ENC[AES256_GCM,data:/OJr23Sw975byjyHN6yqWxuk5FeRfLdQYYOPYJeDHTjzq9X78c3VHqdvnN2a9ZUEtzRi1sx6YLIjNkxBkGbvuQ==,iv:2D0iBj2U3iy3JPtKZBWP5nCfmXMA2/pBhBKUD2f5DoM=,tag:0ZYNxBhUdCBOne0otcG2iQ==,type:str] + jwt-refresh-secret: ENC[AES256_GCM,data:qIaunHUMTUFyp88whrxe65eM3Mfi3EX0ieWOUCmYYojSKQQRudh8d4Cb1zMqPbXJLG3zqTVCaZl9xwQn5K4Z/g==,iv:k5+oSCd0TzdOmIUe8BQBesofjvjuRiPXdLT6H9yQf18=,tag:4wcJjX7MvJNx19PCxgqyhw==,type:str] + creds-key: ENC[AES256_GCM,data:EljwEqFByJaOjd8lRFGwo/FyXHUtl5an0xS1EjRe+kmpo5z4P33EUKbMeeIl69rEcziMHZQLiadzSEcS2cb2uA==,iv:sidBN6VTBeFhMUtN67HZuyofiXCeGFG4tuMRckLZv84=,tag:n7vI8LuPgER3J6r6Q6Jkjg==,type:str] + creds-iv: ENC[AES256_GCM,data:oc0sPm5RM/7AbH3vdDLJ2m0q6C7eAAME0GPbiojHZUspP8Cto5QX5WKnUjUVLLcvgK+t6pnu7BEmAuD3PLr11A==,iv:Z6XJmlqv0ULFiwqHyRO5v7lb/iyv4g9aSTV4xw9VTXU=,tag:7kptbQwc6lBZ70aXw7wOVA==,type:str] + meili-master-key: ENC[AES256_GCM,data:eugFl40a6Ks3ba8hcn83WS76AwA0TXkhu3K4gSrbNHtXRliLQCWhGTEvoaQSeb7whmpszh4zh8cKSxByBdhJiQ==,iv:rrWlcVyBlrE5dnBBFWjheIo6SgQTbkzqskGQvQczR+U=,tag:fjKOSVoPxomA3qUw+baV4w==,type:str] + requesty-key: ENC[AES256_GCM,data:vxr+m3c9qu6ChFvuAbBCFrneDP9xDIPJBRmB9diw5uSQD9XDl0IK954OzmMMXaSl2AeHgY8WiugvgvQjUwywjKG6TxxMEYMFsQkMpSnV7xHYv2MAE/TIC74CsHRL823MesUQ7agoIasFtjr+CnZ5RRUNHZOG,iv:pa64BT1yay5vYwn/XBdK7meYzOBk4M+MmgLzaHR5Hfw=,tag:L+lUPTE0ZI303jtVefIrvQ==,type:str] sops: age: - recipient: age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy @@ -39,7 +44,7 @@ sops: NE5yK3ZaOG5PdXNSUnlIUmFSSmRFancKk57hCmo79HvI3hzzgQvgOK7oK5/dcQR8 f3R4OGF5+212VXEHR/hAEbKzV7CY4y6HhFyrGZ9bUKm1RrxtnVqUyA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-05-02T17:10:11Z" - mac: ENC[AES256_GCM,data:uf5TqZaevyUUjW6pM6K8c4CZFFdwTXFGIaHmYr5Q4XFR1uW3kBsVLeQKxq26duLuQ4UiZkUpW27a/PW797Z+iIpBdqbnoQ35q7RnOW+GpnAv8TaRW1PpqQ+JR3/R0LMXsi3cMt7ioG2ad1bIHztiNz+SmePiv3Yt9WxQ7PIqBdY=,iv:dAzuyKSo0OW+j02AH0chCdLBm7Wv6PZgqZrEWhEVnxQ=,tag:k6EKWHHY4fwTd03d4TVcNg==,type:str] + lastmodified: "2026-05-25T18:14:59Z" + mac: ENC[AES256_GCM,data:eh/jcKrqyCTh+2n4phHQ2LKF71DaCDwrrfXms6HaD0ER4xVOkYERTe7IN4cX//qjY/91wSzAzwLg3yphWK4k920tiYTBog9LcWUz6l6X5lpmKHQp+vdoQH41WrA1ZgOcXzSfmZoblcD1qoJNCaHGt5N8hjXRcUc3lEqcPrdoC7A=,iv:8kBd9Daai3wJgzxONX4eIkeZLMzJO2DX439sBv/pER4=,tag:l8Q3gzMHoSTCdOqwzaKgCA==,type:str] unencrypted_suffix: _unencrypted version: 3.12.1 diff --git a/hosts/rx4/services/default.nix b/hosts/rx4/services/default.nix index 385a957..8694146 100644 --- a/hosts/rx4/services/default.nix +++ b/hosts/rx4/services/default.nix @@ -10,20 +10,20 @@ inputs.clients.nixosModules.syncthing outputs.nixosModules.tailscale + # outputs.nixosModules.promtail ./forgejo.nix ./jirafeau.nix + ./librechat-oci.nix ./miniflux.nix ./netdata.nix ./nginx.nix ./open-webui-oci.nix - ./print-server.nix - ./rss-bridge.nix ./rsshub-oci.nix + ./samba.nix ./vaultwarden.nix # ./alditalk-extender.nix # FIXME - # ./webdav.nix # FIXME ]; # bootstrap diff --git a/hosts/rx4/services/librechat-oci.nix b/hosts/rx4/services/librechat-oci.nix new file mode 100644 index 0000000..25149dc --- /dev/null +++ b/hosts/rx4/services/librechat-oci.nix @@ -0,0 +1,71 @@ +{ + inputs, + constants, + config, + ... +}: + +let + inherit (constants.hosts.rx4) ip; + inherit (constants.services.librechat-oci) fqdn port; +in +{ + imports = [ + inputs.synix.nixosModules.librechat-oci + ]; + + services.librechat-oci = { + enable = true; + inherit port; + configFile = ./librechat.yaml; + externalUrl = "https://${fqdn}"; + environmentFile = config.sops.templates.librechat-env-file.path; + + environment = { + # ALLOW_REGISTRATION = "true"; + SEARXNG_INSTANCE_URL = "https://searxng.website/"; + }; + }; + + services.nginx.virtualHosts."${fqdn}" = { + useACMEHost = fqdn; + forceSSL = true; + listen = [ + { + addr = "${ip}:443"; + ssl = true; + } + ]; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString port}"; + proxyWebsockets = true; + }; + }; + + security.acme.certs."${fqdn}" = { + domain = fqdn; + postRun = "systemctl restart podman-librechat.service"; + group = "nginx"; + }; + + sops = { + # generate with: + # openssl rand -hex 32 + secrets."librechat/jwt-secret" = { }; + secrets."librechat/jwt-refresh-secret" = { }; + secrets."librechat/creds-key" = { }; + secrets."librechat/creds-iv" = { }; + secrets."librechat/meili-master-key" = { }; + + secrets."librechat/requesty-key" = { }; + + templates.librechat-env-file.content = '' + JWT_SECRET=${config.sops.placeholder."librechat/jwt-secret"} + JWT_REFRESH_SECRET=${config.sops.placeholder."librechat/jwt-refresh-secret"} + CREDS_KEY=${config.sops.placeholder."librechat/creds-key"} + CREDS_IV=${config.sops.placeholder."librechat/creds-iv"} + MEILI_MASTER_KEY=${config.sops.placeholder."librechat/meili-master-key"} + REQUESTY_KEY=${config.sops.placeholder."librechat/requesty-key"} + ''; + }; +} diff --git a/hosts/rx4/services/librechat.yaml b/hosts/rx4/services/librechat.yaml new file mode 100644 index 0000000..327012a --- /dev/null +++ b/hosts/rx4/services/librechat.yaml @@ -0,0 +1,53 @@ +version: 1.3.11 +cache: true + +interface: + customWelcome: "Such compose. Much yaml. Wow" + modelSelect: true + parameters: true + presets: true + prompts: + use: true + create: true + bookmarks: true + multiConvo: true + agents: true + fileSearch: true + webSearch: true + +# TODO: add cohere rerank +webSearch: + searchProvider: "searxng" + searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}" + +endpoints: + agents: + disableBuilder: false + recursionLimit: 50 + maxRecursionLimit: 100 + maxCitations: 30 + maxCitationsPerFile: 7 + minRelevanceScore: 0.45 + capabilities: + - "deferred_tools" + - "execute_code" + - "file_search" + - "web_search" + - "artifacts" + - "actions" + - "context" + - "tools" + - "chain" + + custom: + - name: "Requesty" + apiKey: "${REQUESTY_KEY}" + baseURL: "https://router.requesty.ai/v1" + headers: + x-librechat-body-parentmessageid: "{{LIBRECHAT_BODY_PARENTMESSAGEID}}" + models: + default: ["meta-llama/llama-3-70b-instruct"] + fetch: true + titleConvo: true + titleModel: "meta-llama/llama-3-70b-instruct" + dropParams: ["stop"] diff --git a/hosts/rx4/services/netdata.nix b/hosts/rx4/services/netdata.nix index 30d720d..956f612 100644 --- a/hosts/rx4/services/netdata.nix +++ b/hosts/rx4/services/netdata.nix @@ -1,12 +1,14 @@ { config, constants, + pkgs, ... }: { services.netdata = { enable = true; + package = pkgs.netdata.override { withCloudUi = false; }; config.global = { "debug log" = "syslog"; "access log" = "syslog"; @@ -17,6 +19,10 @@ }; }; + services.journald.storage = "persistent"; + + users.users.netdata.extraGroups = [ "systemd-journal" ]; + sops = let owner = config.services.netdata.user; diff --git a/hosts/rx4/services/nginx.nix b/hosts/rx4/services/nginx.nix index c4c24af..cae8e31 100644 --- a/hosts/rx4/services/nginx.nix +++ b/hosts/rx4/services/nginx.nix @@ -7,6 +7,8 @@ let cfg = config.services.nginx; + + inherit (constants) domain; in { imports = [ @@ -34,4 +36,21 @@ in }; }; }; + + security.acme = { + acceptTerms = true; + defaults = { + email = "admin@${domain}"; + dnsProvider = "hetzner"; + credentialFiles = { + HETZNER_API_TOKEN_FILE = config.sops.secrets.hetzner-api-key.path; + }; + }; + }; + + sops.secrets.hetzner-api-key = { + mode = "0400"; + owner = "acme"; + group = "acme"; + }; } diff --git a/hosts/rx4/services/open-webui-oci.nix b/hosts/rx4/services/open-webui-oci.nix index 3459ee8..f22297d 100644 --- a/hosts/rx4/services/open-webui-oci.nix +++ b/hosts/rx4/services/open-webui-oci.nix @@ -2,22 +2,22 @@ inputs, constants, config, + lib, pkgs, ... }: +let + inherit (lib) getExe; +in { - imports = [ inputs.synix.nixosModules.open-webui-oci ]; + imports = [ + inputs.synix.nixosModules.open-webui-oci + inputs.synix.nixosModules.mcpo + ]; services.open-webui-oci = { enable = true; - image = pkgs.dockerTools.pullImage { - imageName = "ghcr.io/open-webui/open-webui"; - imageDigest = "sha256:8113fa5510020ef05a44afc0c42d33eabeeb2524a996e3e3fb8c437c00f0d792"; - hash = "sha256-KxMUORhvLb/MMc8gv3xULNgnAzO1N+Qi2/1lHzbuK6Q="; - finalImageName = "ghcr.io/open-webui/open-webui"; - finalImageTag = "0.8.12"; - }; externalUrl = "https://" + constants.services.open-webui-oci.fqdn; port = 8083; # environmentFile = config.sops.templates."open-webui-oci/environment".path; @@ -28,6 +28,27 @@ }; }; + services.mcpo = { + enable = true; + package = pkgs.synix.mcpo; + port = 8765; + settings = { + mcpServers = { + fetcher-mcp = { + command = getExe pkgs.synix.fetcher-mcp; + }; + nixos = { + command = getExe pkgs.nix; + args = [ + "run" + "github:utensils/mcp-nixos" + "--" + ]; + }; + }; + }; + }; + # sops = { # secrets."open-webui-oci/stt-api-key" = { }; # secrets."open-webui-oci/tts-api-key" = { }; diff --git a/hosts/rx4/services/print-server.nix b/hosts/rx4/services/print-server.nix deleted file mode 100644 index 64037fd..0000000 --- a/hosts/rx4/services/print-server.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ - inputs, - ... -}: - -{ - imports = [ - inputs.synix.nixosModules.print-server - ]; - - services.print-server.enable = true; -} diff --git a/hosts/rx4/services/rss-bridge.nix b/hosts/rx4/services/rss-bridge.nix deleted file mode 100644 index d3bd6a3..0000000 --- a/hosts/rx4/services/rss-bridge.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ inputs, constants, ... }: - -{ - imports = [ inputs.synix.nixosModules.rss-bridge ]; - - services.rss-bridge = { - enable = true; - reverseProxy = { - enable = true; - subdomain = constants.services.rss-bridge.subdomain; - forceSSL = false; - }; - }; -} diff --git a/hosts/rx4/services/samba.nix b/hosts/rx4/services/samba.nix new file mode 100644 index 0000000..2696005 --- /dev/null +++ b/hosts/rx4/services/samba.nix @@ -0,0 +1,27 @@ +{ config, ... }: + +{ + services.samba = { + enable = true; + openFirewall = false; + nmbd.enable = false; + winbindd.enable = false; + settings = { + global = { + workgroup = "WORKGROUP"; + "server string" = config.networking.hostName; + security = "user"; + "map to guest" = "Bad User"; + "guest account" = "nobody"; + }; + share = { + path = "/home/sid"; + browseable = "yes"; + "read only" = "yes"; + "guest ok" = "yes"; + "force user" = "sid"; + "directory mask" = "0750"; + }; + }; + }; +} diff --git a/hosts/rx4/services/vaultwarden.nix b/hosts/rx4/services/vaultwarden.nix index 6f00505..d9f8092 100644 --- a/hosts/rx4/services/vaultwarden.nix +++ b/hosts/rx4/services/vaultwarden.nix @@ -6,6 +6,7 @@ let inherit (constants) domain; + inherit (constants.hosts.rx4) ip; inherit (constants.services.vaultwarden) fqdn port; in { @@ -21,21 +22,52 @@ in environmentFile = [ config.sops.templates."vaultwarden/env-file".path ]; config = { + ENABLE_WEBSOCKET = true; SIGNUPS_ALLOWED = false; SMTP_FROM = "vaultwarden@${domain}"; SMTP_FROM_NAME = "${domain} Vaultwarden server"; - SMTP_HOST = constants.hosts.sid.ip; - SMTP_PORT = 587; - SMTP_SECURITY = "starttls"; + SMTP_HOST = constants.services.mailserver.fqdn; + SMTP_PORT = 465; + SMTP_SECURITY = "force_tls"; SMTP_USERNAME = "vaultwarden@${domain}"; - ROCKET_ADDRESS = "0.0.0.0"; + ROCKET_ADDRESS = "127.0.0.1"; ROCKET_PORT = port; ROCKET_LOG = "critical"; }; }; + services.nginx.virtualHosts."${fqdn}" = { + useACMEHost = "pw-custom"; + forceSSL = true; + listen = [ + { + addr = "${ip}:443"; + ssl = true; + } + ]; + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString port}"; + }; + "= /notifications/alerts" = { + proxyPass = "http://127.0.0.1:${toString port}"; + proxyWebsockets = true; + }; + "= /notifications/hub" = { + proxyPass = "http://127.0.0.1:${toString port}"; + proxyWebsockets = true; + }; + }; + }; + + security.acme.certs."pw-custom" = { + domain = fqdn; + postRun = "systemctl restart vaultwarden.service"; + group = "nginx"; + }; + sops = let owner = config.users.users.vaultwarden.name; diff --git a/hosts/rx4/services/webdav.nix b/hosts/rx4/services/webdav.nix deleted file mode 100644 index 46d01a9..0000000 --- a/hosts/rx4/services/webdav.nix +++ /dev/null @@ -1,86 +0,0 @@ -{ constants, config, ... }: - -# FIXME: floccus throws error: NetworkError when attempting to fetch resource. - -let - cfg = config.services.webdav; - - inherit (constants.services.webdav) fqdn port; -in -{ - services.webdav = { - enable = true; - environmentFile = config.sops.templates."webdav/env-file".path; - - settings = { - inherit port; - address = "127.0.0.1"; - prefix = "/"; - directory = "/srv/webdav"; - users = [ - { - username = "{env}WEBDAV_USER"; - password = "{env}WEBDAV_PASS"; - permissions = "CRUD"; - } - ]; - }; - }; - - systemd.tmpfiles.rules = [ - "d ${cfg.settings.directory} 0750 ${cfg.user} ${cfg.group} -" - ]; - - networking.firewall.allowedTCPPorts = [ port ]; - - services.nginx = { - enable = true; - virtualHosts."${fqdn}" = { - listen = [ - { - addr = "0.0.0.0"; - inherit port; - } - ]; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString port}"; - extraConfig = '' - add_header 'Access-Control-Allow-Origin' '*' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PROPFIND, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Depth' always; - - if ($request_method = 'OPTIONS') { - add_header 'Access-Control-Allow-Origin' '*'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PROPFIND, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Depth'; - return 204; - } - ''; - }; - }; - }; - - sops = - let - owner = cfg.user; - group = cfg.group; - mode = "0400"; - in - { - secrets = { - "webdav/user" = { - inherit owner group mode; - }; - "webdav/pass" = { - inherit owner group mode; - }; - }; - templates."webdav/env-file" = { - inherit owner group mode; - content = '' - WEBDAV_USER=${config.sops.placeholder."webdav/user"} - WEBDAV_PASS=${config.sops.placeholder."webdav/pass"} - ''; - }; - }; -} diff --git a/hosts/sid/secrets/secrets.yaml b/hosts/sid/secrets/secrets.yaml index 8fb0e4a..f94edbf 100644 --- a/hosts/sid/secrets/secrets.yaml +++ b/hosts/sid/secrets/secrets.yaml @@ -35,6 +35,7 @@ radicale: step-ca: password: ENC[AES256_GCM,data:8/6NA3WpII0LmDOp5ISnHKeaXn5LM4gpiI47JTso23c=,iv:fi2eMGG1lOwdK5+98Hp7vZ101GKRip5Xgq9k+vnC9yI=,tag:oENvvsEbKSHFfLoXcJlPkg==,type:str] intermediate-key: ENC[AES256_GCM,data:yGZLSd7ydx9wNFpWWPcyUBwZQZbyziGleCWSxurFniBCauw2h4hcPc4c4I/7cjl1vRUv41WfzWu1PtXnZ3lNHOC6tTbiikHFBgGiHk2Lhddx+NESUWmgNiejJR/UDW4T25W9OHxwLCV9pmHf4fjyT/REymGIB7kbcRryWqcWtoZWYaL7JooJornm5mMU1Be+MCfxusTGQA4gQsT5/bu20iEGPwgY3fEgZLQWzKFI2kD2lYlMC8CRxoZO32uTizzooW1+zKng1qSZ7aobFJsbSKRYpYDv9Vvfwltcczb+xo+yZL3pfoEiqAxPzeG/48lRVNf1nftM5esBRGIIPr9BV9+7fbe5DFbSRDtAWspEnp9R5ENj1rbNint/fjCcStg3OfFMdv6N8cQyIpQyHCiBLiG4z+xyFcn0iW4=,iv:BhUoeaoetI5vJk9wOHhBI2ebHWCPeXz8U2ta/xEeUxM=,tag:7xg5ilOSJP1rFlSmmZVZUg==,type:str] +hetzner-api-key: ENC[AES256_GCM,data:NhgWjitvgJrcBEDSkZH0S0VmaW37NupkiEUcQDZe/6oYyrE/VgEwrGSag/s2Fgv6uHmSsdbv1vqdc0iDO8GJ8w==,iv:ChEicL0jtjQrgn8CCUnrzErRr3YVdDhMbvcIlI3t7H8=,tag:cjjbEEYqEyNa5qDZCytjxw==,type:str] sops: age: - recipient: age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy @@ -55,7 +56,7 @@ sops: RzhnczA0S1pxcXZncGpWVHNYQW96L28K+ytH3PPyg4+wibpAQhp02RiSfZ83EDRB UJ8UV1d+51D0e2A1sI95r2AzDj4jfwUnI+LYDPC/qEpsu5LFLGVyeg== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-05-02T17:10:22Z" - mac: ENC[AES256_GCM,data:f4KQ26/zvg2nLLeW5qVeI8uH2GmPpJUKohNu68nEiIjP5AT53zjBaGoLOTGl9+oVRomSOGZtLGkJGaExB6tLMon5HN6xkQbugqvq08UkZ7FnR1Sa8/OtTr/+eexPNzF8VSdZE2TZCboUSQODV8+0Cy5T918g5kedxnT62SyY4As=,iv:P4TnpJvHwnZPl7kRNjv9d1WLZP9J0sg6R3KbdDMJqyc=,tag:ylYOcg6825jT29lWUaFRYA==,type:str] + lastmodified: "2026-05-22T19:19:21Z" + mac: ENC[AES256_GCM,data:hOtmWizEaIcybM14UEDsXw4GNQZob5SoFn49bWeccxA3dkGlYl67kVkDJGg0cQIO1qr/vGcZ8h/OmnOxU3geP0DaflG0h1/40lDQ3+E6BTb6HP2JmhgEmlRBRBdv87cRDHnDytBzcWARTvff3SsP2J2pLpLBTDiihlaZaiQYtgU=,iv:TvFpvcTydXO3fbh5x9ZXIOtMChlE7WXl2Xx2a9ujh00=,tag:XHvsZh6r9fzbbYFWWQyI5g==,type:str] unencrypted_suffix: _unencrypted version: 3.12.1 diff --git a/hosts/sid/services/coredns.nix b/hosts/sid/services/coredns.nix deleted file mode 100644 index 6129926..0000000 --- a/hosts/sid/services/coredns.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ constants, pkgs, ... }: - -let - blockSrc = builtins.concatStringsSep " " ( - with constants; - [ - hosts."16ach6".ip - hosts.pc.ip - hosts.pixel6a.ip - ] - ); - - corednsCfgDir = "/etc/coredns"; - blocklistFile = corednsCfgDir + "/blocklist.txt"; - blocklistURL = "https://big.oisd.nl/"; -in -{ - services.resolved.enable = false; - networking.resolvconf.enable = false; - - networking.nameservers = [ "127.0.0.1" ]; - - services.coredns = { - enable = true; - config = with constants; '' - .:53 { - bind 0.0.0.0 - hosts { - ${hosts.sid.ip} ${ca-fqdn} - - ${hosts.rx4.ip} rx4.tail - ${hosts.sid.ip} ${services.netdata.fqdn} - ${hosts.sid.ip} ${services.vaultwarden.fqdn} - ${hosts.sid.ip} ${services.webdav.fqdn} - ${hosts.sid.ip} sid.tail - ${hosts.vde.ip} vde.tail - fallthrough - } - - acl { - allow src ${blockSrc} { - forward . 1.1.1.1 8.8.8.8 - block { list ${blocklistFile} } - } - } - - forward . 1.1.1.1 8.8.8.8 - cache 30 - log - errors - } - ''; - }; - - systemd.services.update-blocklist = { - description = "Download CoreDNS blocklist"; - serviceConfig.Type = "oneshot"; - script = '' - mkdir -p ${corednsCfgDir} - ${pkgs.curl}/bin/curl -s -o ${blocklistFile} ${blocklistURL} - ''; - }; - - systemd.timers.update-blocklist = { - description = "Daily update of CoreDNS blocklist"; - wantedBy = [ "timers.target" ]; - after = [ "network-online.target" ]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; - RandomizedDelaySec = "1h"; - }; - }; -} diff --git a/hosts/sid/services/default.nix b/hosts/sid/services/default.nix index c753168..ee63f99 100644 --- a/hosts/sid/services/default.nix +++ b/hosts/sid/services/default.nix @@ -9,14 +9,15 @@ inputs.synix.nixosModules.openssh outputs.nixosModules.tailscale + # outputs.nixosModules.loki + # outputs.nixosModules.promtail - ./coredns.nix ./headscale.nix ./mailserver.nix ./matrix-synapse.nix ./netdata.nix ./nginx.nix ./radicale.nix - ./step-ca.nix + ./rss-bridge.nix ]; } diff --git a/hosts/sid/services/headscale.nix b/hosts/sid/services/headscale.nix index 6c7148f..0d4a03f 100644 --- a/hosts/sid/services/headscale.nix +++ b/hosts/sid/services/headscale.nix @@ -1,5 +1,6 @@ { inputs, + constants, ... }: @@ -24,5 +25,12 @@ enable = true; subdomain = "hs"; }; + settings.dns.extra_records = [ + { + name = constants.services.vaultwarden.fqdn; + type = "A"; + value = constants.hosts.rx4.ip; + } + ]; }; } diff --git a/hosts/sid/services/mailserver.nix b/hosts/sid/services/mailserver.nix index f3af274..c70433e 100644 --- a/hosts/sid/services/mailserver.nix +++ b/hosts/sid/services/mailserver.nix @@ -1,15 +1,21 @@ -{ inputs, config, ... }: +{ inputs, constants, ... }: +let + inherit (constants.services.mailserver) subdomain; +in { imports = [ inputs.synix.nixosModules.mailserver ]; mailserver = { enable = true; + inherit subdomain; stateVersion = 3; - localDnsResolver = !config.services.coredns.enable; accounts = { sid = { - aliases = [ "postmaster" ]; + aliases = [ + "admin" + "postmaster" + ]; }; vaultwarden = { }; }; diff --git a/hosts/sid/services/netdata.nix b/hosts/sid/services/netdata.nix index 005f9e1..6733c2a 100644 --- a/hosts/sid/services/netdata.nix +++ b/hosts/sid/services/netdata.nix @@ -1,18 +1,37 @@ { config, + constants, + lib, pkgs, ... }: let email = "sid@${config.networking.domain}"; + + netdata-dashboard = pkgs.stdenvNoCC.mkDerivation { + pname = "netdata-dashboard"; + version = "2.31.0"; + + src = pkgs.fetchurl { + url = "https://github.com/netdata/dashboard/releases/download/v2.31.0/dashboard.tar.gz"; + hash = "sha256-n7M7Y8LIb4tbgQ8wQIr5bMKxLT5fPDID5LnX47ayH/o="; + }; + + dontUnpack = true; + + installPhase = '' + mkdir -p $out + tar -xzf $src --strip-components=1 -C $out + ''; + + meta.license = lib.licenses.gpl3Only; + }; in { services.netdata = { enable = true; - package = pkgs.netdata.override { - withCloudUi = true; - }; + package = pkgs.netdata.override { withCloudUi = false; }; config.global = { "debug log" = "syslog"; "access log" = "syslog"; @@ -36,6 +55,47 @@ in NETDATA_USER_CONFIG_DIR = "/etc/netdata/conf.d"; }; + services.nginx.virtualHosts."${constants.services.netdata.fqdn}" = { + useACMEHost = constants.services.netdata.fqdn; + forceSSL = true; + listen = [ + { + addr = "${constants.hosts.sid.ip}:443"; + ssl = true; + } + ]; + + locations."/" = { + root = netdata-dashboard; + tryFiles = "$uri $uri/ /index.html"; + }; + + locations."~ ^/(api|v[0-9]+|netdata.conf|registry|stream|version.txt)(/|$)" = { + proxyPass = "http://127.0.0.1:${toString constants.services.netdata.port}"; + recommendedProxySettings = true; + }; + + locations."~ ^/host/" = { + proxyPass = "http://127.0.0.1:${toString constants.services.netdata.port}"; + recommendedProxySettings = true; + }; + }; + + security.acme = { + acceptTerms = true; + certs."${constants.services.netdata.fqdn}" = { + domain = constants.services.netdata.fqdn; + webroot = lib.mkForce null; + dnsProvider = "hetzner"; + credentialFiles.HETZNER_API_TOKEN_FILE = config.sops.secrets.hetzner-api-key.path; + group = "nginx"; + }; + }; + + services.journald.storage = "persistent"; + + users.users.netdata.extraGroups = [ "systemd-journal" ]; + sops = let owner = config.services.netdata.user; @@ -44,6 +104,12 @@ in restartUnits = [ "netdata.service" ]; in { + secrets.hetzner-api-key = { + inherit mode; + owner = "acme"; + group = "acme"; + }; + secrets."netdata/stream/rx4/uuid" = { inherit owner @@ -65,6 +131,7 @@ in [${config.sops.placeholder."netdata/stream/rx4/uuid"}] enabled = yes default history = 3600 + allow from = * ''; }; }; diff --git a/hosts/sid/services/nginx.nix b/hosts/sid/services/nginx.nix index b266858..0844781 100644 --- a/hosts/sid/services/nginx.nix +++ b/hosts/sid/services/nginx.nix @@ -19,6 +19,15 @@ in enable = true; openFirewall = true; forceSSL = ssl; + + commonHttpConfig = '' + map $http_upgrade $connection_upgrade { + default upgrade; + "" 'close'; + } + access_log syslog:server=unix:/dev/log; + ''; + virtualHosts."_" = { forceSSL = false; locations."/.well-known/acme-challenge/" = { @@ -47,48 +56,23 @@ in address = constants.hosts.rx4.ip; port = constants.services.miniflux.port; }; - virtualHosts."${constants.services.netdata.fqdn}" = { - useACMEHost = "sid-internal"; - forceSSL = ssl; - locations."/" = { - proxyPass = "http://${constants.hosts.rx4.ip}:${toString constants.services.netdata.port}"; - proxyWebsockets = true; - }; - }; virtualHosts."${constants.services.open-webui-oci.fqdn}" = mkVirtualHost { inherit ssl; address = constants.hosts.rx4.ip; port = constants.services.open-webui-oci.port; - }; - virtualHosts."${constants.services.rss-bridge.fqdn}" = { - enableACME = ssl; - forceSSL = ssl; - locations."/" = { - proxyPass = "http://${constants.hosts.rx4.ip}"; - }; + extraConfig = '' + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 600s; + access_log /var/log/nginx/open-webui-access.log; + error_log /var/log/nginx/open-webui-error.log debug; + ''; }; virtualHosts."${constants.services.rsshub-oci.fqdn}" = mkVirtualHost { inherit ssl; address = constants.hosts.rx4.ip; port = constants.services.rsshub-oci.port; }; - virtualHosts."${constants.services.vaultwarden.fqdn}" = { - useACMEHost = "sid-internal"; - forceSSL = ssl; - locations = { - "/" = { - proxyPass = "http://${constants.hosts.rx4.ip}:${toString constants.services.vaultwarden.port}"; - }; - }; - }; - virtualHosts."${constants.services.webdav.fqdn}" = { - useACMEHost = "sid-internal"; - forceSSL = ssl; - locations."/" = { - proxyPass = "http://${constants.hosts.rx4.ip}:${toString constants.services.webdav.port}"; - proxyWebsockets = true; - }; - }; # FIXME # virtualHosts."print.sid.ovh" = { # enableACME = true; diff --git a/hosts/sid/services/rss-bridge.nix b/hosts/sid/services/rss-bridge.nix new file mode 100644 index 0000000..a685ae5 --- /dev/null +++ b/hosts/sid/services/rss-bridge.nix @@ -0,0 +1,32 @@ +{ + inputs, + constants, + pkgs, + ... +}: + +{ + imports = [ inputs.synix.nixosModules.rss-bridge ]; + + services.phpfpm.pools.rss-bridge = { + phpPackage = pkgs.php.withExtensions ( + { enabled, all }: + enabled + ++ [ + all.curl + all.mbstring + all.simplexml + all.intl + ] + ); + }; + + services.rss-bridge = { + enable = true; + reverseProxy = { + enable = true; + subdomain = constants.services.rss-bridge.subdomain; + forceSSL = true; + }; + }; +} diff --git a/hosts/sid/services/step-ca.nix b/hosts/sid/services/step-ca.nix deleted file mode 100644 index d3abb11..0000000 --- a/hosts/sid/services/step-ca.nix +++ /dev/null @@ -1,108 +0,0 @@ -{ - constants, - config, - pkgs, - ... -}: - -let - cfg = config.services.step-ca; -in -{ - services.step-ca = { - enable = true; - address = "0.0.0.0"; - port = 8443; - openFirewall = true; - intermediatePasswordFile = config.sops.secrets."step-ca/password".path; - # nix-shell -p step-cli --run "step ca init" - settings = { - # FIXME: nix-store paths do not work - # root = ../../../certs/root_ca.crt; - # crt = ../../../certs/intermediate_ca.crt; - # FIXME: not reproducible - root = "/var/lib/step-ca/certs/root_ca.crt"; - crt = "/var/lib/step-ca/certs/intermediate_ca.crt"; - key = config.sops.secrets."step-ca/intermediate-key".path; - dnsNames = [ - constants.ca-fqdn - constants.hosts.sid.ip - ]; - logger = { - format = "text"; - }; - db = { - type = "badgerv2"; - dataSource = "/var/lib/step-ca/db"; - }; - authority = { - provisioners = [ - { - type = "ACME"; - name = "acme"; - } - { - type = "JWK"; - name = "sid@sid.ovh"; - key = { - use = "sig"; - kty = "EC"; - kid = "w3fV4U-frlyTnBMg4yNYrLsn8_mY98H8HthoscpoVrg"; - crv = "P-256"; - alg = "ES256"; - x = "KZCDecn4sb87T3UO6JsIzJVtr4Aa0UcYzYDNBUM6F7M"; - y = "CbGHn9tXQbV0Ur2VuXITLnWgfxCRmKEoUdMUmrP9Qkw"; - }; - encryptedKey = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiZS1MUDhDYlE4dzVuMF9nUGhXOWtGdyJ9.rgsqo58rJFWaociSqiPg3E1alAeqoHWubJi4n2uoUFYp3YTWaYZzqA.6P6oimHsKGdCWruo.fNaDr50IXCtCe7W7VIXuS3rlfin_R0nogNpIJ9C6szYg8k10UylircUs14Zl1EHQ9lFeJovb1y1uljzBajMGkOAGlMvashrphVkXiSxHWKDhzbrItJx3qChLtSLJJtXiXPbJQKCAeBjztqPuTw6dI4Z6IR9---kiTvzF6I9KE8afGFlMSubGjr9FnqgiOb2JiZuTfcBGDx78puxdWzUrEEVlliHdv2agbKhY0b13x-obaTIWwlqLFbasv7kPneJ8Ggp7IHHr5uDcUrqVKkTfBrD0lelXm6SwJTHGMkty6inlwSflT9mxvkNq7OGV9triPQc8AGVv0c7t7dHoX_E.tSjJqttCS6zLI_-7zPdXNQ"; - } - ]; - }; - tls = { - cipherSuites = [ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" - ]; - renegotiation = false; - }; - }; - }; - - environment.systemPackages = [ - pkgs.step-cli - ]; - - systemd.tmpfiles.rules = [ - "d /var/lib/acme/acme-challenge 0755 acme nginx" - ]; - - security.acme = { - certs."sid-internal" = { - # domain = constants.intranet; - domain = constants.services.vaultwarden.fqdn; - extraDomainNames = [ - constants.services.netdata.fqdn - # constants.services.vaultwarden.fqdn - constants.services.webdav.fqdn - ]; - server = "https://${constants.ca-fqdn}:${toString cfg.port}/acme/acme/directory"; - group = "nginx"; - }; - }; - - sops = - let - owner = "step-ca"; - group = "step-ca"; - mode = "0400"; - in - { - secrets = { - "step-ca/password" = { - inherit owner group mode; - }; - "step-ca/intermediate-key" = { - inherit owner group mode; - }; - }; - }; -} diff --git a/modules/nixos/common/default.nix b/modules/nixos/common/default.nix index 0415b9f..71660e5 100644 --- a/modules/nixos/common/default.nix +++ b/modules/nixos/common/default.nix @@ -5,10 +5,6 @@ ./nix.nix ./overlays.nix - ../pki - inputs.synix.nixosModules.device.server ]; - - nixpkgs.config.allowUnfree = true; } diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 540f4ee..d3511ee 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -5,8 +5,9 @@ forgejo = import ./forgejo; forgejo-runner = import ./forgejo-runner; gnome = import ./gnome; + loki = import ./loki; monero = import ./monero; - pki = import ./pki; + promtail = import ./promtail; rsshub-oci = import ./rsshub-oci; tailscale = import ./tailscale; xfce = import ./xfce; diff --git a/modules/nixos/loki/dashboards/logs.json b/modules/nixos/loki/dashboards/logs.json new file mode 100644 index 0000000..2009e52 --- /dev/null +++ b/modules/nixos/loki/dashboards/logs.json @@ -0,0 +1,75 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Graphics --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "gridPos": { + "h": 24, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "Loki" + }, + "expr": "{job=\"systemd-journal\"}", + "refId": "A" + } + ], + "title": "System Logs", + "type": "logs" + } + ], + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "System Logs", + "uid": "system-logs", + "version": 1 +} diff --git a/modules/nixos/loki/default.nix b/modules/nixos/loki/default.nix new file mode 100644 index 0000000..e5fdd14 --- /dev/null +++ b/modules/nixos/loki/default.nix @@ -0,0 +1,115 @@ +{ + services.loki = { + enable = true; + configuration = { + auth_enabled = false; + + server = { + http_listen_address = "0.0.0.0"; + http_listen_port = 3100; + grpc_listen_port = 9096; + }; + + common = { + ring = { + instance_addr = "127.0.0.1"; + kvstore.store = "inmemory"; + }; + replication_factor = 1; + path_prefix = "/var/lib/loki"; + }; + + ingester = { + wal = { + enabled = true; + dir = "/var/lib/loki/wal"; + }; + chunk_encoding = "snappy"; + chunk_idle_period = "30m"; + max_chunk_age = "2h"; + chunk_target_size = 1572864; + chunk_block_size = 262144; + }; + + limits_config = { + reject_old_samples = true; + reject_old_samples_max_age = "168h"; + ingestion_rate_mb = 10; + ingestion_burst_size_mb = 20; + per_stream_rate_limit = "3MB"; + per_stream_rate_limit_burst = "15MB"; + max_line_size = "256KB"; + }; + + schema_config = { + configs = [ + { + from = "2026-01-01"; + store = "tsdb"; + object_store = "filesystem"; + schema = "v13"; + index = { + prefix = "index_"; + period = "24h"; + }; + } + ]; + }; + + storage_config = { + filesystem = { + directory = "/var/lib/loki/chunks"; + }; + }; + + compactor = { + working_directory = "/var/lib/loki/compactor"; + compaction_interval = "10m"; + retention_enabled = true; + retention_delete_delay = "2h"; + retention_delete_worker_count = 150; + delete_request_store = "filesystem"; + }; + }; + }; + + services.grafana = { + enable = true; + settings = { + server = { + http_addr = "0.0.0.0"; + http_port = 3003; + }; + "auth.anonymous" = { + enabled = true; + org_name = "Main Org."; + org_role = "Admin"; + }; + }; + + provision = { + enable = true; + datasources.settings = { + apiVersion = 1; + datasources = [ + { + name = "Loki"; + type = "loki"; + access = "proxy"; + url = "http://127.0.0.1:3100"; + isDefault = true; + uid = "loki"; + } + ]; + }; + dashboards.settings.providers = [ + { + name = "default"; + options.path = ./dashboards; + } + ]; + }; + }; + + networking.firewall.allowedTCPPorts = [ 3100 ]; +} diff --git a/modules/nixos/pki/default.nix b/modules/nixos/pki/default.nix deleted file mode 100644 index 9b849f6..0000000 --- a/modules/nixos/pki/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - security.pki.certificateFiles = [ ./root_ca.crt ]; -} diff --git a/modules/nixos/pki/root_ca.crt b/modules/nixos/pki/root_ca.crt deleted file mode 100644 index 44abf61..0000000 --- a/modules/nixos/pki/root_ca.crt +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBrzCCAVWgAwIBAgIQDV0M0pLkCXvARpa+ipSx8jAKBggqhkjOPQQDAjA2MRUw -EwYDVQQKEwxzaWQtaW50ZXJuYWwxHTAbBgNVBAMTFHNpZC1pbnRlcm5hbCBSb290 -IENBMB4XDTI2MDQxODIwMzkwMloXDTM2MDQxNTIwMzkwMlowNjEVMBMGA1UEChMM -c2lkLWludGVybmFsMR0wGwYDVQQDExRzaWQtaW50ZXJuYWwgUm9vdCBDQTBZMBMG -ByqGSM49AgEGCCqGSM49AwEHA0IABCH2VmIwKEjdma4UymD7RWuGcaT2algrL5nm -TE0NzP8giezdU9bEP487AvUPPibSYDWxdp4ycbl6qNVTiy29xkmjRTBDMA4GA1Ud -DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRaiBACRDZk -HZMU9y8YsUF4WPB+5TAKBggqhkjOPQQDAgNIADBFAiAh+b49V2VTnT6nRCRM0Qwq -ruzayrrnmF7pIxi9PVFwBQIhANQsL3ok4gCTRAnT0mUXSyWexzSESZ1lkpLYiyoj -RgLi ------END CERTIFICATE----- diff --git a/modules/nixos/promtail/default.nix b/modules/nixos/promtail/default.nix new file mode 100644 index 0000000..cdb99de --- /dev/null +++ b/modules/nixos/promtail/default.nix @@ -0,0 +1,43 @@ +{ config, constants, ... }: + +{ + services.promtail = { + enable = true; + configuration = { + server = { + http_listen_port = 9080; + grpc_listen_port = 0; + }; + + clients = [ + { + url = "http://${constants.hosts.sid.ip}:3100/loki/api/v1/push"; + } + ]; + + scrape_configs = [ + { + job_name = "journal"; + journal = { + max_age = "12h"; + path = "/var/log/journal"; + + labels = { + job = "systemd-journal"; + host = config.networking.hostName; + }; + }; + + relabel_configs = [ + { + source_labels = [ "__journal__systemd_unit" ]; + target_label = "unit"; + } + ]; + } + ]; + }; + }; + + users.users.promtail.extraGroups = [ "systemd-journal" ]; +} diff --git a/modules/nixos/rsshub-oci/default.nix b/modules/nixos/rsshub-oci/default.nix index 79c11d6..5f5bf98 100644 --- a/modules/nixos/rsshub-oci/default.nix +++ b/modules/nixos/rsshub-oci/default.nix @@ -7,23 +7,26 @@ let cfg = config.services.rsshub-oci; + domain = config.networking.domain; + subdomain = cfg.reverseProxy.subdomain; + fqdn = if (cfg.reverseProxy.enable && subdomain != "") then "${subdomain}.${domain}" else domain; images = { # https://github.com/DIYgod/RSSHub/pkgs/container/rsshub rsshub = pkgs.dockerTools.pullImage { imageName = "ghcr.io/diygod/rsshub"; - imageDigest = "sha256:67e23d9afc6081753c1a51a71f5a58f57d43b12ff927c2263a9f767112ec1819"; - hash = "sha256-vEQi12NCY35dGJ3vKUjwocfRn8xOBp/2inIqg8DNTRE="; + imageDigest = "sha256:93660573e0fbfe1062e4fc512acf5043e1399519cdd9a11f130a8332306e8fdd"; + hash = "sha256-cP2RnV6zmLoYzHgvuuHpqlRcNngD+YFRfRkFMNFQxG8="; finalImageName = "ghcr.io/diygod/rsshub"; - finalImageTag = "2026-03-07"; + finalImageTag = "2026-05-04"; }; # https://github.com/browserless/browserless/pkgs/container/chromium browserless = pkgs.dockerTools.pullImage { imageName = "ghcr.io/browserless/chromium"; - imageDigest = "sha256:71ae7fa09bd1bf78efaa2803d75c837ab5a56e0d7637695bff3077d87248e642"; - hash = "sha256-adu+fvWK1X/nVt2cFLDO0Czq6GA+46VvL1wy9KQ+/tI="; + imageDigest = "sha256:af3483eb7f125978d511df0d227d37931941b43d2cdb5f768da57263a7a132bf"; + hash = "sha256-qKx/I9X/GTnoWpHY3gtZUoeL65ndOzU29bGjR6QLYp4="; finalImageName = "ghcr.io/browserless/chromium"; - finalImageTag = "v2.42.0"; + finalImageTag = "v2.48.2"; }; # https://github.com/hyoban/puppeteer-real-browser-hono/pkgs/container/puppeteer-real-browser-hono real-browser = pkgs.dockerTools.pullImage { @@ -36,13 +39,12 @@ let # https://hub.docker.com/_/redis redis = pkgs.dockerTools.pullImage { imageName = "redis"; - imageDigest = "sha256:2afba59292f25f5d1af200496db41bea2c6c816b059f57ae74703a50a03a27d0"; - hash = "sha256-t3SFoeUME8Ntz5QdMfJnJ3QYRNAaHHpXngnBvR8LOlg="; + imageDigest = "sha256:c5e375abb885e6b2021c0377879e4890bf76f9065b8922ffc113f2b226b9fc17"; + hash = "sha256-ls1be+fp+chENJ7OrYng5EY3zdHsfiZCW3fmvwzwzj8="; finalImageName = "redis"; - finalImageTag = "8.6.1-alpine"; + finalImageTag = "8.6.2-alpine"; }; }; - defaultEnv = { NODE_ENV = "production"; CACHE_TYPE = "redis"; @@ -59,6 +61,10 @@ let optional types ; + inherit (lib.utils) + mkReverseProxyOption + mkVirtualHost + ; in { options.services.rsshub-oci = { @@ -78,6 +84,7 @@ in default = null; description = "Environment file for secrets."; }; + reverseProxy = mkReverseProxyOption "RSSHub" "rsshub"; }; config = mkIf cfg.enable { @@ -87,6 +94,13 @@ in dockerCompat = true; }; + services.nginx.virtualHosts = mkIf cfg.reverseProxy.enable { + "${fqdn}" = mkVirtualHost { + inherit (cfg) port; + ssl = cfg.reverseProxy.forceSSL; + }; + }; + networking.firewall.interfaces = let matchAll = if !config.networking.nftables.enable then "podman+" else "podman*"; diff --git a/modules/nixos/tailscale/default.nix b/modules/nixos/tailscale/default.nix index 884847a..f42bb85 100644 --- a/modules/nixos/tailscale/default.nix +++ b/modules/nixos/tailscale/default.nix @@ -11,7 +11,7 @@ loginServer = "https://hs.sid.ovh"; authKeyFile = config.sops.secrets."tailscale/personal-key".path; enableSSH = true; - acceptDNS = false; # use coredns + acceptDNS = true; }; }; }; diff --git a/overlays/default.nix b/overlays/default.nix index 592341b..4dbc64d 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -12,6 +12,7 @@ final: prev: let files = [ + ./rss-bridge.nix ]; imports = builtins.map (f: import f final prev) files; in diff --git a/overlays/rss-bridge.nix b/overlays/rss-bridge.nix new file mode 100644 index 0000000..ca454e5 --- /dev/null +++ b/overlays/rss-bridge.nix @@ -0,0 +1,11 @@ +final: prev: { + rss-bridge = prev.rss-bridge.overrideAttrs (oldAttrs: { + version = "unstable-2026-06"; + src = prev.fetchFromGitHub { + owner = "sid115"; + repo = "rss-bridge"; + rev = "8f2109f53a6dc63811d2c2fc9840edb247c369e7"; + sha256 = "sha256-XA3ei+fps+4DHgtn7ytjH7UUsYFDYq0kTlN6wgrzii8="; + }; + }); +}