Merge pull request #322585 from wegank/limesurvey-security

nixos/limesurvey: drop default encryption key and nonce
This commit is contained in:
Thomas Gerbet 2024-06-28 23:43:24 +02:00 committed by GitHub
commit 85be596266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 9 deletions

View File

@ -18,7 +18,15 @@ let
limesurveyConfig = pkgs.writeText "config.php" ''
<?php
return json_decode('${builtins.toJSON cfg.config}', true);
return \array_merge(
\json_decode('${builtins.toJSON cfg.config}', true),
[
'config' => [
'encryptionnonce' => \trim(\file_get_contents(\getenv('CREDENTIALS_DIRECTORY') . DIRECTORY_SEPARATOR . 'encryption_nonce')),
'encryptionsecretboxkey' => \trim(\file_get_contents(\getenv('CREDENTIALS_DIRECTORY') . DIRECTORY_SEPARATOR . 'encryption_key')),
]
]
);
?>
'';
@ -35,8 +43,9 @@ in
package = mkPackageOption pkgs "limesurvey" { };
encryptionKey = mkOption {
type = types.str;
default = "E17687FC77CEE247F0E22BB3ECF27FDE8BEC310A892347EC13013ABA11AA7EB5";
type = types.nullOr types.str;
default = null;
visible = false;
description = ''
This is a 32-byte key used to encrypt variables in the database.
You _must_ change this from the default value.
@ -44,14 +53,35 @@ in
};
encryptionNonce = mkOption {
type = types.str;
default = "1ACC8555619929DB91310BE848025A427B0F364A884FFA77";
type = types.nullOr types.str;
default = null;
visible = false;
description = ''
This is a 24-byte nonce used to encrypt variables in the database.
You _must_ change this from the default value.
'';
};
encryptionKeyFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
32-byte key used to encrypt variables in the database.
Note: It should be string not a store path in order to prevent the password from being world readable
'';
};
encryptionNonceFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
24-byte used to encrypt variables in the database.
Note: It should be string not a store path in order to prevent the password from being world readable
'';
};
database = {
type = mkOption {
type = types.enum [ "mysql" "pgsql" "odbc" "mssql" ];
@ -183,6 +213,22 @@ in
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "a password cannot be specified if services.limesurvey.database.createLocally is set to true";
}
{ assertion = cfg.encryptionKey != null || cfg.encryptionKeyFile != null;
message = ''
You must set `services.limesurvey.encryptionKeyFile` to a file containing a 32-character uppercase hex string.
If this message appears when updating your system, please turn off encryption
in the LimeSurvey interface and create backups before filling the key.
'';
}
{ assertion = cfg.encryptionNonce != null || cfg.encryptionNonceFile != null;
message = ''
You must set `services.limesurvey.encryptionNonceFile` to a file containing a 24-character uppercase hex string.
If this message appears when updating your system, please turn off encryption
in the LimeSurvey interface and create backups before filling the nonce.
'';
}
];
services.limesurvey.config = mapAttrs (name: mkDefault) {
@ -204,8 +250,6 @@ in
config = {
tempdir = "${stateDir}/tmp";
uploaddir = "${stateDir}/upload";
encryptionnonce = cfg.encryptionNonce;
encryptionsecretboxkey = cfg.encryptionKey;
force_ssl = mkIf (cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL) "on";
config.defaultlang = "en";
};
@ -229,11 +273,26 @@ in
phpPackage = pkgs.php81;
phpEnv.DBENGINE = "${cfg.database.dbEngine}";
phpEnv.LIMESURVEY_CONFIG = "${limesurveyConfig}";
# App code cannot access credentials directly since the service starts
# with the root user so we copy the credentials to a place accessible to Limesurvey
phpEnv.CREDENTIALS_DIRECTORY = "${stateDir}/credentials";
settings = {
"listen.owner" = config.services.httpd.user;
"listen.group" = config.services.httpd.group;
} // cfg.poolConfig;
};
systemd.services.phpfpm-limesurvey.serviceConfig = {
ExecStartPre = pkgs.writeShellScript "limesurvey-phpfpm-exec-pre" ''
cp -f "''${CREDENTIALS_DIRECTORY}"/encryption_key "${stateDir}/credentials/encryption_key"
chown ${user}:${group} "${stateDir}/credentials/encryption_key"
cp -f "''${CREDENTIALS_DIRECTORY}"/encryption_nonce "${stateDir}/credentials/encryption_nonce"
chown ${user}:${group} "${stateDir}/credentials/encryption_nonce"
'';
LoadCredential = [
"encryption_key:${if cfg.encryptionKeyFile != null then cfg.encryptionKeyFile else pkgs.writeText "key" cfg.encryptionKey}"
"encryption_nonce:${if cfg.encryptionNonceFile != null then cfg.encryptionNonceFile else pkgs.writeText "nonce" cfg.encryptionKey}"
];
};
services.httpd = {
enable = true;
@ -277,6 +336,7 @@ in
"d ${stateDir}/tmp/assets 0750 ${user} ${group} - -"
"d ${stateDir}/tmp/runtime 0750 ${user} ${group} - -"
"d ${stateDir}/tmp/upload 0750 ${user} ${group} - -"
"d ${stateDir}/credentials 0700 ${user} ${group} - -"
"C ${stateDir}/upload 0750 ${user} ${group} - ${cfg.package}/share/limesurvey/upload"
];
@ -295,6 +355,10 @@ in
User = user;
Group = group;
Type = "oneshot";
LoadCredential = [
"encryption_key:${if cfg.encryptionKeyFile != null then cfg.encryptionKeyFile else pkgs.writeText "key" cfg.encryptionKey}"
"encryption_nonce:${if cfg.encryptionNonceFile != null then cfg.encryptionNonceFile else pkgs.writeText "nonce" cfg.encryptionKey}"
];
};
};

View File

@ -1,6 +1,6 @@
import ./make-test-python.nix ({ pkgs, ... }: {
import ./make-test-python.nix ({ lib, pkgs, ... }: {
name = "limesurvey";
meta.maintainers = [ pkgs.lib.maintainers.aanderse ];
meta.maintainers = [ lib.maintainers.aanderse ];
nodes.machine = { ... }: {
services.limesurvey = {
@ -9,6 +9,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
hostName = "example.local";
adminAddr = "root@example.local";
};
encryptionKeyFile = pkgs.writeText "key" (lib.strings.replicate 32 "0");
encryptionNonceFile = pkgs.writeText "nonce" (lib.strings.replicate 24 "0");
};
# limesurvey won't work without a dot in the hostname