feat: add postgres
We've made a postgres module which can create databases, users, etc. in
an ergonomic way
Change-Id: Ib574c96b5639797fac373a6384c450e98b25ea46
Reviewed-on: https://git.clicks.codes/c/Infra/NixFiles/+/727
Reviewed-by: Samuel Shuert <coded@clicks.codes>
Tested-by: Samuel Shuert <coded@clicks.codes>
diff --git a/lib/constants/default.nix b/lib/constants/default.nix
new file mode 100644
index 0000000..35e6034
--- /dev/null
+++ b/lib/constants/default.nix
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2024 Clicks Codes
+#
+# SPDX-License-Identifier: GPL-3.0-only
+
+{ lib, inputs, ... }:
+{
+ constants = {
+ hosts = {
+ standard = "127.0.0.1";
+ clicks = "127.0.0.2";
+ caramels = "127.0.0.3";
+ coded = "127.0.0.4";
+ minion = "127.0.0.5";
+ pinea = "127.0.0.6";
+ generic = "127.0.0.255";
+ };
+ };
+}
diff --git a/modules/nixos/clicks/services/postgres/README.md b/modules/nixos/clicks/services/postgres/README.md
new file mode 100644
index 0000000..3efd637
--- /dev/null
+++ b/modules/nixos/clicks/services/postgres/README.md
@@ -0,0 +1,36 @@
+<!--
+SPDX-FileCopyrightText: 2024 Clicks Codes
+
+SPDX-License-Identifier: GPL-3.0-only
+-->
+
+# Clicks Postgres
+
+You can create a database, user and credentials by using `clicks.services.postgres.databases.<name>`. You should set this to a file containing the password for your database user.
+
+We recommend using our secrets module to create this password file.
+
+```nix
+clicks.services.postgres = {
+ enable = true;
+ databases.headscale = config.clicks.secrets."${lib.clicks.secrets.name ./headscale.sops.json}".paths.database_password;
+};
+```
+
+---
+
+If your password file needs a specific group to read, you can use `clicks.services.postgres.secretRequiredGroups` to allow postgres to read the file.
+
+```nix
+clicks.services.postgres.secretRequiredGroups = [ "headscale" ];
+```
+
+---
+
+If you're using a systemd service to start your application, and you're runinng on the same machine as postgres, you may additionally want to ensure that postgres is always
+started and stopped in sequence with your service.
+
+```nix
+systemd.services.headscale.requires = [ "postgresql.service" ];
+systemd.services.headscale.after = [ "postgresql.service" ];
+```
diff --git a/modules/nixos/clicks/services/postgres/default.nix b/modules/nixos/clicks/services/postgres/default.nix
new file mode 100644
index 0000000..0f6b71f
--- /dev/null
+++ b/modules/nixos/clicks/services/postgres/default.nix
@@ -0,0 +1,71 @@
+# SPDX-FileCopyrightText: 2024 Clicks Codes
+#
+# SPDX-License-Identifier: GPL-3.0-only
+
+{ lib, config, ... }:
+let
+ cfg = config.clicks.services.postgres;
+in
+{
+ options.clicks.services.postgres = {
+ enable = lib.mkEnableOption "Postgresql DB";
+
+ databases = lib.mkOption {
+ type = lib.types.attrsOf lib.types.str;
+ description = "An attrset of databases to password files, these databases and users will be automatically created";
+ default = { };
+ example = {
+ headscale = "/run/secrets/headscale_db_password";
+ };
+ };
+
+ secretRequiredGroups = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "A list of groups that the postgres superuser should be given to read the password file secrets";
+ default = [ ];
+ example = [ "headscale" ];
+ };
+ };
+
+ config =
+ let
+ databasesAsList = lib.attrsets.attrsToList cfg.databases;
+ in
+ lib.mkIf cfg.enable {
+ services.postgresql = {
+ enable = true;
+
+ settings = {
+ listen_addresses = lib.mkForce lib.clicks.constants.hosts.standard;
+ log_connections = true;
+ logging_collector = true;
+ log_disconnections = true;
+ log_destination = lib.mkForce "syslog";
+ };
+
+ ensureDatabases = lib.lists.forEach databasesAsList (database: database.name);
+ ensureUsers = lib.lists.forEach databasesAsList (database: {
+ name = database.name;
+ ensureDBOwnership = true;
+ });
+
+ # method database user address auth-method
+ authentication = "host all all samenet scram-sha-256";
+ };
+
+ systemd.services.postgresql.restartTriggers = [ config.systemd.services.postgresql.postStart ];
+
+ systemd.services.postgresql.postStart = (
+ lib.pipe databasesAsList [
+ (map (database: ''
+ $PSQL -tAc "ALTER USER ${database.name} PASSWORD '$(cat ${database.value})';"
+ ''))
+ (lib.concatStringsSep "\n")
+ ]
+ );
+
+ users.users.${config.services.postgresql.superUser}.extraGroups = cfg.secretRequiredGroups;
+
+ clicks.storage.impermanence.persist.directories = [ "/var/lib/postgresql" ];
+ };
+}