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

authored by Marcus Ramberg and committed by GitHub f3cfb1c5 b6f910a2

+448
+2
nixos/doc/manual/release-notes/rl-2505.section.md
··· 166 166 167 167 - [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). 168 168 169 + - [pocket-id](https://pocket-id.org/), an OIDC provider with passkeys support. Available as [services.pocket-id](#opt-services.pocket-id.enable). 170 + 169 171 - [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). 170 172 171 173 - [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
nixos/modules/module-list.nix
··· 1419 1419 ./services/security/paretosecurity.nix 1420 1420 ./services/security/pass-secret-service.nix 1421 1421 ./services/security/physlock.nix 1422 + ./services/security/pocket-id.nix 1422 1423 ./services/security/shibboleth-sp.nix 1423 1424 ./services/security/sks.nix 1424 1425 ./services/security/sshguard.nix
+278
nixos/modules/services/security/pocket-id.nix
··· 1 + { 2 + lib, 3 + pkgs, 4 + config, 5 + ... 6 + }: 7 + 8 + let 9 + inherit (lib) 10 + mkEnableOption 11 + mkIf 12 + mkOption 13 + optionalAttrs 14 + optional 15 + mkPackageOption 16 + ; 17 + inherit (lib.types) 18 + bool 19 + path 20 + str 21 + submodule 22 + ; 23 + 24 + cfg = config.services.pocket-id; 25 + 26 + format = pkgs.formats.keyValue { }; 27 + settingsFile = format.generate "pocket-id-env-vars" cfg.settings; 28 + in 29 + { 30 + meta.maintainers = with lib.maintainers; [ 31 + gepbird 32 + ymstnt 33 + ]; 34 + 35 + options.services.pocket-id = { 36 + enable = mkEnableOption "Pocket ID server"; 37 + 38 + package = mkPackageOption pkgs "pocket-id" { }; 39 + 40 + environmentFile = mkOption { 41 + type = path; 42 + description = '' 43 + Path to an environment file loaded for the Pocket ID service. 44 + 45 + This can be used to securely store tokens and secrets outside of the world-readable Nix store. 46 + 47 + Example contents of the file: 48 + MAXMIND_LICENSE_KEY=your-license-key 49 + ''; 50 + default = "/dev/null"; 51 + example = "/var/lib/secrets/pocket-id"; 52 + }; 53 + 54 + settings = mkOption { 55 + type = submodule { 56 + freeformType = format.type; 57 + 58 + options = { 59 + PUBLIC_APP_URL = mkOption { 60 + type = str; 61 + description = '' 62 + The URL where you will access the app. 63 + ''; 64 + default = "http://localhost"; 65 + }; 66 + 67 + TRUST_PROXY = mkOption { 68 + type = bool; 69 + description = '' 70 + Whether the app is behind a reverse proxy. 71 + ''; 72 + default = false; 73 + }; 74 + }; 75 + }; 76 + 77 + default = { }; 78 + 79 + description = '' 80 + Environment variables that will be passed to Pocket ID, see 81 + [configuration options](https://pocket-id.org/docs/configuration/environment-variables) 82 + for supported values. 83 + ''; 84 + }; 85 + 86 + dataDir = mkOption { 87 + type = path; 88 + default = "/var/lib/pocket-id"; 89 + description = '' 90 + The directory where Pocket ID will store its data, such as the database. 91 + ''; 92 + }; 93 + 94 + user = mkOption { 95 + type = str; 96 + default = "pocket-id"; 97 + description = "User account under which Pocket ID runs."; 98 + }; 99 + 100 + group = mkOption { 101 + type = str; 102 + default = "pocket-id"; 103 + description = "Group account under which Pocket ID runs."; 104 + }; 105 + }; 106 + 107 + config = mkIf cfg.enable { 108 + warnings = ( 109 + optional (cfg.settings ? MAXMIND_LICENSE_KEY) 110 + "config.services.pocket-id.settings.MAXMIND_LICENSE_KEY will be stored as plaintext in the Nix store. Use config.services.pocket-id.environmentFile instead." 111 + ); 112 + 113 + systemd.tmpfiles.rules = [ 114 + "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group}" 115 + ]; 116 + 117 + systemd.services = { 118 + pocket-id-backend = { 119 + description = "Pocket ID backend"; 120 + after = [ "network.target" ]; 121 + wantedBy = [ "multi-user.target" ]; 122 + restartTriggers = [ 123 + cfg.package 124 + cfg.environmentFile 125 + settingsFile 126 + ]; 127 + 128 + serviceConfig = { 129 + Type = "simple"; 130 + User = cfg.user; 131 + Group = cfg.group; 132 + WorkingDirectory = cfg.dataDir; 133 + ExecStart = "${cfg.package}/bin/pocket-id-backend"; 134 + Restart = "always"; 135 + EnvironmentFile = [ 136 + cfg.environmentFile 137 + settingsFile 138 + ]; 139 + 140 + # Hardening 141 + AmbientCapabilities = ""; 142 + CapabilityBoundingSet = ""; 143 + DeviceAllow = ""; 144 + DevicePolicy = "closed"; 145 + #IPAddressDeny = "any"; # communicates with the frontend 146 + LockPersonality = true; 147 + MemoryDenyWriteExecute = true; 148 + NoNewPrivileges = true; 149 + PrivateDevices = true; 150 + PrivateNetwork = false; # communicates with the frontend 151 + PrivateTmp = true; 152 + PrivateUsers = true; 153 + ProcSubset = "pid"; 154 + ProtectClock = true; 155 + ProtectControlGroups = true; 156 + ProtectHome = true; 157 + ProtectHostname = true; 158 + ProtectKernelLogs = true; 159 + ProtectKernelModules = true; 160 + ProtectKernelTunables = true; 161 + ProtectProc = "invisible"; 162 + ProtectSystem = "full"; # needs to write in cfg.dataDir 163 + RemoveIPC = true; 164 + RestrictAddressFamilies = [ 165 + "AF_INET" 166 + "AF_INET6" 167 + ]; 168 + RestrictNamespaces = true; 169 + RestrictRealtime = true; 170 + RestrictSUIDSGID = true; 171 + SystemCallArchitectures = "native"; 172 + SystemCallFilter = lib.concatStringsSep " " [ 173 + "~" 174 + "@clock" 175 + "@cpu-emulation" 176 + "@debug" 177 + "@module" 178 + "@mount" 179 + "@obsolete" 180 + "@privileged" 181 + "@raw-io" 182 + "@reboot" 183 + #"@resources" # vm test segfaults 184 + "@swap" 185 + ]; 186 + UMask = "0077"; 187 + }; 188 + }; 189 + 190 + pocket-id-frontend = { 191 + description = "Pocket ID frontend"; 192 + after = [ 193 + "network.target" 194 + "pocket-id-backend.service" 195 + ]; 196 + wantedBy = [ "multi-user.target" ]; 197 + restartTriggers = [ 198 + cfg.package 199 + cfg.environmentFile 200 + settingsFile 201 + ]; 202 + 203 + serviceConfig = { 204 + Type = "simple"; 205 + User = cfg.user; 206 + Group = cfg.group; 207 + ExecStart = "${cfg.package}/bin/pocket-id-frontend"; 208 + Restart = "always"; 209 + EnvironmentFile = [ 210 + cfg.environmentFile 211 + settingsFile 212 + ]; 213 + 214 + # Hardening 215 + AmbientCapabilities = ""; 216 + CapabilityBoundingSet = ""; 217 + DeviceAllow = ""; 218 + DevicePolicy = "closed"; 219 + #IPAddressDeny = "any"; # communicates with the backend and client 220 + LockPersonality = true; 221 + MemoryDenyWriteExecute = false; # V8_Fatal segfault 222 + NoNewPrivileges = true; 223 + PrivateDevices = true; 224 + PrivateNetwork = false; # communicates with the backend and client 225 + PrivateTmp = true; 226 + PrivateUsers = true; 227 + ProcSubset = "pid"; 228 + ProtectClock = true; 229 + ProtectControlGroups = true; 230 + ProtectHome = true; 231 + ProtectHostname = true; 232 + ProtectKernelLogs = true; 233 + ProtectKernelModules = true; 234 + ProtectKernelTunables = true; 235 + ProtectProc = "invisible"; 236 + ProtectSystem = "strict"; 237 + RemoveIPC = true; 238 + RestrictAddressFamilies = [ 239 + "AF_INET" 240 + "AF_INET6" 241 + ]; 242 + RestrictNamespaces = true; 243 + RestrictRealtime = true; 244 + RestrictSUIDSGID = true; 245 + SystemCallArchitectures = "native"; 246 + SystemCallFilter = lib.concatStringsSep " " [ 247 + "~" 248 + "@clock" 249 + "@cpu-emulation" 250 + "@debug" 251 + "@module" 252 + "@mount" 253 + "@obsolete" 254 + "@privileged" 255 + "@raw-io" 256 + "@reboot" 257 + "@resources" 258 + "@swap" 259 + ]; 260 + UMask = "0077"; 261 + }; 262 + }; 263 + }; 264 + 265 + users.users = optionalAttrs (cfg.user == "pocket-id") { 266 + pocket-id = { 267 + isSystemUser = true; 268 + group = cfg.group; 269 + description = "Pocket ID backend user"; 270 + home = cfg.dataDir; 271 + }; 272 + }; 273 + 274 + users.groups = optionalAttrs (cfg.group == "pocket-id") { 275 + pocket-id = { }; 276 + }; 277 + }; 278 + }
+1
nixos/tests/all-tests.nix
··· 1039 1039 pleroma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./pleroma.nix { }; 1040 1040 plikd = handleTest ./plikd.nix { }; 1041 1041 plotinus = handleTest ./plotinus.nix { }; 1042 + pocket-id = handleTest ./pocket-id.nix { }; 1042 1043 podgrab = handleTest ./podgrab.nix { }; 1043 1044 podman = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./podman/default.nix { }; 1044 1045 podman-tls-ghostunnel = handleTestOn [
+47
nixos/tests/pocket-id.nix
··· 1 + import ./make-test-python.nix ( 2 + { lib, ... }: 3 + 4 + { 5 + name = "pocket-id"; 6 + meta.maintainers = with lib.maintainers; [ 7 + gepbird 8 + ymstnt 9 + ]; 10 + 11 + nodes = { 12 + machine = 13 + { ... }: 14 + { 15 + services.pocket-id = { 16 + enable = true; 17 + settings = { 18 + PORT = 10001; 19 + INTERNAL_BACKEND_URL = "http://localhost:10002"; 20 + BACKEND_PORT = 10002; 21 + }; 22 + }; 23 + }; 24 + }; 25 + 26 + testScript = 27 + { nodes, ... }: 28 + let 29 + inherit (nodes.machine.services.pocket-id) settings; 30 + inherit (builtins) toString; 31 + in 32 + '' 33 + machine.wait_for_unit("pocket-id-backend.service") 34 + machine.wait_for_open_port(${toString settings.BACKEND_PORT}) 35 + machine.wait_for_unit("pocket-id-frontend.service") 36 + machine.wait_for_open_port(${toString settings.PORT}) 37 + 38 + backend_status = machine.succeed("curl -L -o /tmp/backend-output -w '%{http_code}' http://localhost:${toString settings.BACKEND_PORT}/api/users/me") 39 + assert backend_status == "401" 40 + machine.succeed("grep 'You are not signed in' /tmp/backend-output") 41 + 42 + frontend_status = machine.succeed("curl -L -o /tmp/frontend-output -w '%{http_code}' http://localhost:${toString settings.PORT}") 43 + assert frontend_status == "200" 44 + machine.succeed("grep 'Sign in to Pocket ID' /tmp/frontend-output") 45 + ''; 46 + } 47 + )
+119
pkgs/by-name/po/pocket-id/package.nix
··· 1 + { 2 + lib, 3 + fetchFromGitHub, 4 + buildGoModule, 5 + buildNpmPackage, 6 + fetchurl, 7 + makeWrapper, 8 + nodejs, 9 + stdenvNoCC, 10 + nixosTests, 11 + nix-update-script, 12 + }: 13 + 14 + let 15 + version = "0.45.0"; 16 + src = fetchFromGitHub { 17 + owner = "pocket-id"; 18 + repo = "pocket-id"; 19 + tag = "v${version}"; 20 + hash = "sha256-x5Y3ArkIPxiE6avk9DNyFdfkc/pY6h3JH3PZCS8U/GM="; 21 + }; 22 + 23 + backend = buildGoModule { 24 + pname = "pocket-id-backend"; 25 + inherit version src; 26 + 27 + sourceRoot = "${src.name}/backend"; 28 + 29 + vendorHash = "sha256-mqpBP+A2X5ome1Ppg/Kki0C+A77jFtWzUjI/RN+ZCzg="; 30 + 31 + preFixup = '' 32 + mv $out/bin/cmd $out/bin/pocket-id-backend 33 + ''; 34 + }; 35 + 36 + frontend = buildNpmPackage (finalAttrs: { 37 + pname = "pocket-id-frontend"; 38 + inherit version src; 39 + 40 + sourceRoot = "${src.name}/frontend"; 41 + 42 + npmDepsHash = "sha256-cpmZzlz+wusfRLN4iIGdk+I4SWrX/gk2fbhg+Gg3paw="; 43 + npmFlags = [ "--legacy-peer-deps" ]; 44 + 45 + nativeBuildInputs = [ 46 + makeWrapper 47 + ]; 48 + 49 + installPhase = '' 50 + runHook preInstall 51 + 52 + # even though vite build creates most of the minified js files, 53 + # it still needs a few packages from node_modules, try to strip that 54 + npm prune --omit=dev --omit=optional $npmFlags 55 + # larger seemingly unused packages 56 + rm -r node_modules/{lucide-svelte,bits-ui,jiti,@swc,.bin} 57 + # unused file types 58 + for pattern in '*.map' '*.map.js' '*.ts'; do 59 + find . -type f -name "$pattern" -exec rm {} + 60 + done 61 + 62 + mkdir -p $out/{bin,lib/pocket-id-frontend} 63 + cp -r build $out/lib/pocket-id-frontend/dist 64 + cp -r node_modules $out/lib/pocket-id-frontend/node_modules 65 + makeWrapper ${lib.getExe nodejs} $out/bin/pocket-id-frontend \ 66 + --add-flags $out/lib/pocket-id-frontend/dist/index.js 67 + 68 + runHook postInstall 69 + ''; 70 + }); 71 + 72 + in 73 + stdenvNoCC.mkDerivation rec { 74 + pname = "pocket-id"; 75 + inherit 76 + version 77 + src 78 + backend 79 + frontend 80 + ; 81 + 82 + dontUnpack = true; 83 + 84 + installPhase = '' 85 + runHook preInstall 86 + 87 + mkdir -p $out/bin 88 + ln -s ${backend}/bin/pocket-id-backend $out/bin/pocket-id-backend 89 + ln -s ${frontend}/bin/pocket-id-frontend $out/bin/pocket-id-frontend 90 + 91 + runHook postInstall 92 + ''; 93 + 94 + passthru = { 95 + tests = { 96 + inherit (nixosTests) pocket-id; 97 + }; 98 + updateScript = nix-update-script { 99 + extraArgs = [ 100 + "--subpackage" 101 + "backend" 102 + "--subpackage" 103 + "frontend" 104 + ]; 105 + }; 106 + }; 107 + 108 + meta = { 109 + description = "OIDC provider with passkeys support"; 110 + homepage = "https://pocket-id.org"; 111 + changelog = "https://github.com/pocket-id/pocket-id/releases/tag/v${version}"; 112 + license = lib.licenses.bsd2; 113 + maintainers = with lib.maintainers; [ 114 + gepbird 115 + ymstnt 116 + ]; 117 + platforms = lib.platforms.unix; 118 + }; 119 + }