diff --git a/README.md b/README.md index d3a2121..b253091 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,6 @@ nix multi user, multi system, configuration with `sops` secret management, `home ## Chores: - [ ] test out crab hole service -- [ ] qbittorent should be downloading to `rpool/persist/system/qbittorrent` or maybe even `rpool/persist/system/jellyfin` but right now its downloading to `rpool/persist/system/root` this should be fixed ## Tech Debt - [ ] monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/) @@ -66,7 +65,7 @@ nix multi user, multi system, configuration with `sops` secret management, `home - [ ] rotate sops encryption keys periodically (and somehow sync between devices?) - [ ] Secure Boot - https://github.com/nix-community/lanzaboote - [ ] auto turn off on power loss - nut -- [ ] secondary server with data sync. Maybe a Pi with a usb hdd enclosure and use rtcwake to only turn on once a week to sync data over tailscale with connection initiated from pi's side. We could probably put this at LZ. Hoping for it to draw only like $1 of power a month. Initial sync should probably be done here before we move it over because that will take a while. Data should be encrypted so that devices doesn't have access to it. Project will prob cost like $1800 +- [ ] secondary server with data sync ## Data Access - [ ] nfs export should be backed by the same values for server and client diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index e2f9401..62ab1de 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -102,6 +102,13 @@ directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; }; }; + postgres = { + extraUsers = { + leyla = { + isAdmin = true; + }; + }; + }; }; systemd.network = { @@ -213,12 +220,6 @@ }; services = { - # PostgreSQL database server - postgresql = { - enable = true; - adminUsers = ["leyla"]; - }; - # temp enable desktop environment for setup # Enable the X11 windowing system. xserver.enable = true; @@ -326,7 +327,7 @@ enable = true; domain = "home.jan-leila.com"; openFirewall = true; - postgres.enable = true; + database = "postgres"; extensions = { sonos.enable = true; diff --git a/modules/nixos-modules/server/forgejo/database.nix b/modules/nixos-modules/server/forgejo/database.nix index bb8781c..0417aab 100644 --- a/modules/nixos-modules/server/forgejo/database.nix +++ b/modules/nixos-modules/server/forgejo/database.nix @@ -2,31 +2,40 @@ lib, config, ... -}: let - usingPostgres = config.services.forgejo.database.type == "postgres"; -in { - config = lib.mkIf config.services.forgejo.enable { - assertions = [ +}: { + config = lib.mkIf config.services.forgejo.enable ( + lib.mkMerge [ { - assertion = !usingPostgres || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when Forgejo database type is postgres"; - } - { - assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (db: db == "forgejo") config.services.postgresql.ensureDatabases); - message = "Forgejo built-in database creation failed - expected 'forgejo' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (user: user.name == "forgejo") config.services.postgresql.ensureUsers); - message = "Forgejo built-in user creation failed - expected user 'forgejo' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; + host = { + postgres = { + enable = true; + }; + }; - services.forgejo.database.createDatabase = lib.mkDefault usingPostgres; - - systemd.services.forgejo = lib.mkIf usingPostgres { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; + assertions = [ + { + assertion = config.services.forgejo.settings.database.DB_TYPE == "postgres"; + message = "Forgejo database type must be postgres"; + } + ]; + } + (lib.mkIf config.host.postgres.enable { + host = { + postgres = { + extraUsers = { + forgejo = { + isClient = true; + createUser = true; + }; + }; + extraDatabases = { + forgejo = { + name = "forgejo"; + }; + }; + }; + }; + }) + ] + ); } diff --git a/modules/nixos-modules/server/home-assistant/database.nix b/modules/nixos-modules/server/home-assistant/database.nix index f1927ed..0ac8002 100644 --- a/modules/nixos-modules/server/home-assistant/database.nix +++ b/modules/nixos-modules/server/home-assistant/database.nix @@ -2,52 +2,55 @@ lib, config, ... -}: { - options.services.home-assistant = { - postgres = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Use PostgreSQL instead of SQLite"; - }; - user = lib.mkOption { - type = lib.types.str; - default = "hass"; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = "hass"; - description = "Database name"; - }; - }; - }; - - config = lib.mkIf config.services.home-assistant.enable { - assertions = [ +}: let + dbUser = "hass"; +in { + config = lib.mkIf config.services.home-assistant.enable ( + lib.mkMerge [ { - assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; - } - ]; + host = { + postgres = { + enable = true; + }; + }; - services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - enable = true; - user = config.services.home-assistant.postgres.user; - database = config.services.home-assistant.postgres.database; - }; - - services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - extraPackages = python3Packages: - with python3Packages; [ - psycopg2 + assertions = [ + { + assertion = config.services.home-assistant.database == "postgres"; + message = "Home Assistant database type must be postgres"; + } ]; - }; + } + (lib.mkIf config.host.postgres.enable { + host = { + postgres = { + extraUsers = { + ${dbUser} = { + isClient = true; + createUser = true; + }; + }; + extraDatabases = { + ${dbUser} = { + name = dbUser; + }; + }; + }; + }; - systemd.services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; + services.home-assistant = { + extraPackages = python3Packages: + with python3Packages; [ + psycopg2 + ]; + }; + + systemd.services.home-assistant = { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }) + ] + ); } diff --git a/modules/nixos-modules/server/immich/database.nix b/modules/nixos-modules/server/immich/database.nix index 52af51e..74b1aaa 100644 --- a/modules/nixos-modules/server/immich/database.nix +++ b/modules/nixos-modules/server/immich/database.nix @@ -3,28 +3,24 @@ config, ... }: { - config = lib.mkIf config.services.immich.enable { - assertions = [ - { - assertion = !config.services.immich.database.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Immich"; - } - { - assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (db: db == "immich") config.services.postgresql.ensureDatabases); - message = "Immich built-in database creation failed - expected 'immich' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers); - message = "Immich built-in user creation failed - expected user 'immich' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; - - # Note: Immich has built-in database creation via services.immich.database.createDB we only add the systemd dependency - - systemd.services.immich-server = lib.mkIf config.services.immich.database.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; + config = lib.mkIf config.services.immich.enable (lib.mkMerge [ + { + host = { + postgres = { + enable = true; + }; + }; + } + (lib.mkIf config.host.postgres.enable { + host = { + postgres = { + extraUsers = { + ${config.services.immich.database.user} = { + isClient = true; + }; + }; + }; + }; + }) + ]); } diff --git a/modules/nixos-modules/server/panoramax/database.nix b/modules/nixos-modules/server/panoramax/database.nix index 1721726..8679f9a 100644 --- a/modules/nixos-modules/server/panoramax/database.nix +++ b/modules/nixos-modules/server/panoramax/database.nix @@ -3,46 +3,32 @@ config, ... }: { - options.services.panoramax = { - database = { - postgres = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Use PostgreSQL instead of SQLite"; - }; - user = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "Database name"; + config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ + { + host = { + postgres = { + enable = true; }; }; - }; - }; - - config = lib.mkIf config.services.panoramax.enable { - assertions = [ - { - assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Panoramax"; + } + ( + lib.mkIf config.host.postgres.enable { + host = { + postgres = { + extraUsers = { + ${config.services.panoramax.database.user} = { + isClient = true; + createUser = true; + }; + }; + extraDatabases = { + ${config.services.panoramax.database.name} = { + name = config.services.panoramax.database.user; + }; + }; + }; + }; } - ]; - - services.postgresql.databases.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { - enable = true; - user = config.services.panoramax.database.postgres.user; - database = config.services.panoramax.database.postgres.database; - }; - - systemd.services.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; + ) + ]); } diff --git a/modules/nixos-modules/server/paperless/database.nix b/modules/nixos-modules/server/paperless/database.nix index c63e59d..6f4ce51 100644 --- a/modules/nixos-modules/server/paperless/database.nix +++ b/modules/nixos-modules/server/paperless/database.nix @@ -3,28 +3,32 @@ lib, ... }: { - config = lib.mkIf config.services.paperless.enable { - assertions = [ - { - assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using local postgres database for Paperless"; + config = lib.mkIf config.services.paperless.enable (lib.mkMerge [ + { + host = { + postgres = { + enable = true; + }; + }; + } + ( + lib.mkIf config.host.postgres.enable { + host = { + postgres = { + extraUsers = { + ${config.services.paperless.database.user} = { + isClient = true; + createUser = true; + }; + }; + extraDatabases = { + ${config.services.paperless.database.user} = { + name = config.services.paperless.database.user; + }; + }; + }; + }; } - { - assertion = !config.services.paperless.database.createLocally || (builtins.any (db: db == "paperless") config.services.postgresql.ensureDatabases); - message = "Paperless built-in database creation failed - expected 'paperless' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !config.services.paperless.database.createLocally || (builtins.any (user: user.name == "paperless") config.services.postgresql.ensureUsers); - message = "Paperless built-in user creation failed - expected user 'paperless' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; - - services.paperless.database.createLocally = lib.mkDefault true; - - systemd.services.paperless-scheduler = lib.mkIf config.services.paperless.database.createLocally { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; + ) + ]); } diff --git a/modules/nixos-modules/server/postgres/impermanence.nix b/modules/nixos-modules/server/postgres/impermanence.nix index a67fb1a..6c2d295 100644 --- a/modules/nixos-modules/server/postgres/impermanence.nix +++ b/modules/nixos-modules/server/postgres/impermanence.nix @@ -5,7 +5,7 @@ }: let dataDir = "/var/lib/postgresql/16"; in { - config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) { + config = lib.mkIf (config.host.postgres.enable && config.host.impermanence.enable) { assertions = [ { assertion = config.services.postgresql.dataDir == dataDir; diff --git a/modules/nixos-modules/server/postgres/postgres.nix b/modules/nixos-modules/server/postgres/postgres.nix index af7d1b4..e76857c 100644 --- a/modules/nixos-modules/server/postgres/postgres.nix +++ b/modules/nixos-modules/server/postgres/postgres.nix @@ -4,94 +4,70 @@ pkgs, ... }: let - enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases; - extraDatabasesList = config.services.postgresql.extraDatabases; - - serviceDatabaseUsers = lib.mapAttrsToList (_: db: { - name = db.user; - ensureDBOwnership = true; - }) (lib.filterAttrs (_: db: db.ensureUser) enabledDatabases); - - extraDatabaseUsers = - builtins.map (dbName: { - name = dbName; - ensureDBOwnership = true; - }) - extraDatabasesList; - - serviceDatabases = lib.mapAttrsToList (_: db: db.database) enabledDatabases; - extraDatabaseNames = extraDatabasesList; - - serviceUserMappings = lib.mapAttrsToList (_: db: "user_map ${db.user} ${db.user}") enabledDatabases; - extraUserMappings = builtins.map (dbName: "user_map ${dbName} ${dbName}") extraDatabasesList; - - builtinServiceMappings = let - forgejoMapping = lib.optional (config.services.forgejo.enable && config.services.forgejo.database.type == "postgres") "user_map forgejo forgejo"; - immichMapping = lib.optional (config.services.immich.enable && config.services.immich.database.enable) "user_map immich immich"; - paperlessMapping = lib.optional (config.services.paperless.enable && config.services.paperless.database.createLocally) "user_map paperless paperless"; - in - forgejoMapping ++ immichMapping ++ paperlessMapping; + adminUsers = lib.lists.filter (user: user.isAdmin) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); + clientUsers = lib.lists.filter (user: user.isClient) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); + createUsers = lib.lists.filter (user: user.createUser) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); + createDatabases = lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraDatabases; in { options = { - services.postgresql = { - databases = lib.mkOption { + host.postgres = { + enable = lib.mkEnableOption "enable postgres"; + extraUsers = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { options = { - enable = lib.mkOption { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + isAdmin = lib.mkOption { type = lib.types.bool; default = false; - description = "Whether to create this database and user"; }; - user = lib.mkOption { - type = lib.types.str; - default = name; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = name; - description = "Database name"; - }; - ensureUser = lib.mkOption { + isClient = lib.mkOption { type = lib.types.bool; - default = true; - description = "Whether to ensure the user exists"; + default = false; + }; + createUser = lib.mkOption { + type = lib.types.bool; + default = false; }; }; })); default = {}; - description = "Databases to create for services"; }; - extraDatabases = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Additional databases to create (user name will match database name)"; - example = ["custom_db" "test_db"]; - }; - - adminUsers = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "System users who should have PostgreSQL superuser access"; - example = ["leyla" "admin"]; + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + }; + })); + default = {}; }; }; }; - config = lib.mkIf config.services.postgresql.enable { + config = lib.mkIf config.host.postgres.enable { services = { postgresql = { + enable = true; package = pkgs.postgresql_16; - ensureUsers = [ - {name = "postgres";} + { + name = "postgres"; + } ] - ++ serviceDatabaseUsers ++ extraDatabaseUsers; - - ensureDatabases = serviceDatabases ++ extraDatabaseNames; - + ++ ( + builtins.map (user: { + name = user.name; + ensureDBOwnership = true; + }) + createUsers + ); + ensureDatabases = builtins.map (database: database.name) createDatabases; identMap = '' # ArbitraryMapName systemUser DBUser @@ -101,16 +77,16 @@ in { superuser_map postgres postgres '' + ( - lib.strings.concatLines (builtins.map (user: "superuser_map ${user} postgres") config.services.postgresql.adminUsers) + lib.strings.concatLines (builtins.map (user: "superuser_map ${user.name} postgres") adminUsers) ) + '' # Client Users '' + ( - lib.strings.concatLines (serviceUserMappings ++ extraUserMappings ++ builtinServiceMappings) + lib.strings.concatLines (builtins.map (user: "user_map ${user.name} ${user.name}") clientUsers) ); - + # configuration here lets users access the db that matches their name and lets user postgres access everything authentication = pkgs.lib.mkOverride 10 '' # type database DBuser origin-address auth-method optional_ident_map local all postgres peer map=superuser_map