From 0a588023a7f5e7190251758e942a7c934b68284b Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 4 Sep 2025 02:29:10 +0200 Subject: [PATCH] feat(bluesky): fix CORS by serving /config same-origin and pinning BAPP_CONFIG_URL - Add `server.config_upstream_url` default in `roles/web-app-bluesky/config/main.yml` to define upstream for /config (defaults to https://ip.bsky.app/config). - Introduce front-proxy injection `extra_locations.conf.j2` that: - proxies `/config` to the upstream, - sets SNI and correct Host header, - normalizes CORS headers for same-origin consumption. - Wire the proxy injection only for the Web domain in `roles/web-app-bluesky/tasks/main.yml` via `proxy_extra_configuration`. - Force fresh social-app checkout and patch `src/state/geolocation.tsx` to `const BAPP_CONFIG_URL = '/config'` in `roles/web-app-bluesky/tasks/02_social_app.yml`; notify `docker compose build` and `up`. - Tidy and re-group PDS env in `roles/web-app-bluesky/templates/env.j2` (no functional change). - Add vars in `roles/web-app-bluesky/vars/main.yml`: - `BLUESKY_FRONT_PROXY_CONTENT` (renders the extra locations), - `BLUESKY_CONFIG_UPSTREAM_URL` (reads `server.config_upstream_url`). Security/Scope: - Only affects the Bluesky web frontend (same-origin `/config`); PDS/API and AppView remain unchanged. Refs: - Conversation: https://chatgpt.com/share/68b8dd3a-2100-800f-959e-1495f6320aab --- roles/web-app-bluesky/config/main.yml | 3 +- roles/web-app-bluesky/tasks/02_social_app.yml | 12 +++++++- roles/web-app-bluesky/tasks/main.yml | 5 ++-- roles/web-app-bluesky/templates/env.j2 | 29 ++++++++++++------- .../templates/extra_locations.conf.j2 | 16 ++++++++++ roles/web-app-bluesky/vars/main.yml | 3 ++ 6 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 roles/web-app-bluesky/templates/extra_locations.conf.j2 diff --git a/roles/web-app-bluesky/config/main.yml b/roles/web-app-bluesky/config/main.yml index 406926c5..eb972429 100644 --- a/roles/web-app-bluesky/config/main.yml +++ b/roles/web-app-bluesky/config/main.yml @@ -5,6 +5,7 @@ features: central_database: false logout: true server: + config_upstream_url: "https://ip.bsky.app/config" domains: canonical: web: "bskyweb.{{ PRIMARY_DOMAIN }}" @@ -33,7 +34,7 @@ docker: database: enabled: false web: - enabled: true # @see https://github.com/bluesky-social/social-app + enabled: true # @see https://github.com/bluesky-social/social-app view: enabled: false pds: diff --git a/roles/web-app-bluesky/tasks/02_social_app.yml b/roles/web-app-bluesky/tasks/02_social_app.yml index 9eab2420..d3e11800 100644 --- a/roles/web-app-bluesky/tasks/02_social_app.yml +++ b/roles/web-app-bluesky/tasks/02_social_app.yml @@ -3,6 +3,16 @@ repo: "https://github.com/bluesky-social/social-app.git" dest: "{{ BLUESKY_SOCIAL_APP_DIR }}" version: "main" + force: true notify: - docker compose up - - docker compose build \ No newline at end of file + - docker compose build + +- name: Force BAPP_CONFIG_URL to same-origin /config + ansible.builtin.replace: + path: "{{ BLUESKY_SOCIAL_APP_DIR }}/src/state/geolocation.tsx" + regexp: '^\s*const\s+BAPP_CONFIG_URL\s*=\s*.*$' + replace: "const BAPP_CONFIG_URL = '/config'" + notify: + - docker compose build + - docker compose up diff --git a/roles/web-app-bluesky/tasks/main.yml b/roles/web-app-bluesky/tasks/main.yml index 22cf6ba4..7199770c 100644 --- a/roles/web-app-bluesky/tasks/main.yml +++ b/roles/web-app-bluesky/tasks/main.yml @@ -15,8 +15,9 @@ include_role: name: sys-stk-front-proxy vars: - domain: "{{ BLUESKY_WEB_DOMAIN }}" - http_port: "{{ BLUESKY_WEB_PORT }}" + domain: "{{ BLUESKY_WEB_DOMAIN }}" + http_port: "{{ BLUESKY_WEB_PORT }}" + proxy_extra_configuration: "{{ BLUESKY_FRONT_PROXY_CONTENT }}" when: BLUESKY_WEB_ENABLED | bool - name: "Include front proxy for {{ BLUESKY_VIEW_DOMAIN }}:{{ BLUESKY_VIEW_PORT }}" diff --git a/roles/web-app-bluesky/templates/env.j2 b/roles/web-app-bluesky/templates/env.j2 index d35c4275..980e4499 100644 --- a/roles/web-app-bluesky/templates/env.j2 +++ b/roles/web-app-bluesky/templates/env.j2 @@ -1,21 +1,30 @@ +# General PDS_HOSTNAME="{{ BLUESKY_API_DOMAIN }}" -PDS_ADMIN_EMAIL="{{ BLUESKY_ADMIN_EMAIL }}" -PDS_SERVICE_DID="did:web:{{ BLUESKY_API_DOMAIN }}" - -# See https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds/ -PDS_SERVICE_HANDLE_DOMAINS=".{{ PRIMARY_DOMAIN }}" -PDS_JWT_SECRET="{{ BLUESKY_JWT_SECRET }}" -PDS_ADMIN_PASSWORD="{{ BLUESKY_ADMIN_PASSWORD }}" -PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="{{ BLUESKY_ROTATION_KEY }}" PDS_CRAWLERS=https://bsky.network -PDS_EMAIL_SMTP_URL=smtps://{{ users['no-reply'].email }}:{{ users['no-reply'].mailu_token }}@{{ SYSTEM_EMAIL.HOST }}:{{ SYSTEM_EMAIL.PORT }}/ -PDS_EMAIL_FROM_ADDRESS={{ users['no-reply'].email }} LOG_ENABLED={{ MODE_DEBUG | string | lower }} PDS_BLOBSTORE_DISK_LOCATION={{ BLUESKY_PDS_BLOBSTORE_LOCATION }} PDS_DATA_DIRECTORY={{ BLUESKY_PDS_DATA_DIR }} PDS_BLOB_UPLOAD_LIMIT=52428800 PDS_DID_PLC_URL=https://plc.directory + +# See https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds/ +PDS_SERVICE_HANDLE_DOMAINS=".{{ PRIMARY_DOMAIN }}" +PDS_SERVICE_DID="did:web:{{ BLUESKY_API_DOMAIN }}" + +# Email +PDS_ADMIN_EMAIL="{{ BLUESKY_ADMIN_EMAIL }}" +PDS_EMAIL_SMTP_URL=smtps://{{ users['no-reply'].email }}:{{ users['no-reply'].mailu_token }}@{{ SYSTEM_EMAIL.HOST }}:{{ SYSTEM_EMAIL.PORT }}/ +PDS_EMAIL_FROM_ADDRESS={{ users['no-reply'].email }} + +# Credentials +PDS_JWT_SECRET="{{ BLUESKY_JWT_SECRET }}" +PDS_ADMIN_PASSWORD="{{ BLUESKY_ADMIN_PASSWORD }}" +PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="{{ BLUESKY_ROTATION_KEY }}" + +# View PDS_BSKY_APP_VIEW_URL={{ BLUESKY_VIEW_URL }} PDS_BSKY_APP_VIEW_DID={{ BLUESKY_VIEW_DID }} + +# Report PDS_REPORT_SERVICE_URL=https://mod.bsky.app PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac diff --git a/roles/web-app-bluesky/templates/extra_locations.conf.j2 b/roles/web-app-bluesky/templates/extra_locations.conf.j2 new file mode 100644 index 00000000..11d41cfd --- /dev/null +++ b/roles/web-app-bluesky/templates/extra_locations.conf.j2 @@ -0,0 +1,16 @@ +# Injected by web-app-bluesky (same pattern as web-app-yourls) +# Exposes a same-origin /config to avoid CORS when the social-app fetches config. +location = /config { + proxy_pass {{ BLUESKY_CONFIG_UPSTREAM_URL }}; + # Nur Hostname extrahieren: + set $up_host "{{ BLUESKY_CONFIG_UPSTREAM_URL | regex_replace('^https?://', '') | regex_replace('/.*$', '') }}"; + proxy_set_header Host $up_host; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_ssl_server_name on; + + # Make response clearly same-origin for browsers + proxy_hide_header Access-Control-Allow-Origin; + add_header Access-Control-Allow-Origin $scheme://$host always; + add_header Vary Origin always; +} \ No newline at end of file diff --git a/roles/web-app-bluesky/vars/main.yml b/roles/web-app-bluesky/vars/main.yml index ba15f447..bce0943f 100644 --- a/roles/web-app-bluesky/vars/main.yml +++ b/roles/web-app-bluesky/vars/main.yml @@ -43,3 +43,6 @@ BLUESKY_ROTATION_KEY: "{{ applications | get_app_conf(application_id, BLUESKY_ADMIN_EMAIL: "{{ users.administrator.email }}" BLUESKY_ADMIN_PASSWORD: "{{ users.administrator.password }}" +# Front proxy +BLUESKY_FRONT_PROXY_CONTENT: "{{ lookup('template', 'extra_locations.conf.j2') }}" +BLUESKY_CONFIG_UPSTREAM_URL: "{{ applications | get_app_conf(application_id, 'server.config_upstream_url') }}"