ops/nixos/bsky-pds: init
This commit is contained in:
parent
d5996ca89a
commit
0c4a432cf2
4 changed files with 294 additions and 1 deletions
249
ops/nixos/lib/bsky-pds.nix
Normal file
249
ops/nixos/lib/bsky-pds.nix
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
{ depot, config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.my.services.bsky-pds;
|
||||||
|
|
||||||
|
marshalValue = key: value: if builtins.isString value then value
|
||||||
|
else if builtins.isInt value then toString value
|
||||||
|
else if builtins.isBool value then (if value then "true" else "false")
|
||||||
|
else throw "unknown value type for key ${key}";
|
||||||
|
cfgFile = pkgs.writeTextFile {
|
||||||
|
name = "bsky-pds.env";
|
||||||
|
text = lib.concatStringsSep "\n" (lib.mapAttrsToList (attrName: attrVal: lib.optionalString (attrVal != null) ''
|
||||||
|
${lib.toUpper attrName}=${marshalValue attrName attrVal}
|
||||||
|
'') cfg.settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
inSecretsDir = v: builtins.isString v && (builtins.match "/var/lib/bsky-pds/secrets/.*" (toString v)) != null;
|
||||||
|
|
||||||
|
testAndGenerate = how: path: ''
|
||||||
|
test -f "${path}" || {
|
||||||
|
${how} >> "${path}"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
testAndGenerateHex = testAndGenerate "${pkgs.openssl}/bin/openssl rand --hex 16";
|
||||||
|
testAndGenerateK256 = testAndGenerate "${pkgs.openssl}/bin/openssl ecparam --name secp256k1 --genkey --noout --outform DER";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.services.bsky-pds = {
|
||||||
|
enable = lib.mkEnableOption "Bluesky Personal Data Server";
|
||||||
|
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = depot.nix.pkgs.bsky-pds;
|
||||||
|
description = ''
|
||||||
|
Package containing the PDS code.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configureCaddy = (lib.mkEnableOption "configure Caddy to serve the PDS") // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = with lib.types; submodule {
|
||||||
|
freeformType = attrsOf (nullOr (oneOf [
|
||||||
|
str
|
||||||
|
bool
|
||||||
|
int
|
||||||
|
]));
|
||||||
|
|
||||||
|
options.log_enabled = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether or not to output any log messages.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 3000;
|
||||||
|
description = ''
|
||||||
|
Port number that the PDS will listen on.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_hostname = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
Hostname under which the PDS will run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_admin_email = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
Email address of the PDS operator.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_data_directory = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = "/var/lib/bsky-pds/data";
|
||||||
|
description = ''
|
||||||
|
Path to data directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_blobstore_disk_location = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = "/var/lib/bsky-pds/blobs";
|
||||||
|
description = ''
|
||||||
|
Path to location for blobstore storage, if using on-disk storage.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_blob_upload_limit = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 52428800;
|
||||||
|
description = ''
|
||||||
|
Maximum allowable blob size for upload.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_did_plc_url = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://plc.directory";
|
||||||
|
description = ''
|
||||||
|
URL of the PLC directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.pds_bsky_app_view_url = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://api.bsky.app";
|
||||||
|
description = ''
|
||||||
|
URL of the Bluesky AppView.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_bsky_app_view_did = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "did:web:api.bsky.app";
|
||||||
|
description = ''
|
||||||
|
DID of the Bluesky AppView.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_report_service_url = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://mod.bsky.app";
|
||||||
|
description = ''
|
||||||
|
URL of the Bluesky moderation service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_report_service_did = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "did:plc:ar7c4by46qjdydhdevvrndac";
|
||||||
|
description = ''
|
||||||
|
DID of the Bluesky moderation service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.crawlers = lib.mkOption {
|
||||||
|
type = lib.types.commas;
|
||||||
|
default = "https://bsky.network";
|
||||||
|
description = ''
|
||||||
|
URLs of hosts to notify of new data.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Configuration for Bluesky PDS.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
generateSecrets = lib.mkEnableOption "automatically generate required PDS secrets";
|
||||||
|
|
||||||
|
secrets = lib.mkOption {
|
||||||
|
type = with lib.types; submodule {
|
||||||
|
freeformType = attrsOf (nullOr path);
|
||||||
|
|
||||||
|
options.pds_jwt_secret = lib.mkOption {
|
||||||
|
type = nullOr path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Path to a file containing a 16 character hex secret used for JWT secrets.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_admin_password = lib.mkOption {
|
||||||
|
type = nullOr path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Path to a file containing the PDS admin password.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.pds_plc_rotation_key_k256_private_key = lib.mkOption {
|
||||||
|
type = nullOr path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Path to a file containing a secp256k1 private key in DER format.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Path to files containing secrets for Bluesky PDS.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [(lib.mkIf cfg.generateSecrets {
|
||||||
|
my.services.bsky-pds.secrets = {
|
||||||
|
pds_jwt_secret = lib.mkDefault "/var/lib/bsky-pds/secrets/pds_jwt_secret";
|
||||||
|
pds_admin_password = lib.mkDefault "/var/lib/bsky-pds/secrets/pds_admin_password";
|
||||||
|
pds_plc_rotation_key_k256_private_key = lib.mkDefault "/var/lib/bsky-pds/secrets/pds_plc_rotation_key_k256_private_key";
|
||||||
|
};
|
||||||
|
}) (lib.mkIf cfg.enable {
|
||||||
|
systemd.services.bsky-pds = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
unitConfig = {
|
||||||
|
Description = "Bluesky PDS Service";
|
||||||
|
Documentation = "https://github.com/bluesky-social/pds";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
mkdir -p /var/lib/bsky-pds "$PDS_DATA_DIRECTORY"
|
||||||
|
if [[ ! -z "$PDS_BLOBSTORE_DISK_LOCATION" ]]; then
|
||||||
|
mkdir -p "$PDS_BLOBSTORE_DISK_LOCATION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
old_umask=$(umask)
|
||||||
|
umask 077
|
||||||
|
mkdir -p /var/lib/bsky-pds/secrets
|
||||||
|
${lib.optionalString (inSecretsDir cfg.secrets.pds_jwt_secret) (testAndGenerateHex cfg.secrets.pds_jwt_secret)}
|
||||||
|
${lib.optionalString (inSecretsDir cfg.secrets.pds_admin_password) (testAndGenerateHex cfg.secrets.pds_admin_password)}
|
||||||
|
${lib.optionalString (inSecretsDir cfg.secrets.pds_plc_rotation_key_k256_private_key) (testAndGenerateK256 cfg.secrets.pds_plc_rotation_key_k256_private_key)}
|
||||||
|
umask "$old_umask"
|
||||||
|
|
||||||
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: lib.optionalString (v != null) ''
|
||||||
|
export ${lib.toUpper k}="$(cat "${v}")"
|
||||||
|
'') cfg.secrets)}
|
||||||
|
|
||||||
|
${lib.optionalString (cfg.secrets.pds_plc_rotation_key_k256_private_key != null) ''
|
||||||
|
if [[ -z "$PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX ]]; then
|
||||||
|
export PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="$(cat "${cfg.secrets.pds_plc_rotation_key_k256_private_key}" | ${pkgs.coreutils}/bin/tail --bytes=+8 | ${pkgs.coreutils}/bin/head --bytes=32 | ${pkgs.xxd}/bin/xxd --plain --cols 32)"
|
||||||
|
fi
|
||||||
|
''}
|
||||||
|
|
||||||
|
exec ${cfg.package}/bin/bsky-pds
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
User = "bsky-pds";
|
||||||
|
DynamicUser = true;
|
||||||
|
StateDirectory = "bsky-pds";
|
||||||
|
EnvironmentFile = cfgFile;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) (lib.mkIf (cfg.enable && cfg.configureCaddy) {
|
||||||
|
services.caddy = {
|
||||||
|
enable = lib.mkDefault true;
|
||||||
|
globalConfig = ''
|
||||||
|
email ${cfg.settings.pds_admin_email}
|
||||||
|
on_demand_tls {
|
||||||
|
ask http://localhost:${toString cfg.settings.port}/tls-check
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
virtualHosts."${cfg.settings.pds_hostname}" = {
|
||||||
|
serverAliases = [ "*.${cfg.settings.pds_hostname}" ];
|
||||||
|
extraConfig = ''
|
||||||
|
tls {
|
||||||
|
on_demand
|
||||||
|
}
|
||||||
|
reverse_proxy http://localhost:${toString cfg.settings.port}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})];
|
||||||
|
}
|
42
ops/nixos/rexxar/bsky-pds.nix
Normal file
42
ops/nixos/rexxar/bsky-pds.nix
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
bskySecretsFromVault = [
|
||||||
|
"PDS_ADMIN_PASSWORD"
|
||||||
|
"PDS_BLOBSTORE_S3_ACCESS_KEY_ID"
|
||||||
|
"PDS_BLOBSTORE_S3_SECRET_ACCESS_KEY"
|
||||||
|
"PDS_JWT_SECRET"
|
||||||
|
"PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX"
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
imports = [ ../lib/bsky-pds.nix ];
|
||||||
|
|
||||||
|
my.services.bsky-pds = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
pds_hostname = "pds.lukegb.com";
|
||||||
|
pds_admin_email = "bskypds@lukegb.com";
|
||||||
|
pds_blobstore_disk_location = null;
|
||||||
|
pds_blobstore_s3_bucket = "bsky-pds";
|
||||||
|
pds_blobstore_s3_region = "anywhere";
|
||||||
|
pds_blobstore_s3_endpoint = "https://objdump.zxcvbnm.ninja";
|
||||||
|
pds_blobstore_s3_force_path_style = false;
|
||||||
|
pds_blobstore_s3_upload_timeout_ms = 10000;
|
||||||
|
};
|
||||||
|
generateSecrets = false;
|
||||||
|
secrets = lib.listToAttrs (map (k: lib.nameValuePair (lib.toLower k) config.my.vault.secrets."bsky_${lib.toLower k}".path) bskySecretsFromVault);
|
||||||
|
};
|
||||||
|
|
||||||
|
my.vault.secrets = let
|
||||||
|
bskySecret = key: {
|
||||||
|
group = "bsky-pds";
|
||||||
|
template = ''
|
||||||
|
{{- with secret "kv/apps/bsky-pds" -}}
|
||||||
|
{{- .Data.data.${key} -}}
|
||||||
|
{{- end -}}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in lib.listToAttrs (map (k: lib.nameValuePair "bsky_${lib.toLower k}" (bskySecret k)) bskySecretsFromVault);
|
||||||
|
users.groups.bsky-pds = {};
|
||||||
|
users.users.bsky-pds = { isSystemUser = true; group = "bsky-pds"; };
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
../lib/emfminiserv.nix
|
../lib/emfminiserv.nix
|
||||||
../lib/seaweedfs.nix
|
../lib/seaweedfs.nix
|
||||||
../lib/fup.nix
|
../lib/fup.nix
|
||||||
|
./bsky-pds.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# Otherwise _this_ machine won't enumerate things properly.
|
# Otherwise _this_ machine won't enumerate things properly.
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
my.apps.hacky-vouchproxy = {};
|
my.apps.hacky-vouchproxy = {};
|
||||||
my.apps.hackyplayer = {};
|
my.apps.hackyplayer = {};
|
||||||
my.apps.emfminiserv = {};
|
my.apps.emfminiserv = {};
|
||||||
|
my.apps.bsky-pds = {};
|
||||||
|
|
||||||
my.servers.etheroute-lon01.apps = [ "pomerium" ];
|
my.servers.etheroute-lon01.apps = [ "pomerium" ];
|
||||||
my.servers.howl.apps = [ "nixbuild" ];
|
my.servers.howl.apps = [ "nixbuild" ];
|
||||||
|
@ -90,5 +91,5 @@
|
||||||
my.servers.bvm-heptapod.apps = [ "gitlab-runner" ];
|
my.servers.bvm-heptapod.apps = [ "gitlab-runner" ];
|
||||||
my.servers.bvm-nixosmgmt.apps = [ "plex-pass" ];
|
my.servers.bvm-nixosmgmt.apps = [ "plex-pass" ];
|
||||||
my.servers.bvm-netbox.apps = [ "netbox" ];
|
my.servers.bvm-netbox.apps = [ "netbox" ];
|
||||||
my.servers.rexxar.apps = [ "deluge" "gitlab-runner" "nixbuild" "hacky-vouchproxy" "hackyplayer" "emfminiserv" "fup" ];
|
my.servers.rexxar.apps = [ "deluge" "gitlab-runner" "nixbuild" "hacky-vouchproxy" "hackyplayer" "emfminiserv" "fup" "bsky-pds" ];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue