Unverified Commit f3cfb1c5 authored by Marcus Ramberg's avatar Marcus Ramberg Committed by GitHub
Browse files

nixos/pocket-id: init, pocket-id: init at 0.45.0 (#381867)

parents b6f910a2 01be6580
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -166,6 +166,8 @@

- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).

- [pocket-id](https://pocket-id.org/), an OIDC provider with passkeys support. Available as [services.pocket-id](#opt-services.pocket-id.enable).

- [nvidia-gpu](https://github.com/utkuozdemir/nvidia_gpu_exporter), a Prometheus exporter that scrapes `nvidia-smi` for GPU metrics. Available as [services.prometheus.exporters.nvidia-gpu](#opt-services.prometheus.exporters.nvidia-gpu.enable).

- [OpenGamepadUI](https://github.com/ShadowBlip/OpenGamepadUI/), an open source gamepad-native game launcher and overlay for Linux. Available as [programs.opengamepadui](#opt-programs.opengamepadui.enable).
+1 −0
Original line number Diff line number Diff line
@@ -1419,6 +1419,7 @@
  ./services/security/paretosecurity.nix
  ./services/security/pass-secret-service.nix
  ./services/security/physlock.nix
  ./services/security/pocket-id.nix
  ./services/security/shibboleth-sp.nix
  ./services/security/sks.nix
  ./services/security/sshguard.nix
+278 −0
Original line number Diff line number Diff line
{
  lib,
  pkgs,
  config,
  ...
}:

let
  inherit (lib)
    mkEnableOption
    mkIf
    mkOption
    optionalAttrs
    optional
    mkPackageOption
    ;
  inherit (lib.types)
    bool
    path
    str
    submodule
    ;

  cfg = config.services.pocket-id;

  format = pkgs.formats.keyValue { };
  settingsFile = format.generate "pocket-id-env-vars" cfg.settings;
in
{
  meta.maintainers = with lib.maintainers; [
    gepbird
    ymstnt
  ];

  options.services.pocket-id = {
    enable = mkEnableOption "Pocket ID server";

    package = mkPackageOption pkgs "pocket-id" { };

    environmentFile = mkOption {
      type = path;
      description = ''
        Path to an environment file loaded for the Pocket ID service.

        This can be used to securely store tokens and secrets outside of the world-readable Nix store.

        Example contents of the file:
        MAXMIND_LICENSE_KEY=your-license-key
      '';
      default = "/dev/null";
      example = "/var/lib/secrets/pocket-id";
    };

    settings = mkOption {
      type = submodule {
        freeformType = format.type;

        options = {
          PUBLIC_APP_URL = mkOption {
            type = str;
            description = ''
              The URL where you will access the app.
            '';
            default = "http://localhost";
          };

          TRUST_PROXY = mkOption {
            type = bool;
            description = ''
              Whether the app is behind a reverse proxy.
            '';
            default = false;
          };
        };
      };

      default = { };

      description = ''
        Environment variables that will be passed to Pocket ID, see
        [configuration options](https://pocket-id.org/docs/configuration/environment-variables)
        for supported values.
      '';
    };

    dataDir = mkOption {
      type = path;
      default = "/var/lib/pocket-id";
      description = ''
        The directory where Pocket ID will store its data, such as the database.
      '';
    };

    user = mkOption {
      type = str;
      default = "pocket-id";
      description = "User account under which Pocket ID runs.";
    };

    group = mkOption {
      type = str;
      default = "pocket-id";
      description = "Group account under which Pocket ID runs.";
    };
  };

  config = mkIf cfg.enable {
    warnings = (
      optional (cfg.settings ? MAXMIND_LICENSE_KEY)
        "config.services.pocket-id.settings.MAXMIND_LICENSE_KEY will be stored as plaintext in the Nix store. Use config.services.pocket-id.environmentFile instead."
    );

    systemd.tmpfiles.rules = [
      "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group}"
    ];

    systemd.services = {
      pocket-id-backend = {
        description = "Pocket ID backend";
        after = [ "network.target" ];
        wantedBy = [ "multi-user.target" ];
        restartTriggers = [
          cfg.package
          cfg.environmentFile
          settingsFile
        ];

        serviceConfig = {
          Type = "simple";
          User = cfg.user;
          Group = cfg.group;
          WorkingDirectory = cfg.dataDir;
          ExecStart = "${cfg.package}/bin/pocket-id-backend";
          Restart = "always";
          EnvironmentFile = [
            cfg.environmentFile
            settingsFile
          ];

          # Hardening
          AmbientCapabilities = "";
          CapabilityBoundingSet = "";
          DeviceAllow = "";
          DevicePolicy = "closed";
          #IPAddressDeny = "any"; # communicates with the frontend
          LockPersonality = true;
          MemoryDenyWriteExecute = true;
          NoNewPrivileges = true;
          PrivateDevices = true;
          PrivateNetwork = false; # communicates with the frontend
          PrivateTmp = true;
          PrivateUsers = true;
          ProcSubset = "pid";
          ProtectClock = true;
          ProtectControlGroups = true;
          ProtectHome = true;
          ProtectHostname = true;
          ProtectKernelLogs = true;
          ProtectKernelModules = true;
          ProtectKernelTunables = true;
          ProtectProc = "invisible";
          ProtectSystem = "full"; # needs to write in cfg.dataDir
          RemoveIPC = true;
          RestrictAddressFamilies = [
            "AF_INET"
            "AF_INET6"
          ];
          RestrictNamespaces = true;
          RestrictRealtime = true;
          RestrictSUIDSGID = true;
          SystemCallArchitectures = "native";
          SystemCallFilter = lib.concatStringsSep " " [
            "~"
            "@clock"
            "@cpu-emulation"
            "@debug"
            "@module"
            "@mount"
            "@obsolete"
            "@privileged"
            "@raw-io"
            "@reboot"
            #"@resources" # vm test segfaults
            "@swap"
          ];
          UMask = "0077";
        };
      };

      pocket-id-frontend = {
        description = "Pocket ID frontend";
        after = [
          "network.target"
          "pocket-id-backend.service"
        ];
        wantedBy = [ "multi-user.target" ];
        restartTriggers = [
          cfg.package
          cfg.environmentFile
          settingsFile
        ];

        serviceConfig = {
          Type = "simple";
          User = cfg.user;
          Group = cfg.group;
          ExecStart = "${cfg.package}/bin/pocket-id-frontend";
          Restart = "always";
          EnvironmentFile = [
            cfg.environmentFile
            settingsFile
          ];

          # Hardening
          AmbientCapabilities = "";
          CapabilityBoundingSet = "";
          DeviceAllow = "";
          DevicePolicy = "closed";
          #IPAddressDeny = "any"; # communicates with the backend and client
          LockPersonality = true;
          MemoryDenyWriteExecute = false; # V8_Fatal segfault
          NoNewPrivileges = true;
          PrivateDevices = true;
          PrivateNetwork = false; # communicates with the backend and client
          PrivateTmp = true;
          PrivateUsers = true;
          ProcSubset = "pid";
          ProtectClock = true;
          ProtectControlGroups = true;
          ProtectHome = true;
          ProtectHostname = true;
          ProtectKernelLogs = true;
          ProtectKernelModules = true;
          ProtectKernelTunables = true;
          ProtectProc = "invisible";
          ProtectSystem = "strict";
          RemoveIPC = true;
          RestrictAddressFamilies = [
            "AF_INET"
            "AF_INET6"
          ];
          RestrictNamespaces = true;
          RestrictRealtime = true;
          RestrictSUIDSGID = true;
          SystemCallArchitectures = "native";
          SystemCallFilter = lib.concatStringsSep " " [
            "~"
            "@clock"
            "@cpu-emulation"
            "@debug"
            "@module"
            "@mount"
            "@obsolete"
            "@privileged"
            "@raw-io"
            "@reboot"
            "@resources"
            "@swap"
          ];
          UMask = "0077";
        };
      };
    };

    users.users = optionalAttrs (cfg.user == "pocket-id") {
      pocket-id = {
        isSystemUser = true;
        group = cfg.group;
        description = "Pocket ID backend user";
        home = cfg.dataDir;
      };
    };

    users.groups = optionalAttrs (cfg.group == "pocket-id") {
      pocket-id = { };
    };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -1039,6 +1039,7 @@ in
  pleroma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./pleroma.nix { };
  plikd = handleTest ./plikd.nix { };
  plotinus = handleTest ./plotinus.nix { };
  pocket-id = handleTest ./pocket-id.nix { };
  podgrab = handleTest ./podgrab.nix { };
  podman = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./podman/default.nix { };
  podman-tls-ghostunnel = handleTestOn [
+47 −0
Original line number Diff line number Diff line
import ./make-test-python.nix (
  { lib, ... }:

  {
    name = "pocket-id";
    meta.maintainers = with lib.maintainers; [
      gepbird
      ymstnt
    ];

    nodes = {
      machine =
        { ... }:
        {
          services.pocket-id = {
            enable = true;
            settings = {
              PORT = 10001;
              INTERNAL_BACKEND_URL = "http://localhost:10002";
              BACKEND_PORT = 10002;
            };
          };
        };
    };

    testScript =
      { nodes, ... }:
      let
        inherit (nodes.machine.services.pocket-id) settings;
        inherit (builtins) toString;
      in
      ''
        machine.wait_for_unit("pocket-id-backend.service")
        machine.wait_for_open_port(${toString settings.BACKEND_PORT})
        machine.wait_for_unit("pocket-id-frontend.service")
        machine.wait_for_open_port(${toString settings.PORT})

        backend_status = machine.succeed("curl -L -o /tmp/backend-output -w '%{http_code}' http://localhost:${toString settings.BACKEND_PORT}/api/users/me")
        assert backend_status == "401"
        machine.succeed("grep 'You are not signed in' /tmp/backend-output")

        frontend_status = machine.succeed("curl -L -o /tmp/frontend-output -w '%{http_code}' http://localhost:${toString settings.PORT}")
        assert frontend_status == "200"
        machine.succeed("grep 'Sign in to Pocket ID' /tmp/frontend-output")
      '';
  }
)
Loading