Wolfgang Walther 41c5662cbe
nixos/postgresql: move postStart into separate unit
This avoids restarting the postgresql server, when only ensureDatabases
or ensureUsers have been changed. It will also allow to properly wait
for recovery to finish later.

To wait for "postgresql is ready" in other services, we now provide a
postgresql.target.

Resolves #400018

Co-authored-by: Marcel <me@m4rc3l.de>
2025-06-24 15:26:47 +02:00

171 lines
5.1 KiB
Nix

{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.services.dex;
fixClient =
client:
if client ? secretFile then
(
(builtins.removeAttrs client [ "secretFile" ])
// {
secret = client.secretFile;
}
)
else
client;
filteredSettings = mapAttrs (
n: v: if n == "staticClients" then (builtins.map fixClient v) else v
) cfg.settings;
secretFiles = flatten (
builtins.map (c: optional (c ? secretFile) c.secretFile) (cfg.settings.staticClients or [ ])
);
settingsFormat = pkgs.formats.yaml { };
configFile = settingsFormat.generate "config.yaml" filteredSettings;
startPreScript = pkgs.writeShellScript "dex-start-pre" (
concatStringsSep "\n" (
map (file: ''
replace-secret '${file}' '${file}' /run/dex/config.yaml
'') secretFiles
)
);
restartTriggers =
[ ]
++ (optionals (cfg.environmentFile != null) [ cfg.environmentFile ])
++ (filter (file: builtins.typeOf file == "path") secretFiles);
in
{
options.services.dex = {
enable = mkEnableOption "the OpenID Connect and OAuth2 identity provider";
package = mkPackageOption pkgs "dex-oidc" { };
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file (see {manpage}`systemd.exec(5)`
"EnvironmentFile=" section for the syntax) to define variables for dex.
This option can be used to safely include secret keys into the dex configuration.
'';
};
settings = mkOption {
type = settingsFormat.type;
default = { };
example = literalExpression ''
{
# External url
issuer = "http://127.0.0.1:5556/dex";
storage = {
type = "postgres";
config.host = "/var/run/postgres";
};
web = {
http = "127.0.0.1:5556";
};
enablePasswordDB = true;
staticClients = [
{
id = "oidcclient";
name = "Client";
redirectURIs = [ "https://example.com/callback" ];
secretFile = "/etc/dex/oidcclient"; # The content of `secretFile` will be written into to the config as `secret`.
}
];
}
'';
description = ''
The available options can be found in
[the example configuration](https://github.com/dexidp/dex/blob/v${cfg.package.version}/config.yaml.dist).
It's also possible to refer to environment variables (defined in [services.dex.environmentFile](#opt-services.dex.environmentFile))
using the syntax `$VARIABLE_NAME`.
'';
};
};
config = mkIf cfg.enable {
systemd.services.dex = {
description = "dex identity provider";
wantedBy = [ "multi-user.target" ];
after = [
"networking.target"
] ++ (optional (cfg.settings.storage.type == "postgres") "postgresql.target");
path = with pkgs; [ replace-secret ];
restartTriggers = restartTriggers;
serviceConfig =
{
ExecStart = "${cfg.package}/bin/dex serve /run/dex/config.yaml";
ExecStartPre = [
"${pkgs.coreutils}/bin/install -m 600 ${configFile} /run/dex/config.yaml"
"+${startPreScript}"
];
RuntimeDirectory = "dex";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/dex"
"-/etc/hosts"
"-/etc/localtime"
"-/etc/nsswitch.conf"
"-/etc/resolv.conf"
"${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
];
BindPaths = optional (cfg.settings.storage.type == "postgres") "/var/run/postgresql";
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = "";
DynamicUser = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
# Port needs to be exposed to the host network
#PrivateNetwork = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectHome = true;
ProtectHostname = true;
ProtectSystem = "strict";
ProtectControlGroups = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged @setuid @keyring"
];
UMask = "0066";
}
// optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};
};
# uses attributes of the linked package
meta.buildDocsInSandbox = false;
}