diff --git a/docker/batch-test.env b/docker/batch-test.env index 8e4e8d67d..223f151d1 100644 --- a/docker/batch-test.env +++ b/docker/batch-test.env @@ -72,7 +72,6 @@ INTERNETNL_CACHE_RESET_ALLOWLIST=target.test # allow localhost for healthchecks, the public domain for the app and it's subdomains for connection tests ALLOWED_HOSTS=127.0.0.1,::1,localhost,.internet.test,internet.test -CSP_DEFAULT_SRC="'self',*.internet.test" IPV6_TEST_ADDR=fd00:43:1::100 CONN_TEST_DOMAIN=internet.test diff --git a/docker/compose.yaml b/docker/compose.yaml index 98bc5bf59..811420175 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -143,7 +143,6 @@ services: - CONN_TEST_DOMAIN - SMTP_EHLO_DOMAIN - IPV6_TEST_ADDR - - CSP_DEFAULT_SRC - IPV4_IP_RESOLVER_INTERNAL_VALIDATING - IPV4_IP_RESOLVER_INTERNAL_PERMISSIVE - SENTRY_DSN diff --git a/docker/defaults.env b/docker/defaults.env index b4d2feac2..bebe5f775 100644 --- a/docker/defaults.env +++ b/docker/defaults.env @@ -158,7 +158,6 @@ POSTGRES_DB=internetnl_db1 # allow localhost for healthchecks, the public domain for the app and it's subdomains for connection tests ALLOWED_HOSTS=127.0.0.1,::1,localhost,.internet.nl,internet.nl,host.docker.internal -CSP_DEFAULT_SRC="'self',*.internet.nl" # to low of an interval burdens the services, to high causes slow compose up/restarts HEALTHCHECK_INTERVAL=60s diff --git a/docker/develop.env b/docker/develop.env index beee1765b..36493c41e 100644 --- a/docker/develop.env +++ b/docker/develop.env @@ -30,7 +30,6 @@ INTERNETNL_CACHE_TTL=30 # allow localhost for healthchecks, the public domain for the app and it's subdomains for connection tests ALLOWED_HOSTS=127.0.0.1,::1,localhost,.internet.test,internet.test,host.docker.internal,host-gateway -CSP_DEFAULT_SRC="'self',*.internet.test" # domainname used for connection test CONN_TEST_DOMAIN=internet.test diff --git a/docker/host-dist.env b/docker/host-dist.env index 86f76962a..bba3f5710 100644 --- a/docker/host-dist.env +++ b/docker/host-dist.env @@ -12,7 +12,6 @@ IPV6_IP_PUBLIC=$IPV6_IP_PUBLIC IPV4_IP_PUBLIC=$IPV4_IP_PUBLIC ALLOWED_HOSTS=127.0.0.1,::1,localhost,.$INTERNETNL_DOMAINNAME,$INTERNETNL_DOMAINNAME -CSP_DEFAULT_SRC="'self',https://*.$INTERNETNL_DOMAINNAME" MATOMO_SUBDOMAIN_TRACKING=*.$INTERNETNL_DOMAINNAME diff --git a/docker/test.env b/docker/test.env index 6449e7744..3d9bca4be 100644 --- a/docker/test.env +++ b/docker/test.env @@ -69,7 +69,6 @@ INTERNETNL_CACHE_RESET_ALLOWLIST=target.test # allow localhost for healthchecks, the public domain for the app and it's subdomains for connection tests ALLOWED_HOSTS=127.0.0.1,::1,localhost,.internet.test,internet.test -CSP_DEFAULT_SRC="'self',*.internet.test" IPV6_TEST_ADDR=fd00:43:1::100 CONN_TEST_DOMAIN=internet.test diff --git a/docker/webserver.Dockerfile b/docker/webserver.Dockerfile index a46609a2b..cd9af4fa3 100644 --- a/docker/webserver.Dockerfile +++ b/docker/webserver.Dockerfile @@ -36,3 +36,6 @@ COPY interface/static/favicon.ico /var/www/internet.nl/ COPY docker/webserver/nginx_templates/* /etc/nginx/templates/ COPY docker/webserver/mime.types /etc/nginx/ +COPY docker/webserver/http.headers /etc/nginx/ +COPY docker/webserver/hsts.header /etc/nginx/ +COPY docker/webserver/all.headers /etc/nginx/ diff --git a/docker/webserver/10-variables.envsh b/docker/webserver/10-variables.envsh index da0670bd5..bf8f4b9f2 100755 --- a/docker/webserver/10-variables.envsh +++ b/docker/webserver/10-variables.envsh @@ -2,4 +2,5 @@ # split comma separated list into space separated export REDIRECT_DOMAINS_LIST=$(echo $REDIRECT_DOMAINS | sed 's/,/ /') -export LANGUAGES_REGEX=$(echo $LANGUAGES | tr ',' '|') +export LANGUAGES_REGEX=$(echo $LANGUAGES | sed -r ';s/,/\\.|/g;s/$/\\./') +export INTERNETNL_DOMAINNAME_REGEX=$(echo $INTERNETNL_DOMAINNAME | sed 's/\./\\./g') diff --git a/docker/webserver/all.headers b/docker/webserver/all.headers new file mode 100644 index 000000000..27880a705 --- /dev/null +++ b/docker/webserver/all.headers @@ -0,0 +1,3 @@ +include http.headers; +include hsts.header; +include conf.d/csp.header; diff --git a/docker/webserver/hsts.header b/docker/webserver/hsts.header new file mode 100644 index 000000000..d9077490a --- /dev/null +++ b/docker/webserver/hsts.header @@ -0,0 +1,4 @@ + # Do *not* add includeSubdomains before https://github.com/internetstandards/Internet.nl/issues/324 is resolved, + # adding includeSubdomains without adding the wildcard SSL certificates will otherwise break the connection test. + add_header 'Strict-Transport-Security' 'max-age=31536000;' always; + \ No newline at end of file diff --git a/docker/webserver/http.headers b/docker/webserver/http.headers new file mode 100644 index 000000000..ea7436391 --- /dev/null +++ b/docker/webserver/http.headers @@ -0,0 +1,7 @@ + # default headers added to all responses + add_header 'X-Frame-Options' 'SAMEORIGIN' always; + add_header 'X-Content-Type-Options' 'nosniff' always; + add_header 'X-Clacks-Overhead' 'GNU Terry Pratchett' always; + add_header 'Referrer-Policy' 'same-origin' always; + add_header 'X-XSS-Protection' '1; mode=block' always; + \ No newline at end of file diff --git a/docker/webserver/nginx_templates/app.conf.template b/docker/webserver/nginx_templates/app.conf.template index 8da9661f8..e2ef2d393 100644 --- a/docker/webserver/nginx_templates/app.conf.template +++ b/docker/webserver/nginx_templates/app.conf.template @@ -22,27 +22,26 @@ server_tokens off; # caching settings proxy_cache_path /var/tmp/nginx_cache levels=1:2 keys_zone=default_cache:10m inactive=24h max_size=1g; -# default headers added to all responses -add_header 'X-Frame-Options' 'SAMEORIGIN' always; -add_header 'X-Content-Type-Options' 'nosniff' always; -add_header 'X-Clacks-Overhead' 'GNU Terry Pratchett' always; -add_header 'Referrer-Policy' 'same-origin' always; -add_header 'X-XSS-Protection' '1; mode=block' always; +include http.headers; root /var/www/internet.nl; # enable OSCP stapling ssl_stapling on; ssl_stapling_verify on; +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + +http2 on; + # default server for http, primary used for ACME and https redirect server { listen 80; listen [::]:80; - http2 on; - # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: nl|en - server_name ${INTERNETNL_DOMAINNAME} ~(${LANGUAGES_REGEX}|www|ipv6)\.${INTERNETNL_DOMAINNAME} ${REDIRECT_DOMAINS_LIST}; + # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: `nl\.|en\.` + server_name ~^((${LANGUAGES_REGEX})?(ipv6\.)?|www\.)${INTERNETNL_DOMAINNAME_REGEX}$ ${REDIRECT_DOMAINS_LIST}; # letsencrypt/ACME location /.well-known/acme-challenge/ { @@ -63,26 +62,21 @@ server { listen 80; listen [::]:80; - http2 on; - - # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: nl|en - server_name ~(conn|(?(${LANGUAGES_REGEX}|www)\.)conn).${INTERNETNL_DOMAINNAME}; + # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: `nl\.|en\.` + server_name ~^(?${LANGUAGES_REGEX})?conn\.(?ipv6\.)?${INTERNETNL_DOMAINNAME_REGEX}$; # pass specific connection test paths to backend # /connection/ # /connection/gettestid/ # /connection/finished/6330d6a09e56387e4dd59502418fa642/results location ~ ^(/connection/?|/connection/gettestid/?|/connection/finished/.+)$ { - # forward information about the connecting client to the connection test - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # forward information about the connecting client to the connection test + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # pass host for Django's allowed_hosts - proxy_set_header Host $host; - - # disable CSP on connection test - proxy_hide_header Content-Security-Policy; + # pass host for Django's allowed_hosts + proxy_set_header Host $host; - proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; + proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; } # letsencrypt/ACME @@ -94,9 +88,9 @@ server { } # redirect everything else to https and non conn. domain - # used named capture `subdomain` from `server_name` above as prefix + # used named capture `lang` and `ipv6` from `server_name` above as prefix location / { - return 301 https://${subdomain}${INTERNETNL_DOMAINNAME}$request_uri; + return 301 https://${lang}${ipv6}${INTERNETNL_DOMAINNAME}$request_uri; } } # http server for connection test XHR requests @@ -104,8 +98,6 @@ server { listen 80; listen [::]:80; - http2 on; - server_name *.test-ns-signed.${INTERNETNL_DOMAINNAME} *.test-ns6-signed.${INTERNETNL_DOMAINNAME} @@ -115,16 +107,13 @@ server { # / # /connection/addr-test/6330d6a09e56387e4dd59502418fa642/ location ~ ^(/|/connection/addr-test/.+/)$ { - # forward information about the connecting client to the connection test - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - # pass host for Django's allowed_hosts - proxy_set_header Host $host; + # forward information about the connecting client to the connection test + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # disable CSP on connection test - proxy_hide_header Content-Security-Policy; + # pass host for Django's allowed_hosts + proxy_set_header Host $host; - proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; + proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; } } @@ -133,34 +122,12 @@ server { listen 443 ssl; listen [::]:443 ssl; - http2 on; - # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: nl|en - server_name www.${INTERNETNL_DOMAINNAME} ~(${LANGUAGES_REGEX}|conn)\.www.${INTERNETNL_DOMAINNAME} ${REDIRECT_DOMAINS_LIST}; + server_name www.${INTERNETNL_DOMAINNAME} ${REDIRECT_DOMAINS_LIST}; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; - - ssl_certificate /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/privkey.pem; - - # letsencrypt/ACME - location /.well-known/acme-challenge/ { - # basic auth should not apply to this path - auth_basic off; - # IP allowlist should also not apply - allow all; - } - - add_header 'X-Frame-Options' 'SAMEORIGIN' always; - add_header 'X-Content-Type-Options' 'nosniff' always; - add_header 'X-Clacks-Overhead' 'GNU Terry Pratchett' always; - add_header 'Referrer-Policy' 'same-origin' always; - add_header 'X-XSS-Protection' '1; mode=block' always; - add_header 'Strict-Transport-Security' 'max-age=31536000' always; - add_header 'Content-Security-Policy' "default-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always; + include all.headers; # redirect to no-www domainname - location ~ /(.*) { + location / { return 301 https://${INTERNETNL_DOMAINNAME}$request_uri; } } @@ -170,50 +137,34 @@ server { listen 443 ssl; listen [::]:443 ssl; - http2 on; - - # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: nl|en - server_name ${INTERNETNL_DOMAINNAME} ~(?(${LANGUAGES_REGEX}|www|ipv6)\.)${INTERNETNL_DOMAINNAME}; + # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: `nl\.|en\.` + server_name ~^(?${LANGUAGES_REGEX})?(?ipv6\.)?${INTERNETNL_DOMAINNAME_REGEX}$ ${REDIRECT_DOMAINS_LIST}; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; - - ssl_certificate /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/privkey.pem; - - # include all default headers again and add HSTS header - add_header 'X-Frame-Options' 'SAMEORIGIN' always; - add_header 'X-Content-Type-Options' 'nosniff' always; - add_header 'X-Clacks-Overhead' 'GNU Terry Pratchett' always; - add_header 'Referrer-Policy' 'same-origin' always; - add_header 'X-XSS-Protection' '1; mode=block' always; - # Do *not* add includeSubdomains before https://github.com/internetstandards/Internet.nl/issues/324 is resolved, - # adding includeSubdomains without adding the wildcard SSL certificates will otherwise break the connection test. - add_header 'Strict-Transport-Security' 'max-age=31536000' always; + include all.headers; # by default proxy everything to the application location / { - # pass host for Django's allowed_hosts - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $scheme; - - # enable cache - proxy_cache ${NGINX_PROXY_CACHE}; - # have at least some cache, togetherwith use_stale this will result in visitors not hitting the backend directly - proxy_cache_valid 200 1m; - # server old version of files when backend experiences errors or when fetching new content - proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_503 http_504; - # tell client browser to also cache these resources - expires 1m; - # make sure to cache separate for languages - proxy_cache_key $scheme$host$uri$is_args$args$http_accept_language; - - proxy_set_header REMOTE-USER $remote_user; - include /etc/nginx/conf.d/basic_auth.include; - proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; + # pass host for Django's allowed_hosts + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + + # enable cache + proxy_cache ${NGINX_PROXY_CACHE}; + # have at least some cache, togetherwith use_stale this will result in visitors not hitting the backend directly + proxy_cache_valid 200 1m; + # server old version of files when backend experiences errors or when fetching new content + proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_503 http_504; + # tell client browser to also cache these resources + expires 1m; + # make sure to cache separate for languages + proxy_cache_key $scheme$host$uri$is_args$args$http_accept_language; + + proxy_set_header REMOTE-USER $remote_user; + include /etc/nginx/conf.d/basic_auth.include; + proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; } - # letsencrypt/ACME and security.txt file + # security.txt file location /.well-known/ { # basic auth should not apply to this path auth_basic off; @@ -234,16 +185,16 @@ server { # disable security.txt if branding is disabled set $internetnl_branding "${INTERNETNL_BRANDING}"; if ($internetnl_branding = "True"){ - set $security_txt "/var/www/internet.nl/.well-known/security.txt"; + set $security_txt "/var/www/internet.nl/.well-known/security.txt"; } if ($internetnl_branding != "True"){ - set $security_txt "/var/www/internet.nl/.well-known/security-custom.txt"; + set $security_txt "/var/www/internet.nl/.well-known/security-custom.txt"; } location = /.well-known/security.txt { - # basic auth should not apply to this path - auth_basic off; - # IP allowlist should also not apply - alias $security_txt; + # basic auth should not apply to this path + auth_basic off; + # IP allowlist should also not apply + alias $security_txt; } # static files served from Nginx container @@ -256,59 +207,65 @@ server { # static files served from app location /static { - # enable cache - proxy_cache ${NGINX_PROXY_CACHE}; - # static files don't change often, cache for long - proxy_cache_valid 200 1d; - # server old version of files when backend experiences errors - proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; - # tell client browser to also cache these resources - expires 1d; - - # pass host for Django's allowed_hosts - proxy_set_header Host $host; - - proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; + # enable cache + proxy_cache ${NGINX_PROXY_CACHE}; + # static files don't change often, cache for long + proxy_cache_valid 200 1d; + # server old version of files when backend experiences errors + proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; + # tell client browser to also cache these resources + expires 1d; + + # pass host for Django's allowed_hosts + proxy_set_header Host $host; + + proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; } # redirect connection test to http subdomain to start test, needs 301 permanent # otherwise browsers might ignore the protocol change # only redirect connection test start, other connection test paths still need to # pass to the application - # used named capture `subdomain` from `server_name` above as prefix + # used named capture `lang` and `ipv6` from `server_name` above as prefix location = /connection/ { - return 301 http://${subdomain}conn.${INTERNETNL_DOMAINNAME}/connection/; + return 301 http://${lang}conn.${ipv6}${INTERNETNL_DOMAINNAME}/connection/; } # batch API, requires authentication and passes basic auth user to Django App via headers location /api/batch/v2 { - auth_basic "Please enter your batch username and password"; - auth_basic_user_file /etc/nginx/htpasswd/external/users.htpasswd; + auth_basic "Please enter your batch username and password"; + auth_basic_user_file /etc/nginx/htpasswd/external/users.htpasswd; - # pass logged in user to Django - proxy_set_header REMOTE-USER $remote_user; + # pass logged in user to Django + proxy_set_header REMOTE-USER $remote_user; - # pass host for Django's allowed_hosts - proxy_set_header Host $host; + # pass host for Django's allowed_hosts + proxy_set_header Host $host; - proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; + proxy_pass http://${IPV4_IP_APP_INTERNAL}:8080; } - # monitoring, requires authentication + # monitoring, requires authentication, override headers, since CSP is too strict location /grafana { - auth_basic "Please enter your monitoring username and password"; - auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; - proxy_pass http://${IPV4_IP_GRAFANA_INTERNAL}:3000; + include http.headers; + include hsts.header; + auth_basic "Please enter your monitoring username and password"; + auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; + proxy_pass http://${IPV4_IP_GRAFANA_INTERNAL}:3000; } location /prometheus { - auth_basic "Please enter your monitoring username and password"; - auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; - proxy_pass http://${IPV4_IP_PROMETHEUS_INTERNAL}:9090; + include http.headers; + include hsts.header; + auth_basic "Please enter your monitoring username and password"; + auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; + proxy_pass http://${IPV4_IP_PROMETHEUS_INTERNAL}:9090; } location /alertmanager { - auth_basic "Please enter your monitoring username and password"; - auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; - proxy_pass http://${IPV4_IP_ALERTMANAGER_INTERNAL}:9093; + include http.headers; + include hsts.header; + auth_basic "Please enter your monitoring username and password"; + auth_basic_user_file /etc/nginx/htpasswd/monitoring.htpasswd; + proxy_pass http://${IPV4_IP_ALERTMANAGER_INTERNAL}:9093; } } @@ -318,31 +275,18 @@ server { listen 443 ssl; listen [::]:443 ssl; - http2 on; + # LANGUAGES_REGEX is a list of language prefixes separated by pipes, eg: `nl\.|en\.` + server_name ~^(?${LANGUAGES_REGEX})?conn\.(?ipv6\.)?${INTERNETNL_DOMAINNAME_REGEX}$; - server_name conn.${INTERNETNL_DOMAINNAME}; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; - - ssl_certificate /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/privkey.pem; - - # include all default headers again and add HSTS header - add_header 'X-Frame-Options' 'SAMEORIGIN' always; - add_header 'X-Content-Type-Options' 'nosniff' always; - add_header 'X-Clacks-Overhead' 'GNU Terry Pratchett' always; - add_header 'Referrer-Policy' 'same-origin' always; - add_header 'X-XSS-Protection' '1; mode=block' always; - # Do *not* add includeSubdomains before https://github.com/internetstandards/Internet.nl/issues/324 is resolved, - # adding includeSubdomains without adding the wildcard SSL certificates will otherwise break the connection test. + include http.headers; # Set max-age to 0 to effectivily disable HSTS on this subdomain to undo any HSTS settings done in the past. # This can be removed 1 year after initial release. See issue #894. add_header 'Strict-Transport-Security' 'max-age=0' always; # redirect to non-https version for connection test + # used named capture `lang` and `ipv6` from `server_name` above as prefix location / { - return 301 http://conn.${INTERNETNL_DOMAINNAME}$request_uri; + return 301 http://${lang}conn.${ipv6}${INTERNETNL_DOMAINNAME}$request_uri; } } @@ -351,10 +295,11 @@ server { listen 443 ssl default_server; listen [::]:443 ssl default_server; - http2 on; + server_name _; ssl_reject_handshake on; + # only reachable if a correct SNI is send, but different unknown host (see test_default_sni_none). location / { return 404; } @@ -365,8 +310,6 @@ server { listen 80 default_server; listen [::]:80 default_server; - http2 on; - server_name _; location / { diff --git a/docker/webserver/nginx_templates/csp.header.template b/docker/webserver/nginx_templates/csp.header.template new file mode 100644 index 000000000..33743270c --- /dev/null +++ b/docker/webserver/nginx_templates/csp.header.template @@ -0,0 +1 @@ +add_header 'Content-Security-Policy' "base-uri 'self' https://*.${INTERNETNL_DOMAINNAME}; form-action 'self' https://*.${INTERNETNL_DOMAINNAME}; frame-ancestors 'none'; default-src 'self' https://*.${INTERNETNL_DOMAINNAME}"; diff --git a/docker/webserver/nginx_templates/letsencrypt.conf.template b/docker/webserver/nginx_templates/letsencrypt.conf.template new file mode 100644 index 000000000..ce3f174b4 --- /dev/null +++ b/docker/webserver/nginx_templates/letsencrypt.conf.template @@ -0,0 +1,2 @@ +ssl_certificate /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/${INTERNETNL_DOMAINNAME}/privkey.pem; diff --git a/integration_tests/integration/test_connection.py b/integration_tests/integration/test_connection.py index 46e3b9444..3098d76a6 100644 --- a/integration_tests/integration/test_connection.py +++ b/integration_tests/integration/test_connection.py @@ -72,7 +72,7 @@ def test_direct_connect_browser_to_webserver(unique_id): @pytest.mark.parametrize( "subdomain", - ["en", "nl", "www"], + ["en", "nl"], ) def test_conn_subdomain_redirects(subdomain, app_domain): """These subdomains should redirect to a domain without conn. in it and https.""" diff --git a/internetnl/internet.nl.dist.env b/internetnl/internet.nl.dist.env index 102578446..d82f9b8f8 100644 --- a/internetnl/internet.nl.dist.env +++ b/internetnl/internet.nl.dist.env @@ -27,8 +27,6 @@ export ADMIN_EMAIL=django@internet.nl ### String, e-mail address export SERVER_EMAIL=django@internet.nl ### CSV String -export CSP_DEFAULT_SRC='self',https://*.internet.nl -### CSV String export INTERNAL_IPS="localhost,127.0.0.1" ### String export TIME_ZONE=UTC diff --git a/internetnl/settings.py b/internetnl/settings.py index f2358ee57..331aed486 100644 --- a/internetnl/settings.py +++ b/internetnl/settings.py @@ -50,8 +50,6 @@ ADMIN_NAME = getenv("ADMIN_NAME", "Administrator") ADMIN_EMAIL = getenv("ADMIN_EMAIL", "Administrator") SERVER_EMAIL = getenv("SERVER_EMAIL", "django@internet.nl") -CSP_DEFAULT_SRC = split_csv_trim(getenv("CSP_DEFAULT_SRC", "'self',*.internet.nl")) -CSP_BASE_URI = CSP_FORM_ACTION = CSP_DEFAULT_SRC INTERNAL_IPS = split_csv_trim(getenv("INTERNAL_IPS", "")) TIME_ZONE = getenv("TIME_ZONE", "UTC") @@ -186,11 +184,9 @@ "django.contrib.messages.middleware.MessageMiddleware", "django_hosts.middleware.HostsResponseMiddleware", "internetnl.custom_middlewares.ActivateTranslationMiddleware", - "csp.middleware.CSPMiddleware", ] ADMINS = ((ADMIN_NAME, ADMIN_EMAIL),) -CSP_FRAME_ANCESTORS = "'none'" ROOT_URLCONF = "internetnl.urls" ROOT_HOSTCONF = "internetnl.hosts" DEFAULT_HOST = "www" diff --git a/requirements.in b/requirements.in index 422b37a30..cd5492803 100644 --- a/requirements.in +++ b/requirements.in @@ -14,7 +14,6 @@ setuptools-rust beautifulsoup4 cryptography<39.0.0 django-bleach<3 # 3 and up has no Python 3.7 support -django-csp django-enumfields django-hosts django-markdown_deux diff --git a/requirements.txt b/requirements.txt index 65ac84e33..5337edea2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,12 +62,9 @@ django==4.2.16 # via # -r requirements.in # django-bleach - # django-csp # django-redis django-bleach==2.0.0 # via -r requirements.in -django-csp==3.7 - # via -r requirements.in django-enumfields==2.1.1 # via -r requirements.in django-hosts==5.2