From 0682aaff4ec29106b14880727eff4614bc5927d5 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Fri, 17 Oct 2025 02:13:44 +0900 Subject: [PATCH] local-infra: local, sandboxed atmosphere infra Change-Id: wotxqrqrxmzwtqzpxmsvpuzvlwkuyklz Signed-off-by: Seongmin Lee --- local-infra/Caddyfile | 54 ++++++++++++ .../cert/localtangled/intermediate.crt | 12 +++ .../cert/localtangled/intermediate.key | 5 ++ local-infra/cert/localtangled/root.crt | 11 +++ local-infra/cert/localtangled/root.key | 5 ++ local-infra/docker-compose.yml | 82 +++++++++++++++++++ local-infra/pds.env | 17 ++++ local-infra/readme.md | 9 ++ local-infra/scripts/create-test-account.sh | 63 ++++++++++++++ nix/vm.nix | 5 ++ 10 files changed, 263 insertions(+) create mode 100644 local-infra/Caddyfile create mode 100644 local-infra/cert/localtangled/intermediate.crt create mode 100644 local-infra/cert/localtangled/intermediate.key create mode 100644 local-infra/cert/localtangled/root.crt create mode 100644 local-infra/cert/localtangled/root.key create mode 100644 local-infra/docker-compose.yml create mode 100644 local-infra/pds.env create mode 100644 local-infra/readme.md create mode 100755 local-infra/scripts/create-test-account.sh diff --git a/local-infra/Caddyfile b/local-infra/Caddyfile new file mode 100644 index 00000000..f4e940ea --- /dev/null +++ b/local-infra/Caddyfile @@ -0,0 +1,54 @@ +{ + storage file_system /data/ + debug + pki { + ca localtangled { + name "LocalTangledCA" + } + } + auto_https disable_redirects +} + +plc.tngl.boltless.dev { + tls { + issuer internal { + ca localtangled + } + } + reverse_proxy http://plc:8080 +} + +*.pds.tngl.boltless.dev, pds.tngl.boltless.dev { + tls { + issuer internal { + ca localtangled + } + } + reverse_proxy http://pds:3000 +} + +jetstream.tngl.boltless.dev { + tls { + issuer internal { + ca localtangled + } + } + reverse_proxy http://jetstream:6008 +} + +http://knot.tngl.boltless.dev { + reverse_proxy http://host.docker.internal:6000 +} + +https://knot.tngl.boltless.dev { + tls { + issuer internal { + ca localtangled + } + } + reverse_proxy http://host.docker.internal:6000 +} + +http://spindle.tngl.boltless.dev { + reverse_proxy http://host.docker.internal:6555 +} diff --git a/local-infra/cert/localtangled/intermediate.crt b/local-infra/cert/localtangled/intermediate.crt new file mode 100644 index 00000000..c9307241 --- /dev/null +++ b/local-infra/cert/localtangled/intermediate.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuTCCAWCgAwIBAgIRALKb0dndMd7jlCHAzm0G+N4wCgYIKoZIzj0EAwIwKTEn +MCUGA1UEAxMeTG9jYWxUYW5nbGVkQ0EgLSAyMDI1IEVDQyBSb290MB4XDTI1MTAy +MTA3NDAwNloXDTI1MTAyODA3NDAwNlowLDEqMCgGA1UEAxMhTG9jYWxUYW5nbGVk +Q0EgLSBFQ0MgSW50ZXJtZWRpYXRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +bX+zyr9rLxF3E8oCZwJluCKX/xmU4waabkjaTGbI5K0cemiAAmZRJ2lVhgh+KfXD +PpTmt+YE6FUF4xAWADOUuaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQAwHQYDVR0OBBYEFIoGsfx3Qg/9qG7tm7CZ1pHYl3prMB8GA1UdIwQY +MBaAFCkl8dPP2IAMTPru6WEHLP1hySEQMAoGCCqGSM49BAMCA0cAMEQCIFc3gOEl +aUR/OWbQuWvYwoTZs81ERj73ZeQWy4a3i4ooAiAB7Mnih/7kEvLyfkjLRgRXrtlq +kVmXVyWHIncR6Bsktw== +-----END CERTIFICATE----- diff --git a/local-infra/cert/localtangled/intermediate.key b/local-infra/cert/localtangled/intermediate.key new file mode 100644 index 00000000..05c5087d --- /dev/null +++ b/local-infra/cert/localtangled/intermediate.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIB1EH4KZGLcfO0neWDuV3oWMXPEze8JTsyKFoQuYApFSoAoGCCqGSM49 +AwEHoUQDQgAEbX+zyr9rLxF3E8oCZwJluCKX/xmU4waabkjaTGbI5K0cemiAAmZR +J2lVhgh+KfXDPpTmt+YE6FUF4xAWADOUuQ== +-----END EC PRIVATE KEY----- diff --git a/local-infra/cert/localtangled/root.crt b/local-infra/cert/localtangled/root.crt new file mode 100644 index 00000000..d6db736b --- /dev/null +++ b/local-infra/cert/localtangled/root.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBlTCCATygAwIBAgIRAMDTcwNxYDMgtUNC5LkCeEQwCgYIKoZIzj0EAwIwKTEn +MCUGA1UEAxMeTG9jYWxUYW5nbGVkQ0EgLSAyMDI1IEVDQyBSb290MB4XDTI1MTAx +NzE2MTE0NVoXDTM1MDgyNjE2MTE0NVowKTEnMCUGA1UEAxMeTG9jYWxUYW5nbGVk +Q0EgLSAyMDI1IEVDQyBSb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7rFM +4oNfT0UMqMuc3L60TCLeTd58WFSUYnKl7R1HOHDWeWZhhoNdWguXJSHhFPiWmQ5E ++fiI7KvDAVQGHzfUAqNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB +Af8CAQEwHQYDVR0OBBYEFCkl8dPP2IAMTPru6WEHLP1hySEQMAoGCCqGSM49BAMC +A0cAMEQCIFjSGjvie1gO/JuNtP2HqeUHQNEh82K1fXdks54up3KEAiBWQDaOYeZ2 +zVTiKe8ZQHpH3glXsIS0USsxeKaohMp0zA== +-----END CERTIFICATE----- diff --git a/local-infra/cert/localtangled/root.key b/local-infra/cert/localtangled/root.key new file mode 100644 index 00000000..21762ad3 --- /dev/null +++ b/local-infra/cert/localtangled/root.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBqEj1iG3q+OLBgHjWQ3UkvKjq4sy5ej47syIYWn/Ql/oAoGCCqGSM49 +AwEHoUQDQgAE7rFM4oNfT0UMqMuc3L60TCLeTd58WFSUYnKl7R1HOHDWeWZhhoNd +WguXJSHhFPiWmQ5E+fiI7KvDAVQGHzfUAg== +-----END EC PRIVATE KEY----- diff --git a/local-infra/docker-compose.yml b/local-infra/docker-compose.yml new file mode 100644 index 00000000..bb5db6db --- /dev/null +++ b/local-infra/docker-compose.yml @@ -0,0 +1,82 @@ +name: tangled-local-infra +services: + caddy: + container_name: caddy + image: caddy:2 + depends_on: + - pds + restart: unless-stopped + cap_add: + - NET_ADMIN + ports: + - "80:80" + - "443:443" + - "443:443/udp" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile + - ./cert/localtangled:/data/pki/authorities/localtangled + - caddy_data:/data + - caddy_config:/config + + plc: + image: ghcr.io/bluesky-social/did-method-plc:plc-f2ab7516bac5bc0f3f86842fa94e996bd1b3815b + # did-method-plc only provides linux/amd64 + platform: linux/amd64 + container_name: plc + restart: unless-stopped + depends_on: + - plc_db + environment: + DEBUG_MODE: 1 + LOG_ENABLED: "true" + LOG_LEVEL: "debug" + LOG_DESTINATION: 1 + DB_CREDS_JSON: &DB_CREDS_JSON '{"username":"pg","password":"password","host":"plc_db","port":5432}' + DB_MIGRATE_CREDS_JSON: *DB_CREDS_JSON + PLC_VERSION: 0.0.1 + PORT: 8080 + + plc_db: + image: postgres:14.4-alpine + container_name: plc_db + environment: + - POSTGRES_USER=pg + - POSTGRES_PASSWORD=password + - PGPORT=5432 + volumes: + - plc:/var/lib/postgresql/data + + pds: + container_name: pds + image: ghcr.io/bluesky-social/pds:0.4 + restart: unless-stopped + volumes: + - pds:/pds + env_file: + - ./pds.env + + # I can change the knot-docker and spindle-docker images, + # which means I can inject the cert to those containers + # + # so define *.tngl.boltless.dev as extra_hosts & inject certs to those two containers + # extra_hosts: + # plc.tngl.boltless.dev:host.docker.internal + + jetstream: + container_name: jetstream + image: ghcr.io/bluesky-social/jetstream:sha-0ab10bd + restart: unless-stopped + volumes: + - jetstream:/data + environment: + - JETSTREAM_DATA_DIR=/data + # livness check interval to restart when no events are received (default: 15sec) + - JETSTREAM_LIVENESS_TTL=300s + - JETSTREAM_WS_URL=ws://pds:3000/xrpc/com.atproto.sync.subscribeRepos + +volumes: + caddy_config: + caddy_data: + plc: + pds: + jetstream: diff --git a/local-infra/pds.env b/local-infra/pds.env new file mode 100644 index 00000000..79adeb5a --- /dev/null +++ b/local-infra/pds.env @@ -0,0 +1,17 @@ +PDS_JWT_SECRET=8cae8bffcc73d9932819650791e4e89a +PDS_ADMIN_PASSWORD=d6a902588cd93bee1af83f924f60cfd3 +PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=2e92e336a50a618458e1097d94a1db86ec3fd8829d7735020cbae80625c761d7 + +LOG_ENABLED=true + +# PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app +# PDS_BSKY_APP_VIEW_URL=https://api.bsky.app + +PDS_DATA_DIRECTORY=/pds +PDS_BLOBSTORE_DISK_LOCATION=/pds/blocks + +PDS_DID_PLC_URL=http://plc:8080 +PDS_HOSTNAME=pds.tngl.boltless.dev + +# PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac +# PDS_REPORT_SERVICE_URL=https://mod.bsky.app diff --git a/local-infra/readme.md b/local-infra/readme.md new file mode 100644 index 00000000..8ae27030 --- /dev/null +++ b/local-infra/readme.md @@ -0,0 +1,9 @@ +run compose +``` +docker compose up -d +``` + +trust the cert (macOS) +``` +sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./local-infra/cert/localtangled/root.crt +``` diff --git a/local-infra/scripts/create-test-account.sh b/local-infra/scripts/create-test-account.sh new file mode 100755 index 00000000..d1a612a8 --- /dev/null +++ b/local-infra/scripts/create-test-account.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +source "$(dirname "$0")/../pds.env" + +# curl a URL and fail if the request fails. +function curl_cmd_get { + curl --fail --silent --show-error "$@" +} + +# curl a URL and fail if the request fails. +function curl_cmd_post { + curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +# curl a URL but do not fail if the request fails. +function curl_cmd_post_nofail { + curl --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +USERNAME="${1:-}" + +if [[ "${USERNAME}" == "" ]]; then + read -p "Enter a username: " USERNAME +fi + +if [[ "${USERNAME}" == "" ]]; then + echo "ERROR: missing USERNAME parameter." >/dev/stderr + echo "Usage: $0 ${SUBCOMMAND} " >/dev/stderr + exit 1 +fi + +PASSWORD="password" +INVITE_CODE="$(curl_cmd_post \ + --user "admin:${PDS_ADMIN_PASSWORD}" \ + --data '{"useCount": 1}' \ + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code' +)" +RESULT="$(curl_cmd_post_nofail \ + --data "{\"email\":\"${USERNAME}@${PDS_HOSTNAME}\", \"handle\":\"${USERNAME}.${PDS_HOSTNAME}\", \"password\":\"${PASSWORD}\", \"inviteCode\":\"${INVITE_CODE}\"}" \ + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createAccount" +)" + +DID="$(echo $RESULT | jq --raw-output '.did')" +if [[ "${DID}" != did:* ]]; then + ERR="$(echo ${RESULT} | jq --raw-output '.message')" + echo "ERROR: ${ERR}" >/dev/stderr + echo "Usage: $0 " >/dev/stderr + exit 1 +fi + +echo +echo "Account created successfully!" +echo "-----------------------------" +echo "Handle : ${USERNAME}.${PDS_HOSTNAME}" +echo "DID : ${DID}" +echo "Password : ${PASSWORD}" +echo "-----------------------------" +echo "This is a test account with an insecure password." +echo "Make sure it's only used for development." +echo diff --git a/nix/vm.nix b/nix/vm.nix index 0e6d20da..8b083034 100644 --- a/nix/vm.nix +++ b/nix/vm.nix @@ -79,6 +79,11 @@ in }; # This is fine because any and all ports that are forwarded to host are explicitly marked above, we don't need a separate guest firewall networking.firewall.enable = false; + services.dnsmasq.enable = true; + services.dnsmasq.settings.address = "/tngl.boltless.dev/10.0.2.2"; + security.pki.certificates = [ + (builtins.readFile ../local-infra/cert/localtangled/root.crt) + ]; time.timeZone = "Europe/London"; services.getty.autologinUser = "root"; environment.systemPackages = with pkgs; [curl vim git sqlite litecli]; -- 2.43.0