From 2b944c987c4c321a735e3d466b895341a5935b59 Mon Sep 17 00:00:00 2001 From: stuebinm Date: Thu, 18 Mar 2021 16:35:19 +0100 Subject: [PATCH] hainich: init limesurvey Have I spent entirely too much time on this? Yes! Featuring: - a heavily modified version of the default limesurvey NixOS module - limesurvey 4.4 instead of the default limesurvey 3.23 - lots and lots of weird hacks - postgres instead of mysql - nginx instead of apache - slightly less weird module options (in my opinion) - /slightly/ fewer XSS vulnerabilities, I hope (this is still limesurvey) - kind of trivial limesurvey updates, unless upstream decides to break things again the way they did when jumping from v3 to v4 - a full copy of limesurvey in /var/lib/limesurvey, since limesurvey v4 won't run when it can write in its config dir, which is a well-defined path if and only if the entire rest of limesurvey is next to it, and the `configdir` var is NOT actually set in limesurvey's config file - no symlinks. limesurvey sees through these. --- hosts/parsons/configuration.nix | 1 + modules/limesurvey.nix | 162 ++++++++++++++++++++++++++++++++ services/limesurvey.nix | 74 +++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 modules/limesurvey.nix create mode 100644 services/limesurvey.nix diff --git a/hosts/parsons/configuration.nix b/hosts/parsons/configuration.nix index c41dfa0..545e382 100644 --- a/hosts/parsons/configuration.nix +++ b/hosts/parsons/configuration.nix @@ -21,6 +21,7 @@ ../../services/gitlab-runner.nix ../../services/unifi.nix ../../services/lantifa.nix + ../../services/limesurvey.nix ./lxc.nix ]; diff --git a/modules/limesurvey.nix b/modules/limesurvey.nix new file mode 100644 index 0000000..fb1684e --- /dev/null +++ b/modules/limesurvey.nix @@ -0,0 +1,162 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption; + inherit (lib) literalExample mapAttrs optional optionalString types; + + cfg = config.services.limesurvey-patched; + fpm = config.services.phpfpm.pools.limesurvey; + + user = "limesurvey"; + group = config.services.nginx.group; + stateDir = cfg.stateDir; + + pkg = cfg.package; + + configType = with types; oneOf [ (attrsOf configType) str int bool ] // { + description = "limesurvey config type (str, int, bool or attribute set thereof)"; + }; + + limesurveyConfig = pkgs.writeText "config.php" '' + + ''; +in +{ + # interface + + options.services.limesurvey-patched = { + enable = mkEnableOption "Limesurvey web application."; + + package = mkOption { + type = types.package; + default = pkgs.limesurvey; + description = "The Limesurvey package to use"; + }; + + domain = mkOption { + type = types.str; + default = ""; + example = "example.org"; + description = "the domain name of limesurvey"; + }; + + poolConfig = mkOption { + type = with types; attrsOf (oneOf [ str int bool ]); + default = { + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 4; + "pm.max_requests" = 500; + }; + description = '' + Options for the LimeSurvey PHP pool. See the documentation on php-fpm.conf + for details on configuration directives. + ''; + }; + + config = mkOption { + type = configType; + default = {}; + description = '' + LimeSurvey configuration. Refer to + + for details on supported values. + ''; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/lib/limesurvey"; + description = '' + LimeSurvey keeps its state in here. For a variety of stupid php reasons, it will also keep a complete + copy of itself in here. + ''; + }; + }; + + # implementation + + config = mkIf cfg.enable { + + services.limesurvey-patched.config = mapAttrs (name: mkDefault) { + runtimePath = "${stateDir}/tmp/runtime"; + components = { + assetManager.basePath = "${stateDir}/tmp/assets"; + urlManager = { + urlFormat = "path"; + showScriptName = false; + }; + }; + }; + + services.phpfpm.pools.limesurvey = { + inherit user group; + phpEnv.LIMESURVEY_CONFIG = "/var/lib/limesurvey/config/config.php"; + settings = { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + } // cfg.poolConfig; + }; + + services.nginx = { + enable = true; + + virtualHosts.${cfg.domain} = { + root = stateDir; + locations."/".tryFiles = "$uri /index.php?$args"; + + locations."~ [^/]\\.php(/|$)".extraConfig = '' + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + if (!-f $document_root$fastcgi_script_name) { + return 404; + } + fastcgi_pass unix:${fpm.socket}; + fastcgi_index index.php; + ''; + }; + }; + + systemd.tmpfiles.rules = [ + "C ${stateDir} 0750 ${user} ${group} - ${pkg}/share/limesurvey" + "d ${stateDir}/tmp 0750 ${user} ${group} - -" + "d ${stateDir}/tmp/assets 0750 ${user} ${group} - -" + "d ${stateDir}/tmp/runtime 0750 ${user} ${group} - -" + "d ${stateDir}/tmp/upload 0750 ${user} ${group} - -" + ]; + + systemd.services.limesurvey-init = { + wantedBy = [ "multi-user.target" ]; + before = [ "phpfpm-limesurvey.service" ]; + after = [ "postgresql.service" ]; + environment.LIMESURVEY_CONFIG = "/var/lib/limesurvey/config/config.php"; + script = '' + + chmod -R +w ${stateDir} + mkdir -p ${stateDir}/config + cp -f ${limesurveyConfig} ${stateDir}/config/config.php + + # update or install the database as required + ${pkgs.php}/bin/php /var/lib/limesurvey/application/commands/console.php updatedb || \ + ${pkgs.php}/bin/php /var/lib/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose + ''; + serviceConfig = { + User = user; + Group = group; + Type = "oneshot"; + }; + }; + + #systemd.services.httpd.after = [ "postgresql.service" ]; + systemd.services.nginx.after = [ "postgresql.service" ]; + + users.users.${user} = { + group = group; + isSystemUser = true; + }; + }; +} diff --git a/services/limesurvey.nix b/services/limesurvey.nix new file mode 100644 index 0000000..75d5e20 --- /dev/null +++ b/services/limesurvey.nix @@ -0,0 +1,74 @@ +{config, pkgs, lib, ...}: + +{ + + containers.limesurvey = { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.100.40"; + localAddress = "192.168.100.41"; + + config = {config, pkgs, lib, ...}: { + imports = [ ./../modules/limesurvey.nix ]; + + services.limesurvey-patched = { + enable = true; + domain = "localhost"; + + config = { + name = "LimeSurvey"; + components = { + db = { + connectionString = "pgsql:dbname=limesurvey;host=localhost;port=5432;user=limesurvey"; + username = "limesurvey"; + tablePrefix = "limesurvey_"; + }; + assetManager.basePath = "/var/lib/limesurvey/tmp/assets"; + urlManager = { + urlFormat = "path"; + showScriptName = false; + }; + }; + config = { + siteadminemail = "info@infra4future.de"; + defaultlang = "de"; + }; + }; + + package = pkgs.limesurvey.overrideAttrs (old: rec { + version = "4.4.12+210308"; + src = pkgs.fetchFromGitHub { + owner = "LimeSurvey"; + repo = "LimeSurvey"; + rev = version; + sha256 = "0kjya8if751mh35symzas186ya27nq62adzp2j58agd5ssrb2a8f"; + }; + meta.knownVulnerabilities = []; + }); + }; + + + services.postgresql = { + enable = true; + ensureDatabases = [ "limesurvey" ]; + ensureUsers = [ { + name = "limesurvey"; + ensurePermissions = { "DATABASE limesurvey" = "ALL PRIVILEGES"; }; + } ]; + + authentication = lib.mkForce '' + # Generated file; do not edit! + local all all trust + host limesurvey limesurvey ::1/128 trust + ''; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + + services.nginx.virtualHosts."survey.infra4future.de" = { + locations."/".proxyPass = "http://${config.containers.limesurvey.localAddress}"; + enableACME = true; + forceSSL = true; + }; +}