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
8.5 kB 244 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; 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 }) project.apps; 27 }) cfg.projects; 28 } 29 ); 30in 31{ 32 options.lass.zitadel = { 33 loginPolicy = lib.mkOption { 34 type = lib.types.attrsOf lib.types.anything; 35 default = { }; 36 example = { 37 allowRegister = true; 38 }; 39 description = '' 40 Fields of the instance's default login policy, as in Zitadel's admin 41 API (PUT /admin/v1/policies/login). Unlisted fields keep their value. 42 ''; 43 }; 44 45 idps = lib.mkOption { 46 default = { }; 47 description = '' 48 External identity providers, added to the login policy. An IdP whose 49 client id or secret is empty is skipped. 50 ''; 51 type = lib.types.attrsOf ( 52 lib.types.submodule ( 53 { name, ... }: 54 { 55 options = { 56 type = lib.mkOption { 57 type = lib.types.enum [ 58 "github" 59 "google" 60 "oidc" 61 ]; 62 }; 63 name = lib.mkOption { 64 type = lib.types.str; 65 default = name; 66 description = "Display name on the login page; the IdP is found by it."; 67 }; 68 issuer = lib.mkOption { 69 type = lib.types.nullOr lib.types.str; 70 default = null; 71 description = "Issuer of a generic OIDC IdP (type = \"oidc\")."; 72 }; 73 clientId = lib.mkOption { 74 type = lib.types.nullOr lib.types.str; 75 default = null; 76 }; 77 clientIdFile = lib.mkOption { 78 type = lib.types.nullOr lib.types.str; 79 default = null; 80 }; 81 clientSecretFile = lib.mkOption { type = lib.types.str; }; 82 scopes = lib.mkOption { 83 type = lib.types.listOf lib.types.str; 84 default = [ 85 "openid" 86 "profile" 87 "email" 88 ]; 89 }; 90 providerOptions = lib.mkOption { 91 type = lib.types.attrsOf lib.types.bool; 92 default = { 93 isLinkingAllowed = true; 94 isCreationAllowed = true; 95 isAutoUpdate = true; 96 }; 97 description = '' 98 Options of the IdP, as in Zitadel's admin API 99 (zitadel.idp.v1.Options). 100 ''; 101 }; 102 }; 103 } 104 ) 105 ); 106 }; 107 108 projects = lib.mkOption { 109 default = { }; 110 description = '' 111 Zitadel projects, one per service, in the default organization. 112 Projects and apps removed from here are left in Zitadel. 113 ''; 114 type = lib.types.attrsOf ( 115 lib.types.submodule ( 116 { name, ... }: 117 let 118 project = name; 119 in 120 { 121 options = { 122 access = lib.mkOption { 123 type = lib.types.enum [ 124 "public" 125 "declarative" 126 "managed" 127 ]; 128 description = '' 129 Who may log in to the project's apps: 130 - public: every user of the instance. 131 - declarative: the users listed in `users`. All other user 132 grants of the project are removed. 133 - managed: users with a user grant on the project, given by 134 an admin in the console. 135 ''; 136 }; 137 users = lib.mkOption { 138 type = lib.types.listOf lib.types.str; 139 default = [ ]; 140 description = '' 141 Verified email addresses of the users that get a user 142 grant on the project (access = "declarative"). Not login 143 names: anyone can register a free one that looks like 144 someone's address. Users who have not registered or 145 verified their address yet are skipped until the next run. 146 ''; 147 }; 148 apps = lib.mkOption { 149 default = { }; 150 description = "OIDC web apps of the project."; 151 type = lib.types.attrsOf ( 152 lib.types.submodule ( 153 { name, ... }: 154 { 155 options = { 156 redirectUris = lib.mkOption { type = lib.types.listOf lib.types.str; }; 157 authMethod = lib.mkOption { 158 type = lib.types.enum [ 159 "basic" 160 "post" 161 ]; 162 default = "basic"; 163 description = "How the app sends its client secret to the token endpoint."; 164 }; 165 envFile = { 166 path = lib.mkOption { 167 type = lib.types.str; 168 default = "${appsDir}/${project}-${name}.env"; 169 description = "Env file with the app's client id and secret."; 170 }; 171 owner = lib.mkOption { 172 type = lib.types.str; 173 default = "root"; 174 }; 175 clientIdVariable = lib.mkOption { 176 type = lib.types.str; 177 default = "CLIENT_ID"; 178 }; 179 clientSecretVariable = lib.mkOption { 180 type = lib.types.str; 181 default = "CLIENT_SECRET"; 182 }; 183 }; 184 }; 185 } 186 ) 187 ); 188 }; 189 }; 190 } 191 ) 192 ); 193 }; 194 }; 195 196 config = lib.mkIf zitadel.enable { 197 assertions = 198 lib.mapAttrsToList (name: project: { 199 assertion = project.users == [ ] || project.access == "declarative"; 200 message = "lass.zitadel.projects.${name}.users needs access = \"declarative\""; 201 }) cfg.projects 202 ++ lib.mapAttrsToList (name: idp: { 203 assertion = (idp.clientId == null) != (idp.clientIdFile == null); 204 message = "lass.zitadel.idps.${name} needs exactly one of clientId and clientIdFile"; 205 }) cfg.idps 206 ++ lib.mapAttrsToList (name: idp: { 207 assertion = (idp.type == "oidc") == (idp.issuer != null); 208 message = "lass.zitadel.idps.${name}.issuer is needed for, and only for, type = \"oidc\""; 209 }) cfg.idps; 210 211 services.zitadel.steps.FirstInstance = { 212 PatPath = patFile; 213 Org.Machine.Machine = { 214 Username = "reconcile"; 215 Name = "zitadel-reconcile"; 216 }; 217 # Zitadel's default for tokens without expiry; an empty Pat section 218 # creates no token. 219 Org.Machine.Pat.ExpirationDate = "9999-01-01T00:00:00Z"; 220 }; 221 systemd.services.zitadel.serviceConfig = { 222 StateDirectory = "zitadel"; 223 StateDirectoryMode = "0700"; 224 }; 225 226 systemd.services.zitadel-reconcile = { 227 description = "Apply lass.zitadel to Zitadel"; 228 wantedBy = [ "multi-user.target" ]; 229 wants = [ "zitadel.service" ]; 230 after = [ "zitadel.service" ]; 231 environment.PYTHONUNBUFFERED = "1"; 232 serviceConfig = { 233 Type = "oneshot"; 234 RemainAfterExit = true; 235 ExecStart = "${ 236 pkgs.writers.writePython3 "zitadel-reconcile" { } ./reconcile.py 237 } ${reconcileConfig}"; 238 StateDirectory = "zitadel-apps"; 239 StateDirectoryMode = "0711"; 240 UMask = "0077"; 241 }; 242 }; 243 }; 244}