{ lib, pkgs, ... }: let inherit (builtins) readFile; inherit (lib) mkOption types; in { _class = "clan.service"; manifest = { name = "amarth-services/zitadel"; description = "Zitadel service module"; categories = [ "System" "Identity" "IAM" ]; readme = readFile ./README.md; }; #============================================================================================================== # Controller configuration #============================================================================================================== roles.controller = { interface = { options = { hostName = mkOption { type = types.str; example = "auth.example.com"; description = '' The domain at which zitadel will be hosted ''; }; displayName = mkOption { type = types.str; example = "My awesome org"; description = '' The Name of the zitadel organisation ''; }; emergencyAccessPublicKey = mkOption { type = types.str; example = "ssh-ed25519 ..."; description = '' The public key with which you want access to ''; }; }; }; perInstance = { instanceName, settings, machine, roles, ... }: { nixosModule = { config, pkgs, inputs, system, ... }: let terraform = inputs.terranix.lib.terranixConfiguration { inherit pkgs system; modules = [ ({ config, ... }: { config = { terraform.required_providers.zitadel = { source = "zitadel/zitadel"; version = "2.2.0"; }; provider.zitadel = { domain = "localhost"; insecure = "true"; port = "8080"; jwt_profile_file = "local-token"; }; resource.zitadel_human_user.default = { org_id = ""; }; }; }) ]; }; in { clan.core.vars.generators.zitadel = { share = false; files.masterKey = { deploy = true; secret = true; owner = "zitadel"; group = "zitadel"; mode = "0400"; }; files.initialAdminPassword = { deploy = false; secret = false; }; files.emailPassword = { deploy = false; secret = true; }; runtimeInputs = with pkgs; [ pwgen ]; script = '' pwgen 50 1 > "$out/initialAdminPassword" # https://zitadel.com/docs/self-hosting/manage/configure#masterkey # The master key has to be 32 bytes head -c 32 /dev/urandom > "$out/masterKey" touch $out/emailPassword ''; }; services.zitadel = { enable = true; openFirewall = true; masterKeyFile = config.clan.core.vars.generators.zitadel.files.masterKey.path; tlsMode = "external"; settings = { Port = 9092; ExternalDomain = settings.hostName; ExternalPort = 443; ExternalSecure = true; Metrics.Type = "otel"; Tracing.Type = "otel"; Telemetry.Enabled = true; SystemDefaults = { PasswordHasher.Hasher.Algorithm = "argon2id"; SecretHasher.Hasher.Algorithm = "argon2id"; }; DefaultInstance = { PasswordComplexityPolicy = { MinLength = 20; HasLowercase = false; HasUppercase = false; HasNumber = false; HasSymbol = false; }; LoginPolicy = { AllowRegister = false; ForceMFA = true; }; LockoutPolicy = { MaxPasswordAttempts = 5; MaxOTPAttempts = 10; }; SMTPConfiguration = { SMTP = { Host = "black-mail.nl:587"; User = "info@amarth.cloud"; Password = ""; #config.clan.core.vars.generators.zitadel.files.emailPassword.value; }; FromName = "Amarth Zitadel"; }; }; Database.postgres = { Host = "localhost"; # Zitadel will report error if port is not set Port = 5432; Database = "zitadel"; User = { Username = "zitadel"; SSL.Mode = "disable"; }; Admin = { Username = "postgres"; SSL.Mode = "disable"; }; }; Machine.Identification = { PrivateIp.Enabled = true; # In the docs this uses a google service. I want a self hosted one # TODO :: Figure out how to self-host webhooks, if I want them at all Webhook.Enabled = false; }; SystemAPIUsers = { emergencyAccess = { # Path = settings.emergencyAccessPublicKey; KeyData = settings.emergencyAccessPublicKey; # This is the default value # Memberships = [ # { MemberType = "System"; Roles = [ "SYSTEM_OWNER" ]; } # ]; }; }; }; steps.FirstInstance = { InstanceName = settings.hostName; Org = { Name = settings.displayName; Human = { UserName = "chris"; FirstName = "Chris"; LastName = "Kruining"; Email = { Address = "chris@kruining.eu"; Verified = true; }; Password = config.clan.core.vars.generators.zitadel.files.initialAdminPassword.value; }; }; }; }; services.postgresql = { enable = true; authentication = '' # Generated file, do not edit! # TYPE DATABASE USER ADDRESS METHOD local all all trust host all all 127.0.0.1/32 trust host all all ::1/128 trust ''; ensureDatabases = [ "zitadel" ]; ensureUsers = [ { name = "zitadel"; ensureDBOwnership = true; } ]; }; }; }; }; #============================================================================================================== # Peer configuration #============================================================================================================== roles.peer = { interface = { options = {}; }; perInstance = { instanceName, settings, machine, roles, ... }: { nixosModule = {}; }; }; }