From d4fd1d0b8ef8139161765fadf8b2b2dc771c396f Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Fri, 25 Sep 2026 19:26:44 -0400 Subject: [PATCH] fix(cinny/nginx): let the app frame its bundled Element Call again Web calls broke ("nobody can join calls from the web version"): 23ad133 (cinny #210) put the security-headers snippet, including the app CSP, on every .html response. /public/element-call/index.html then carried frame-ancestors 'none', so the browser refused to load it inside the app ("Content-Security-Policy ... frame-ancestors 'none'"). Desktop was fine (it loads Element Call from its own bundle). Before #210 that page had no CSP. A `location ^~ /public/element-call/` now serves it with cinny-security-headers-framed.conf: the same headers minus the CSP (X-Frame-Options SAMEORIGIN still limits framing to chat.lotusguild.org), and the same caching (HTML no-cache, hashed assets 1 year). Applied live on LXC 106 (backup sites-available/cinny.bak-ecframe-*), nginx -t ok; verified from outside: no CSP on the call page, and the live site frames it and it loads ("Element Call"). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- cinny/nginx-security-headers-framed.conf | 11 +++++++++++ cinny/nginx.conf | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 cinny/nginx-security-headers-framed.conf diff --git a/cinny/nginx-security-headers-framed.conf b/cinny/nginx-security-headers-framed.conf new file mode 100644 index 0000000..d63212c --- /dev/null +++ b/cinny/nginx-security-headers-framed.conf @@ -0,0 +1,11 @@ +# Headers for the bundled Element Call page (/public/element-call/). +# Same as cinny-security-headers.conf minus the Content-Security-Policy: the app +# embeds this page in an iframe, and the app CSP's frame-ancestors 'none' (plus a +# connect-src that doesn't list the call backends) blocked it. X-Frame-Options +# SAMEORIGIN still limits framing to chat.lotusguild.org itself. +add_header X-Frame-Options SAMEORIGIN always; +add_header X-Content-Type-Options nosniff always; +add_header X-XSS-Protection "1; mode=block" always; +add_header Referrer-Policy strict-origin-when-cross-origin always; +add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; +add_header Permissions-Policy "accelerometer=(), autoplay=(self), camera=(self), display-capture=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self), midi=(), payment=(), usb=()" always; diff --git a/cinny/nginx.conf b/cinny/nginx.conf index fb311b5..2e20552 100644 --- a/cinny/nginx.conf +++ b/cinny/nginx.conf @@ -51,6 +51,23 @@ server { add_header Cache-Control "no-cache, no-store, must-revalidate" always; } + # Bundled Element Call: framed by the app itself, so it must not carry the + # app CSP (frame-ancestors 'none' blocked every web call). Same caching as + # below: HTML never cached, hashed assets for a year. + location ^~ /public/element-call/ { + include snippets/cinny-security-headers-framed.conf; + location ~* \.html$ { + include snippets/cinny-security-headers-framed.conf; + expires -1; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; + } + location ~* \.(?:js|css|woff2?|png|svg|ico|webp|wasm)$ { + include snippets/cinny-security-headers-framed.conf; + expires 1y; + add_header Cache-Control "public, immutable" always; + } + } + # Cache content-addressed static assets aggressively location ~* \.(?:js|css|woff2?|png|svg|ico|webp)$ { include snippets/cinny-security-headers.conf;