3
0
Fork 0
forked from mirrors/nixpkgs

nixos/lemmy: limit impurity by secrets

Split `services.lemmy.secretFile` into
multiple options to allow only secrets.
This commit is contained in:
Eric Wolf 2023-07-04 23:49:12 +02:00 committed by Yt
parent e6f2df7798
commit 318d8cc4c5
2 changed files with 58 additions and 34 deletions

View file

@ -1,4 +1,4 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, utils, ... }:
with lib; with lib;
let let
cfg = config.services.lemmy; cfg = config.services.lemmy;
@ -41,6 +41,30 @@ in
default = null; default = null;
description = lib.mdDoc "The connection URI to use. Takes priority over the configuration file if set."; description = lib.mdDoc "The connection URI to use. Takes priority over the configuration file if set.";
}; };
uriFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc "File which contains the database uri.";
};
};
pictrsApiKeyFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc "File which contains the value of `pictrs.api_key`.";
};
smtpPasswordFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc "File which contains the value of `email.smtp_password`.";
};
adminPasswordFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc "File which contains the value of `setup.admin_password`.";
}; };
settings = mkOption { settings = mkOption {
@ -76,17 +100,20 @@ 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 =
let
secretOptions = {
pictrsApiKeyFile = { setting = [ "pictrs" "api_key" ]; path = cfg.pictrsApiKeyFile; };
smtpPasswordFile = { setting = [ "email" "smtp_password" ]; path = cfg.smtpPasswordFile; };
adminPasswordFile = { setting = [ "setup" "admin_password" ]; path = cfg.adminPasswordFile; };
uriFile = { setting = [ "database" "uri" ]; path = cfg.database.uriFile; };
};
secrets = lib.filterAttrs (option: data: data.path != null) secretOptions;
in
lib.mkIf cfg.enable { lib.mkIf cfg.enable {
services.lemmy.settings = (mapAttrs (name: mkDefault) services.lemmy.settings = lib.attrsets.recursiveUpdate (mapAttrs (name: mkDefault)
{ {
bind = "127.0.0.1"; bind = "127.0.0.1";
tls_enabled = true; tls_enabled = true;
@ -104,14 +131,15 @@ in
rate_limit.image = 6; rate_limit.image = 6;
rate_limit.image_per_second = 3600; rate_limit.image_per_second = 3600;
} // { } // {
database = mapAttrs (name: mkDefault) { database = mapAttrs (name: mkDefault) {
user = "lemmy"; user = "lemmy";
host = "/run/postgresql"; host = "/run/postgresql";
port = 5432; port = 5432;
database = "lemmy"; database = "lemmy";
pool_size = 5; pool_size = 5;
}; };
}); }) (lib.foldlAttrs (acc: option: data: acc // lib.setAttrByPath data.setting { _secret = option; }) {} secrets);
# the option name is the id of the credential loaded by LoadCredential
services.postgresql = mkIf cfg.database.createLocally { services.postgresql = mkIf cfg.database.createLocally {
enable = true; enable = true;
@ -202,16 +230,19 @@ in
assertion = (!(hasAttrByPath ["federation"] cfg.settings)) && (!(hasAttrByPath ["federation" "enabled"] cfg.settings)); assertion = (!(hasAttrByPath ["federation"] cfg.settings)) && (!(hasAttrByPath ["federation" "enabled"] cfg.settings));
message = "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect"; message = "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect";
} }
{
assertion = cfg.database.uriFile != null -> cfg.database.uri == null && !cfg.database.createLocally;
message = "specifying a database uri while also specifying a database uri file is not allowed";
}
]; ];
systemd.services.lemmy = let systemd.services.lemmy = let
configFile = settingsFormat.generate "config.hjson" cfg.settings; substitutedConfig = "/run/lemmy/config.hjson";
mergedConfig = "/run/lemmy/config.hjson";
in { in {
description = "Lemmy server"; description = "Lemmy server";
environment = { environment = {
LEMMY_CONFIG_LOCATION = if cfg.secretFile == null then configFile else mergedConfig; LEMMY_CONFIG_LOCATION = if secrets == {} then settingsFormat.generate "config.hjson" cfg.settings else substitutedConfig;
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");
}; };
@ -226,21 +257,20 @@ in
requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
path = mkIf (cfg.secretFile != null) [ pkgs.jq ]; # substitute secrets and prevent others from reading the result
# merge the two configs and prevent others from reading the result
# if somehow $CREDENTIALS_DIRECTORY is not set we fail # if somehow $CREDENTIALS_DIRECTORY is not set we fail
preStart = mkIf (cfg.secretFile != null) '' preStart = mkIf (secrets != {}) ''
set -u set -u
umask 177 umask u=rw,g=,o=
jq --slurp '.[0] * .[1]' ${lib.escapeShellArg configFile} "$CREDENTIALS_DIRECTORY/secretFile" > ${lib.escapeShellArg mergedConfig} cd "$CREDENTIALS_DIRECTORY"
${utils.genJqSecretsReplacementSnippet cfg.settings substitutedConfig}
''; '';
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}"; LoadCredential = lib.foldlAttrs (acc: option: data: acc ++ [ "${option}:${toString data.path}" ]) [] secrets;
PrivateTmp = true; PrivateTmp = true;
MemoryDenyWriteExecute = true; MemoryDenyWriteExecute = true;
NoNewPrivileges = true; NoNewPrivileges = true;

View file

@ -26,17 +26,11 @@ in
admin_email = "mightyiam@example.com"; admin_email = "mightyiam@example.com";
}; };
}; };
secretFile = /etc/lemmy-config.hjson; adminPasswordFile = /etc/lemmy-admin-password.txt;
caddy.enable = true; caddy.enable = true;
}; };
environment.etc."lemmy-config.hjson".text = '' environment.etc."lemmy-admin-password.txt".text = "ThisIsWhatIUseEverywhereTryIt";
{
"setup": {
"admin_password": "ThisIsWhatIUseEverywhereTryIt"
}
}
'';
networking.firewall.allowedTCPPorts = [ 80 ]; networking.firewall.allowedTCPPorts = [ 80 ];