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:
parent
e6f2df7798
commit
318d8cc4c5
|
@ -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;
|
||||||
|
|
|
@ -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 ];
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue