Skip to content
Snippets Groups Projects
Verified Commit 4f872bae authored by Kristian Klausen's avatar Kristian Klausen :tada:
Browse files

Fix missing HSTS header for some URLs due to nginx "directive inheritance"[1]

F5/nginx has blogged about this[1] and it is also mentioned in nginx's
documentation[2]:
"There could be several add_header directives. These directives are
inherited from the previous configuration level if and only if there are
no add_header directives defined on the current level. "

The problem occurs when add_header is used in a child context like a
server{} or location{} block. It is solved by moving the HSTS header
into a snippet, which is now included before all add_header lines.

For now the HSTS header is the only global header, but in the future we
may need to add more global headers, like the Alt-Svc header[3] for
HTTP/3.

[1] https://www.f5.com/company/blog/nginx/avoiding-top-10-nginx-configuration-mistakes#directive-inheritance
[2] https://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
[3] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc

Fix #608
parent a816ca06
No related branches found
No related tags found
1 merge request!859Fix missing HSTS header for some URLs due to nginx "directive inheritance"[1]
Showing
with 29 additions and 8 deletions
...@@ -49,6 +49,7 @@ server { ...@@ -49,6 +49,7 @@ server {
# Client-cache for Django's static assets # Client-cache for Django's static assets
location /static/ { location /static/ {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public"; add_header Cache-Control "public";
alias {{ archmanweb_dir }}/repo/collected_static/; alias {{ archmanweb_dir }}/repo/collected_static/;
......
...@@ -41,6 +41,7 @@ server { ...@@ -41,6 +41,7 @@ server {
# Cache django's css, js and png files. # Cache django's css, js and png files.
location /static/ { location /static/ {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public"; add_header Cache-Control "public";
alias /srv/http/archweb/collected_static/; alias /srv/http/archweb/collected_static/;
......
...@@ -120,6 +120,7 @@ server { ...@@ -120,6 +120,7 @@ server {
location = /.well-known/matrix/client { location = /.well-known/matrix/client {
default_type application/json; default_type application/json;
include snippets/headers.conf;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }'; return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }';
} }
...@@ -167,6 +168,7 @@ server { ...@@ -167,6 +168,7 @@ server {
# Cache django's css, js and png files. # Cache django's css, js and png files.
location /static/ { location /static/ {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public"; add_header Cache-Control "public";
alias {{ archweb_dir }}/collected_static/; alias {{ archweb_dir }}/collected_static/;
......
...@@ -42,6 +42,7 @@ server { ...@@ -42,6 +42,7 @@ server {
include snippets/letsencrypt.conf; include snippets/letsencrypt.conf;
location /.well-known/ { location /.well-known/ {
include snippets/headers.conf;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
return 301 https://$server_name$request_uri; return 301 https://$server_name$request_uri;
} }
...@@ -67,6 +68,7 @@ server { ...@@ -67,6 +68,7 @@ server {
ssl_trusted_certificate /etc/letsencrypt/live/{{ archweb_domain }}/chain.pem; ssl_trusted_certificate /etc/letsencrypt/live/{{ archweb_domain }}/chain.pem;
location /.well-known/ { location /.well-known/ {
include snippets/headers.conf;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
return 301 https://{{ archweb_domain }}{{ domain['redirect']|default('$request_uri') }}; return 301 https://{{ archweb_domain }}{{ domain['redirect']|default('$request_uri') }};
} }
...@@ -120,6 +122,7 @@ server { ...@@ -120,6 +122,7 @@ server {
location = /.well-known/matrix/client { location = /.well-known/matrix/client {
default_type application/json; default_type application/json;
include snippets/headers.conf;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }'; return 200 '{"m.homeserver": {"base_url": "https://{{ matrix_domain }}"}, "m.identity_server": {"base_url": "https://matrix.org"} }';
} }
...@@ -169,6 +172,7 @@ server { ...@@ -169,6 +172,7 @@ server {
# Cache django's css, js and png files. # Cache django's css, js and png files.
location /static/ { location /static/ {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public"; add_header Cache-Control "public";
alias {{ archweb_dir }}/collected_static/; alias {{ archweb_dir }}/collected_static/;
...@@ -189,6 +193,7 @@ server { ...@@ -189,6 +193,7 @@ server {
uwsgi_cache archwebcache; uwsgi_cache archwebcache;
uwsgi_cache_revalidate on; uwsgi_cache_revalidate on;
include snippets/headers.conf;
add_header X-Cache-Status $upstream_cache_status; add_header X-Cache-Status $upstream_cache_status;
limit_req zone=rsslimit burst=10 nodelay; limit_req zone=rsslimit burst=10 nodelay;
...@@ -202,6 +207,7 @@ server { ...@@ -202,6 +207,7 @@ server {
uwsgi_cache archwebcache; uwsgi_cache archwebcache;
uwsgi_cache_revalidate on; uwsgi_cache_revalidate on;
uwsgi_cache_key $cache_key; uwsgi_cache_key $cache_key;
include snippets/headers.conf;
add_header X-Cache-Status $upstream_cache_status; add_header X-Cache-Status $upstream_cache_status;
limit_req zone=mirrorstatuslimit burst=10 nodelay; limit_req zone=mirrorstatuslimit burst=10 nodelay;
...@@ -235,11 +241,9 @@ server { ...@@ -235,11 +241,9 @@ server {
uwsgi_cache archwebcache; uwsgi_cache archwebcache;
uwsgi_cache_revalidate on; uwsgi_cache_revalidate on;
uwsgi_cache_key $cache_key; uwsgi_cache_key $cache_key;
include snippets/headers.conf;
add_header X-Cache-Status $upstream_cache_status; 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; limit_req zone=archweblimit burst=10 nodelay;
} }
} }
...@@ -125,6 +125,7 @@ server { ...@@ -125,6 +125,7 @@ server {
fastcgi_cache_use_stale updating; fastcgi_cache_use_stale updating;
fastcgi_cache_lock on; fastcgi_cache_lock on;
include snippets/headers.conf;
add_header X-Cache $upstream_cache_status; add_header X-Cache $upstream_cache_status;
{% endblock %} {% endblock %}
} }
...@@ -143,6 +144,7 @@ server { ...@@ -143,6 +144,7 @@ server {
# normal PHP FastCGI handler # normal PHP FastCGI handler
location ~ ^/[^/]+\.php$ { location ~ ^/[^/]+\.php$ {
if ($challenge) { if ($challenge) {
include snippets/headers.conf;
add_header Set-Cookie "challenge={{ archwiki_nginx_challenge_value }}; SameSite=Strict"; add_header Set-Cookie "challenge={{ archwiki_nginx_challenge_value }}; SameSite=Strict";
return 303 $scheme://$server_name/$request_uri; return 303 $scheme://$server_name/$request_uri;
} }
...@@ -165,12 +167,14 @@ server { ...@@ -165,12 +167,14 @@ server {
# MediaWiki assets # MediaWiki assets
location ~ ^/(?:images|resources/(?:assets|lib|src)|(?:skins|extensions)/.+\.(?:css|js|gif|jpg|jpeg|png|svg|wasm)$) { location ~ ^/(?:images|resources/(?:assets|lib|src)|(?:skins|extensions)/.+\.(?:css|js|gif|jpg|jpeg|png|svg|wasm)$) {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
location /images/ { location /images/ {
# Add the nosniff header to the images folder (required for mw 1.40+) # Add the nosniff header to the images folder (required for mw 1.40+)
include snippets/headers.conf;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
} }
......
...@@ -110,6 +110,7 @@ server { ...@@ -110,6 +110,7 @@ server {
location ~ \.gz$ { location ~ \.gz$ {
root {{ aurweb_dir }}/archives; root {{ aurweb_dir }}/archives;
default_type text/plain; default_type text/plain;
include snippets/headers.conf;
add_header Content-Encoding gzip; add_header Content-Encoding gzip;
expires 5m; expires 5m;
} }
...@@ -118,6 +119,7 @@ server { ...@@ -118,6 +119,7 @@ server {
rewrite ^/static(/.*)$ $1 break; rewrite ^/static(/.*)$ $1 break;
expires 7d; expires 7d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
......
...@@ -76,12 +76,14 @@ server { ...@@ -76,12 +76,14 @@ server {
location ^~ /style/ { location ^~ /style/ {
expires 7d; expires 7d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
location ^~ /img/ { location ^~ /img/ {
expires 7d; expires 7d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
......
...@@ -45,6 +45,7 @@ server { ...@@ -45,6 +45,7 @@ server {
access_log /var/log/nginx/{{ matrix_domain }}/access.log main; access_log /var/log/nginx/{{ matrix_domain }}/access.log main;
access_log /var/log/nginx/{{ matrix_domain }}/access.log.json json_main; access_log /var/log/nginx/{{ matrix_domain }}/access.log.json json_main;
{% if location.add_cors | default(false) %} {% if location.add_cors | default(false) %}
include snippets/headers.conf;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, DELETE, OPTIONS"; 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"; add_header Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization, Date";
......
...@@ -17,6 +17,7 @@ server { ...@@ -17,6 +17,7 @@ server {
ssl_certificate_key /etc/letsencrypt/live/{{ item.value.mirror_domain }}/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/{{ item.value.mirror_domain }}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/{{ item.value.mirror_domain }}/chain.pem; ssl_trusted_certificate /etc/letsencrypt/live/{{ item.value.mirror_domain }}/chain.pem;
include snippets/headers.conf;
add_header X-Served-By "{{ inventory_hostname }}"; add_header X-Served-By "{{ inventory_hostname }}";
autoindex on; autoindex on;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
with_items: with_items:
- letsencrypt.conf - letsencrypt.conf
- sslsettings.conf - sslsettings.conf
- headers.conf
notify: notify:
- Reload nginx - Reload nginx
......
add_header Strict-Transport-Security $hsts_header always;
...@@ -90,6 +90,7 @@ http { ...@@ -90,6 +90,7 @@ http {
access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_http main; access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_http main;
include snippets/sslsettings.conf; include snippets/sslsettings.conf;
include snippets/headers.conf;
include nginx.d/*.conf; include nginx.d/*.conf;
} }
...@@ -13,10 +13,9 @@ ssl_session_tickets off; ...@@ -13,10 +13,9 @@ ssl_session_tickets off;
ssl_stapling on; ssl_stapling on;
ssl_stapling_verify on; ssl_stapling_verify on;
# See headers.conf for the HSTS add_header line.
map $scheme $hsts_header { map $scheme $hsts_header {
https "max-age=31536000; includeSubdomains; preload"; https "max-age=31536000; includeSubdomains; preload";
} }
add_header Strict-Transport-Security $hsts_header always;
resolver 127.0.0.53; resolver 127.0.0.53;
...@@ -26,6 +26,7 @@ server { ...@@ -26,6 +26,7 @@ server {
# https://man.archlinux.org/man/NetworkManager.conf.5#CONNECTIVITY_SECTION # https://man.archlinux.org/man/NetworkManager.conf.5#CONNECTIVITY_SECTION
location = /nm-check.txt { location = /nm-check.txt {
access_log off; access_log off;
include snippets/headers.conf;
add_header Cache-Control "max-age=0, must-revalidate"; add_header Cache-Control "max-age=0, must-revalidate";
return 200 'NetworkManager is online\n'; return 200 'NetworkManager is online\n';
} }
......
...@@ -30,6 +30,7 @@ server { ...@@ -30,6 +30,7 @@ server {
ssl_trusted_certificate /etc/letsencrypt/live/{{ rebuilderd_domain }}/chain.pem; ssl_trusted_certificate /etc/letsencrypt/live/{{ rebuilderd_domain }}/chain.pem;
# Security headers # Security headers
include snippets/headers.conf;
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1; mode=block" always; add_header X-Xss-Protection "1; mode=block" always;
add_header Referrer-Policy "same-origin"; add_header Referrer-Policy "same-origin";
...@@ -37,13 +38,11 @@ server { ...@@ -37,13 +38,11 @@ server {
add_header Content-Security-Policy "default-src 'self';"; add_header Content-Security-Policy "default-src 'self';";
add_header X-Content-Type-Options "nosniff" always; 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 }}; root {{ rebuilder_website_loc }};
location ~* (css|js|svg)$ { location ~* (css|js|svg)$ {
expires 30d; expires 30d;
include snippets/headers.conf;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
......
...@@ -19,6 +19,7 @@ server { ...@@ -19,6 +19,7 @@ server {
ssl_trusted_certificate /etc/letsencrypt/live/{{ domain }}/chain.pem; ssl_trusted_certificate /etc/letsencrypt/live/{{ domain }}/chain.pem;
{% if 'geo_mirrors' in group_names and domain == geo_mirror_domain %} {% if 'geo_mirrors' in group_names and domain == geo_mirror_domain %}
include snippets/headers.conf;
add_header X-Served-By "{{ inventory_hostname }}"; add_header X-Served-By "{{ inventory_hostname }}";
{% endif %} {% endif %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment