From 16abf8f35f879ba2ec617b65f7c0cc6aff1d02fd Mon Sep 17 00:00:00 2001 From: Kalle Struik Date: Fri, 11 Apr 2025 21:26:14 +0200 Subject: [PATCH] Add freshrss service and cloud host --- README.md | 1 + docs/roles/freshrss.md | 22 ++++++++++++ example_secrets.yaml | 3 ++ hosts/cloud.nix | 20 +++++++++++ roles/freshrss.nix | 79 ++++++++++++++++++++++++++++++++++++++++++ secrets/cloud.yaml | 43 +++++++++++++++++++++++ 6 files changed, 168 insertions(+) create mode 100644 docs/roles/freshrss.md create mode 100644 hosts/cloud.nix create mode 100644 roles/freshrss.nix create mode 100644 secrets/cloud.yaml diff --git a/README.md b/README.md index 1075b39..fb102a0 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ also available for each unmanaged host in `docs/hosts`. 192.168.10.[10-50] -> VM hosts 10 -> Proxy 11 -> Git + 12 -> Cloud 192.168.10.[100-200] -> DHCP range 174 -> Bluesky PDS 188 -> Portainer (Deprecated) diff --git a/docs/roles/freshrss.md b/docs/roles/freshrss.md new file mode 100644 index 0000000..fce1217 --- /dev/null +++ b/docs/roles/freshrss.md @@ -0,0 +1,22 @@ +# FreshRSS role + +## Notes +- Requires the postgres role to be enabled on the same host. +- By default no SSO is configured. + +## Options +### `freshrss.domain` +The domain used by freshrss. + +### `freshrss.adminUser` +The administrator user for freshrss. This users password is controlled by the +`freshrss/admin_pass` secret. + +## Secrets +### `freshrss/db_pass` +The password for the freshrss postgres database. This should be the same as +`postgres/freshrss` on the same host. + +### `freshrss/admin_pass` +The password for the administrator user. + diff --git a/example_secrets.yaml b/example_secrets.yaml index 5d3d7be..3cd5e24 100644 --- a/example_secrets.yaml +++ b/example_secrets.yaml @@ -19,9 +19,12 @@ forgejo: email_from: mail@example.com email_username: mail@example.com email_password: ADifferentVeryLongSecurePassword +freshrss: + db_pass: AVeryLongSecurePassword postgres: # Every database used should have an entry with the password here authentik: AVeryLongSecurePassword forgejo: AVeryLongSecurePassword + freshrss: AVeryLongSecurePassword diff --git a/hosts/cloud.nix b/hosts/cloud.nix new file mode 100644 index 0000000..1459dd6 --- /dev/null +++ b/hosts/cloud.nix @@ -0,0 +1,20 @@ +{ + roles, + hlConfig, +}: +{ + hostname = "cloud"; + managed = true; + ip = "192.168.10.12"; + + roles = with roles; [ + postgres + + freshrss + ]; + config = { + freshrss.domain = "rss.${hlConfig.domain}"; + freshrss.adminUser = "kalle"; + }; + stateVersion = "24.05"; +} diff --git a/roles/freshrss.nix b/roles/freshrss.nix new file mode 100644 index 0000000..28cade7 --- /dev/null +++ b/roles/freshrss.nix @@ -0,0 +1,79 @@ +{ + name = "FreshRSS"; + description = '' + RSS reader and sync server + ''; + + traefikRoutes = + { + host, + ... + }: + let + hostname = host.hostname; + config = host.config.freshrss; + in + [ + { + name = "${hostname}-freshrss"; + rule = "Host(`${config.domain}`)"; + target = "http://${host.ip}:80"; + } + ]; + + nixosModule = + { lib, config, ... }: + { + options.freshrss = { + domain = lib.mkOption { + type = lib.types.str; + }; + adminUser = lib.mkOption { + type = lib.types.str; + }; + }; + + config = + let + cfg = config.freshrss; + secrets = config.sops.secrets; + in + { + networking.firewall.allowedTCPPorts = [ + 80 # Nginx running freshrss + ]; + + sops.secrets = { + "freshrss/db_pass" = { + owner = "freshrss"; + }; + "freshrss/admin_pass" = { + owner = "freshrss"; + }; + }; + systemd.tmpfiles.rules = [ + "d '${config.services.freshrss.dataDir}/cache' 0750 ${config.services.freshrss.user} ${config.services.freshrss.user} - -" + "d '${config.services.freshrss.dataDir}/users' 0750 ${config.services.freshrss.user} ${config.services.freshrss.user} - -" + "d '${config.services.freshrss.dataDir}/favicons' 0750 ${config.services.freshrss.user} ${config.services.freshrss.user} - -" + ]; + + # Create the database + postgres.databases = [ "freshrss" ]; + + # Enable and configure the service + services.freshrss = { + enable = true; + baseUrl = "https://${cfg.domain}"; + virtualHost = cfg.domain; + dataDir = "/cephfs/appdata/freshrss"; + defaultUser = cfg.adminUser; + passwordFile = secrets."freshrss/admin_pass".path; + + database = { + type = "pgsql"; + passFile = secrets."freshrss/db_pass".path; + }; + }; + }; + }; +} diff --git a/secrets/cloud.yaml b/secrets/cloud.yaml new file mode 100644 index 0000000..f3f0f90 --- /dev/null +++ b/secrets/cloud.yaml @@ -0,0 +1,43 @@ +freshrss: + db_pass: ENC[AES256_GCM,data:6/DOnp9vzUUdibx1FdEMucgXzxsyae7UHwDMC7byaQ8YrQmkGCCDi3Q4ZqE=,iv:LS/IMe97HifOq5uoP5n0++vMLfaiJC6FOQ7tKmR5438=,tag:XLhYQ5N+HbrUOPY6VVB8qA==,type:str] + admin_pass: ENC[AES256_GCM,data:jyMRdALA/Niy2SQXk37sYUApGZl8i6yDWS+5EsLDmAslEkbqPv49kXv8I2I=,iv:xxVu1CFJQFgfaMOv0lzbloZkSkUetpzK8SCtGlMFZXI=,tag:RQWNHLc0e9Dcf4govwfjjA==,type:str] +postgres: + freshrss: ENC[AES256_GCM,data:qlo1HBwm7V2WKuhdy8aAKheTL2mUuVuMslSTLYX30ZKHt9IvjmsG6/e3Gjo=,iv:3FF13Hv3X8YG7Nj9oEKX1tuzhbaQv56oKsBvR6u5LT0=,tag:gMh7z+fPnPud2nQA6Lu3KQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1y86zket4wccf9kfp65gmlcsf0a9drjux7r3zlcfqqdkh99dfnyeqts8jra + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVU1pLb0I2M0RYYkJTbmQ5 + WkRsUTZSQUhJNGkrR3BPQ3pUL3BzLzNpaEZzClFBUEZUcGRhTFY4QUJsQXoxV3RP + SjZDeENpV3ZPR3FMajZuWHlSRFhwNDAKLS0tIFJyTWhCUkl6WXBXeVdiSU5YSVpF + QlJxM0Jkakd0a2g2U0g4OEsxdGlaRTgK+M1PDd2PBEctia8NLP7l/4jKUlFtznis + moZB1GPzvoe7Z+nsGzaGY8kzEMBWNK53Da5wYnTOEPUl9po9aLwzpA== + -----END AGE ENCRYPTED FILE----- + - recipient: age1htf3j7d0me9f24fadwth7avs40qm8yzhczljfgh0wjepdr8utfvqd369xp + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6VHRLMlpUUm80U0R2bUxv + K1VNdzZ4TW5hOEFzUURKMGdKWG4wZE5JazNjClR6ODJYZWlzdE1NNTBBaFJYemhW + dFE2UGMrRUh4RENwM2owejZJMmVRSVkKLS0tIFlLNHc2UVRQekZDeEg2N2NrVTBk + Skw5N21MQUJlN0o0azF2ZWU5OTRUTUUKrrtLXZpJzSD2rMjxgYWvFmeoHvZheNKV + iUzkwLfVx7DjJdDuFy3UYa8ZIFC9WN0/5xNxeA5BJEtvqISfmvlBYQ== + -----END AGE ENCRYPTED FILE----- + - recipient: age1w8flykazkwxewcxpe2mn50cawn857ylcdp4r7vp459p3q7cx9uasap4stz + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiQU1QRHJKZ0dEQ2E2ODk2 + dnJDSy9HY0xRSlo3dWt5ZmNLYWFRZ05SRlNrCnREQkFKZmEySXkwaGY0MWFTRy8z + by9PYmxLQmxldkNSN3VPQi9pSTl3aUkKLS0tIFoxNTgxd2ZJeEtUczN1S1NFaFpm + aWxTNjVPTmZGMUJFK2ZCMTg1eHlEeTAK7EPDDmFXMGSe96L6vv7ZCrebLxITYHQ/ + TmMTLj6YN+PsdVv3AgKnOytgJll5/GFsmvR5HnDuHaEqDI71q+8nIQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-04-11T19:59:02Z" + mac: ENC[AES256_GCM,data:RSSDQB8KB1pLWtmbkEGrc1qoh2h/12EY4Wtyuvf5NbgsYEo1nMt8Uhieol3/EtIzE3LL2nszwuECxcOlW7wSQvU+eYjOT403+E/oFqhSfg1QYePJlJCGw/c4F6Hb8xLwLxdWrLpe1JNyDv1e2ENoHrZK75ZADmb3GWOVKOIMp5U=,iv:NINSNtWz5YFLoj3VXTak4lwCwp8bl6ogO1XWwUXDJbs=,tag:mSUjfar7aTJireuUOVTzWg==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.9.4