From 638d19ac92232265a6e7031878ea35a0734c8def Mon Sep 17 00:00:00 2001 From: Ruby Juric Date: Tue, 3 Oct 2023 05:11:18 +0000 Subject: [PATCH] zitadel: init at 2.37.2; nixos/zitadel: init (#254896) --- maintainers/maintainer-list.nix | 10 + .../manual/release-notes/rl-2311.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/zitadel.nix | 223 ++++++++++++++++++ pkgs/by-name/zi/zitadel/console.nix | 51 ++++ pkgs/by-name/zi/zitadel/package.json | 88 +++++++ pkgs/by-name/zi/zitadel/package.nix | 150 ++++++++++++ 7 files changed, 525 insertions(+) create mode 100644 nixos/modules/services/web-apps/zitadel.nix create mode 100644 pkgs/by-name/zi/zitadel/console.nix create mode 100644 pkgs/by-name/zi/zitadel/package.json create mode 100644 pkgs/by-name/zi/zitadel/package.nix diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 8b771a03a051d2..d3f781ccb80f5f 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -16285,6 +16285,16 @@ githubId = 53029739; name = "Joshua Ortiz"; }; + Sorixelle = { + email = "ruby+nixpkgs@srxl.me"; + matrix = "@ruby:isincredibly.gay"; + name = "Ruby Iris Juric"; + github = "Sorixelle"; + githubId = 38685302; + keys = [{ + fingerprint = "2D76 76C7 A28E 16FC 75C7 268D 1B55 6ED8 4B0E 303A"; + }]; + }; sorki = { email = "srk@48.io"; github = "sorki"; diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index f235bca8e7ce59..b7d41f4b3f22ca 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -89,6 +89,8 @@ - [audiobookshelf](https://github.com/advplyr/audiobookshelf/), a self-hosted audiobook and podcast server. Available as [services.audiobookshelf](#opt-services.audiobookshelf.enable). +- [ZITADEL](https://zitadel.com), a turnkey identity and access management platform. Available as [services.zitadel](#opt-services.zitadel.enable). + ## Backward Incompatibilities {#sec-release-23.11-incompatibilities} - The `boot.loader.raspberryPi` options have been marked deprecated, with intent for removal for NixOS 24.11. They had a limited use-case, and do not work like people expect. They required either very old installs ([before mid-2019](https://github.com/NixOS/nixpkgs/pull/62462)) or customized builds out of scope of the standard and generic AArch64 support. That option set never supported the Raspberry Pi 4 family of devices. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index ec6f410a48f68a..4e2275cc661de6 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1302,6 +1302,7 @@ ./services/web-apps/writefreely.nix ./services/web-apps/youtrack.nix ./services/web-apps/zabbix.nix + ./services/web-apps/zitadel.nix ./services/web-servers/agate.nix ./services/web-servers/apache-httpd/default.nix ./services/web-servers/caddy/default.nix diff --git a/nixos/modules/services/web-apps/zitadel.nix b/nixos/modules/services/web-apps/zitadel.nix new file mode 100644 index 00000000000000..f225d138cc4340 --- /dev/null +++ b/nixos/modules/services/web-apps/zitadel.nix @@ -0,0 +1,223 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.zitadel; + + settingsFormat = pkgs.formats.yaml { }; +in +{ + options.services.zitadel = + let inherit (lib) mkEnableOption mkOption mkPackageOption types; + in { + enable = mkEnableOption "ZITADEL, a user and identity access management platform."; + + package = mkPackageOption pkgs "ZITADEL" { default = [ "zitadel" ]; }; + + user = mkOption { + type = types.str; + default = "zitadel"; + description = "The user to run ZITADEL under."; + }; + + group = mkOption { + type = types.str; + default = "zitadel"; + description = "The group to run ZITADEL under."; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the port specified in `listenPort` in the firewall. + ''; + }; + + masterKeyFile = mkOption { + type = types.path; + description = '' + Path to a file containing a master encryption key for ZITADEL. The + key must be 32 bytes. + ''; + }; + + tlsMode = mkOption { + type = types.enum [ "external" "enabled" "disabled" ]; + default = "external"; + example = "enabled"; + description = '' + The TLS mode to use. Options are: + + - enabled: ZITADEL accepts HTTPS connections directly. You must + configure TLS if this option is selected. + - external: ZITADEL forces HTTPS connections, with TLS terminated at a + reverse proxy. + - disabled: ZITADEL accepts HTTP connections only. Should only be used + for testing. + ''; + }; + + settings = mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options = { + Port = mkOption { + type = types.port; + default = 8080; + description = "The port that ZITADEL listens on."; + }; + + TLS = { + KeyPath = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the TLS certificate private key."; + }; + Key = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The TLS certificate private key, as a base64-encoded string. + + Note that the contents of this option will be added to the Nix + store as world-readable plain text. Set + [KeyPath](#opt-services.zitadel.settings.TLS.KeyPath) instead + if this is undesired. + ''; + }; + CertPath = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the TLS certificate."; + }; + Cert = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The TLS certificate, as a base64-encoded string. + + Note that the contents of this option will be added to the Nix + store as world-readable plain text. Set + [CertPath](#opt-services.zitadel.settings.TLS.CertPath) instead + if this is undesired. + ''; + }; + }; + }; + }; + default = { }; + example = lib.literalExpression '' + { + Port = 8123; + ExternalDomain = "example.com"; + TLS = { + CertPath = "/path/to/cert.pem"; + KeyPath = "/path/to/cert.key"; + }; + Database.cockroach.Host = "db.example.com"; + }; + ''; + description = '' + Contents of the runtime configuration file. See + https://zitadel.com/docs/self-hosting/manage/configure for more + details. + ''; + }; + + extraSettingsPaths = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + A list of paths to extra settings files. These will override the + values set in [settings](#opt-services.zitadel.settings). Useful if + you want to keep sensitive secrets out of the Nix store. + ''; + }; + + steps = mkOption { + type = settingsFormat.type; + default = { }; + example = lib.literalExpression '' + { + FirstInstance = { + InstanceName = "Example"; + Org.Human = { + UserName = "foobar"; + FirstName = "Foo"; + LastName = "Bar"; + }; + }; + } + ''; + description = '' + Contents of the database initialization config file. See + https://zitadel.com/docs/self-hosting/manage/configure for more + details. + ''; + }; + + extraStepsPaths = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + A list of paths to extra steps files. These will override the values + set in [steps](#opt-services.zitadel.steps). Useful if you want to + keep sensitive secrets out of the Nix store. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [{ + assertion = cfg.tlsMode == "enabled" + -> ((cfg.settings.TLS.Key != null || cfg.settings.TLS.KeyPath != null) + && (cfg.settings.TLS.Cert != null || cfg.settings.TLS.CertPath + != null)); + message = '' + A TLS certificate and key must be configured in + services.zitadel.settings.TLS if services.zitadel.tlsMode is enabled. + ''; + }]; + + networking.firewall.allowedTCPPorts = + lib.mkIf cfg.openFirewall [ cfg.settings.Port ]; + + systemd.services.zitadel = + let + configFile = settingsFormat.generate "config.yaml" cfg.settings; + stepsFile = settingsFormat.generate "steps.yaml" cfg.steps; + + args = lib.cli.toGNUCommandLineShell { } { + config = cfg.extraSettingsPaths ++ [ configFile ]; + steps = cfg.extraStepsPaths ++ [ stepsFile ]; + masterkeyFile = cfg.masterKeyFile; + inherit (cfg) tlsMode; + }; + in + { + description = "ZITADEL identity access management"; + path = [ cfg.package ]; + wantedBy = [ "multi-user.target" ]; + + script = '' + zitadel start-from-init ${args} + ''; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + }; + }; + + users.users.zitadel = lib.mkIf (cfg.user == "zitadel") { + isSystemUser = true; + group = cfg.group; + }; + users.groups.zitadel = lib.mkIf (cfg.group == "zitadel") { }; + }; + + meta.maintainers = with lib.maintainers; [ Sorixelle ]; +} diff --git a/pkgs/by-name/zi/zitadel/console.nix b/pkgs/by-name/zi/zitadel/console.nix new file mode 100644 index 00000000000000..f2e1f26f3b8798 --- /dev/null +++ b/pkgs/by-name/zi/zitadel/console.nix @@ -0,0 +1,51 @@ +{ generateProtobufCode +, version +, zitadelRepo +}: + +{ mkYarnPackage +, fetchYarnDeps +, lib +}: + +let + protobufGenerated = generateProtobufCode { + pname = "zitadel-console"; + workDir = "console"; + bufArgs = "../proto --include-imports --include-wkt"; + outputPath = "src/app/proto"; + hash = "sha256-s0dzmcjKd8ot7t+KlRlNVA9oiIDKVMnGOT/HjdaUjGI="; + }; +in +mkYarnPackage rec { + name = "zitadel-console"; + inherit version; + + src = "${zitadelRepo}/console"; + + packageJSON = ./package.json; + offlineCache = fetchYarnDeps { + yarnLock = "${src}/yarn.lock"; + hash = "sha256-48IC4LxqbkH+95k7rCmhRWT+qAlJ9CDXWwRjbric9no="; + }; + + postPatch = '' + substituteInPlace src/styles.scss \ + --replace "/node_modules/flag-icons" "flag-icons" + + substituteInPlace angular.json \ + --replace "./node_modules/tinycolor2" "../../node_modules/tinycolor2" + ''; + + buildPhase = '' + mkdir deps/console/src/app/proto + cp -r ${protobufGenerated}/* deps/console/src/app/proto/ + yarn --offline build + ''; + + installPhase = '' + cp -r deps/console/dist/console $out + ''; + + doDist = false; +} diff --git a/pkgs/by-name/zi/zitadel/package.json b/pkgs/by-name/zi/zitadel/package.json new file mode 100644 index 00000000000000..0add6d2556b183 --- /dev/null +++ b/pkgs/by-name/zi/zitadel/package.json @@ -0,0 +1,88 @@ +{ + "name": "console", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "node prebuild.development.js && ng serve", + "build": "ng build --configuration production --base-href=/ui/console/", + "prelint": "npm run generate", + "lint": "ng lint && prettier --check src", + "lint:fix": "prettier --write src", + "generate": "buf generate ../proto --include-imports --include-wkt" + }, + "private": true, + "dependencies": { + "@angular/animations": "^16.2.0", + "@angular/cdk": "^16.2.0", + "@angular/common": "^16.2.0", + "@angular/compiler": "^16.2.0", + "@angular/core": "^16.2.0", + "@angular/forms": "^16.2.0", + "@angular/material": "^16.2.0", + "@angular/material-moment-adapter": "^16.2.0", + "@angular/platform-browser": "^16.2.0", + "@angular/platform-browser-dynamic": "^16.2.0", + "@angular/router": "^16.2.0", + "@angular/service-worker": "^16.2.0", + "@ctrl/ngx-codemirror": "^6.1.0", + "@grpc/grpc-js": "^1.8.14", + "@ngx-translate/core": "^14.0.0", + "angular-oauth2-oidc": "^15.0.1", + "angularx-qrcode": "^16.0.0", + "buffer": "^6.0.3", + "codemirror": "^5.65.8", + "cors": "^2.8.5", + "file-saver": "^2.0.5", + "flag-icons": "^6.7.0", + "google-proto-files": "^3.0.3", + "google-protobuf": "^3.21.2", + "grpc-web": "^1.4.1", + "i18n-iso-countries": "^7.6.0", + "libphonenumber-js": "^1.10.30", + "material-design-icons-iconfont": "^6.1.1", + "moment": "^2.29.4", + "opentype.js": "^1.3.4", + "ngx-color": "^9.0.0", + "rxjs": "~7.8.0", + "tinycolor2": "^1.6.0", + "tslib": "^2.4.1", + "uuid": "^9.0.0", + "zone.js": "~0.13.1" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^16.2.0", + "@angular-eslint/builder": "16.1.0", + "@angular-eslint/eslint-plugin": "16.1.0", + "@angular-eslint/eslint-plugin-template": "16.1.0", + "@angular-eslint/schematics": "16.1.0", + "@angular-eslint/template-parser": "16.1.0", + "@angular/cli": "^16.2.0", + "@angular/compiler-cli": "^16.2.0", + "@angular/language-service": "^16.2.0", + "@bufbuild/buf": "^1.23.1", + "@types/file-saver": "^2.0.2", + "@types/google-protobuf": "^3.15.3", + "@types/jasmine": "~4.3.3", + "@types/jasminewd2": "~2.0.10", + "@types/jsonwebtoken": "^9.0.1", + "@types/node": "^18.15.11", + "@types/opentype.js": "^1.3.4", + "@types/qrcode": "^1.5.0", + "@types/uuid": "^9.0.2", + "@typescript-eslint/eslint-plugin": "^5.59.11", + "@typescript-eslint/parser": "^5.60.1", + "codelyzer": "^6.0.2", + "eslint": "^8.44.0", + "jasmine-core": "~4.6.0", + "jasmine-spec-reporter": "~7.0.0", + "karma": "^6.4.2", + "karma-chrome-launcher": "^3.2.0", + "karma-coverage-istanbul-reporter": "^3.0.3", + "karma-jasmine": "^5.1.0", + "karma-jasmine-html-reporter": "^2.1.0", + "prettier": "^2.8.7", + "prettier-plugin-organize-imports": "^3.2.2", + "protractor": "~7.0.0", + "typescript": "^4.9.5" + } +} diff --git a/pkgs/by-name/zi/zitadel/package.nix b/pkgs/by-name/zi/zitadel/package.nix new file mode 100644 index 00000000000000..cd4780965e138f --- /dev/null +++ b/pkgs/by-name/zi/zitadel/package.nix @@ -0,0 +1,150 @@ +{ stdenv +, buildGo121Module +, callPackage +, fetchFromGitHub +, lib + +, buf +, cacert +, grpc-gateway +, protoc-gen-go +, protoc-gen-go-grpc +, protoc-gen-validate +, sass +, statik +}: + +let + version = "2.37.2"; + zitadelRepo = fetchFromGitHub { + owner = "zitadel"; + repo = "zitadel"; + rev = "v${version}"; + hash = "sha256-iWEL7R7eNDV4c1CZhmxxiHHI9ExwU6gnmHI6ildaXWY="; + }; + goModulesHash = "sha256-lk4jEiI85EKk0G4JCHvCazqBBTfiNJqSfzvrJgDZ1Nc="; + + buildZitadelProtocGen = name: + buildGo121Module { + pname = "protoc-gen-${name}"; + inherit version; + + src = zitadelRepo; + + proxyVendor = true; + vendorHash = goModulesHash; + + buildPhase = '' + go install internal/protoc/protoc-gen-${name}/main.go + ''; + + postInstall = '' + mv $out/bin/main $out/bin/protoc-gen-${name} + ''; + }; + + protoc-gen-authoption = buildZitadelProtocGen "authoption"; + protoc-gen-zitadel = buildZitadelProtocGen "zitadel"; + + # Buf downloads dependencies from an external repo - there doesn't seem to + # really be any good way around it. We'll use a fixed-output derivation so it + # can download what it needs, and output the relevant generated code for use + # during the main build. + generateProtobufCode = + { pname + , nativeBuildInputs ? [ ] + , bufArgs ? "" + , workDir ? "." + , outputPath + , hash + }: + stdenv.mkDerivation { + name = "${pname}-buf-generated"; + + src = zitadelRepo; + + nativeBuildInputs = nativeBuildInputs ++ [ buf ]; + + buildPhase = '' + cd ${workDir} + export SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-bundle.crt" + HOME=$TMPDIR buf generate ${bufArgs} + ''; + + installPhase = '' + cp -r ${outputPath} $out + ''; + + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + outputHash = hash; + }; + + protobufGenerated = generateProtobufCode { + pname = "zitadel"; + nativeBuildInputs = [ + grpc-gateway + protoc-gen-authoption + protoc-gen-go + protoc-gen-go-grpc + protoc-gen-validate + protoc-gen-zitadel + ]; + outputPath = ".artifacts"; + hash = "sha256-+9UFBWBuSYNbfimKwJUSoiUh+8bDHGnPdx1MKDul1U4="; + }; +in +buildGo121Module rec { + name = "zitadel"; + inherit version; + + src = zitadelRepo; + + nativeBuildInputs = [ sass statik ]; + + proxyVendor = true; + vendorHash = goModulesHash; + + # Adapted from Makefile in repo, with dependency fetching and protobuf codegen + # bits removed + buildPhase = '' + mkdir -p pkg/grpc + cp -r ${protobufGenerated}/grpc/github.com/zitadel/zitadel/pkg/grpc/* pkg/grpc + mkdir -p openapi/v2/zitadel + cp -r ${protobufGenerated}/grpc/zitadel/ openapi/v2/zitadel + + go generate internal/api/ui/login/static/resources/generate.go + go generate internal/api/ui/login/statik/generate.go + go generate internal/notification/statik/generate.go + go generate internal/statik/generate.go + + mkdir -p docs/apis/assets + go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/ -assets=docs/apis/assets/assets.md + + cp -r ${passthru.console}/* internal/api/ui/console/static + CGO_ENABLED=0 go build -o zitadel -v -ldflags="-s -w -X 'github.com/zitadel/zitadel/cmd/build.version=${version}'" + ''; + + installPhase = '' + mkdir -p $out/bin + install -Dm755 zitadel $out/bin/ + ''; + + passthru = { + console = callPackage + (import ./console.nix { + inherit generateProtobufCode version zitadelRepo; + }) + { }; + }; + + meta = with lib; { + description = "Identity and access management platform"; + homepage = "https://zitadel.com/"; + downloadPage = "https://github.com/zitadel/zitadel/releases"; + platforms = platforms.linux ++ platforms.darwin; + license = licenses.asl20; + sourceProvenance = [ sourceTypes.fromSource ]; + maintainers = with maintainers; [ Sorixelle ]; + }; +}