neptun: add Zitadel (identity/OIDC provider)
Local Postgres, peer-authed over the unix socket (the "zitadel" role is granted createdb+createrole and doubles as both the runtime and bootstrap DB user - no password anywhere). TLS terminates at Caddy; Zitadel listens on localhost:8080 and is proxied at auth.mgaction.town. Master key and admin bootstrap password come from sops - the admin password specifically needs the sops.templates -> rendered-file route (services.zitadel.steps would leak it into the world-readable Nix store), same pattern as mercury's pihole.env. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
../../common.nix # shared base: user / ssh / nix / firewall
|
../../common.nix # shared base: user / ssh / nix / firewall
|
||||||
../../services/caddy.nix
|
../../services/caddy.nix
|
||||||
../../services/tailscale.nix
|
../../services/tailscale.nix
|
||||||
|
../../services/zitadel.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# ---- Boot (UEFI) ----
|
# ---- Boot (UEFI) ----
|
||||||
@@ -48,6 +49,14 @@
|
|||||||
'';
|
'';
|
||||||
# TODO: port your other VPS services' vhosts here before deploying.
|
# TODO: port your other VPS services' vhosts here before deploying.
|
||||||
|
|
||||||
|
# ---- Zitadel (identity/OIDC provider) ----
|
||||||
|
# Runs locally on neptun (see services/zitadel.nix); Caddy just terminates
|
||||||
|
# TLS and proxies to it.
|
||||||
|
services.zitadel.settings.ExternalDomain = "auth.mgaction.town";
|
||||||
|
services.caddy.virtualHosts."auth.mgaction.town".extraConfig = ''
|
||||||
|
reverse_proxy http://localhost:8080
|
||||||
|
'';
|
||||||
|
|
||||||
# ---- Gitea SSH forward ----
|
# ---- Gitea SSH forward ----
|
||||||
# Caddy only proxies HTTP; git-over-ssh needs a raw TCP forward. Gitea's
|
# Caddy only proxies HTTP; git-over-ssh needs a raw TCP forward. Gitea's
|
||||||
# own built-in SSH server runs on jupiter:2222 (see services/gitea.nix —
|
# own built-in SSH server runs on jupiter:2222 (see services/gitea.nix —
|
||||||
|
|||||||
@@ -13,4 +13,27 @@
|
|||||||
# darman's console password (own hash = distinct from jupiter/mercury).
|
# darman's console password (own hash = distinct from jupiter/mercury).
|
||||||
sops.secrets.darman_password.neededForUsers = true;
|
sops.secrets.darman_password.neededForUsers = true;
|
||||||
users.users.darman.hashedPasswordFile = config.sops.secrets.darman_password.path;
|
users.users.darman.hashedPasswordFile = config.sops.secrets.darman_password.path;
|
||||||
|
|
||||||
|
# Zitadel: masterKeyFile takes a path natively (no store leak). The admin
|
||||||
|
# bootstrap password is different — services.zitadel.steps would render it
|
||||||
|
# into a world-readable store path, so render a FirstInstance steps file
|
||||||
|
# from the secret instead and point extraStepsPaths at it (see
|
||||||
|
# services/zitadel.nix and the pihole.env template on mercury for the same
|
||||||
|
# pattern).
|
||||||
|
sops.secrets.zitadel_master_key = { };
|
||||||
|
sops.secrets.zitadel_admin_password = { };
|
||||||
|
sops.templates."zitadel-first-instance.yaml".content = ''
|
||||||
|
FirstInstance:
|
||||||
|
Org:
|
||||||
|
Name: mgaction
|
||||||
|
Human:
|
||||||
|
UserName: admin
|
||||||
|
FirstName: Admin
|
||||||
|
LastName: Admin
|
||||||
|
Email:
|
||||||
|
Address: erik.simon.me@gmail.com
|
||||||
|
Verified: true
|
||||||
|
Password: ${config.sops.placeholder.zitadel_admin_password}
|
||||||
|
PasswordChangeRequired: false
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
tailscale_authkey: ENC[AES256_GCM,data:CBMidfdilBn1HP3TAu9TCOpuwPnzVQ/3OGCsf0vhFd8=,iv:V4UdZBPyzH8NkiXV40CxLdnfkCv5oG6xEQbNSmpmrmI=,tag:h8R2AIaFXgPycMIP8NyHVA==,type:str]
|
tailscale_authkey: ENC[AES256_GCM,data:CBMidfdilBn1HP3TAu9TCOpuwPnzVQ/3OGCsf0vhFd8=,iv:V4UdZBPyzH8NkiXV40CxLdnfkCv5oG6xEQbNSmpmrmI=,tag:h8R2AIaFXgPycMIP8NyHVA==,type:str]
|
||||||
darman_password: ENC[AES256_GCM,data:otLevSn6bXBrjCu7Dv9PN7Umwrb7CD1PhC1i+J8rp2mURMYVuwF/ZnrlPC7/RDZgINIRBOXOkG9lZo3UjuRyqrtvfUzIm1jGL51l1zvZ/07oNW6OUp4IZEQpoOmz1GuXBlx+yKRQeRDHYA==,iv:pnwp3uAsBoiJdF+QV27h0jztgFJKrIqFlmMT7JkcHoA=,tag:Zb9ziCGsLQt+TcJRCv8tWA==,type:str]
|
darman_password: ENC[AES256_GCM,data:otLevSn6bXBrjCu7Dv9PN7Umwrb7CD1PhC1i+J8rp2mURMYVuwF/ZnrlPC7/RDZgINIRBOXOkG9lZo3UjuRyqrtvfUzIm1jGL51l1zvZ/07oNW6OUp4IZEQpoOmz1GuXBlx+yKRQeRDHYA==,iv:pnwp3uAsBoiJdF+QV27h0jztgFJKrIqFlmMT7JkcHoA=,tag:Zb9ziCGsLQt+TcJRCv8tWA==,type:str]
|
||||||
|
zitadel_master_key: ENC[AES256_GCM,data:LDZwbpwZdQ03O8I2tvIMYnK3MoQTgvOYwrBt2UuCHbk=,iv:sM66sj3JO+bb0Sk2D7M/Ehk7t7VZneuvTRP4IkT1se0=,tag:YKQ4shwZ4sIDWdNehTugew==,type:str]
|
||||||
|
zitadel_admin_password: ENC[AES256_GCM,data:q+Od3Juzvl/dVPkm0w1C42pjK7kU8ur8V5fl2JAdQkA=,iv:n8NFzQczJVeqe1ybBldbkv4QGj0xyf9/P+uvsHs3NjI=,tag:rkXvmC8kZZOrWRQd+mIanA==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
@@ -20,7 +22,7 @@ sops:
|
|||||||
Wptkf76aP9UpjhgNkxzedRebQPB7ti+UiVqCvLVimtuHcsm/NJPcRg==
|
Wptkf76aP9UpjhgNkxzedRebQPB7ti+UiVqCvLVimtuHcsm/NJPcRg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1hp72xyx2cnd05937e4eww95g5kdtn0wsf9j2nypw330pa69gfdxqn0lpkp
|
recipient: age1hp72xyx2cnd05937e4eww95g5kdtn0wsf9j2nypw330pa69gfdxqn0lpkp
|
||||||
lastmodified: "2026-07-13T18:50:14Z"
|
lastmodified: "2026-07-19T20:28:23Z"
|
||||||
mac: ENC[AES256_GCM,data:52kyoG1moXQ9rDT7NLbxJby5ZiUn0Gym3u4kuLTsXxlGIvots7FipAg98BN4LZrL+8sGYMy2k8EAFzSB3F0tU5fi0f+R0F7CtKhK4nKCC5Hg70odN7HePobDw9dfR6hExLcSpUJBlUmpU0Lw1DL3MLjEEcfgzOYHmmrBv9rI4KU=,iv:2bIH2i874H1vg+nBJhsxohSYsjpFTb1toRw8mK5UN40=,tag:V2uijc0BymsLg3vDsHob9w==,type:str]
|
mac: ENC[AES256_GCM,data:3dRmmSJqaKi8w1mtmQIaL8AynSUCH4WRUpUax5g+3qKrhQeect7TdCFyTU991mfmUnxfQ8nI7nyAI2DdAb/TTHxAjDgXd0Qn31olGYZRQFRV25YOiXozvXoHVjroW86IQDgh6UBp5BkpU+HrfCfLs/opAx97jLu14M4sLLuu8R4=,iv:qmKCCX70IJ/JQvpjMW1eYM2cgjVx+1PS44ahhTXiOp0=,tag:hi7V9zwo4uZxAjEm3dUxBw==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.13.1
|
version: 3.13.1
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
# Zitadel — self-hosted identity/OIDC provider. Local Postgres (peer-authed
|
||||||
|
# over the unix socket, no password anywhere) since Zitadel is latency-
|
||||||
|
# sensitive to its DB. TLS is terminated at Caddy (see the host's
|
||||||
|
# configuration.nix for the public vhost); Zitadel itself only listens on
|
||||||
|
# localhost:8080.
|
||||||
|
#
|
||||||
|
# The "zitadel" Postgres role doubles as both Database.postgres.User (normal
|
||||||
|
# runtime queries) and .Admin (bootstrap: creates the db/extensions on first
|
||||||
|
# start) — granted createdb+createrole instead of using the real postgres
|
||||||
|
# superuser, matching Zitadel's own guidance to keep bootstrap privileges
|
||||||
|
# scoped to a dedicated role.
|
||||||
|
#
|
||||||
|
# Needs, wired via sops in the host's secrets.nix:
|
||||||
|
# - masterKeyFile: 32 raw bytes, e.g. `openssl rand -hex 16`
|
||||||
|
# - extraStepsPaths: a FirstInstance admin bootstrap file (keeps the admin
|
||||||
|
# password out of the Nix store — settings.steps would leak it, since
|
||||||
|
# it's rendered into a world-readable store path)
|
||||||
|
{
|
||||||
|
services.postgresql = {
|
||||||
|
enable = true;
|
||||||
|
ensureDatabases = [ "zitadel" ];
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = "zitadel";
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
ensureClauses = {
|
||||||
|
createdb = true;
|
||||||
|
createrole = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.zitadel = {
|
||||||
|
enable = true;
|
||||||
|
tlsMode = "external";
|
||||||
|
masterKeyFile = config.sops.secrets.zitadel_master_key.path;
|
||||||
|
extraStepsPaths = [ config.sops.templates."zitadel-first-instance.yaml".path ];
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
Port = 8080;
|
||||||
|
ExternalPort = 443;
|
||||||
|
ExternalSecure = true;
|
||||||
|
|
||||||
|
Database.postgres = {
|
||||||
|
Host = "/run/postgresql";
|
||||||
|
Port = 5432;
|
||||||
|
Database = "zitadel";
|
||||||
|
User = {
|
||||||
|
Username = "zitadel";
|
||||||
|
SSL.Mode = "disable";
|
||||||
|
};
|
||||||
|
Admin = {
|
||||||
|
Username = "zitadel";
|
||||||
|
SSL.Mode = "disable";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user