diff --git a/roles/archmanweb/templates/nginx.d.conf.j2 b/roles/archmanweb/templates/nginx.d.conf.j2 index 275f38162b0c6565b0e91b18ac4603c4fe980ce0..3c51fe125b053cc51ee0b48101e43dd4a838f9c0 100644 --- a/roles/archmanweb/templates/nginx.d.conf.j2 +++ b/roles/archmanweb/templates/nginx.d.conf.j2 @@ -49,6 +49,7 @@ server { # Client-cache for Django's static assets location /static/ { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public"; alias {{ archmanweb_dir }}/repo/collected_static/; diff --git a/roles/archweb/templates/ipxe.archlinux.org.j2 b/roles/archweb/templates/ipxe.archlinux.org.j2 index 2df4e1d6007c854437a4340c8ee5e6e6e6aeaf96..dd6c6f374d165307ef596d2bc1549910bf6eabf2 100644 --- a/roles/archweb/templates/ipxe.archlinux.org.j2 +++ b/roles/archweb/templates/ipxe.archlinux.org.j2 @@ -41,6 +41,7 @@ server { # Cache django's css, js and png files. location /static/ { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public"; alias /srv/http/archweb/collected_static/; diff --git a/roles/archweb/templates/maintenance-nginx.d.conf.j2 b/roles/archweb/templates/maintenance-nginx.d.conf.j2 index 6eddee94891bc5189a37bcfc1578d3fdde6dd19f..e9f743772ac7276aa3b98853594f23d32c25e1e6 100644 --- a/roles/archweb/templates/maintenance-nginx.d.conf.j2 +++ b/roles/archweb/templates/maintenance-nginx.d.conf.j2 @@ -120,6 +120,7 @@ server { location = /.well-known/matrix/client { default_type application/json; + include snippets/headers.conf; add_header Access-Control-Allow-Origin *; return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }'; } @@ -167,6 +168,7 @@ server { # Cache django's css, js and png files. location /static/ { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public"; alias {{ archweb_dir }}/collected_static/; diff --git a/roles/archweb/templates/nginx.d.conf.j2 b/roles/archweb/templates/nginx.d.conf.j2 index 8dcd8d7b0c022166fc85d9570f30590f4706888d..186befa292592a4ae0d96ce462b63a49951608e9 100644 --- a/roles/archweb/templates/nginx.d.conf.j2 +++ b/roles/archweb/templates/nginx.d.conf.j2 @@ -42,6 +42,7 @@ server { include snippets/letsencrypt.conf; location /.well-known/ { + include snippets/headers.conf; add_header Access-Control-Allow-Origin *; return 301 https://$server_name$request_uri; } @@ -67,6 +68,7 @@ server { ssl_trusted_certificate /etc/letsencrypt/live/{{ archweb_domain }}/chain.pem; location /.well-known/ { + include snippets/headers.conf; add_header Access-Control-Allow-Origin *; return 301 https://{{ archweb_domain }}{{ domain['redirect']|default('$request_uri') }}; } @@ -120,6 +122,7 @@ server { location = /.well-known/matrix/client { default_type application/json; + include snippets/headers.conf; add_header Access-Control-Allow-Origin *; return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }'; } @@ -169,6 +172,7 @@ server { # Cache django's css, js and png files. location /static/ { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public"; alias {{ archweb_dir }}/collected_static/; @@ -189,6 +193,7 @@ server { uwsgi_cache archwebcache; uwsgi_cache_revalidate on; + include snippets/headers.conf; add_header X-Cache-Status $upstream_cache_status; limit_req zone=rsslimit burst=10 nodelay; @@ -202,6 +207,7 @@ server { uwsgi_cache archwebcache; uwsgi_cache_revalidate on; uwsgi_cache_key $cache_key; + include snippets/headers.conf; add_header X-Cache-Status $upstream_cache_status; limit_req zone=mirrorstatuslimit burst=10 nodelay; @@ -235,11 +241,9 @@ server { uwsgi_cache archwebcache; uwsgi_cache_revalidate on; uwsgi_cache_key $cache_key; + include snippets/headers.conf; add_header X-Cache-Status $upstream_cache_status; - # re-add HSTS (inheritance from sslsettings.conf broken by above header) - add_header Strict-Transport-Security $hsts_header always; - limit_req zone=archweblimit burst=10 nodelay; } } diff --git a/roles/archwiki/templates/nginx.d.conf.j2 b/roles/archwiki/templates/nginx.d.conf.j2 index 6a63335447acd5e09f9a990a50210e7b87f2479a..0be7fe3187b7254fdeb86c4466cec916dad46900 100644 --- a/roles/archwiki/templates/nginx.d.conf.j2 +++ b/roles/archwiki/templates/nginx.d.conf.j2 @@ -125,6 +125,7 @@ server { fastcgi_cache_use_stale updating; fastcgi_cache_lock on; + include snippets/headers.conf; add_header X-Cache $upstream_cache_status; {% endblock %} } @@ -143,6 +144,7 @@ server { # normal PHP FastCGI handler location ~ ^/[^/]+\.php$ { if ($challenge) { + include snippets/headers.conf; add_header Set-Cookie "challenge={{ archwiki_nginx_challenge_value }}; SameSite=Strict"; return 303 $scheme://$server_name/$request_uri; } @@ -165,12 +167,14 @@ server { # MediaWiki assets location ~ ^/(?:images|resources/(?:assets|lib|src)|(?:skins|extensions)/.+\.(?:css|js|gif|jpg|jpeg|png|svg|wasm)$) { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } location /images/ { # Add the nosniff header to the images folder (required for mw 1.40+) + include snippets/headers.conf; add_header X-Content-Type-Options nosniff; } diff --git a/roles/aurweb/templates/nginx.d.conf.j2 b/roles/aurweb/templates/nginx.d.conf.j2 index 1373b66231a9c2857fa23e84fdc0f978f8195f57..a594ef98c7e84e8e84ded8fafa3f3390589f80fe 100644 --- a/roles/aurweb/templates/nginx.d.conf.j2 +++ b/roles/aurweb/templates/nginx.d.conf.j2 @@ -110,6 +110,7 @@ server { location ~ \.gz$ { root {{ aurweb_dir }}/archives; default_type text/plain; + include snippets/headers.conf; add_header Content-Encoding gzip; expires 5m; } @@ -118,6 +119,7 @@ server { rewrite ^/static(/.*)$ $1 break; expires 7d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } diff --git a/roles/fluxbb/templates/nginx.conf.j2 b/roles/fluxbb/templates/nginx.conf.j2 index f4678cf833fa64b6c539b340e578ce7e97b95478..563cfb13e5c4dc0bd973bc5f375ec276bf804400 100644 --- a/roles/fluxbb/templates/nginx.conf.j2 +++ b/roles/fluxbb/templates/nginx.conf.j2 @@ -76,12 +76,14 @@ server { location ^~ /style/ { expires 7d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } location ^~ /img/ { expires 7d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } diff --git a/roles/matrix/templates/nginx.d.conf.j2 b/roles/matrix/templates/nginx.d.conf.j2 index a353415c317ec21e46cf25761fdeedf916f8d800..85fdc500f40a4f48dfefc05a57a557e8affca11f 100644 --- a/roles/matrix/templates/nginx.d.conf.j2 +++ b/roles/matrix/templates/nginx.d.conf.j2 @@ -45,6 +45,7 @@ server { access_log /var/log/nginx/{{ matrix_domain }}/access.log main; access_log /var/log/nginx/{{ matrix_domain }}/access.log.json json_main; {% if location.add_cors | default(false) %} + include snippets/headers.conf; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization, Date"; diff --git a/roles/mirrorsync/templates/nginx.d.conf.j2 b/roles/mirrorsync/templates/nginx.d.conf.j2 index 22604eb5718d766a42dd8ea847fb333bd937de2d..4eb9cd844612f9fc117f0bd73264937b5d019298 100644 --- a/roles/mirrorsync/templates/nginx.d.conf.j2 +++ b/roles/mirrorsync/templates/nginx.d.conf.j2 @@ -17,6 +17,7 @@ server { ssl_certificate_key /etc/letsencrypt/live/{{ item.value.mirror_domain }}/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/{{ item.value.mirror_domain }}/chain.pem; + include snippets/headers.conf; add_header X-Served-By "{{ inventory_hostname }}"; autoindex on; diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index 5ff618a573ef8e64e496420bfd3ebc663afc8050..1129b280c9319ffaebe38f6529c5853fd7f125f6 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -23,6 +23,7 @@ with_items: - letsencrypt.conf - sslsettings.conf + - headers.conf notify: - Reload nginx diff --git a/roles/nginx/templates/headers.conf b/roles/nginx/templates/headers.conf new file mode 100644 index 0000000000000000000000000000000000000000..d427430373b3ebda009fe5518b3cee417e34ae1b --- /dev/null +++ b/roles/nginx/templates/headers.conf @@ -0,0 +1 @@ +add_header Strict-Transport-Security $hsts_header always; diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index 584c26406e984abf1a12733bf77688d60a09ec45..29ba71416fae23b104ce8fb717a9387456a74c4d 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -90,6 +90,7 @@ http { access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_http main; include snippets/sslsettings.conf; + include snippets/headers.conf; include nginx.d/*.conf; } diff --git a/roles/nginx/templates/sslsettings.conf b/roles/nginx/templates/sslsettings.conf index 1c98ad9ffd976a9fbcc41f3733c68f23dcf8132a..43d7f9382c2f6af3c3e81d6f7d4d6f689cceabc3 100644 --- a/roles/nginx/templates/sslsettings.conf +++ b/roles/nginx/templates/sslsettings.conf @@ -13,10 +13,9 @@ ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; +# See headers.conf for the HSTS add_header line. map $scheme $hsts_header { https "max-age=31536000; includeSubdomains; preload"; } -add_header Strict-Transport-Security $hsts_header always; - resolver 127.0.0.53; diff --git a/roles/ping/templates/nginx.d.conf.j2 b/roles/ping/templates/nginx.d.conf.j2 index 0149561ca94dce43b9f2a73d1002f3b21ae6611f..ea37574269ba60617c953c0997f5e3bcf8c524c3 100644 --- a/roles/ping/templates/nginx.d.conf.j2 +++ b/roles/ping/templates/nginx.d.conf.j2 @@ -26,6 +26,7 @@ server { # https://man.archlinux.org/man/NetworkManager.conf.5#CONNECTIVITY_SECTION location = /nm-check.txt { access_log off; + include snippets/headers.conf; add_header Cache-Control "max-age=0, must-revalidate"; return 200 'NetworkManager is online\n'; } diff --git a/roles/rebuilderd/templates/nginx.d.conf.j2 b/roles/rebuilderd/templates/nginx.d.conf.j2 index 54ec638903ff40a34b3d44cf7afd41efd274e91f..bd4cc6c5a15f1e21b8d6bf1244cbb1b83fed283c 100644 --- a/roles/rebuilderd/templates/nginx.d.conf.j2 +++ b/roles/rebuilderd/templates/nginx.d.conf.j2 @@ -30,6 +30,7 @@ server { ssl_trusted_certificate /etc/letsencrypt/live/{{ rebuilderd_domain }}/chain.pem; # Security headers + include snippets/headers.conf; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Xss-Protection "1; mode=block" always; add_header Referrer-Policy "same-origin"; @@ -37,13 +38,11 @@ server { add_header Content-Security-Policy "default-src 'self';"; add_header X-Content-Type-Options "nosniff" always; - # Apply HSTS header again, since adding a header removes previous headers - add_header Strict-Transport-Security $hsts_header; - root {{ rebuilder_website_loc }}; location ~* (css|js|svg)$ { expires 30d; + include snippets/headers.conf; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } diff --git a/roles/syncrepo/templates/nginx.d.conf.j2 b/roles/syncrepo/templates/nginx.d.conf.j2 index e5925f8372dd52c5b4d2f87165e233be71ff27db..c5743639e531a6341ce45b8f06a291d46b6e9137 100644 --- a/roles/syncrepo/templates/nginx.d.conf.j2 +++ b/roles/syncrepo/templates/nginx.d.conf.j2 @@ -19,6 +19,7 @@ server { ssl_trusted_certificate /etc/letsencrypt/live/{{ domain }}/chain.pem; {% if 'geo_mirrors' in group_names and domain == geo_mirror_domain %} + include snippets/headers.conf; add_header X-Served-By "{{ inventory_hostname }}"; {% endif %}