feat(homelab): implement SSO

This commit is contained in:
Nickolaj Jepsen 2025-04-26 20:02:56 +02:00
parent db85aeb044
commit ab6f8e21dc
17 changed files with 245 additions and 48 deletions

View file

@ -12,18 +12,15 @@
locations."/" = { locations."/" = {
proxyPass = "http://localhost:${toString port}"; proxyPass = "http://localhost:${toString port}";
}; };
basicAuthFile = "${config.age.secrets.arr-basic-auth.path}"; locations."/api" = {
proxyPass = "http://localhost:${toString port}";
extraConfig = ''
auth_request off;
'';
};
}; };
in { in {
# for linux ISOs # for linux ISOs
age.secrets = {
arr-basic-auth = {
rekeyFile = ../../secrets/hosts/homelab/basic-auth.age;
owner = config.services.nginx.user;
inherit (config.services.nginx) group;
};
};
users.groups."${group}" = { users.groups."${group}" = {
members = [username]; members = [username];
}; };
@ -33,19 +30,37 @@ in {
}; };
services = { services = {
oauth2-proxy.nginx.virtualHosts = {
"radarr.nickolaj.com".allowed_groups = ["arr"];
"sonarr.nickolaj.com".allowed_groups = ["arr"];
"prowlarr.nickolaj.com".allowed_groups = ["arr"];
"sabnzbd.nickolaj.com".allowed_groups = ["arr"];
"bazarr.nickolaj.com".allowed_groups = ["arr"];
};
nginx.virtualHosts = { nginx.virtualHosts = {
"radarr.nickolaj.com" = mkVirtualHost 7878; "radarr.nickolaj.com" = mkVirtualHost 7878;
"sonarr.nickolaj.com" = mkVirtualHost 8989; "sonarr.nickolaj.com" = mkVirtualHost 8989;
"prowlarr.nickolaj.com" = mkVirtualHost 9696; "prowlarr.nickolaj.com" = mkVirtualHost 9696;
"sabnzbd.nickolaj.com" = mkVirtualHost 8080; "sabnzbd.nickolaj.com" = mkVirtualHost 8080;
"bazarr.nickolaj.com" = mkVirtualHost config.services.bazarr.listenPort;
}; };
restic.backups.homelab.paths = [ restic.backups.homelab = {
paths = [
"/var/lib/radarr" "/var/lib/radarr"
"/var/lib/sonarr" "/var/lib/sonarr"
"/var/lib/prowlarr" "/var/lib/prowlarr"
"/var/lib/sabnzbd" "/var/lib/sabnzbd"
"/var/lib/bazarr"
]; ];
exclude = [
# arrs logs and media cover
"/var/lib/*/.config/*/logs/"
"/var/lib/*/.config/*/MediaCover/"
"/var/lib/sabnzbd/Downloads/"
"/var/lib/sabnzbd/logs/"
];
};
sabnzbd = { sabnzbd = {
inherit user group; inherit user group;
@ -59,6 +74,10 @@ in {
inherit user group; inherit user group;
enable = true; enable = true;
}; };
bazarr = {
inherit user group;
enable = true;
};
prowlarr.enable = true; prowlarr.enable = true;
}; };
} }

View file

@ -2,7 +2,10 @@ _: let
dataDir = "/var/lib/flame"; dataDir = "/var/lib/flame";
domain = "flame.nickolaj.com"; domain = "flame.nickolaj.com";
in { in {
services.restic.backups.homelab.paths = [dataDir]; services.restic.backups.homelab = {
paths = [dataDir];
exclude = ["/var/lib/flame/db_backups"];
};
services.nginx.virtualHosts."${domain}" = { services.nginx.virtualHosts."${domain}" = {
enableACME = true; enableACME = true;

View file

@ -14,11 +14,6 @@ in {
owner = "zigbee2mqtt"; owner = "zigbee2mqtt";
group = "zigbee2mqtt"; group = "zigbee2mqtt";
}; };
z2m-basic-auth = {
rekeyFile = ../../secrets/hosts/homelab/basic-auth.age;
owner = config.services.nginx.user;
inherit (config.services.nginx) group;
};
mosquitto-zigbee2mqtt.rekeyFile = ../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age; mosquitto-zigbee2mqtt.rekeyFile = ../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age;
mosquitto-sas.rekeyFile = ../../secrets/hosts/homelab/mosquitto-sas.age; mosquitto-sas.rekeyFile = ../../secrets/hosts/homelab/mosquitto-sas.age;
mosquitto-ha.rekeyFile = ../../secrets/hosts/homelab/mosquitto-ha.age; mosquitto-ha.rekeyFile = ../../secrets/hosts/homelab/mosquitto-ha.age;
@ -29,11 +24,17 @@ in {
]; ];
services = { services = {
restic.backups.homelab.paths = [ restic.backups.homelab = {
paths = [
config.services.zigbee2mqtt.dataDir config.services.zigbee2mqtt.dataDir
config.services.home-assistant.configDir config.services.home-assistant.configDir
]; ];
exclude = [
"/var/lib/zigbee2mqtt/log/"
];
};
oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"];
nginx.virtualHosts = { nginx.virtualHosts = {
"zigbee.nickolaj.com" = { "zigbee.nickolaj.com" = {
enableACME = true; enableACME = true;
@ -42,7 +43,6 @@ in {
proxyPass = "http://localhost:${toString zigbee2mqttPort}"; proxyPass = "http://localhost:${toString zigbee2mqttPort}";
proxyWebsockets = true; proxyWebsockets = true;
}; };
basicAuthFile = "${config.age.secrets.z2m-basic-auth.path}";
}; };
"ha.nickolaj.com" = { "ha.nickolaj.com" = {
enableACME = true; enableACME = true;

View file

@ -1,13 +0,0 @@
{
config,
pkgsUnstable,
...
}: {
age.secrets.netdata-claim-token.rekeyFile = ../../secrets/netdata-claim-token.age;
services.netdata = {
enable = true;
package = pkgsUnstable.netdataCloud;
claimTokenFile = "${config.age.secrets.netdata-claim-token.path}";
};
}

View file

@ -27,6 +27,9 @@
adminpassFile = "${config.age.secrets.nextcloud-admin-pass.path}"; adminpassFile = "${config.age.secrets.nextcloud-admin-pass.path}";
dbtype = "pgsql"; dbtype = "pgsql";
}; };
extraApps = {
inherit (config.services.nextcloud.package.packages.apps) sociallogin;
};
}; };
}; };
} }

View file

@ -3,7 +3,11 @@ _: {
services.nginx = { services.nginx = {
enable = true; enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true; recommendedProxySettings = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
}; };
security.acme = { security.acme = {
acceptTerms = true; acceptTerms = true;

View file

@ -0,0 +1,52 @@
{
config,
hostname,
...
}: let
mkScrapeConfig = name: {
job_name = name;
static_configs = [
{
labels = {
instance = hostname;
};
targets = [
"${toString config.services.prometheus.exporters.${name}.listenAddress}:${toString config.services.prometheus.exporters.${name}.port}"
];
}
];
};
in {
age.secrets.grafana-cloud-prometheus-api-key = {
rekeyFile = ../../secrets/grafana-cloud-prometheus.age;
owner = "prometheus";
group = "prometheus";
};
services.prometheus = {
enable = true;
enableAgentMode = true;
globalConfig.scrape_interval = "1m";
remoteWrite = [
{
url = "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push";
basic_auth = {
username = "432120";
password_file = "${config.age.secrets.grafana-cloud-prometheus-api-key.path}";
};
}
];
scrapeConfigs = [
(mkScrapeConfig "node")
];
exporters.node = {
enable = true;
extraFlags = [
"--web.disable-exporter-metrics"
];
};
};
}

105
hosts/homelab/sso.nix Normal file
View file

@ -0,0 +1,105 @@
{config, pkgsUnstable, ...}: let
port = 9190;
rootDomain = "nickolaj.com";
zitadelDomain = "sso.${rootDomain}";
oathproxyDomain = "oauth2-proxy.${rootDomain}";
in {
age.secrets.zitadel-master = {
rekeyFile = ../../secrets/hosts/homelab/zitadel-master.age;
owner = config.services.zitadel.user;
};
age.secrets.oauth2-proxy = {
rekeyFile = ../../secrets/hosts/homelab/oauth2-proxy-keyfile.age;
owner = "oauth2-proxy";
};
services.nginx.virtualHosts."${zitadelDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
grpc_pass grpc://127.0.0.1:${toString port};
grpc_set_header Host $host:$server_port;
'';
};
};
services.nginx.virtualHosts."${oathproxyDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://127.0.0.1:4180";
};
};
services.postgresql = {
ensureDatabases = ["zitadel"];
ensureUsers = [
{
name = "zitadel";
ensureDBOwnership = true;
ensureClauses.login = true;
ensureClauses.superuser = true;
}
];
};
services.zitadel = {
enable = true;
package = pkgsUnstable.zitadel;
masterKeyFile = config.age.secrets.zitadel-master.path;
settings = {
Port = port;
Database.postgres = {
Host = "/var/run/postgresql/";
Port = 5432;
Database = "zitadel";
User = {
Username = "zitadel";
SSL.Mode = "disable";
};
Admin = {
Username = "zitadel";
SSL.Mode = "disable";
ExistingDatabase = "zitadel";
};
};
ExternalDomain = zitadelDomain;
ExternalPort = 443;
ExternalSecure = true;
};
steps.FirstInstance = {
InstanceName = "Fireproof Auth";
Org = {
Name = "Fireproof Auth";
Human = {
UserName = "nickolaj1177@gmail.com";
FirstName = "Nickolaj";
LastName = "Jepsen";
Email.Verified = true;
Password = "Password1!";
PasswordChangeRequired = true;
};
};
LoginPolicy.AllowRegister = false;
};
};
services.oauth2-proxy = {
enable = true;
package = pkgsUnstable.oauth2-proxy;
provider = "oidc";
reverseProxy = true;
redirectURL = "https://${oathproxyDomain}/oauth2/callback";
validateURL = "https://${zitadelDomain}/oauth2/";
oidcIssuerUrl = "https://${zitadelDomain}:443";
keyFile = config.age.secrets.oauth2-proxy.path;
nginx.domain = oathproxyDomain;
email.domains = ["*"];
extraConfig = {
whitelist-domain = ".${rootDomain}";
cookie-domain = ".${rootDomain}";
};
};
}

View file

@ -11,7 +11,13 @@ in {
ROCKET_PORT = 8222; ROCKET_PORT = 8222;
}; };
}; };
restic.backups.homelab.paths = ["/var/lib/vaultwarden"]; restic.backups.homelab = {
paths = ["/var/lib/vaultwarden"];
exclude = [
"/var/lib/vaultwarden/icon_cache"
"/var/lib/vaultwarden/tmp"
];
};
nginx.virtualHosts."${domain}" = { nginx.virtualHosts."${domain}" = {
enableACME = true; enableACME = true;

Binary file not shown.

View file

@ -1,7 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 uxq+Zw O7Lgl8nCBN+7PkfnDJg1QlJuJsIWHsC0ph5HRvc+U3E
bdllRkmn3RDwg1DVPSHLtYWfay/Y+hUeCtdiCYc1rDM
-> JC5p)q|.-grease J<5Gb -#' }(d &;SYO
9sFJvUrPneffSRN4a5VLMZBMJYluYh7efNGdJHnEnDLS2NJt33px
--- Ct3YI+Fb2sCve9NRhIMZfAlDEN0jANs6kJREaSeRxUo
IÇ|#+ÎR :Êù õËO¤FÞY\ìÝΤcË|ÃXéÓ²¸Fm˰˜X«¼©/Oâȃ]Q<>¡5<>Ýtë!ö¡vÂSîVRÜM»˜×<C397>42ocüözMFX »Ý½Ñ¡+ÿ™€Þ$#»Ii<49>üóaÇ(¾ÚK<C39A><4B>8û™Ï¦¡×̦Ùñi£ ñø2þô2ó}ÐЦO°Ý@ zñqÕPº

View file

@ -0,0 +1,8 @@
age-encryption.org/v1
-> ssh-ed25519 uxq+Zw QFvSKN7maaWY8fYzwxBASBvTXg55zUhkZLJZVeQCBSg
tNvUOsiH0p1QfaAFbj1XqNmOUijykTaEdmxohFuNqlQ
-> tqn,r-grease FV& }tddM
vfFh7GoVlDh4YaZkFvROgw
--- SCregMmWnU6Ihuzv8E14/jnXY3Kd09235BvQZXePLRI
<EFBFBD>d6ÿȤ¤ÎN/,€—na<6E> Ý儌 RÌì$çÂ4HãU±'Q¯
<EFBFBD>xA[s¤—çv·2 »9{<7B>øÚÜ ”èVl=ÿ)æX~DÝA[¹Ÿuðn…² ѰiÄÿßûßÚ¶<C39A>ø<EFBFBD>ŒtÜëMXÒmklu]-«)<29>߬¥‰*i˜ ”ß 런ѿ´$½<ÁÑcL,¾ªµ-{¬ò»Ø<Í<>

View file

@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 uxq+Zw g1Pw9Cgly2Wy9jiaKGqzVCJOuDDPfxfxDLe0XIyTtgU
vnYYw7sfYFc5hBI8ub5hzEXJaK04vno+uUChpkd1g9s
-> q,.q!V-grease 9$zhS U^zTl
nJEewmCjlrNA+8UTjveja2AYbl481OTdb6JkaZMr
--- rw1uP9RF+ehWosrklvIK9CbcKbwc9founpsG0XEXcgk
Z _8x†<EFBFBD>O<EFBFBD>e¯}²êklK¸ÏÑË l>qžxŠŒÔd;ÐÛÎz&-µœ<>º —>ÕÓ`÷0å_¬¥¼æ£3

Binary file not shown.

View file

@ -0,0 +1,10 @@
age-encryption.org/v1
-> X25519 8fy15guw0en6anMwXtYtgNORD8Lj5RiymCgYZqMPoHo
AQJP0q/gkG/JbwuUCCeILemcGM8sgXVsPzTyZK2Gm14
-> piv-p256 q3LNVw A+5W/lITCh0xb5xDxwQQWCjfhKrAEFU1Ix/SwbKjGxSG
kSentadMboREK630i0dAp/NWcgta2BDO878p6Rxg1vU
-> ?/=3-grease x7td
3S2C9b7mavQzhSWlkEzioYuD2+HmjkXdiyKMgkd1KwUw6Jz/FoT6+skE5wXVT0jA
q9B0aa66NhAoT7WKxlqvrUJPnjq9QuFeV7UNN3PCG1pstkIk+s4
--- 2B0VffwckYQT11chc0laW9oRHaaZSy3wrnjP0xe/Yrc
»I>Q}Zz<7A>'¶z=„`<14>Æ»@ƒŠ„˜ÉÀ]±48¹°Z<C2B0>ÀLzpƒk^;×IÛ—áH¸ŽÚr©Ño:Ú