This repository has no description
0

Configure Feed

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

superconfig / modules / zitadel / default.nix
10 kB 283 lines
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.lass.zitadel; 9 zitadel = config.services.zitadel; 10 appsDir = "/var/lib/zitadel-apps"; 11 # Written by zitadel's setup when it creates the first instance. 12 patFile = "/var/lib/zitadel/reconcile.pat"; 13 14 reconcileConfig = pkgs.writeText "zitadel-reconcile.json" ( 15 builtins.toJSON { 16 url = "http://127.0.0.1:${toString zitadel.settings.Port}"; 17 domain = zitadel.settings.ExternalDomain; 18 stateDir = appsDir; 19 inherit patFile; 20 inherit (cfg) loginPolicy idps smtp; 21 projects = lib.mapAttrs (_: project: { 22 inherit (project) access users; 23 apps = lib.mapAttrs (_: app: { 24 inherit (app) redirectUris envFile; 25 authMethodType = "OIDC_AUTH_METHOD_TYPE_${lib.toUpper app.authMethod}"; 26 grantTypes = [ 27 "OIDC_GRANT_TYPE_AUTHORIZATION_CODE" 28 ] 29 ++ lib.optional app.refreshTokens "OIDC_GRANT_TYPE_REFRESH_TOKEN"; 30 }) project.apps; 31 }) cfg.projects; 32 } 33 ); 34in 35{ 36 options.lass.zitadel = { 37 loginPolicy = lib.mkOption { 38 type = lib.types.attrsOf lib.types.anything; 39 default = { }; 40 example = { 41 allowRegister = true; 42 }; 43 description = '' 44 Fields of the instance's default login policy, as in Zitadel's admin 45 API (PUT /admin/v1/policies/login). Unlisted fields keep their value. 46 ''; 47 }; 48 49 smtp = lib.mkOption { 50 type = lib.types.nullOr (lib.types.attrsOf lib.types.anything); 51 default = null; 52 example = { 53 senderAddress = "id@example.org"; 54 senderName = "example.org ID"; 55 host = "mail.example.org:465"; 56 tls = true; 57 user = "id@example.org"; 58 passwordFile = "/run/secrets/id-mail-password"; 59 }; 60 description = '' 61 SMTP provider for the instance's emails (verification codes, 62 password resets), as in Zitadel's admin API (POST /admin/v1/smtp), 63 with the password read from passwordFile (Zitadel ignores providers 64 without one). It is created when missing, updated when it differs 65 and activated. Without one, users whose email address is not 66 verified cannot finish logging in. 67 ''; 68 }; 69 70 idps = lib.mkOption { 71 default = { }; 72 description = '' 73 External identity providers, added to the login policy. An IdP whose 74 client id or secret is empty is skipped. 75 ''; 76 type = lib.types.attrsOf ( 77 lib.types.submodule ( 78 { name, ... }: 79 { 80 options = { 81 type = lib.mkOption { 82 type = lib.types.enum [ 83 "github" 84 "google" 85 "oidc" 86 ]; 87 }; 88 name = lib.mkOption { 89 type = lib.types.str; 90 default = name; 91 description = "Display name on the login page; the IdP is found by it."; 92 }; 93 issuer = lib.mkOption { 94 type = lib.types.nullOr lib.types.str; 95 default = null; 96 description = "Issuer of a generic OIDC IdP (type = \"oidc\")."; 97 }; 98 clientId = lib.mkOption { 99 type = lib.types.nullOr lib.types.str; 100 default = null; 101 }; 102 clientIdFile = lib.mkOption { 103 type = lib.types.nullOr lib.types.str; 104 default = null; 105 }; 106 clientSecretFile = lib.mkOption { type = lib.types.str; }; 107 scopes = lib.mkOption { 108 type = lib.types.listOf lib.types.str; 109 default = [ 110 "openid" 111 "profile" 112 "email" 113 ]; 114 }; 115 providerOptions = lib.mkOption { 116 type = lib.types.attrsOf lib.types.bool; 117 default = { 118 isLinkingAllowed = true; 119 isCreationAllowed = true; 120 isAutoUpdate = true; 121 }; 122 description = '' 123 Options of the IdP, as in Zitadel's admin API 124 (zitadel.idp.v1.Options). 125 ''; 126 }; 127 }; 128 } 129 ) 130 ); 131 }; 132 133 projects = lib.mkOption { 134 default = { }; 135 description = '' 136 Zitadel projects, one per service, in the default organization. 137 Projects and apps removed from here are left in Zitadel. 138 ''; 139 type = lib.types.attrsOf ( 140 lib.types.submodule ( 141 { name, ... }: 142 let 143 project = name; 144 in 145 { 146 options = { 147 access = lib.mkOption { 148 type = lib.types.enum [ 149 "public" 150 "declarative" 151 "managed" 152 ]; 153 description = '' 154 Who may log in to the project's apps: 155 - public: every user of the instance. 156 - declarative: the users listed in `users`. All other user 157 grants of the project are removed. 158 - managed: users with a user grant on the project, given by 159 an admin in the console. 160 ''; 161 }; 162 users = lib.mkOption { 163 type = lib.types.listOf lib.types.str; 164 default = [ ]; 165 description = '' 166 Verified email addresses of the users that get a user 167 grant on the project (access = "declarative"). Not login 168 names: anyone can register a free one that looks like 169 someone's address. Users who have not registered or 170 verified their address yet are skipped until the next run. 171 ''; 172 }; 173 apps = lib.mkOption { 174 default = { }; 175 description = "OIDC web apps of the project."; 176 type = lib.types.attrsOf ( 177 lib.types.submodule ( 178 { name, ... }: 179 { 180 options = { 181 redirectUris = lib.mkOption { type = lib.types.listOf lib.types.str; }; 182 authMethod = lib.mkOption { 183 type = lib.types.enum [ 184 "basic" 185 "post" 186 "none" 187 ]; 188 default = "basic"; 189 description = '' 190 How the app sends its client secret to the token 191 endpoint; "none" for a public client without a 192 secret, which has to use PKCE. 193 ''; 194 }; 195 refreshTokens = lib.mkOption { 196 type = lib.types.bool; 197 default = false; 198 description = '' 199 Allow the refresh_token grant. Zitadel hands out 200 refresh tokens only when the app requests the 201 offline_access scope. 202 ''; 203 }; 204 envFile = { 205 path = lib.mkOption { 206 type = lib.types.str; 207 default = "${appsDir}/${project}-${name}.env"; 208 description = "Env file with the app's client id and secret (if any)."; 209 }; 210 owner = lib.mkOption { 211 type = lib.types.str; 212 default = "root"; 213 }; 214 clientIdVariable = lib.mkOption { 215 type = lib.types.str; 216 default = "CLIENT_ID"; 217 }; 218 clientSecretVariable = lib.mkOption { 219 type = lib.types.str; 220 default = "CLIENT_SECRET"; 221 }; 222 }; 223 }; 224 } 225 ) 226 ); 227 }; 228 }; 229 } 230 ) 231 ); 232 }; 233 }; 234 235 config = lib.mkIf zitadel.enable { 236 assertions = 237 lib.mapAttrsToList (name: project: { 238 assertion = project.users == [ ] || project.access == "declarative"; 239 message = "lass.zitadel.projects.${name}.users needs access = \"declarative\""; 240 }) cfg.projects 241 ++ lib.mapAttrsToList (name: idp: { 242 assertion = (idp.clientId == null) != (idp.clientIdFile == null); 243 message = "lass.zitadel.idps.${name} needs exactly one of clientId and clientIdFile"; 244 }) cfg.idps 245 ++ lib.mapAttrsToList (name: idp: { 246 assertion = (idp.type == "oidc") == (idp.issuer != null); 247 message = "lass.zitadel.idps.${name}.issuer is needed for, and only for, type = \"oidc\""; 248 }) cfg.idps; 249 250 services.zitadel.steps.FirstInstance = { 251 PatPath = patFile; 252 Org.Machine.Machine = { 253 Username = "reconcile"; 254 Name = "zitadel-reconcile"; 255 }; 256 # Zitadel's default for tokens without expiry; an empty Pat section 257 # creates no token. 258 Org.Machine.Pat.ExpirationDate = "9999-01-01T00:00:00Z"; 259 }; 260 systemd.services.zitadel.serviceConfig = { 261 StateDirectory = "zitadel"; 262 StateDirectoryMode = "0700"; 263 }; 264 265 systemd.services.zitadel-reconcile = { 266 description = "Apply lass.zitadel to Zitadel"; 267 wantedBy = [ "multi-user.target" ]; 268 wants = [ "zitadel.service" ]; 269 after = [ "zitadel.service" ]; 270 environment.PYTHONUNBUFFERED = "1"; 271 serviceConfig = { 272 Type = "oneshot"; 273 RemainAfterExit = true; 274 ExecStart = "${ 275 pkgs.writers.writePython3 "zitadel-reconcile" { } ./reconcile.py 276 } ${reconcileConfig}"; 277 StateDirectory = "zitadel-apps"; 278 StateDirectoryMode = "0711"; 279 UMask = "0077"; 280 }; 281 }; 282 }; 283}