From d310dd60cb321fa304227ae0db32b5a643d9711a Mon Sep 17 00:00:00 2001 From: t4ccer Date: Tue, 22 Oct 2024 20:11:48 -0600 Subject: [PATCH] nixos/pds: init module --- .../manual/release-notes/rl-2505.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/pds.nix | 233 ++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 nixos/modules/services/web-apps/pds.nix diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index fd625646c11e8f..7e9a8e9fd803db 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -93,6 +93,8 @@ - [Zipline](https://zipline.diced.sh/), a ShareX/file upload server that is easy to use, packed with features, and with an easy setup. Available as [services.zipline](#opt-services.zipline.enable). +- [PDS](https://github.com/bluesky-social/pds), Personal Data Server for [bsky](https://bsky.social/). Available as [services.pds](option.html#opt-services.pds). + - [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). - [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). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index c8f6dbb22a374e..b3feba36e14ab0 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1542,6 +1542,7 @@ ./services/web-apps/mobilizon.nix ./services/web-apps/openwebrx.nix ./services/web-apps/outline.nix + ./services/web-apps/pds.nix ./services/web-apps/peering-manager.nix ./services/web-apps/peertube.nix ./services/web-apps/pgpkeyserver-lite.nix diff --git a/nixos/modules/services/web-apps/pds.nix b/nixos/modules/services/web-apps/pds.nix new file mode 100644 index 00000000000000..b17cd628c1577c --- /dev/null +++ b/nixos/modules/services/web-apps/pds.nix @@ -0,0 +1,233 @@ +{ + lib, + pkgs, + config, + ... +}: +let + cfg = config.services.pds; + + inherit (lib) + getExe + mkEnableOption + mkIf + mkOption + mkPackageOption + escapeShellArgs + concatMapStringsSep + types + literalExpression + ; + + pdsadminWrapper = + let + cfgSystemd = config.systemd.services.pds.serviceConfig; + in + pkgs.writeShellScriptBin "pdsadmin" '' + DUMMY_PDS_ENV_FILE="$(mktemp)" + trap 'rm -f "$DUMMY_PDS_ENV_FILE"' EXIT + env "PDS_ENV_FILE=$DUMMY_PDS_ENV_FILE" \ + ${escapeShellArgs cfgSystemd.Environment} \ + ${concatMapStringsSep " " (envFile: "$(cat ${envFile})") cfgSystemd.EnvironmentFile} \ + ${getExe pkgs.pdsadmin} "$@" + ''; +in +# All defaults are from https://github.com/bluesky-social/pds/blob/8b9fc24cec5f30066b0d0b86d2b0ba3d66c2b532/installer.sh +{ + options.services.pds = { + enable = mkEnableOption "pds"; + + package = mkPackageOption pkgs "pds" { }; + + settings = mkOption { + type = types.submodule { + freeformType = types.attrsOf ( + types.oneOf [ + (types.nullOr types.str) + types.port + ] + ); + options = { + PDS_PORT = mkOption { + type = types.port; + default = 3000; + description = "Port to listen on"; + }; + + PDS_HOSTNAME = mkOption { + type = types.str; + example = "pds.example.com"; + description = "Instance hostname (base domain name)"; + }; + + PDS_BLOB_UPLOAD_LIMIT = mkOption { + type = types.str; + default = "52428800"; + description = "Size limit of uploaded blobs in bytes"; + }; + + PDS_DID_PLC_URL = mkOption { + type = types.str; + default = "https://plc.directory"; + description = "URL of DID PLC directory"; + }; + + PDS_BSKY_APP_VIEW_URL = mkOption { + type = types.str; + default = "https://api.bsky.app"; + description = "URL of bsky frontend"; + }; + + PDS_BSKY_APP_VIEW_DID = mkOption { + type = types.str; + default = "did:web:api.bsky.app"; + description = "DID of bsky frontend"; + }; + + PDS_REPORT_SERVICE_URL = mkOption { + type = types.str; + default = "https://mod.bsky.app"; + description = "URL of mod service"; + }; + + PDS_REPORT_SERVICE_DID = mkOption { + type = types.str; + default = "did:plc:ar7c4by46qjdydhdevvrndac"; + description = "DID of mod service"; + }; + + PDS_CRAWLERS = mkOption { + type = types.str; + default = "https://bsky.network"; + description = "URL of crawlers"; + }; + + PDS_DATA_DIRECTORY = mkOption { + type = types.str; + default = "/var/lib/pds"; + description = "Directory to store state"; + }; + + PDS_BLOBSTORE_DISK_LOCATION = mkOption { + type = types.nullOr types.str; + default = "/var/lib/pds/blocks"; + description = "Store blobs at this location, set to null to use e.g. S3"; + }; + + LOG_ENABLED = mkOption { + type = types.nullOr types.str; + default = "true"; + description = "Enable logging"; + }; + }; + }; + + description = '' + Environment variables to set for the service. Secrets should be + specified using {option}`environmentFile`. + + Refer to for available environment variables. + ''; + }; + + environmentFiles = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + File to load environment variables from. Loaded variables override + values set in {option}`environment`. + + Use it to set values of `PDS_JWT_SECRET`, `PDS_ADMIN_PASSWORD`, + and `PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX` secrets. + `PDS_JWT_SECRET` and `PDS_ADMIN_PASSWORD` can be generated with + ``` + openssl rand --hex 16 + ``` + `PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX` can be generated with + ``` + openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32 + ``` + ''; + }; + + pdsadmin = { + enable = mkOption { + type = types.bool; + default = cfg.enable; + defaultText = literalExpression "config.services.pds.enable"; + description = "Add pdsadmin script to PATH"; + }; + }; + }; + + config = mkIf cfg.enable { + environment = mkIf cfg.pdsadmin.enable { + systemPackages = [ pdsadminWrapper ]; + }; + + systemd.services.pds = { + description = "pds"; + + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = getExe cfg.package; + Environment = lib.mapAttrsToList (k: v: "${k}=${if builtins.isInt v then toString v else v}") ( + lib.filterAttrs (_: v: v != null) cfg.settings + ); + + EnvironmentFile = cfg.environmentFiles; + User = "pds"; + Group = "pds"; + StateDirectory = "pds"; + StateDirectoryMode = "0755"; + Restart = "always"; + + # Hardening + RemoveIPC = true; + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectClock = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + PrivateMounts = true; + SystemCallArchitectures = [ "native" ]; + MemoryDenyWriteExecute = false; # required by V8 JIT + RestrictNamespaces = true; + RestrictSUIDSGID = true; + ProtectHostname = true; + LockPersonality = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + RestrictRealtime = true; + DeviceAllow = [ "" ]; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectHome = true; + PrivateUsers = true; + PrivateTmp = true; + UMask = "0077"; + }; + }; + + users = { + users.pds = { + group = "pds"; + isSystemUser = true; + }; + groups.pds = { }; + }; + + }; + + meta.maintainers = with lib.maintainers; [ t4ccer ]; +}