Add postgres role
parent
1b75419d21
commit
4137675a1c
|
@ -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
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
hosts
|
hosts
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
nodeSpecialArgs = builtins.mapAttrs (_: cfg: { host = cfg; }) hosts;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// (nixpkgs.lib.mapAttrs (
|
// (nixpkgs.lib.mapAttrs (
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue