lemmy: Support secret options
This commit implements #101777 by merging the config with an external file at startup.
This commit is contained in:
parent
b5eafe654a
commit
ee5cc38432
|
@ -77,6 +77,11 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
secretFile = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc "Path to a secret JSON configuration file which is merged at runtime with the one generated from {option}`services.lemmy.settings`.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config =
|
config =
|
||||||
|
@ -197,11 +202,14 @@ in
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.lemmy = {
|
systemd.services.lemmy = let
|
||||||
|
configFile = settingsFormat.generate "config.hjson" cfg.settings;
|
||||||
|
mergedConfig = "/run/lemmy/config.hjson";
|
||||||
|
in {
|
||||||
description = "Lemmy server";
|
description = "Lemmy server";
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
LEMMY_CONFIG_LOCATION = "${settingsFormat.generate "config.hjson" cfg.settings}";
|
LEMMY_CONFIG_LOCATION = if cfg.secretFile == null then configFile else mergedConfig;
|
||||||
LEMMY_DATABASE_URL = if cfg.database.uri != null then cfg.database.uri else (mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy");
|
LEMMY_DATABASE_URL = if cfg.database.uri != null then cfg.database.uri else (mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,10 +224,24 @@ in
|
||||||
|
|
||||||
requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
|
requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
|
||||||
|
|
||||||
|
path = mkIf (cfg.secretFile != null) [ pkgs.jq ];
|
||||||
|
|
||||||
|
# merge the two configs and prevent others from reading the result
|
||||||
|
# if somehow $CREDENTIALS_DIRECTORY is not set we fail
|
||||||
|
preStart = mkIf (cfg.secretFile != null) ''
|
||||||
|
set -u
|
||||||
|
umask 177
|
||||||
|
jq --slurp '.[0] * .[1]' ${lib.escapeShellArg configFile} "$CREDENTIALS_DIRECTORY/secretFile" > ${lib.escapeShellArg mergedConfig}
|
||||||
|
'';
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
DynamicUser = true;
|
DynamicUser = true;
|
||||||
RuntimeDirectory = "lemmy";
|
RuntimeDirectory = "lemmy";
|
||||||
ExecStart = "${cfg.server.package}/bin/lemmy_server";
|
ExecStart = "${cfg.server.package}/bin/lemmy_server";
|
||||||
|
LoadCredential = mkIf (cfg.secretFile != null) "secretFile:${toString cfg.secretFile}";
|
||||||
|
PrivateTmp = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,24 @@ in
|
||||||
# Without setup, the /feeds/* and /nodeinfo/* API endpoints won't return 200
|
# Without setup, the /feeds/* and /nodeinfo/* API endpoints won't return 200
|
||||||
setup = {
|
setup = {
|
||||||
admin_username = "mightyiam";
|
admin_username = "mightyiam";
|
||||||
admin_password = "ThisIsWhatIUseEverywhereTryIt";
|
|
||||||
site_name = "Lemmy FTW";
|
site_name = "Lemmy FTW";
|
||||||
admin_email = "mightyiam@example.com";
|
admin_email = "mightyiam@example.com";
|
||||||
};
|
};
|
||||||
# https://github.com/LemmyNet/lemmy/blob/50efb1d519c63a7007a07f11cc8a11487703c70d/crates/utils/src/settings/mod.rs#L52
|
# https://github.com/LemmyNet/lemmy/blob/50efb1d519c63a7007a07f11cc8a11487703c70d/crates/utils/src/settings/mod.rs#L52
|
||||||
database.uri = "postgres:///lemmy?host=/run/postgresql&user=lemmy";
|
database.uri = "postgres:///lemmy?host=/run/postgresql&user=lemmy";
|
||||||
};
|
};
|
||||||
|
secretFile = /etc/lemmy-config.hjson;
|
||||||
caddy.enable = true;
|
caddy.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environment.etc."lemmy-config.hjson".text = ''
|
||||||
|
{
|
||||||
|
"setup": {
|
||||||
|
"admin_password": "ThisIsWhatIUseEverywhereTryIt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
|
||||||
# pict-rs seems to need more than 1025114112 bytes
|
# pict-rs seems to need more than 1025114112 bytes
|
||||||
|
@ -42,8 +50,14 @@ in
|
||||||
testScript = ''
|
testScript = ''
|
||||||
server = ${lemmyNodeName}
|
server = ${lemmyNodeName}
|
||||||
|
|
||||||
with subtest("the backend starts and responds"):
|
with subtest("the merged config is secure"):
|
||||||
server.wait_for_unit("lemmy.service")
|
server.wait_for_unit("lemmy.service")
|
||||||
|
config_permissions = server.succeed("stat --format %A /run/lemmy/config.hjson").rstrip()
|
||||||
|
assert config_permissions == "-rw-------", f"merged config permissions {config_permissions} are insecure"
|
||||||
|
directory_permissions = server.succeed("stat --format %A /run/lemmy").rstrip()
|
||||||
|
assert directory_permissions[5] == directory_permissions[8] == "-", "merged config can be replaced"
|
||||||
|
|
||||||
|
with subtest("the backend starts and responds"):
|
||||||
server.wait_for_open_port(${toString backendPort})
|
server.wait_for_open_port(${toString backendPort})
|
||||||
server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
|
server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue