Add postgres role

main
kalle 2025-02-11 17:21:09 +01:00
parent 1b75419d21
commit 4137675a1c
8 changed files with 136 additions and 32 deletions

View File

@ -1,7 +1,7 @@
traefik: traefik:
acmeEmail: email@example.com acmeEmail: email@example.com
CLOUDFLARE_EMAIL: email@example.com CLOUDFLARE_EMAIL: email@example.com
CLOUDFLARE_DNS_API_TOKEN: AVeryLongAPIKeyHere CLOUDFLARE_DNS_API_TOKEN: AVeryLongAPIKeyHere
authentik: authentik:
db_pass: AVeryLongSecurePassword db_pass: AVeryLongSecurePassword
secret_key: AVeryLongSecretKey secret_key: AVeryLongSecretKey
@ -11,3 +11,6 @@ authentik:
email_from: mail@example.com email_from: mail@example.com
email_username: mail@example.com email_username: mail@example.com
email_password: ADifferentVeryLongSecurePassword email_password: ADifferentVeryLongSecurePassword
postgres:
# Every database used should have an entry with the password here
authentik: AVeryLongSecurePassword

View File

@ -50,6 +50,7 @@
hosts hosts
; ;
}; };
nodeSpecialArgs = builtins.mapAttrs (_: cfg: { host = cfg; }) hosts;
}; };
} }
// (nixpkgs.lib.mapAttrs ( // (nixpkgs.lib.mapAttrs (

View File

@ -8,9 +8,11 @@
ip = "192.168.10.99"; ip = "192.168.10.99";
roles = with roles; [ roles = with roles; [
postgres
podman
traefik traefik
sonarr sonarr
podman
authentik authentik
]; ];
config = { config = {

View File

@ -26,6 +26,7 @@
lib, lib,
config, config,
pkgs, pkgs,
host,
... ...
}: }:
let let
@ -55,10 +56,6 @@
# TODO: Persist some/all of this into ceph cluster # TODO: Persist some/all of this into ceph cluster
environment.persistence."/persistent" = { environment.persistence."/persistent" = {
directories = [ directories = [
{
directory = "/appdata/authentik/postgres";
mode = "0700";
}
{ {
directory = "/appdata/authentik/redis"; directory = "/appdata/authentik/redis";
mode = "0700"; mode = "0700";
@ -101,7 +98,6 @@
owner = "authentik"; owner = "authentik";
content = '' content = ''
AUTHENTIK_POSTGRESQL__PASSWORD=${config.sops.placeholder."authentik/db_pass"} AUTHENTIK_POSTGRESQL__PASSWORD=${config.sops.placeholder."authentik/db_pass"}
POSTGRES_PASSWORD=${config.sops.placeholder."authentik/db_pass"}
AUTHENTIK_SECRET_KEY="${config.sops.placeholder."authentik/secret_key"}" AUTHENTIK_SECRET_KEY="${config.sops.placeholder."authentik/secret_key"}"
AUTHENTIK_EMAIL__HOST="${config.sops.placeholder."authentik/email_host"}" AUTHENTIK_EMAIL__HOST="${config.sops.placeholder."authentik/email_host"}"
AUTHENTIK_EMAIL__PORT="${config.sops.placeholder."authentik/email_port"}" AUTHENTIK_EMAIL__PORT="${config.sops.placeholder."authentik/email_port"}"
@ -111,23 +107,11 @@
''; '';
}; };
# Create the database
postgres.databases = [ "authentik" ];
podman.containers = { podman.containers = {
# TODO: Use system postgres here instead of a separate container # TODO: Does using system redis make sense here?
"authentik-postgres" = {
image = "docker.io/library/postgres:16-alpine";
autoStart = true;
volumes = [
"/appdata/authentik/postgres:/var/lib/postgresql/data"
];
environment = {
POSTGRES_USER = "authentik";
POSTGRES_DB = "authentik";
};
environmentFiles = [
config.sops.templates."authentik-secret.env".path
publicEnv
];
};
"authentik-redis" = { "authentik-redis" = {
image = "docker.io/library/redis:7.4.2-alpine"; image = "docker.io/library/redis:7.4.2-alpine";
autoStart = true; autoStart = true;
@ -137,12 +121,11 @@
}; };
"authentik-server" = { "authentik-server" = {
image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}"; image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}";
user = "authentik";
autoStart = true; autoStart = true;
cmd = [ "server" ]; cmd = [ "server" ];
environment = { environment = {
AUTHENTIK_REDIS__HOST = "authentik-redis"; AUTHENTIK_REDIS__HOST = "authentik-redis";
AUTHENTIK_POSTGRESQL__HOST = "authentik-postgres"; AUTHENTIK_POSTGRESQL__HOST = "host.containers.internal";
AUTHENTIK_POSTGRESQL__USER = "authentik"; AUTHENTIK_POSTGRESQL__USER = "authentik";
AUTHENTIK_POSTGRESQL__NAME = "authentik"; AUTHENTIK_POSTGRESQL__NAME = "authentik";
}; };
@ -159,12 +142,12 @@
}; };
"authentik-worker" = { "authentik-worker" = {
image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}"; image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}";
user = "authentik"; user = "root";
autoStart = true; autoStart = true;
cmd = [ "worker" ]; cmd = [ "worker" ];
environment = { environment = {
AUTHENTIK_REDIS__HOST = "authentik-redis"; AUTHENTIK_REDIS__HOST = "authentik-redis";
AUTHENTIK_POSTGRESQL__HOST = "authentik-postgres"; AUTHENTIK_POSTGRESQL__HOST = "host.containers.internal";
AUTHENTIK_POSTGRESQL__USER = "authentik"; AUTHENTIK_POSTGRESQL__USER = "authentik";
AUTHENTIK_POSTGRESQL__NAME = "authentik"; AUTHENTIK_POSTGRESQL__NAME = "authentik";
}; };

View File

@ -3,8 +3,12 @@
... ...
}: }:
{ {
# Utility
postgres = utils.mkRole (import ./postgres.nix);
podman = utils.mkRole (import ./podman.nix);
# Services
sonarr = utils.mkRole (import ./sonarr.nix); sonarr = utils.mkRole (import ./sonarr.nix);
traefik = utils.mkRole (import ./traefik.nix); traefik = utils.mkRole (import ./traefik.nix);
podman = utils.mkRole (import ./podman.nix);
authentik = utils.mkRole (import ./authentik.nix); authentik = utils.mkRole (import ./authentik.nix);
} }

View File

@ -23,6 +23,8 @@
virtualisation.podman.defaultNetwork.settings.dns_enabled = true; virtualisation.podman.defaultNetwork.settings.dns_enabled = true;
virtualisation.oci-containers.backend = "podman"; virtualisation.oci-containers.backend = "podman";
# TODO: Maybe we want to pre-fetch the images during build?
# This would ensure the config always reproduces the exact same system
virtualisation.oci-containers.containers = cfg.containers; virtualisation.oci-containers.containers = cfg.containers;
}; };
}; };

107
roles/postgres.nix Normal file
View File

@ -0,0 +1,107 @@
{
name = "PostgreSQL";
description = ''
Runs a PostgreSQL database server on this host.
Other roles can use this role to create the required databases on this host through the
`postgres` attribute.
'';
nixosModule =
{
lib,
pkgs,
config,
...
}:
with lib;
{
options.postgres = {
databases = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
config =
let
cfg = config.postgres;
in
{
# Create the postgresql service
services.postgresql = {
enable = true;
enableTCPIP = true;
ensureDatabases = map (db: db) cfg.databases;
ensureUsers = map (db: {
name = db;
ensureDBOwnership = true;
ensureClauses.login = true;
}) cfg.databases;
identMap = ''
# ArbitraryMapName systemUser DBUser
superuser_map root postgres
superuser_map postgres postgres
'';
authentication = pkgs.lib.mkOverride 10 ''
# Allow local users to log into the database user with the same name
local sameuser postgres peer map=superuser_map
# Allow "md5" (password) authentication
local all all md5
host all all 0.0.0.0/0 md5
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
'';
# FIXME: For debug
settings = {
log_connections = true;
};
};
networking.firewall.allowedTCPPorts = [
5432
];
# Persist the database contents across reboots
# TODO: This should be automatically backed up daily into the CEPH
# cluster storage. It can't be on the cluster all the time, since
# CEPH doesn't perform great with databases (according to people
# online).
environment.persistence."/persistent" = {
directories = [
{
directory = "/var/lib/postgresql";
user = "postgres";
mode = "0700";
}
];
};
# Create the required secret files
sops.secrets = listToAttrs (
map (db: {
name = "postgres/${db}";
value = {
owner = "postgres";
};
}) cfg.databases
);
# Use the secret files from sops to set database user passwords
systemd.services.postgresql.postStart = concatMapStrings (db: ''
$PSQL -tA <<'EOF'
DO $$
DECLARE username TEXT;
DECLARE password TEXT;
BEGIN
username := trim(both from replace('${db}', E'\n', '''));
password := trim(both from replace(pg_read_file('${
(attrsets.getAttrFromPath [ "postgres/${db}" ] config.sops.secrets).path
}'), E'\n', '''));
EXECUTE format('ALTER ROLE %s WITH PASSWORD '''%s''';', username, password);
END $$;
EOF
'') cfg.databases;
};
};
}

View File

@ -10,6 +10,8 @@ authentik:
email_from: ENC[AES256_GCM,data:X6NP2i3uAZQFK7JdeviIMFhNPw==,iv:dwZFyzzzzFNTVfe1nhWebXrTolCa991p+vJUAOxFJf8=,tag:gClo9mZfaVFP35yZath0Nw==,type:str] email_from: ENC[AES256_GCM,data:X6NP2i3uAZQFK7JdeviIMFhNPw==,iv:dwZFyzzzzFNTVfe1nhWebXrTolCa991p+vJUAOxFJf8=,tag:gClo9mZfaVFP35yZath0Nw==,type:str]
email_username: ENC[AES256_GCM,data:c1lu5Tw6N6w96uUujSj1wHh7fQ==,iv:XX2iYXOzz8EhcZ75NlmLsasnZnCrihE9K17qS2nhAyI=,tag:qfhh3bB530IIsJwmjG20Lw==,type:str] email_username: ENC[AES256_GCM,data:c1lu5Tw6N6w96uUujSj1wHh7fQ==,iv:XX2iYXOzz8EhcZ75NlmLsasnZnCrihE9K17qS2nhAyI=,tag:qfhh3bB530IIsJwmjG20Lw==,type:str]
email_password: ENC[AES256_GCM,data:2f/LN5q/5RRIzAc8ol9RByf+RrQ=,iv:gy/UvcKzpvC0r4nQFbTYta8alzTjPWhFWCjGIw/PnuU=,tag:LLOk7NMuQ3VZ2zA779A5dw==,type:str] email_password: ENC[AES256_GCM,data:2f/LN5q/5RRIzAc8ol9RByf+RrQ=,iv:gy/UvcKzpvC0r4nQFbTYta8alzTjPWhFWCjGIw/PnuU=,tag:LLOk7NMuQ3VZ2zA779A5dw==,type:str]
postgres:
authentik: ENC[AES256_GCM,data:45DJfPHXeGyT8KDty5Po68whOVSTbT+iAfBpJ/6dKy0EeaKLKq/w1A==,iv:CtmwN+9tKmsCcU46OvBME/urkAvjEtVBqfqgs8dkkCU=,tag:j+yZfVv62IhkgF7HRT6zLQ==,type:str]
sops: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
@ -43,8 +45,8 @@ sops:
OHkvUTViMVZSUGFSeDN1ZDcxN3NtNzQK48qiEMcKbsrh8ZhnMD7lkhsy0JRMYiOU OHkvUTViMVZSUGFSeDN1ZDcxN3NtNzQK48qiEMcKbsrh8ZhnMD7lkhsy0JRMYiOU
EtXwHxEzIXukStQ9kXazfHJJouuqv7mhx12tgv+QKvrfWxCJ5WvE2A== EtXwHxEzIXukStQ9kXazfHJJouuqv7mhx12tgv+QKvrfWxCJ5WvE2A==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-02-10T22:59:35Z" lastmodified: "2025-02-11T16:20:59Z"
mac: ENC[AES256_GCM,data:uTzFEW5na0YCBLGb9k/yICjNGKWCefGgiSH8M0QZgrvRf3ioXz1W/rGUJqTmIh3QdQ45evHoj6cKSIys2gU28dR98qGX2sCBmnWYMB2oT57YLXeACsaqXKtXFsOx/YnOy8baQpckUYOwHJpM+dhKf/s348X1jfx1k8TOoNE3aj0=,iv:Jr1HvXKsxKfMBUu2r6ezodySMWduVdlPw+EIckpi5i0=,tag:fDjBfC7nKwkO3mm91iB/HQ==,type:str] mac: ENC[AES256_GCM,data:e1uqTmgVc60EHKEwsrcdh+qA2pA+Acy89DHEMCTN2eTR9hb9ya2FkEa6+X2ckgMQsngWGg/N+wAxR+wOyqLNuX1Rcb0ee3YxzaHxQZamTp09XL5IPooTNqfQdEiexPHBoJr+OtftkvsvhxmBUvh9+/VZpvnEVHwBcJAPF9KRrCI=,iv:66Uq37oID3XrRY+xcza2VNZCqhSKkAz6SJeJ3scfTmE=,tag:r6hua8+RfF99DtJsTALRpQ==,type:str]
pgp: [] pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.2 version: 3.9.2