about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRuby Juric <ruby@srxl.me>2023-10-03 05:11:18 +0000
committerGitHub <noreply@github.com>2023-10-03 07:11:18 +0200
commit638d19ac92232265a6e7031878ea35a0734c8def (patch)
tree44e683fe562d5b6d4f14f5b9c6f0f4b1bbb0cefc
parent4d29250d5b55fe14280906afad7afacd910850b8 (diff)
zitadel: init at 2.37.2; nixos/zitadel: init (#254896)
-rw-r--r--maintainers/maintainer-list.nix10
-rw-r--r--nixos/doc/manual/release-notes/rl-2311.section.md2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/web-apps/zitadel.nix223
-rw-r--r--pkgs/by-name/zi/zitadel/console.nix51
-rw-r--r--pkgs/by-name/zi/zitadel/package.json88
-rw-r--r--pkgs/by-name/zi/zitadel/package.nix150
7 files changed, 525 insertions, 0 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index 8b771a03a051d..d3f781ccb80f5 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 f235bca8e7ce5..b7d41f4b3f22c 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 ec6f410a48f68..4e2275cc661de 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 0000000000000..f225d138cc434
--- /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 0000000000000..f2e1f26f3b879
--- /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 0000000000000..0add6d2556b18
--- /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 0000000000000..cd4780965e138
--- /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 ];
+  };
+}