[Backport release-26.05] nixos/flarum: add initialAdminPasswordFile and databasePasswordFile options (#544362)

This commit is contained in:
kirillrdy
2026-07-22 01:47:17 +00:00
committed by GitHub
2 changed files with 87 additions and 8 deletions

View File

@@ -10,16 +10,23 @@ with lib;
let
cfg = config.services.flarum;
# Only placeholders reach the world-readable Nix store; the install
# script substitutes the real secrets at runtime.
flarumInstallConfig = pkgs.writeText "config.json" (
builtins.toJSON {
debug = false;
offline = false;
baseUrl = cfg.baseUrl;
databaseConfiguration = cfg.database;
databaseConfiguration =
cfg.database
// optionalAttrs (cfg.databasePasswordFile != null) {
password = "@databasePassword@";
};
adminUser = {
username = cfg.adminUser;
password = cfg.initialAdminPassword;
password =
if cfg.initialAdminPasswordFile != null then "@adminPassword@" else cfg.initialAdminPassword;
email = cfg.adminEmail;
};
settings = {
@@ -69,7 +76,26 @@ in
initialAdminPassword = mkOption {
type = types.str;
default = "flarum";
description = "Initial password for the adminUser";
description = ''
Initial password for the adminUser.
WARNING: This is stored world-readable in the Nix store.
Use {option}`initialAdminPasswordFile` instead.
'';
};
initialAdminPasswordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/secrets/flarum-admin-password";
description = ''
File containing the initial password for adminUser.
Must be readable by the flarum user.
Takes precedence over {option}`initialAdminPassword`.
The password must not contain `"` or `\` characters, as it is
substituted into a JSON installation config verbatim.
'';
};
user = mkOption {
@@ -98,7 +124,12 @@ in
bool
int
]);
description = "MySQL database parameters";
description = ''
MySQL database parameters.
WARNING: A `password` set here is stored world-readable in the
Nix store. Use {option}`databasePasswordFile` instead.
'';
default = {
# the database driver; i.e. MySQL; MariaDB...
driver = "mysql";
@@ -118,6 +149,20 @@ in
};
};
databasePasswordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/secrets/flarum-db-password";
description = ''
File containing the database password.
Must be readable by the flarum user.
Takes precedence over `database.password`.
The password must not contain `"` or `\` characters, as it is
substituted into a JSON installation config verbatim.
'';
};
createDatabaseLocally = mkOption {
type = types.bool;
default = false;
@@ -210,6 +255,8 @@ in
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
# The secret-filled install config is staged in /tmp
PrivateTmp = true;
};
path = [ config.services.phpfpm.phpPackage ];
script = ''
@@ -222,7 +269,18 @@ in
''
+ optionalString (cfg.createDatabaseLocally && cfg.database.driver == "mysql") ''
if [ ! -f config.php ]; then
php flarum install --file=${flarumInstallConfig}
install -m 0600 ${flarumInstallConfig} /tmp/flarum-install.json
${optionalString (cfg.initialAdminPasswordFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@adminPassword@' \
${escapeShellArg cfg.initialAdminPasswordFile} /tmp/flarum-install.json
''}
${optionalString (cfg.databasePasswordFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@databasePassword@' \
${escapeShellArg cfg.databasePasswordFile} /tmp/flarum-install.json
''}
php flarum install --file=/tmp/flarum-install.json
# config.php contains the database password; stateDir is world-readable
chmod 600 config.php
fi
''
+ ''

View File

@@ -10,7 +10,7 @@
};
nodes.machine =
{ ... }:
{ pkgs, ... }:
{
# Flarum installs and migrates the database on first boot and runs a
# MariaDB server alongside PHP-FPM and nginx, so give the VM some headroom.
@@ -28,8 +28,11 @@
adminUser = "admin";
adminEmail = "admin@example.com";
# Flarum rejects admin passwords shorter than 8 characters.
initialAdminPassword = "flarum-admin-password";
# The trailing newline matches how secret managers typically write files.
initialAdminPasswordFile = "${pkgs.writeText "admin-pass" "flarum-admin-password\n"}";
# MariaDB authenticates via unix socket and never checks this password;
# setting it still exercises the substitution path.
databasePasswordFile = "${pkgs.writeText "db-pass" "flarum-db-password\n"}";
};
};
@@ -48,5 +51,23 @@
# The admin API endpoint should respond, confirming the app booted cleanly.
machine.succeed("curl -sf http://localhost/api -o /dev/null")
# Only the placeholders may appear in the install config in the Nix store.
machine.succeed("grep -q '@adminPassword@' /nix/store/*-config.json")
machine.succeed("grep -q '@databasePassword@' /nix/store/*-config.json")
machine.fail("grep -qe 'flarum-admin-password' -e 'flarum-db-password' /nix/store/*-config.json")
# A successful login proves the admin password was substituted intact.
machine.succeed(
"curl -sf http://localhost/api/token "
+ "-H 'Content-Type: application/json' "
+ "-d '{\"identification\": \"admin\", \"password\": \"flarum-admin-password\"}' "
+ "| grep -F token"
)
# The database password must arrive intact in config.php, which must
# not be world-readable.
machine.succeed("grep -q 'flarum-db-password' /var/lib/flarum/config.php")
machine.succeed("[ $(stat -c %a /var/lib/flarum/config.php) = 600 ]")
'';
}