From 302babe7d9a510606b079db0aed153dfa39c577a Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Sat, 17 Jan 2026 00:55:08 +0800 Subject: [PATCH] focaccia: add bluesky PDS + sops-nix secrets mgmt Sovereign infrastructure baby --- .sops.yaml | 11 +++ age_from_1password.nu | 43 ++++++++++ flake.lock | 93 +++++++++++---------- flake.nix | 27 ++++--- systems/focaccia/configuration.nix | 14 +++- systems/focaccia/pds.nix | 99 +++++++++++++++++++++++ systems/focaccia/secrets/bluesky-pds.env | 11 +++ systems/focaccia/secrets/tranquil-pds.env | 19 +++++ 8 files changed, 264 insertions(+), 53 deletions(-) create mode 100644 .sops.yaml create mode 100755 age_from_1password.nu create mode 100644 systems/focaccia/pds.nix create mode 100644 systems/focaccia/secrets/bluesky-pds.env create mode 100644 systems/focaccia/secrets/tranquil-pds.env diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..3ca232e --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,11 @@ +# NOTE: Additions/deletions _MUST_ be synchronized with age_from_1password.nu! +keys: + - &leah "age1lh4sn2s9gxj2s3naqdl4wpmz3uhpd3p8l0jfy6k5hu6cu34uyygsdwadd5" + - &focaccia "age1wtr58sze4sxjjzq9jmsq7ztkvkjakvnfzuqzn025p92htz7zsdesjpc2c8" + +creation_rules: + - path_regex: systems/focaccia/secrets/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *leah + - *focaccia diff --git a/age_from_1password.nu b/age_from_1password.nu new file mode 100755 index 0000000..1cadb16 --- /dev/null +++ b/age_from_1password.nu @@ -0,0 +1,43 @@ +#!/usr/bin/env nix-shell +#!nix-shell -p nushell ssh-to-age -i nu + +#============================================================================== +# SOPS w/ Age helper for retrieving my 1Password secret keys on the fly +# +# Because I am a very lazy person and don't like to watch out for the +# implications of storing my SSH keys on mobile devices that may hypothetically +# be seized at any second, I'm storing all my private keys with 1Password. +# +# Now, should you do this? For convenience, *maybe*? For maximum security? +# Definitely not. Go use a Yubikey or something. I'm just not a fan of +# keeping a metal dongle on my person this entire time. I'll probably just +# write out my master passphrase in my last will or something so people +# I trust can actually regain access to all my stuff in case something goes +# awry. Just in case. +#============================================================================== + +# 1Password UUIDs of my private keys. +# +# I feel *somewhat* safe to make this globally visible since you do +# need to sign in with my 1Password account to actually access them, +# and if you ever get to that point, my security has already been defeated. +# +# All of these should be Ed25519. +# +# NOTE: Additions/deletions _MUST_ be synchronized with .sops.yaml! +let items = [ + # Main SSH key + "bkk3jg6qjnwyymb6gjiopczlba" +] + +# Make sure to sign in first. +# Does nothing if already signed in +^op signin + +$items + | par-each { |item| + ^op read $"op://Development/($item)/private key?ssh-format=openssh" + | ^ssh-to-age -private-key + } + | str join "\n" + diff --git a/flake.lock b/flake.lock index 1b0858d..2035e53 100644 --- a/flake.lock +++ b/flake.lock @@ -22,21 +22,6 @@ "type": "github" } }, - "empty": { - "locked": { - "lastModified": 1759502707, - "narHash": "sha256-ML60zVlqK+R0R6EH2aWhWJh9CYPj2XuaRya9AuMl/GY=", - "owner": "MidAutumnMoon", - "repo": "empty-flake", - "rev": "3b830d637cf569096d7442e57fd221ecb887feda", - "type": "github" - }, - "original": { - "owner": "MidAutumnMoon", - "repo": "empty-flake", - "type": "github" - } - }, "flake-compat": { "flake": false, "locked": { @@ -456,7 +441,6 @@ "root": { "inputs": { "deploy-rs": "deploy-rs", - "empty": "empty", "flake-parts": "flake-parts", "ghostty": "ghostty", "hjem": "hjem", @@ -465,7 +449,9 @@ "nixos-generators": "nixos-generators", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs_2", - "tangled": "tangled" + "sops-nix": "sops-nix", + "tangled": "tangled", + "tranquil-pds": "tranquil-pds" } }, "rust-overlay": { @@ -560,6 +546,26 @@ "type": "github" } }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768481291, + "narHash": "sha256-NjKtkJraCZEnLHAJxLTI+BfdU//9coAz9p5TqveZwPU=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "e085e303dfcce21adcb5fec535d65aacb066f101", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, "sqlite-lib-src": { "flake": false, "locked": { @@ -650,31 +656,15 @@ }, "tangled": { "inputs": { - "actor-typeahead-src": [ - "empty" - ], - "flake-compat": [ - "empty" - ], + "actor-typeahead-src": [], + "flake-compat": [], "gomod2nix": "gomod2nix", - "htmx-src": [ - "empty" - ], - "htmx-ws-src": [ - "empty" - ], - "ibm-plex-mono-src": [ - "empty" - ], - "indigo": [ - "empty" - ], - "inter-fonts-src": [ - "empty" - ], - "lucide-src": [ - "empty" - ], + "htmx-src": [], + "htmx-ws-src": [], + "ibm-plex-mono-src": [], + "indigo": [], + "inter-fonts-src": [], + "lucide-src": [], "nixpkgs": [ "nixpkgs" ], @@ -695,6 +685,27 @@ "url": "https://tangled.org/tangled.org/core" } }, + "tranquil-pds": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768063083, + "narHash": "sha256-n4wojr8uD3FgNRUfvUZzLw8w3K5UA7zq/T13c7h7ANs=", + "ref": "refs/heads/main", + "rev": "32fee7a7fff8493b78ca078a840b5819718f297d", + "revCount": 133, + "type": "git", + "url": "https://tangled.org/lewis.moe/bspds-sandbox" + }, + "original": { + "rev": "32fee7a7fff8493b78ca078a840b5819718f297d", + "type": "git", + "url": "https://tangled.org/lewis.moe/bspds-sandbox" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index 9b4d886..858c618 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,6 @@ inputs = { nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; - empty.url = "github:MidAutumnMoon/empty-flake"; # NOTE: please keep this in alphabetical order. @@ -53,22 +52,32 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + tangled = { url = "git+https://tangled.org/tangled.org/core?shallow=1"; inputs = { nixpkgs.follows = "nixpkgs"; # We don't need any of these - flake-compat.follows = "empty"; - indigo.follows = "empty"; - htmx-src.follows = "empty"; - htmx-ws-src.follows = "empty"; - lucide-src.follows = "empty"; - inter-fonts-src.follows = "empty"; - actor-typeahead-src.follows = "empty"; - ibm-plex-mono-src.follows = "empty"; + flake-compat.follows = ""; + indigo.follows = ""; + htmx-src.follows = ""; + htmx-ws-src.follows = ""; + lucide-src.follows = ""; + inter-fonts-src.follows = ""; + actor-typeahead-src.follows = ""; + ibm-plex-mono-src.follows = ""; }; }; + + tranquil-pds = { + url = "git+https://tangled.org/lewis.moe/bspds-sandbox?rev=32fee7a7fff8493b78ca078a840b5819718f297d"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = diff --git a/systems/focaccia/configuration.nix b/systems/focaccia/configuration.nix index a585cfa..f1d41f0 100644 --- a/systems/focaccia/configuration.nix +++ b/systems/focaccia/configuration.nix @@ -9,6 +9,8 @@ ./networking.nix ../../modules/nixos/hysteria.nix inputs.tangled.nixosModules.knot + inputs.sops-nix.nixosModules.sops + ./pds.nix ]; networking = { @@ -33,7 +35,7 @@ home = "/home/leah"; openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKbsavGX9rGRx5R+7ovLn+r7D/w3zkbqCik4bS31moSz" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINcpWY17MNJBx56APRSvLOfUjHllXn9gY/cV51JaLoh6" ]; }; @@ -83,7 +85,12 @@ services.caddy = { enable = true; email = "srv@acc.pluie.me"; - virtualHosts."focaccia.pluie.me" = { + virtualHosts."pds.pluie.me" = { + extraConfig = '' + reverse_proxy :11037 + ''; + }; + virtualHosts."knot.pluie.me" = { extraConfig = '' reverse_proxy :8964 ''; @@ -99,7 +106,8 @@ listenAddr = "0.0.0.0:8964"; internalListenAddr = "127.0.0.1:4698"; owner = "did:plc:e4f33w5yt2m54tq6vsagpwiu"; - hostname = "focaccia.pluie.me"; + hostname = "knot.pluie.me"; }; }; + } diff --git a/systems/focaccia/pds.nix b/systems/focaccia/pds.nix new file mode 100644 index 0000000..a7c52da --- /dev/null +++ b/systems/focaccia/pds.nix @@ -0,0 +1,99 @@ +{ + # inputs, + config, + # lib, + # pkgs, + ... +}: +{ + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + sops.secrets.bluesky-pds = { + sopsFile = ./secrets/bluesky-pds.env; + format = "dotenv"; + }; + + # TODO: replace with tranquil PDS once i had more time + services.bluesky-pds = { + enable = true; + environmentFiles = [ config.sops.secrets.bluesky-pds.path ]; + settings = { + PDS_HOSTNAME = "pds.pluie.me"; + PDS_PORT = 11037; + }; + }; + + # services.postgresql = { + # enable = true; + # authentication = '' + # host all postgres samehost trust + # ''; + # ensureDatabases = [ "pds" ]; + # }; + + # users.users.pds = { + # group = "pds"; + # isSystemUser = true; + # }; + + # users.groups.pds = { }; + + # systemd.services.tranquil-pds = { + # description = "Tranquil PDS"; + + # after = [ "network-online.target" ]; + # wants = [ "network-online.target" ]; + # wantedBy = [ "multi-user.target" ]; + + # serviceConfig = { + # ExecStart = + # lib.getExe' inputs.tranquil-pds.packages.${pkgs.stdenv.hostPlatform.system}.default + # "tranquil-pds"; + + # Environment = lib.mapAttrsToList (k: v: "${k}=${toString v}") { + # PDS_HOSTNAME = "pds.pluie.me"; + # SERVER_HOST = "127.0.0.1"; + # SERVER_PORT = 11037; + # DATABASE_URL = "postgres://postgres:postgres@localhost:5432/pds"; + # }; + + # EnvironmentFile = [ config.sops.secrets.tranquil-pds.path ]; + # 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"; + # }; + # }; +} diff --git a/systems/focaccia/secrets/bluesky-pds.env b/systems/focaccia/secrets/bluesky-pds.env new file mode 100644 index 0000000..4874cc3 --- /dev/null +++ b/systems/focaccia/secrets/bluesky-pds.env @@ -0,0 +1,11 @@ +PDS_JWT_SECRET=ENC[AES256_GCM,data:j+fIFm9mTzZmGYmqFl5WG8SEP9G2t9VGivd7Xr/CsBY=,iv:Nc3F7PMbDp1AaQ3Y21SVwSI2MKaDE6hy99BGgZVLjGA=,tag:7198yKpl3iWLwQOlb/BNkQ==,type:str] +PDS_ADMIN_PASSWORD=ENC[AES256_GCM,data:zxaNpbCXkyYOT6rnxdXK7NBJ//tAR4ZBes7L607MaW8=,iv:j8J59JH879HVls01qXenprrSica4jLR4xW5r1dEy654=,tag:4PwaijN6/aTiFingb8ykFw==,type:str] +PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=ENC[AES256_GCM,data:iD/cSlJGPkpYaUlkzcl5rdAmF36YyIo8gvFVngTb2UANZFUg/pq9qhWoh4U0EbHnjbrQVQw7Mv6ltoFq61oVBw==,iv:k/LSB1/G+QbgBDuM4M3InzvZfI4U8YGxrZm0SBnGHkk=,tag:mQOtL2ZiOI4vKke9pv/sRw==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEeFVIRm1lckRhKzkzZ0Rs\nUmNpMDh3ditkL3o3cTAzUi9RcW1TaEdoTlVnCmpZamMyb1Irc0dYRWdVem84YWRk\nN0R5cnh6WXhvd3dOdmgxb1A5NDZ4alkKLS0tIHd5bGFPUEtCUDdUNUsrMVl0allS\nL2FOOWRYeEkrTWc5MklLY3BRVGwzYzAKDbCFUNiw7qlmI21NiCYHqf3q2BjKUOuV\nKXO8ccEkPmmOwP/b2mqlsWsQNo8iGRqpoAgz1GJ5TQop/u9QP3rRkA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1lh4sn2s9gxj2s3naqdl4wpmz3uhpd3p8l0jfy6k5hu6cu34uyygsdwadd5 +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcklib1I1eWZvT3UyeFR1\nN05DeHorUHdhRnhMdlFSUEw2TTF3UGVEZFFNCmcyZWI5Z2RVYlJnSkxHNnhDdENP\nVDVqTGlMNGowN2pMUjNjVTN1RENFU1EKLS0tIGw2U0EzckRnYjRTS0JoaVpvN05x\nWnc2UURmZExtemJYL2RjTm0rZkRBZWsKut1/Aeay31L4D1pUm/k7rWOWGFlJL+zz\nt/qqRbDQhvBaUBSal1xM7hELJbcZwAwH2x4xHcy1rMFyWVurMc/Rnw==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1wtr58sze4sxjjzq9jmsq7ztkvkjakvnfzuqzn025p92htz7zsdesjpc2c8 +sops_lastmodified=2026-01-16T21:28:17Z +sops_mac=ENC[AES256_GCM,data:GBGmWfc8Vf6cDNX3C1+CWs5VOquwyOqDgfG5l2ciGYXwcNcwyjmWmHyhb7e3MkaxZespb7vn2FjOxFmDBZAJX7PoFe5XTDDvTpDPc02KN5cbilcdkbj7tppIYTaoa+QTX3/Vy43mTsFnD0DrZgi4niJc3IKJwRTDpmeigw+otp0=,iv:7LgaTaLBLM/I8sfHjWm6ve6ZB+oA1Xnxe/m22hV+uLQ=,tag:hb13g+eXLjYpx440S4Jv/A==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 diff --git a/systems/focaccia/secrets/tranquil-pds.env b/systems/focaccia/secrets/tranquil-pds.env new file mode 100644 index 0000000..05e09be --- /dev/null +++ b/systems/focaccia/secrets/tranquil-pds.env @@ -0,0 +1,19 @@ +JWT_SECRET=ENC[AES256_GCM,data:541+zhzilGnokp4FPa2sgAQf69pYTsvrCJzO7YA7IrI=,iv:2efoLLQ3Y4voEHRcIv42MWb9X6yVccpo4AVqOKiQ1eg=,tag:n88PqhcoMvc3TBNYsT2vOg==,type:str] +DPOP_SECRET=ENC[AES256_GCM,data:9PXTgjJIDdYl5WKuVl/iC7vbOUUiXMzB3qfcB/baeHI=,iv:19ZkOeGA9dknU6OlmD58K8iaUjDY1MlAGOKuyop6RQ8=,tag:WEClGenSAUk6UMmJsrbFsg==,type:str] +MASTER_KEY=ENC[AES256_GCM,data:4mG75YaQOsE2jIN2jjeM2e6EYZNymNNyrSxiTp+BGUabcpIJY8gqR8SJfAJhMAsVE5vuwRMafiEX3o1iX+T/Ug==,iv:7TnjnPBrI2dtDvVioM8fRqgBIWYc9SnVnGXkNc4mYjI=,tag:GFzVZ9vs87sFvCq4t5bU8Q==,type:str] +S3_ENDPOINT=ENC[AES256_GCM,data:kYgUqN41tt5cyYXPGMYqbDcsdybULR6TO6TqactUsM27z2I=,iv:os++rHvHDan/nWg5WB7liZUKyWRs9PqQ/0WOgCG2qYU=,tag:S/bDXvG/Kd/5tnnThGJVBw==,type:str] +S3_BUCKET=ENC[AES256_GCM,data:XPwx,iv:m9AaTv7Y67zXHBuKvFVPZO6XjfAie7qLQi3ZMVq/JN0=,tag:4MV5sMLoL4Jips6oIF6LSw==,type:str] +AWS_ACCESS_KEY_ID=ENC[AES256_GCM,data:wb4VbEwWZOWNn/nO0sMwu6jUD9c=,iv:HDn6qMEjQ3qjRsVMBnIqXfZ6R+u6tvQAtPs02QSAXGg=,tag:7rrmmYBXDOE/GiY+SRSOyw==,type:str] +AWS_SECRET_ACCESS_KEY=ENC[AES256_GCM,data:za1WkqB3ONCZRlJvcD/Javro0oGSpBa/7E9g4GtcFrL5Ef9wIDdf5w==,iv:+pOS+5VBUevUT87D1gIkwMHTiafFoTDA0Esip2D135s=,tag:pJ56v/BikKpRw+D0iNT9Uw==,type:str] +#ENC[AES256_GCM,data:ijngQOCRL/Z1TNU3+d1GhepidIbO08sY2ZbPWV94G/9f2qEyElC89XrD4U7jMd1Y/O/e,iv:UKQxnqSoUQuJ5frQj9TS/+w4AVdLAs/Za7/e2gCOjTU=,tag:BkwmSxONocjl7jcZD4siEQ==,type:comment] +INVITE_CODE_REQUIRED=ENC[AES256_GCM,data:WmTt3A==,iv:MxsP7j6JW+be92byD4TKujnIDVZ1cfb3f2WJj55+oyw=,tag:ZTXKQ5P37BkkdRWSV5FIDg==,type:str] +#ENC[AES256_GCM,data:PIWNrLzuxx18+HP7h6hyf/g6DfXEfp7iH8IXmA==,iv:bfevAkO62VxGkdgiP2d7ATX7/Eqz7e7jCCQ/7C1H9dA=,tag:DjrrPzkVmiaMWrwDXc+TXA==,type:comment] +PDS_AGE_ASSURANCE_OVERRIDE=ENC[AES256_GCM,data:Pg==,iv:oErlmO6hnM8qxaKckKEIOfu2Qd+D5xqqRUk9/LSbk94=,tag:Z9YGhBQTEVwKV6ftqVuN8A==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEeFVIRm1lckRhKzkzZ0Rs\nUmNpMDh3ditkL3o3cTAzUi9RcW1TaEdoTlVnCmpZamMyb1Irc0dYRWdVem84YWRk\nN0R5cnh6WXhvd3dOdmgxb1A5NDZ4alkKLS0tIHd5bGFPUEtCUDdUNUsrMVl0allS\nL2FOOWRYeEkrTWc5MklLY3BRVGwzYzAKDbCFUNiw7qlmI21NiCYHqf3q2BjKUOuV\nKXO8ccEkPmmOwP/b2mqlsWsQNo8iGRqpoAgz1GJ5TQop/u9QP3rRkA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1lh4sn2s9gxj2s3naqdl4wpmz3uhpd3p8l0jfy6k5hu6cu34uyygsdwadd5 +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcklib1I1eWZvT3UyeFR1\nN05DeHorUHdhRnhMdlFSUEw2TTF3UGVEZFFNCmcyZWI5Z2RVYlJnSkxHNnhDdENP\nVDVqTGlMNGowN2pMUjNjVTN1RENFU1EKLS0tIGw2U0EzckRnYjRTS0JoaVpvN05x\nWnc2UURmZExtemJYL2RjTm0rZkRBZWsKut1/Aeay31L4D1pUm/k7rWOWGFlJL+zz\nt/qqRbDQhvBaUBSal1xM7hELJbcZwAwH2x4xHcy1rMFyWVurMc/Rnw==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1wtr58sze4sxjjzq9jmsq7ztkvkjakvnfzuqzn025p92htz7zsdesjpc2c8 +sops_lastmodified=2026-01-16T21:28:02Z +sops_mac=ENC[AES256_GCM,data:jL4Mn2igySD+fQ3ugrlcc2NUY/EN9vdukkWMM7pkpKb/hPY9CMpOoAMu62ZYwcOkJq2lGhW+80ZUxshNPPvpUn+LRgQXz64XpDjn6sXrRXJgPb+CnbM6egRQsAeFI/M1L4d3/3nSKslVQCqYrt4MBF5X/FDGeFV4MReuYtytDK8=,iv:iqSynPjysmQQGfSF8IfGvqPR0YNI6qKfOv8qwyHJoz4=,tag:fJyRHaQ5vOZWXRc59fx6Yw==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 -- 2.51.2