diff --git a/mastus/configuration.nix b/mastus/configuration.nix index 134338c..f313dce 100644 --- a/mastus/configuration.nix +++ b/mastus/configuration.nix @@ -13,6 +13,7 @@ ./letsencrypt.nix ./blog.nix ./radicale.nix + ./ttrss.nix ]; boot.loader.grub.enable = true; diff --git a/mastus/ttrss.nix b/mastus/ttrss.nix new file mode 100644 index 0000000..75451f4 --- /dev/null +++ b/mastus/ttrss.nix @@ -0,0 +1,116 @@ +{ config, pkgs, ... }: +{ + containers.ttrss = { + config = { + users.extraUsers.ttrss = {}; + + services.postgresql = { + enable = true; + package = pkgs.postgresql95; + initialScript = pkgs.writeText "ttrss-init.sql" '' + create database ttrss; + create user ttrss with password 'ttrss'; + grant all privileges on database ttrss to ttrss; + ''; + }; + + nixpkgs.config.packageOverrides = pkgs: { tt-rss = pkgs.callPackage ./ttrss/drv.nix {}; }; + imports = [ ./ttrss/mod.nix ]; + services.tt-rss = { + enable = true; + user = "ttrss"; + + database = { + type = "pgsql"; + host = "localhost"; + name = "ttrss"; + user = "ttrss"; + password = "ttrss"; + }; + + selfUrlPath = "https://reader.gebner.org/"; + }; + + services.phpfpm = { + extraConfig = '' + error_log = /var/log/phpfpm.log + log_level = notice + ''; + + poolConfigs = { + ttrss = '' + listen = 9000 + user = ttrss + pm = dynamic + pm.max_children = 75 + pm.start_servers = 10 + pm.min_spare_servers = 5 + pm.max_spare_servers = 20 + pm.max_requests = 500 + catch_workers_output = 1 + ''; + }; + }; + + networking.firewall.allowedTCPPorts = [ 9000 ]; + }; + + autoStart = true; + hostAddress = "192.168.100.10"; + localAddress = "192.168.100.11"; + privateNetwork = true; + }; + + networking.nat.enable = true; + networking.nat.internalInterfaces = ["ve-+"]; + networking.nat.externalInterface = "eth0"; + + security.acme.certs."gebner.org".extraDomains."reader.gebner.org" = null; + + services.nginx.httpConfig = '' + server { + listen [::]:80; + listen 80; + server_name reader.gebner.org; + + location /.well-known/acme-challenge { + default_type text/plain; + alias /var/lib/acme/www/.well-known/acme-challenge; + } + + location / { + rewrite ^(.*) https://$host$1 permanent; + } + } + + server { + listen [::]:443; + listen 443; + server_name reader.gebner.org; + + ssl on; + ssl_certificate_key /var/lib/acme/gebner.org/key.pem; + ssl_certificate /var/lib/acme/gebner.org/fullchain.pem; + ssl_dhparam /etc/nginx/dhparam.pem; + ssl_protocols TLSv1.1 TLSv1.2; + ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; + ssl_prefer_server_ciphers on; + add_header Strict-Transport-Security max-age=15768000; + ssl_stapling on; + ssl_stapling_verify on; + + location / { + root /var/lib/containers/ttrss/var/lib/tt-rss; + index index.php; + } + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass 192.168.100.11:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /var/lib/tt-rss/$fastcgi_script_name; + include ${pkgs.nginx}/conf/fastcgi_params; + } + } + ''; +} diff --git a/mastus/ttrss/drv.nix b/mastus/ttrss/drv.nix new file mode 100644 index 0000000..e5bef6e --- /dev/null +++ b/mastus/ttrss/drv.nix @@ -0,0 +1,27 @@ +{ stdenv, fetchgit }: + +stdenv.mkDerivation rec { + name = "tt-rss-${version}"; + version = "16.3"; + + src = fetchgit { + url = "https://tt-rss.org/gitlab/fox/tt-rss.git"; + rev = "refs/tags/${version}"; + sha256 = "1584lcq6kcy9f8ik5djb9apck9hxvfpl54sn6yhl3pdfrfdj3nw5"; + }; + + buildPhases = ["unpackPhase" "installPhase"]; + + installPhase = '' + mkdir $out + cp -ra * $out/ + ''; + + meta = with stdenv.lib; { + description = "Web-based news feed (RSS/Atom) aggregator"; + license = licenses.gpl2Plus; + homepage = http://tt-rss.org; + platforms = platforms.all; + }; +} + diff --git a/mastus/ttrss/mod.nix b/mastus/ttrss/mod.nix new file mode 100644 index 0000000..8047bbc --- /dev/null +++ b/mastus/ttrss/mod.nix @@ -0,0 +1,502 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.tt-rss; + + configVersion = 26; + + boolToString = b: if b then "true" else "false"; + + cacheDir = "cache"; + lockDir = "lock"; + feedIconsDir = "feed-icons"; + + dbPort = if cfg.database.port == null + then (if cfg.database.type == "pgsql" then 5432 else 3306) + else cfg.database.port; + + tt-rss-config = pkgs.stdenv.mkDerivation { + name = "tt-rss-config"; + + buildCommand = let + cfgFile = pkgs.writeText "config.php" '' + System), syslog - logs to system log. + Setting this to blank uses PHP logging (usually to http server + error.log). + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.tt-rss = let + dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service"; + in { + + description = "Tiny Tiny RSS feeds update daemon"; + + preStart = let + root = "/var/lib/tt-rss"; + + callSql = if cfg.database.type == "pgsql" then (e: '' + ${optionalString (cfg.database.password != null) + "PGPASSWORD=${cfg.database.password}"} ${pkgs.postgresql95}/bin/psql \ + -U ${cfg.database.user} \ + -h ${cfg.database.host} \ + --port ${toString dbPort} \ + -c '${e}' \ + ${cfg.database.name}'') + + else if cfg.database.type == "mysql" then (e: '' + echo '${e}' | ${pkgs.mysql}/bin/mysql \ + ${optionalString (cfg.database.password != null) + "-p${cfg.database.password}"} \ + -u ${cfg.database.user} \ + -h ${cfg.database.host} \ + -P ${toString dbPort} \ + ${cfg.database.name}'') + + else ""; + + in '' + rm -rf "${root}/*" + mkdir -m 755 -p "${root}" + cp -r "${pkgs.tt-rss}/"* "${root}" + ln -sf "${tt-rss-config}" "${root}/config.php" + chown -R "${cfg.user}" "${root}" + chmod -R 755 "${root}" + '' + (optionalString (cfg.database.type == "pgsql") '' + + exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ + | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') + + if [ "$exists" == 'f' ]; then + ${callSql "\\i ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} + else + echo 'The database contains some data. Leaving it as it is.' + fi; + '') + (optionalString (cfg.database.type == "mysql") '' + + exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \ + | tail -n+2 | sed -e 's/[ \n\t]*//') + + if [ "$exists" == '0' ]; then + ${callSql "\\. ${pkgs.tt-rss}/schema/ttrss_schema_${cfg.database.type}.sql"} + else + echo 'The database contains some data. Leaving it as it is.' + fi; + ''); + + serviceConfig = { + User = "${cfg.user}"; + ExecStart = "${pkgs.php}/bin/php /var/lib/tt-rss/update.php --daemon"; + StandardOutput = "syslog"; + StandardError = "syslog"; + PermissionsStartOnly = true; + }; + + wantedBy = [ "multi-user.target" ]; + requires = ["${dbService}"]; + after = ["network.target" "${dbService}"]; + }; + }; +} + diff --git a/mastus/vmtest.nix b/mastus/vmtest.nix index df573eb..4640706 100644 --- a/mastus/vmtest.nix +++ b/mastus/vmtest.nix @@ -21,7 +21,11 @@ let ''; }; - environment.systemPackages = with pkgs; [ elinks carddav-util ]; + environment.systemPackages = with pkgs; [ elinks carddav-util fcgi ]; + + networking.extraHosts = '' + 127.0.0.1 gebner.org www.gebner.org reader.gebner.org git.gebner.org mail.gebner.org radicale.gebner.org + ''; }; nixos = import { configuration = configuration; };