Compare commits

..

No commits in common. "f9c27c82b67930a90db943587543abfe46fda406" and "f91f20be7c68920c1c52378aba82dba2a8ec472d" have entirely different histories.

9 changed files with 206 additions and 232 deletions

View file

@ -49,7 +49,6 @@ nix multi user, multi system, configuration with `sops` secret management, `home
## Chores: ## Chores:
- [ ] test out crab hole service - [ ] 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 ## 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/) - [ ] 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?) - [ ] rotate sops encryption keys periodically (and somehow sync between devices?)
- [ ] Secure Boot - https://github.com/nix-community/lanzaboote - [ ] Secure Boot - https://github.com/nix-community/lanzaboote
- [ ] auto turn off on power loss - nut - [ ] 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 ## Data Access
- [ ] nfs export should be backed by the same values for server and client - [ ] nfs export should be backed by the same values for server and client

View file

@ -102,6 +102,13 @@
directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; directories = ["leyla_documents" "eve_documents" "users_documents" "media"];
}; };
}; };
postgres = {
extraUsers = {
leyla = {
isAdmin = true;
};
};
};
}; };
systemd.network = { systemd.network = {
@ -213,12 +220,6 @@
}; };
services = { services = {
# PostgreSQL database server
postgresql = {
enable = true;
adminUsers = ["leyla"];
};
# temp enable desktop environment for setup # temp enable desktop environment for setup
# Enable the X11 windowing system. # Enable the X11 windowing system.
xserver.enable = true; xserver.enable = true;
@ -326,7 +327,7 @@
enable = true; enable = true;
domain = "home.jan-leila.com"; domain = "home.jan-leila.com";
openFirewall = true; openFirewall = true;
postgres.enable = true; database = "postgres";
extensions = { extensions = {
sonos.enable = true; sonos.enable = true;

View file

@ -2,31 +2,40 @@
lib, lib,
config, config,
... ...
}: let }: {
usingPostgres = config.services.forgejo.database.type == "postgres"; config = lib.mkIf config.services.forgejo.enable (
in { lib.mkMerge [
config = lib.mkIf config.services.forgejo.enable {
assertions = [
{ {
assertion = !usingPostgres || config.services.postgresql.enable; host = {
message = "PostgreSQL must be enabled when Forgejo database type is postgres"; postgres = {
} enable = true;
{ };
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)}";
}
];
services.forgejo.database.createDatabase = lib.mkDefault usingPostgres; assertions = [
{
systemd.services.forgejo = lib.mkIf usingPostgres { assertion = config.services.forgejo.settings.database.DB_TYPE == "postgres";
requires = [ message = "Forgejo database type must be postgres";
config.systemd.services.postgresql.name }
]; ];
}; }
}; (lib.mkIf config.host.postgres.enable {
host = {
postgres = {
extraUsers = {
forgejo = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
forgejo = {
name = "forgejo";
};
};
};
};
})
]
);
} }

View file

@ -2,52 +2,55 @@
lib, lib,
config, config,
... ...
}: { }: let
options.services.home-assistant = { dbUser = "hass";
postgres = { in {
enable = lib.mkOption { config = lib.mkIf config.services.home-assistant.enable (
type = lib.types.bool; lib.mkMerge [
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 = [
{ {
assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; host = {
message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; postgres = {
} enable = true;
]; };
};
services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { assertions = [
enable = true; {
user = config.services.home-assistant.postgres.user; assertion = config.services.home-assistant.database == "postgres";
database = config.services.home-assistant.postgres.database; message = "Home Assistant database type must be postgres";
}; }
services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable {
extraPackages = python3Packages:
with python3Packages; [
psycopg2
]; ];
}; }
(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 { services.home-assistant = {
requires = [ extraPackages = python3Packages:
config.systemd.services.postgresql.name with python3Packages; [
]; psycopg2
}; ];
}; };
systemd.services.home-assistant = {
requires = [
config.systemd.services.postgresql.name
];
};
})
]
);
} }

View file

@ -3,28 +3,24 @@
config, config,
... ...
}: { }: {
config = lib.mkIf config.services.immich.enable { config = lib.mkIf config.services.immich.enable (lib.mkMerge [
assertions = [ {
{ host = {
assertion = !config.services.immich.database.enable || config.services.postgresql.enable; postgres = {
message = "PostgreSQL must be enabled when using postgres database for Immich"; enable = true;
} };
{ };
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}"; (lib.mkIf config.host.postgres.enable {
} host = {
{ postgres = {
assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers); extraUsers = {
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)}"; ${config.services.immich.database.user} = {
} isClient = true;
]; };
};
# 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
];
};
};
} }

View file

@ -3,46 +3,32 @@
config, config,
... ...
}: { }: {
options.services.panoramax = { config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [
database = { {
postgres = { host = {
enable = lib.mkOption { postgres = {
type = lib.types.bool; enable = true;
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";
}; };
}; };
}; }
}; (
lib.mkIf config.host.postgres.enable {
config = lib.mkIf config.services.panoramax.enable { host = {
assertions = [ postgres = {
{ extraUsers = {
assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable; ${config.services.panoramax.database.user} = {
message = "PostgreSQL must be enabled when using postgres database for Panoramax"; 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
];
};
};
} }

View file

@ -3,28 +3,32 @@
lib, lib,
... ...
}: { }: {
config = lib.mkIf config.services.paperless.enable { config = lib.mkIf config.services.paperless.enable (lib.mkMerge [
assertions = [ {
{ host = {
assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable; postgres = {
message = "PostgreSQL must be enabled when using local postgres database for Paperless"; 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
];
};
};
} }

View file

@ -5,7 +5,7 @@
}: let }: let
dataDir = "/var/lib/postgresql/16"; dataDir = "/var/lib/postgresql/16";
in { in {
config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) { config = lib.mkIf (config.host.postgres.enable && config.host.impermanence.enable) {
assertions = [ assertions = [
{ {
assertion = config.services.postgresql.dataDir == dataDir; assertion = config.services.postgresql.dataDir == dataDir;

View file

@ -4,94 +4,70 @@
pkgs, pkgs,
... ...
}: let }: let
enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases; adminUsers = lib.lists.filter (user: user.isAdmin) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers);
extraDatabasesList = config.services.postgresql.extraDatabases; 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);
serviceDatabaseUsers = lib.mapAttrsToList (_: db: { createDatabases = lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraDatabases;
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;
in { in {
options = { options = {
services.postgresql = { host.postgres = {
databases = lib.mkOption { enable = lib.mkEnableOption "enable postgres";
extraUsers = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = { options = {
enable = lib.mkOption { name = lib.mkOption {
type = lib.types.str;
default = name;
};
isAdmin = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
description = "Whether to create this database and user";
}; };
user = lib.mkOption { isClient = 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 {
type = lib.types.bool; type = lib.types.bool;
default = true; default = false;
description = "Whether to ensure the user exists"; };
createUser = lib.mkOption {
type = lib.types.bool;
default = false;
}; };
}; };
})); }));
default = {}; default = {};
description = "Databases to create for services";
}; };
extraDatabases = lib.mkOption { extraDatabases = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
default = []; options = {
description = "Additional databases to create (user name will match database name)"; name = lib.mkOption {
example = ["custom_db" "test_db"]; type = lib.types.str;
}; default = name;
};
adminUsers = lib.mkOption { };
type = lib.types.listOf lib.types.str; }));
default = []; default = {};
description = "System users who should have PostgreSQL superuser access";
example = ["leyla" "admin"];
}; };
}; };
}; };
config = lib.mkIf config.services.postgresql.enable { config = lib.mkIf config.host.postgres.enable {
services = { services = {
postgresql = { postgresql = {
enable = true;
package = pkgs.postgresql_16; package = pkgs.postgresql_16;
ensureUsers = ensureUsers =
[ [
{name = "postgres";} {
name = "postgres";
}
] ]
++ serviceDatabaseUsers ++ extraDatabaseUsers; ++ (
builtins.map (user: {
ensureDatabases = serviceDatabases ++ extraDatabaseNames; name = user.name;
ensureDBOwnership = true;
})
createUsers
);
ensureDatabases = builtins.map (database: database.name) createDatabases;
identMap = identMap =
'' ''
# ArbitraryMapName systemUser DBUser # ArbitraryMapName systemUser DBUser
@ -101,16 +77,16 @@ in {
superuser_map postgres postgres 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 # 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 '' authentication = pkgs.lib.mkOverride 10 ''
# type database DBuser origin-address auth-method optional_ident_map # type database DBuser origin-address auth-method optional_ident_map
local all postgres peer map=superuser_map local all postgres peer map=superuser_map