diff --git a/modules/services/default.nix b/modules/services/default.nix index 1b9ec11..9736553 100644 --- a/modules/services/default.nix +++ b/modules/services/default.nix @@ -3,6 +3,7 @@ ./media-mgr.nix ./nextcloud.nix ./plex.nix + ./postgres.nix # ./searx.nix ./vaultwarden.nix ]; diff --git a/modules/services/postgres.nix b/modules/services/postgres.nix new file mode 100644 index 0000000..d482a26 --- /dev/null +++ b/modules/services/postgres.nix @@ -0,0 +1,177 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.my.services.postgresql; + + upgrade-script = + old: new: + let + oldStr = builtins.toString old; + newStr = builtins.toString new; + + oldPkg = pkgs.${"postgresql_${oldStr}"}; + newPkg = pkgs.${"postgresql_${newStr}"}; + in + pkgs.writeScriptBin "upgrade-pg-cluster-${oldStr}-${newStr}" '' + set -eux + # XXX it's perhaps advisable to stop all services that depend on postgresql + systemctl stop postgresql + + export NEWDATA="/var/lib/postgresql/${newPkg.psqlSchema}" + export NEWBIN="${newPkg}/bin" + + export OLDDATA="/var/lib/postgresql/${oldPkg.psqlSchema}" + export OLDBIN="${oldPkg}/bin" + + install -d -m 0700 -o postgres -g postgres "$NEWDATA" + cd "$NEWDATA" + sudo -u postgres $NEWBIN/initdb -D "$NEWDATA" + + sudo -u postgres $NEWBIN/pg_upgrade \ + --old-datadir "$OLDDATA" --new-datadir "$NEWDATA" \ + --old-bindir $OLDBIN --new-bindir $NEWBIN \ + "$@" + ''; +in +{ + options.my.services.postgresql = { + + enable = lib.mkEnableOption "Enable Postgres module"; + + debug = lib.mkOption { + type = lib.types.bool; + description = '' + Enable debugging options. + + Currently enables shared_preload_libraries = "auto_explain, pg_stat_statements" + + See https://www.postgresql.org/docs/current/pgstatstatements.html''; + default = false; + }; + + enableTCPIP = lib.mkOption { + type = lib.types.bool; + description = "Enable TCP/IP connection on given port."; + default = false; + }; + + ensures = lib.mkOption { + description = "List of username, database and/or passwords that should be created."; + type = lib.types.listOf ( + lib.types.submodule { + options = { + username = lib.mkOption { + type = lib.types.str; + description = "Postgres user name."; + }; + + database = lib.mkOption { + type = lib.types.str; + description = "Postgres database."; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "Optional password file for the postgres user. If not given, only peer auth is accepted for this user, otherwise password auth is allowed."; + default = null; + example = "/run/secrets/postgresql/password"; + }; + }; + } + ); + default = [ ]; + }; + }; + + config = + let + commonConfig = { + systemd.services.postgresql.serviceConfig.Restart = "always"; + + services.postgresql.settings = { + }; + }; + + tcpConfig = { + services.postgresql.enableTCPIP = true; + services.postgresql.authentication = lib.mkOverride 10 '' + #type database DBuser origin-address auth-method + local all all peer + # ipv4 + host all all 127.0.0.1/32 password + # ipv6 + host all all ::1/128 password + ''; + }; + + dbConfig = ensureCfgs: { + services.postgresql.enable = lib.mkDefault ((builtins.length ensureCfgs) > 0); + services.postgresql.ensureDatabases = map ({ database, ... }: database) ensureCfgs; + services.postgresql.ensureUsers = map ( + { username, database, ... }: + { + name = username; + ensureDBOwnership = true; + ensureClauses.login = true; + } + ) ensureCfgs; + services.postgresql.authentication = pkgs.lib.mkOverride 10 '' + #type database DBuser auth-method + local all all trust + ''; + }; + + pwdConfig = ensureCfgs: { + systemd.services.postgresql.postStart = + let + prefix = '' + $PSQL -tA <<'EOF' + DO $$ + DECLARE password TEXT; + BEGIN + ''; + suffix = '' + END $$; + EOF + ''; + exec = + { username, passwordFile, ... }: + '' + password := trim(both from replace(pg_read_file('${passwordFile}'), E'\n', ''')); + EXECUTE format('ALTER ROLE ${username} WITH PASSWORD '''%s''';', password); + ''; + cfgsWithPasswords = builtins.filter (cfg: cfg.passwordFile != null) ensureCfgs; + in + if (builtins.length cfgsWithPasswords) == 0 then + "" + else + prefix + (lib.concatStrings (map exec cfgsWithPasswords)) + suffix; + }; + + debugConfig = + enableDebug: + lib.mkIf enableDebug { + services.postgresql.settings.shared_preload_libraries = "auto_explain, pg_stat_statements"; + }; + in + lib.mkIf cfg.enable ( + lib.mkMerge ([ + commonConfig + (dbConfig cfg.ensures) + (pwdConfig cfg.ensures) + (lib.mkIf cfg.enableTCPIP tcpConfig) + (debugConfig cfg.debug) + { + environment.systemPackages = lib.mkIf config.services.postgresql.enable [ + (upgrade-script 14 15) + (upgrade-script 15 16) + (upgrade-script 16 17) + ]; + } + ]) + ); +} diff --git a/modules/services/vaultwarden.nix b/modules/services/vaultwarden.nix index 33df8af..1741285 100644 --- a/modules/services/vaultwarden.nix +++ b/modules/services/vaultwarden.nix @@ -6,8 +6,7 @@ }: let cfg = config.my.services.vaultwarden; - user = config.users.users.vaultwarden.name; - group = config.users.groups.vaultwarden.name; + rocketPort = 8222; in { @@ -41,6 +40,16 @@ in age.secrets.vaultwarden-admin-pwd.file = ../../secrets/vaultwarden-admin-pwd.age; + my.services.postgresql = { + enable = true; + ensures = [ + { + username = "vaultwarden"; + database = "vaultwarden"; + } + ]; + }; + services.vaultwarden = { enable = true; dbBackend = "postgresql"; @@ -51,16 +60,23 @@ in SIGNUPS_ALLOWED = false; WEBSOCKET_ENABLED = true; ROCKET_ADDRESS = "0.0.0.0"; - ROCKET_PORT = 8222; + ROCKET_PORT = rocketPort; + DATABASE_URL = "postgresql:///vaultwarden?host=/run/postgresql"; + SMTP_HOST = "smtp.eu.mailgun.org"; + SMTP_FROM = "vault@pazpi.top"; + SMTP_SECURITY = "starttls"; + SMTP_USERNAME = "vault@pazpi.top"; }; }; + networking.firewall.allowedTCPPorts = [ rocketPort ]; + }) (lib.mkIf cfg.proxy.enable { services.caddy = with cfg.proxy; { virtualHosts."vault.${domain}".extraConfig = '' - reverse_proxy http://${host}:80 + reverse_proxy http://${host}:${toString rocketPort} import cloudflare ''; };