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.
This commit is contained in:
stuebinm 2021-03-18 16:35:19 +01:00
parent 20398cf2c8
commit 2b944c987c
No known key found for this signature in database
GPG key ID: 8FBE8AAD32FA12B7
3 changed files with 237 additions and 0 deletions

View file

@ -21,6 +21,7 @@
../../services/gitlab-runner.nix ../../services/gitlab-runner.nix
../../services/unifi.nix ../../services/unifi.nix
../../services/lantifa.nix ../../services/lantifa.nix
../../services/limesurvey.nix
./lxc.nix ./lxc.nix
]; ];

162
modules/limesurvey.nix Normal file
View file

@ -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" ''
<?php
return json_decode('${builtins.toJSON cfg.config}', true);
?>
'';
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 <literal>php-fpm.conf</literal>
for details on configuration directives.
'';
};
config = mkOption {
type = configType;
default = {};
description = ''
LimeSurvey configuration. Refer to
<link xlink:href="https://manual.limesurvey.org/Optional_settings"/>
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;
};
};
}

74
services/limesurvey.nix Normal file
View file

@ -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;
};
}