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 278 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 ]; 187 default = "basic"; 188 description = "How the app sends its client secret to the token endpoint."; 189 }; 190 refreshTokens = lib.mkOption { 191 type = lib.types.bool; 192 default = false; 193 description = '' 194 Allow the refresh_token grant. Zitadel hands out 195 refresh tokens only when the app requests the 196 offline_access scope. 197 ''; 198 }; 199 envFile = { 200 path = lib.mkOption { 201 type = lib.types.str; 202 default = "${appsDir}/${project}-${name}.env"; 203 description = "Env file with the app's client id and secret."; 204 }; 205 owner = lib.mkOption { 206 type = lib.types.str; 207 default = "root"; 208 }; 209 clientIdVariable = lib.mkOption { 210 type = lib.types.str; 211 default = "CLIENT_ID"; 212 }; 213 clientSecretVariable = lib.mkOption { 214 type = lib.types.str; 215 default = "CLIENT_SECRET"; 216 }; 217 }; 218 }; 219 } 220 ) 221 ); 222 }; 223 }; 224 } 225 ) 226 ); 227 }; 228 }; 229 230 config = lib.mkIf zitadel.enable { 231 assertions = 232 lib.mapAttrsToList (name: project: { 233 assertion = project.users == [ ] || project.access == "declarative"; 234 message = "lass.zitadel.projects.${name}.users needs access = \"declarative\""; 235 }) cfg.projects 236 ++ lib.mapAttrsToList (name: idp: { 237 assertion = (idp.clientId == null) != (idp.clientIdFile == null); 238 message = "lass.zitadel.idps.${name} needs exactly one of clientId and clientIdFile"; 239 }) cfg.idps 240 ++ lib.mapAttrsToList (name: idp: { 241 assertion = (idp.type == "oidc") == (idp.issuer != null); 242 message = "lass.zitadel.idps.${name}.issuer is needed for, and only for, type = \"oidc\""; 243 }) cfg.idps; 244 245 services.zitadel.steps.FirstInstance = { 246 PatPath = patFile; 247 Org.Machine.Machine = { 248 Username = "reconcile"; 249 Name = "zitadel-reconcile"; 250 }; 251 # Zitadel's default for tokens without expiry; an empty Pat section 252 # creates no token. 253 Org.Machine.Pat.ExpirationDate = "9999-01-01T00:00:00Z"; 254 }; 255 systemd.services.zitadel.serviceConfig = { 256 StateDirectory = "zitadel"; 257 StateDirectoryMode = "0700"; 258 }; 259 260 systemd.services.zitadel-reconcile = { 261 description = "Apply lass.zitadel to Zitadel"; 262 wantedBy = [ "multi-user.target" ]; 263 wants = [ "zitadel.service" ]; 264 after = [ "zitadel.service" ]; 265 environment.PYTHONUNBUFFERED = "1"; 266 serviceConfig = { 267 Type = "oneshot"; 268 RemainAfterExit = true; 269 ExecStart = "${ 270 pkgs.writers.writePython3 "zitadel-reconcile" { } ./reconcile.py 271 } ${reconcileConfig}"; 272 StateDirectory = "zitadel-apps"; 273 StateDirectoryMode = "0711"; 274 UMask = "0077"; 275 }; 276 }; 277 }; 278}