From 391d3169e1b891c61fb53c34f4c0572c45972e80 Mon Sep 17 00:00:00 2001 From: Stefan Wehrmeyer Date: Fri, 10 Dec 2021 11:52:15 +0000 Subject: [PATCH] Add CSP connect-src for websocket connections connect-src: self may not allow wss:// in some browsers, see: https://github.com/w3c/webappsec-csp/issues/7 --- group_vars/all/default.yml | 10 ++++++---- roles/nginx/templates/_utils.j2 | 12 ++++++------ roles/nginx/templates/nginx_web_config.j2 | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/group_vars/all/default.yml b/group_vars/all/default.yml index aaf0cffd..c0704594 100644 --- a/group_vars/all/default.yml +++ b/group_vars/all/default.yml @@ -22,6 +22,7 @@ mail_domain: mail.fragdenstaat.de domain_name: fragdenstaat.de web_server_name: fragdenstaat.de web_server_url: "https://{{ web_server_name }}" +wsweb_server_url: "wss://{{ web_server_name }}" media_server_name: media.frag-den-staat.de media_server_url: "https://{{ media_server_name }}" static_server_name: static.frag-den-staat.de @@ -67,6 +68,7 @@ gunicorn_onion_num_workers: 2 uvicorn_onion_num_workers: 1 onion_web_server: "fdstaat23zv6kdmntgkvdzkr7hipl5oqswwi3xawzkj2w2gwsbxmrwyd.onion" onion_web_server_url: "http://{{ onion_web_server }}" +onion_wsweb_server_url: "ws://{{ onion_web_server }}" onion_media_server: "media.fdscdncc46svip3qd3vtyrrw7435sdftp4wd3crarr3m6iwenitoryad.onion" onion_static_server: "static.fdscdncc46svip3qd3vtyrrw7435sdftp4wd3crarr3m6iwenitoryad.onion" onion_media_server_url: "http://{{ onion_media_server }}" @@ -329,7 +331,7 @@ cors_exceptions: - "__media__" - "https://okfde.github.io" -# CSP supports __web__, __media__ and __static__ variable__s +# CSP supports __wsweb__ __web__, __media__ and __static__ variable__s content_security_policy: "default-src": "'none'" @@ -340,7 +342,7 @@ content_security_policy: "worker-src": "'self' blob: __static__" "frame-src": "'self' blob: __static__ __media__ https://okfde.github.io https://player.vimeo.com https://www.youtube-nocookie.com https://media.ccc.de https://js.stripe.com https://hooks.stripe.com https://www.paypal.com" "object-src": "'self' __media__" - "connect-src": "'self' __static__ __media__ https://api.betterplace.org/ https://sentry.okfn.de https://api.stripe.com https://traffic.okfn.de" + "connect-src": "'self' __wsweb__ __static__ __media__ https://sentry.okfn.de https://api.stripe.com https://traffic.okfn.de" "child-src": "'self' blob: __static__" "base-uri": "'none'" "font-src": "data: __static__" @@ -358,7 +360,7 @@ content_security_policy_overrides: static_content_security_policy: - "default-src 'self' data: blob: __web__ __media__" - "style-src 'self' data: 'unsafe-inline'" - - "connect-src 'self' __web__ __media__" + - "connect-src 'self' __wsweb__ __web__ __media__" - "frame-ancestors *" - "report-uri https://sentry.okfn.de/api/3/security/?sentry_key=f00c20a879414df69051163a90597a8c" @@ -380,7 +382,7 @@ priviliged_media_content_security_policy: - "worker-src 'self' blob: __static__" - "frame-src 'self' blob: __static__ __web__ https://okfde.github.io https://player.vimeo.com https://www.youtube-nocookie.com https://media.ccc.de" - "object-src 'self' __web__" - - "connect-src 'self' __web__" + - "connect-src 'self' __web__ __wsweb__" - "child-src blob: __static__" - "frame-ancestors *" - "report-uri https://sentry.okfn.de/api/3/security/?sentry_key=f00c20a879414df69051163a90597a8c" diff --git a/roles/nginx/templates/_utils.j2 b/roles/nginx/templates/_utils.j2 index 819a1b68..5586a6e6 100644 --- a/roles/nginx/templates/_utils.j2 +++ b/roles/nginx/templates/_utils.j2 @@ -39,19 +39,19 @@ add_header Vary "Origin"; }{%- endmacro %} -{% macro csp_value(csp_value, web=web_server_url, media=media_server_url, static=static_server_url) -%}{{ csp_value | regex_replace('__web__', web) | regex_replace('__media__', media) | regex_replace('__static__', static) }};{%- endmacro %} +{% macro csp_value(csp_value, web=web_server_url, media=media_server_url, static=static_server_url, wsweb=wsweb_server_url) -%}{{ csp_value | regex_replace('__web__', web) | regex_replace('__media__', media) | regex_replace('__static__', static) | regex_replace('__wsweb__', wsweb) }};{%- endmacro %} -{% macro csp(csp_list, web=web_server_url, media=media_server_url, static=static_server_url) -%}{% for v in csp_list %}{{ csp_value(v, web=web, media=media, static=static) }}{% endfor %}{%- endmacro %} +{% macro csp(csp_list, web=web_server_url, media=media_server_url, static=static_server_url, wsweb=wsweb_server_url) -%}{% for v in csp_list %}{{ csp_value(v, web=web, media=media, static=static, wsweb=wsweb) }}{% endfor %}{%- endmacro %} -{% macro csp_block(csp_list, web=web_server_url, media=media_server_url, static=static_server_url) -%} - add_header Content-Security-Policy "{{ csp(csp_list, web=web, media=media, static=static) }}"; +{% macro csp_block(csp_list, web=web_server_url, media=media_server_url, static=static_server_url, wsweb=wsweb_server_url) -%} + add_header Content-Security-Policy "{{ csp(csp_list, web=web, media=media, static=static, wsweb=wsweb) }}"; {%- endmacro %} -{% macro csp_header_map(header_var='csp_header', web=web_server_url, media=media_server_url, static=static_server_url) %} +{% macro csp_header_map(header_var='csp_header', web=web_server_url, media=media_server_url, static=static_server_url, wsweb=wsweb_server_url) %} map $request_uri ${{ header_var }} { default "{% for k, v in content_security_policy.items() %}{{ k }} {{ csp_value(v, web=web, media=media, static=static) }}{% endfor %}"; {% for override in content_security_policy_overrides %} - "{{ override.url }}" "{% for k, v in content_security_policy.items() %}{{ k }} {% if k in override.directives %}{{ csp_value(override.directives[k], web=web, media=media, static=static) }}{% else %}{{ csp_value(v, web=web, media=media, static=static) }}{% endif %}{% endfor %}"; + "{{ override.url }}" "{% for k, v in content_security_policy.items() %}{{ k }} {% if k in override.directives %}{{ csp_value(override.directives[k], web=web, media=media, static=static, wsweb=wsweb) }}{% else %}{{ csp_value(v, web=web, media=media, static=static, wsweb=wsweb) }}{% endif %}{% endfor %}"; {% endfor %} } {% endmacro %} diff --git a/roles/nginx/templates/nginx_web_config.j2 b/roles/nginx/templates/nginx_web_config.j2 index cb52d871..bac7e74b 100644 --- a/roles/nginx/templates/nginx_web_config.j2 +++ b/roles/nginx/templates/nginx_web_config.j2 @@ -134,7 +134,7 @@ server { {% with onion_service = onion_services['onion_web'], csp_header_var="csp_header_onion" %} -{{ csp_header_map(header_var=csp_header_var, web=onion_web_server_url, media=onion_media_server_url, static=onion_static_server_url) }} +{{ csp_header_map(header_var=csp_header_var, web=onion_web_server_url, media=onion_media_server_url, static=onion_static_server_url, wsweb=onion_wsweb_server_url) }} upstream {{ application_name }}_onion_wsgi_server { # fail_timeout=0 means we always retry an upstream even if it failed @@ -156,7 +156,7 @@ server { listen 127.0.0.1:{{ onion_service.internal_port }}; server_name {{ onion_service.hostname }}; - {% with web_server_name = onion_service.hostname, media_server_name=onion_media_server, static_server_name=onion_static_server, wsgi_app_name=application_name + "_onion", asgi_app_name=application_name + "_onion", web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url %} + {% with web_server_name = onion_service.hostname, media_server_name=onion_media_server, static_server_name=onion_static_server, wsgi_app_name=application_name + "_onion", asgi_app_name=application_name + "_onion", web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url, wsweb_server_url=onion_wsweb_server_url %} {% include '_web_server.j2' %} {% endwith %} } @@ -173,7 +173,7 @@ server { listen 127.0.0.1:{{ onion_service.internal_port }}; server_name media.{{ onion_service.hostname }}; - {% with media_server_name = "media." + onion_service.hostname, web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url, wsgi_app_name=application_name + "_onion" %} + {% with media_server_name = "media." + onion_service.hostname, web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url, wsgi_app_name=application_name + "_onion", wsweb_server_url=onion_wsweb_server_url %} {% include '_media_server.j2' %} {% endwith %} } @@ -182,7 +182,7 @@ server { listen 127.0.0.1:{{ onion_service.internal_port }}; server_name static.{{ onion_service.hostname }}; - {% with static_server_name = "static." + onion_service.hostname, web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url %} + {% with static_server_name = "static." + onion_service.hostname, web_server_url=onion_web_server_url, media_server_url=onion_media_server_url, static_server_url=onion_static_server_url, wsweb_server_url=onion_wsweb_server_url %} {% include '_static_server.j2' %} {% endwith %} }