From 0b204f0c28945d2c2553282fbe5830ace2878977 Mon Sep 17 00:00:00 2001 From: Felix Buehler Date: Wed, 7 Sep 2022 21:06:44 +0200 Subject: freshrss: init at 1.20.0, tests and module --- .../from_md/release-notes/rl-2211.section.xml | 7 + nixos/doc/manual/release-notes/rl-2211.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/freshrss.nix | 274 +++++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/freshrss.nix | 19 ++ 6 files changed, 304 insertions(+) create mode 100644 nixos/modules/services/web-apps/freshrss.nix create mode 100644 nixos/tests/freshrss.nix (limited to 'nixos') diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml index 0f1dffc798e..06179a0f809 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml @@ -270,6 +270,13 @@ services.dolibarr. + + + FreshRSS, a + free, self-hostable RSS feed aggregator. Available as + services.freshrss. + + expressvpn, diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md index 7214937781d..5d9946ae3f8 100644 --- a/nixos/doc/manual/release-notes/rl-2211.section.md +++ b/nixos/doc/manual/release-notes/rl-2211.section.md @@ -96,6 +96,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [Dolibarr](https://www.dolibarr.org/), an enterprise resource planning and customer relationship manager. Enable using [services.dolibarr](#opt-services.dolibarr.enable). +- [FreshRSS](https://freshrss.org/), a free, self-hostable RSS feed aggregator. Available as [services.freshrss](#opt-services.freshrss.enable). + - [expressvpn](https://www.expressvpn.com), the CLI client for ExpressVPN. Available as [services.expressvpn](#opt-services.expressvpn.enable). - [go-autoconfig](https://github.com/L11R/go-autoconfig), IMAP/SMTP autodiscover server. Available as [services.go-autoconfig](#opt-services.go-autoconfig.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 699c5c7dead..d601e257ec3 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1072,6 +1072,7 @@ ./services/web-apps/engelsystem.nix ./services/web-apps/ethercalc.nix ./services/web-apps/fluidd.nix + ./services/web-apps/freshrss.nix ./services/web-apps/galene.nix ./services/web-apps/gerrit.nix ./services/web-apps/gotify-server.nix diff --git a/nixos/modules/services/web-apps/freshrss.nix b/nixos/modules/services/web-apps/freshrss.nix new file mode 100644 index 00000000000..68780a3fe9d --- /dev/null +++ b/nixos/modules/services/web-apps/freshrss.nix @@ -0,0 +1,274 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.freshrss; + + poolName = "freshrss"; +in +{ + meta.maintainers = with maintainers; [ etu stunkymonkey ]; + + options.services.freshrss = { + enable = mkEnableOption (mdDoc "FreshRSS feed reader"); + + package = mkOption { + type = types.package; + default = pkgs.freshrss; + defaultText = lib.literalExpression "pkgs.freshrss"; + description = mdDoc "Which FreshRSS package to use."; + }; + + defaultUser = mkOption { + type = types.str; + default = "admin"; + description = mdDoc "Default username for FreshRSS."; + example = "eva"; + }; + + passwordFile = mkOption { + type = types.path; + description = mdDoc "Password for the defaultUser for FreshRSS."; + example = "/run/secrets/freshrss"; + }; + + baseUrl = mkOption { + type = types.str; + description = mdDoc "Default URL for FreshRSS."; + example = "https://freshrss.example.com"; + }; + + language = mkOption { + type = types.str; + default = "en"; + description = mdDoc "Default language for FreshRSS."; + example = "de"; + }; + + database = { + type = mkOption { + type = types.enum [ "sqlite" "pgsql" "mysql" ]; + default = "sqlite"; + description = mdDoc "Database type."; + example = "pgsql"; + }; + + host = mkOption { + type = types.nullOr types.str; + default = "localhost"; + description = mdDoc "Database host for FreshRSS."; + }; + + port = mkOption { + type = with types; nullOr port; + default = null; + description = mdDoc "Database port for FreshRSS."; + example = 3306; + }; + + user = mkOption { + type = types.nullOr types.str; + default = "freshrss"; + description = mdDoc "Database user for FreshRSS."; + }; + + passFile = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc "Database password file for FreshRSS."; + example = "/run/secrets/freshrss"; + }; + + name = mkOption { + type = types.nullOr types.str; + default = "freshrss"; + description = mdDoc "Database name for FreshRSS."; + }; + + tableprefix = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc "Database table prefix for FreshRSS."; + example = "freshrss"; + }; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/freshrss"; + description = mdDoc "Default data folder for FreshRSS."; + example = "/mnt/freshrss"; + }; + + virtualHost = mkOption { + type = types.nullOr types.str; + default = "freshrss"; + description = mdDoc '' + Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost. + ''; + }; + + pool = mkOption { + type = types.str; + default = poolName; + description = mdDoc '' + Name of the phpfpm pool to use and setup. If not specified, a pool will be created + with default values. + ''; + }; + }; + + + config = + let + systemd-hardening = { + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + DeviceAllow = ""; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; + UMask = "0007"; + }; + in + mkIf cfg.enable { + # Set up a Nginx virtual host. + services.nginx = mkIf (cfg.virtualHost != null) { + enable = true; + virtualHosts.${cfg.virtualHost} = { + root = "${cfg.package}/p"; + + locations."~ ^.+?\.php(/.*)?$".extraConfig = '' + fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket}; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include ${pkgs.nginx}/conf/fastcgi_params; + include ${pkgs.nginx}/conf/fastcgi.conf; + ''; + + locations."/" = { + tryFiles = "$uri $uri/ index.php"; + index = "index.php index.html index.htm"; + }; + }; + }; + + # Set up phpfpm pool + services.phpfpm.pools = mkIf (cfg.pool == poolName) { + ${poolName} = { + user = "freshrss"; + settings = { + "listen.owner" = "nginx"; + "listen.group" = "nginx"; + "listen.mode" = "0600"; + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.max_requests" = 500; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 5; + "catch_workers_output" = true; + }; + phpEnv = { + FRESHRSS_DATA_PATH = "${cfg.dataDir}"; + }; + }; + }; + + users.users.freshrss = { + description = "FreshRSS service user"; + isSystemUser = true; + group = "freshrss"; + }; + users.groups.freshrss = { }; + + systemd.services.freshrss-config = + let + settingsFlags = concatStringsSep " \\\n " + (mapAttrsToList (k: v: "${k} ${toString v}") { + "--default_user" = ''"${cfg.defaultUser}"''; + "--auth_type" = ''"form"''; + "--base_url" = ''"${cfg.baseUrl}"''; + "--language" = ''"${cfg.language}"''; + "--db-type" = ''"${cfg.database.type}"''; + # The following attributes are optional depending on the type of + # database. Those that evaluate to null on the left hand side + # will be omitted. + ${if cfg.database.name != null then "--db-base" else null} = ''"${cfg.database.name}"''; + ${if cfg.database.passFile != null then "--db-password" else null} = ''"$(cat ${cfg.database.passFile})"''; + ${if cfg.database.user != null then "--db-user" else null} = ''"${cfg.database.user}"''; + ${if cfg.database.tableprefix != null then "--db-prefix" else null} = ''"${cfg.database.tableprefix}"''; + ${if cfg.database.host != null && cfg.database.port != null then "--db-host" else null} = ''"${cfg.database.host}:${toString cfg.database.port}"''; + }); + in + { + description = "Set up the state directory for FreshRSS before use"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + User = "freshrss"; + Group = "freshrss"; + StateDirectory = "freshrss"; + WorkingDirectory = cfg.package; + } // systemd-hardening; + environment = { + FRESHRSS_DATA_PATH = cfg.dataDir; + }; + + script = '' + # create files with correct permissions + mkdir -m 755 -p ${cfg.dataDir} + + # do installation or reconfigure + if test -f ${cfg.dataDir}/config.php; then + # reconfigure with settings + ${pkgs.php}/bin/php ./cli/reconfigure.php ${settingsFlags} + ${pkgs.php}/bin/php ./cli/update-user.php --user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})" + else + # Copy the user data template directory + cp -r ./data ${cfg.dataDir} + + # check correct folders in data folder + ${pkgs.php}/bin/php ./cli/prepare.php + # install with settings + ${pkgs.php}/bin/php ./cli/do-install.php ${settingsFlags} + ${pkgs.php}/bin/php ./cli/create-user.php --user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})" + fi + ''; + }; + + systemd.services.freshrss-updater = { + description = "FreshRSS feed updater"; + after = [ "freshrss-config.service" ]; + wantedBy = [ "multi-user.target" ]; + startAt = "*:0/5"; + environment = { + FRESHRSS_DATA_PATH = cfg.dataDir; + }; + serviceConfig = { + Type = "oneshot"; + User = "freshrss"; + Group = "freshrss"; + StateDirectory = "freshrss"; + WorkingDirectory = cfg.package; + ExecStart = "${pkgs.php}/bin/php ./app/actualize_script.php"; + } // systemd-hardening; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index b486be5fee3..7df2069327d 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -170,6 +170,7 @@ in { fluidd = handleTest ./fluidd.nix {}; fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {}; freeswitch = handleTest ./freeswitch.nix {}; + freshrss = handleTest ./freshrss.nix {}; frr = handleTest ./frr.nix {}; fsck = handleTest ./fsck.nix {}; ft2-clone = handleTest ./ft2-clone.nix {}; diff --git a/nixos/tests/freshrss.nix b/nixos/tests/freshrss.nix new file mode 100644 index 00000000000..7bdbf29e923 --- /dev/null +++ b/nixos/tests/freshrss.nix @@ -0,0 +1,19 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "freshrss"; + meta.maintainers = with lib.maintainers; [ etu stunkymonkey ]; + + nodes.machine = { pkgs, ... }: { + services.freshrss = { + enable = true; + baseUrl = "http://localhost"; + passwordFile = pkgs.writeText "password" "secret"; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.wait_for_open_port(80) + response = machine.succeed("curl -vvv -s -H 'Host: freshrss' http://127.0.0.1:80/i/") + assert 'Login ยท FreshRSS' in response, "Login page didn't load successfully" + ''; +}) -- cgit 1.4.1