diff --git a/cinny/nginx-security-headers-call.conf b/cinny/nginx-security-headers-call.conf new file mode 100644 index 0000000..3aaf8ac --- /dev/null +++ b/cinny/nginx-security-headers-call.conf @@ -0,0 +1,14 @@ +# Headers for the Element Call page on its own origin, call.chat.lotusguild.org +# (cinny #43). Installed on LXC 106 as /etc/nginx/snippets/cinny-security-headers-call.conf +# (deploy/lxc106-cinny.sh does NOT copy snippets — install by hand, like the others). +# +# frame-ancestors: only the web app may frame the call page (replaces +# X-Frame-Options SAMEORIGIN, which would block the now cross-origin parent; +# browsers honour frame-ancestors over X-Frame-Options anyway). +# Permissions-Policy: "self" here is the call origin, which is what uses the +# mic/camera/screen; the app's own policy delegates them to this origin. +add_header Content-Security-Policy "frame-ancestors https://chat.lotusguild.org" always; +add_header X-Content-Type-Options nosniff 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=(), gyroscope=(), magnetometer=(), microphone=(self), midi=(), payment=(), usb=()" always; diff --git a/cinny/nginx-security-headers.conf b/cinny/nginx-security-headers.conf index 05577f7..5673fcc 100644 --- a/cinny/nginx-security-headers.conf +++ b/cinny/nginx-security-headers.conf @@ -3,10 +3,13 @@ # nginx drops inherited add_header directives in any block that defines one, # so without the include, static assets lost nosniff and /sw.js lost its CSP # (a service worker's CSP comes from its own script response). cinny #210. +# Permissions-Policy delegates autoplay/camera/display-capture/microphone to the +# call page's own origin (call.chat.lotusguild.org, cinny #43); without it the +# cross-origin call frame's getUserMedia is refused (NotAllowedError). 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; +add_header Permissions-Policy 'accelerometer=(), autoplay=(self "https://call.chat.lotusguild.org"), camera=(self "https://call.chat.lotusguild.org"), display-capture=(self "https://call.chat.lotusguild.org"), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self "https://call.chat.lotusguild.org"), midi=(), payment=(), usb=()' always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://matrix.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://drive.lotusguild.org https://media.giphy.com https://media0.giphy.com https://media1.giphy.com https://media2.giphy.com https://media3.giphy.com https://media4.giphy.com https://www.openstreetmap.org https://tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' https://matrix.lotusguild.org wss://matrix.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://chat.mozilla.org https://vector.im https://api.giphy.com https://*.giphy.com wss:; media-src 'self' https: blob:; frame-src 'self' https:; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always; diff --git a/cinny/nginx.conf b/cinny/nginx.conf index 2e20552..6e4a797 100644 --- a/cinny/nginx.conf +++ b/cinny/nginx.conf @@ -108,3 +108,53 @@ server { rewrite ^(.+)$ /index.html break; } } + +# [cinny #43] Element Call on its own origin. The web app frames +# https://call.chat.lotusguild.org/public/element-call/index.html (config.json +# `elementCallUrl`), so the call page can no longer read the app's storage +# (login token, crypto store) or use its service worker. Serves ONLY the call +# page — the same files as chat.lotusguild.org/public/element-call/ — and 404s +# everything else, so this hostname exposes nothing new. +server { + listen 80; + listen [::]:80; + server_name call.chat.lotusguild.org; + + brotli on; + brotli_static on; + brotli_comp_level 6; + brotli_types text/plain text/css application/javascript application/json + image/svg+xml application/wasm font/woff2; + + root /var/www/html; + server_tokens off; + limit_req zone=chat_limit burst=60 nodelay; + limit_conn chat_conn 25; + + include snippets/cinny-security-headers-call.conf; + + location ^~ /public/element-call/ { + include snippets/cinny-security-headers-call.conf; + location ~* \.map$ { + return 404; + } + location ~ /\. { + return 404; + } + location ~* \.html$ { + include snippets/cinny-security-headers-call.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-call.conf; + expires 1y; + add_header Cache-Control "public, immutable" always; + } + try_files $uri =404; + } + + location / { + return 404; + } +}