This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / zitadel.nix
7.7 kB 229 lines
1let 2 domain = "id.lassul.us"; 3 port = 1411; 4in 5{ 6 self, 7 config, 8 lib, 9 pkgs, 10 ... 11}: 12let 13 cfg = config.services.zitadel; 14 vars = config.clan.core.vars.generators.zitadel.files; 15 yaml = pkgs.formats.yaml { }; 16 configFile = yaml.generate "config.yaml" cfg.settings; 17 args = lib.cli.toCommandLineShellGNU { } { 18 config = [ configFile ]; 19 steps = [ 20 (yaml.generate "steps.yaml" cfg.steps) 21 vars."steps.yaml".path 22 ]; 23 masterkeyFile = cfg.masterKeyFile; 24 inherit (cfg) tlsMode; 25 }; 26 idpCredentials = name: url: { 27 prompts.client_id = { 28 description = '' 29 ${name} login on ${domain}: client id of an OAuth app (${url}) 30 with the callback URL 31 https://${domain}/ui/login/login/externalidp/callback 32 Leave empty to not offer ${name} logins. 33 ''; 34 persist = true; 35 }; 36 prompts.client_secret = { 37 description = "${name} login on ${domain}: client secret of that OAuth app"; 38 type = "hidden"; 39 persist = true; 40 }; 41 }; 42in 43{ 44 # Zitadel: identity provider for lassul.us services (OIDC). Anyone can 45 # register; users sign in with a passkey or an external login (GitHub, 46 # Google, their own OpenID provider via byoidc) linked to their account. The 47 # login policy, these logins and the services' projects and OIDC apps come from 48 # lass.zitadel (modules/zitadel); the zitadel-reconcile service applies 49 # them. Further IdPs and the grants of "managed" projects are done in the 50 # console at https://id.lassul.us/ui/console. 51 # 52 # Example projects for services on other hosts, once they speak OIDC (the 53 # client secret ends up in /var/lib/zitadel-apps/<project>-<app>.env here): 54 # lass.zitadel.projects.assist = { 55 # access = "declarative"; 56 # users = [ "lass@lassul.us" ]; 57 # apps.web.redirectUris = [ "https://assist.lassul.us/<callback>" ]; 58 # }; 59 # lass.zitadel.projects.flix = { 60 # access = "managed"; 61 # # jellyfin-plugin-sso with a provider named id.lassul.us 62 # apps.jellyfin.redirectUris = [ "https://flix.lassul.us/sso/OID/redirect/id.lassul.us" ]; 63 # }; 64 65 clan.core.vars.generators.zitadel = { 66 files."masterkey".owner = "zitadel"; 67 # initial password of the admin user lass, changed at first login 68 files."admin-password".deploy = false; 69 files."steps.yaml".owner = "zitadel"; 70 runtimeInputs = [ 71 pkgs.jq 72 pkgs.openssl 73 pkgs.pwgen 74 ]; 75 script = '' 76 openssl rand -hex 16 | tr -d '\n' > "$out/masterkey" 77 pwgen -s -c -n -y 32 1 | tr -d '\n' > "$out/admin-password" 78 jq -n --rawfile pw "$out/admin-password" \ 79 '{FirstInstance: {Org: {Human: {Password: $pw}}}}' > "$out/steps.yaml" 80 ''; 81 }; 82 83 services.zitadel = { 84 enable = true; 85 package = self.packages.${pkgs.stdenv.hostPlatform.system}.zitadel; 86 masterKeyFile = vars."masterkey".path; 87 settings = { 88 # daily usage report to zitadel.com, on by default 89 ServicePing.Enabled = false; 90 # a new instance would otherwise send logins to the separate Login V2 app, which isn't deployed 91 DefaultInstance.Features.LoginV2.Required = false; 92 Port = port; 93 ExternalDomain = domain; 94 ExternalPort = 443; 95 ExternalSecure = true; 96 Machine.Identification = { 97 PrivateIp.Enabled = false; 98 Hostname.Enabled = true; 99 Webhook.Enabled = false; 100 }; 101 Database.postgres = { 102 Host = "/run/postgresql"; 103 Port = 5432; 104 Database = "zitadel"; 105 User.Username = "zitadel"; 106 Admin.Username = "zitadel"; 107 Admin.ExistingDatabase = "zitadel"; 108 }; 109 }; 110 steps.FirstInstance = { 111 InstanceName = "lassul.us"; 112 Org = { 113 Name = "lassul.us"; 114 Human = { 115 UserName = "lass"; 116 FirstName = "lass"; 117 LastName = "lassulus"; 118 Email = { 119 Address = "lass@lassul.us"; 120 Verified = true; 121 }; 122 PasswordChangeRequired = true; 123 }; 124 }; 125 }; 126 }; 127 128 lass.zitadel.loginPolicy = { 129 allowRegister = true; 130 allowExternalIdp = true; 131 allowUsernamePassword = true; 132 passwordlessType = "PASSWORDLESS_TYPE_ALLOWED"; 133 }; 134 # Verification codes and password resets. Zitadel asks every user whose 135 # address is unverified (registration, byoidc) to verify it before the 136 # login finishes. Sent as the send-only account id@lassul.us of the 137 # mailserver here (configs/mailserver.nix), which DKIM-signs it. 138 lass.zitadel.smtp = { 139 senderAddress = "id@lassul.us"; 140 senderName = "lassul.us ID"; 141 host = "mail.lassul.us:465"; 142 tls = true; 143 user = "id@lassul.us"; 144 passwordFile = config.clan.core.vars.generators.mailserver-id.files."password".path; 145 }; 146 lass.zitadel.idps = { 147 GitHub = { 148 type = "github"; 149 clientIdFile = config.clan.core.vars.generators.zitadel-github.files.client_id.path; 150 clientSecretFile = config.clan.core.vars.generators.zitadel-github.files.client_secret.path; 151 scopes = [ 152 "read:user" 153 "user:email" 154 ]; 155 }; 156 Google = { 157 type = "google"; 158 clientIdFile = config.clan.core.vars.generators.zitadel-google.files.client_id.path; 159 clientSecretFile = config.clan.core.vars.generators.zitadel-google.files.client_secret.path; 160 scopes = [ 161 "openid" 162 "profile" 163 "email" 164 ]; 165 }; 166 # byoidc (configs/byoidc.nix): sign in with any OpenID provider. Anyone 167 # can run one that claims any email address, so these accounts are never 168 # linked to existing ones. 169 byoidc = { 170 type = "oidc"; 171 name = "Your own provider"; 172 issuer = "https://byoidc.lassul.us"; 173 clientId = "zitadel"; 174 clientSecretFile = config.clan.core.vars.generators.byoidc.files."client-secret".path; 175 providerOptions = { 176 isLinkingAllowed = false; 177 isCreationAllowed = true; 178 isAutoCreation = true; 179 isAutoUpdate = true; 180 }; 181 }; 182 }; 183 clan.core.vars.generators.zitadel-github = 184 idpCredentials "GitHub" "https://github.com/settings/applications/new"; 185 clan.core.vars.generators.zitadel-google = 186 idpCredentials "Google" "https://console.cloud.google.com/apis/credentials"; 187 188 # The database and its owner come from ensureDatabases/ensureUsers, so the 189 # zitadel role needs no CREATEROLE/CREATEDB. `zitadel init zitadel` creates 190 # only the schemas inside it; start-from-setup then skips the role and 191 # database creation that start-from-init would attempt. 192 systemd.services.zitadel = { 193 after = [ "postgresql.target" ]; 194 requires = [ "postgresql.target" ]; 195 serviceConfig = { 196 ExecStartPre = "${lib.getExe' cfg.package "zitadel"} init zitadel --config ${configFile}"; 197 ExecStart = lib.mkForce "${lib.getExe' cfg.package "zitadel"} start-from-setup ${args}"; 198 }; 199 }; 200 201 services.postgresql = { 202 enable = true; 203 ensureDatabases = [ "zitadel" ]; 204 ensureUsers = [ 205 { 206 name = "zitadel"; 207 ensureDBOwnership = true; 208 } 209 ]; 210 }; 211 212 # users, passkeys, IdP links, OIDC apps; its secrets are encrypted with the 213 # masterkey from clan vars 214 lass.backup.state.zitadel.preBackup = '' 215 mkdir -p /var/backup/postgresql 216 ${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dump \ 217 zitadel | ${pkgs.gzip}/bin/gzip > /var/backup/postgresql/zitadel.sql.gz 218 ''; 219 220 # Zitadel serves the console, OIDC and its gRPC APIs on one port over h2c. 221 services.nginx.virtualHosts.${domain} = { 222 enableACME = true; 223 forceSSL = true; 224 locations."/".extraConfig = '' 225 grpc_pass grpc://127.0.0.1:${toString port}; 226 grpc_set_header Host $host; 227 ''; 228 }; 229}