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.4 kB 243 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 Login names or verified email addresses of the users that 142 get a user grant on the project (access = "declarative"). 143 Users who have not registered yet are skipped until the 144 next run. 145 ''; 146 }; 147 apps = lib.mkOption { 148 default = { }; 149 description = "OIDC web apps of the project."; 150 type = lib.types.attrsOf ( 151 lib.types.submodule ( 152 { name, ... }: 153 { 154 options = { 155 redirectUris = lib.mkOption { type = lib.types.listOf lib.types.str; }; 156 authMethod = lib.mkOption { 157 type = lib.types.enum [ 158 "basic" 159 "post" 160 ]; 161 default = "basic"; 162 description = "How the app sends its client secret to the token endpoint."; 163 }; 164 envFile = { 165 path = lib.mkOption { 166 type = lib.types.str; 167 default = "${appsDir}/${project}-${name}.env"; 168 description = "Env file with the app's client id and secret."; 169 }; 170 owner = lib.mkOption { 171 type = lib.types.str; 172 default = "root"; 173 }; 174 clientIdVariable = lib.mkOption { 175 type = lib.types.str; 176 default = "CLIENT_ID"; 177 }; 178 clientSecretVariable = lib.mkOption { 179 type = lib.types.str; 180 default = "CLIENT_SECRET"; 181 }; 182 }; 183 }; 184 } 185 ) 186 ); 187 }; 188 }; 189 } 190 ) 191 ); 192 }; 193 }; 194 195 config = lib.mkIf zitadel.enable { 196 assertions = 197 lib.mapAttrsToList (name: project: { 198 assertion = project.users == [ ] || project.access == "declarative"; 199 message = "lass.zitadel.projects.${name}.users needs access = \"declarative\""; 200 }) cfg.projects 201 ++ lib.mapAttrsToList (name: idp: { 202 assertion = (idp.clientId == null) != (idp.clientIdFile == null); 203 message = "lass.zitadel.idps.${name} needs exactly one of clientId and clientIdFile"; 204 }) cfg.idps 205 ++ lib.mapAttrsToList (name: idp: { 206 assertion = (idp.type == "oidc") == (idp.issuer != null); 207 message = "lass.zitadel.idps.${name}.issuer is needed for, and only for, type = \"oidc\""; 208 }) cfg.idps; 209 210 services.zitadel.steps.FirstInstance = { 211 PatPath = patFile; 212 Org.Machine.Machine = { 213 Username = "reconcile"; 214 Name = "zitadel-reconcile"; 215 }; 216 # Zitadel's default for tokens without expiry; an empty Pat section 217 # creates no token. 218 Org.Machine.Pat.ExpirationDate = "9999-01-01T00:00:00Z"; 219 }; 220 systemd.services.zitadel.serviceConfig = { 221 StateDirectory = "zitadel"; 222 StateDirectoryMode = "0700"; 223 }; 224 225 systemd.services.zitadel-reconcile = { 226 description = "Apply lass.zitadel to Zitadel"; 227 wantedBy = [ "multi-user.target" ]; 228 wants = [ "zitadel.service" ]; 229 after = [ "zitadel.service" ]; 230 environment.PYTHONUNBUFFERED = "1"; 231 serviceConfig = { 232 Type = "oneshot"; 233 RemainAfterExit = true; 234 ExecStart = "${ 235 pkgs.writers.writePython3 "zitadel-reconcile" { } ./reconcile.py 236 } ${reconcileConfig}"; 237 StateDirectory = "zitadel-apps"; 238 StateDirectoryMode = "0711"; 239 UMask = "0077"; 240 }; 241 }; 242 }; 243}