
This is useless for TLSv1.3 since there's no longer any distinction in the protocol based on whether the server is using stateless or stateful session resumption. OpenSSL has a non-standard anti-replay mechanism for 0-RTT based on stateful session resumption but 0-RTT still ends up being a downgrade for the TLS security properties. nginx disables that feature since otherwise 0-RTT wouldn't work with the default stateless approach. Since this cache is only used for TLSv1.2 when stateless resumption isn't disabled and nearly all TLSv1.2 clients support tickets, it isn't getting any significant use. It provides worse forward secrecy than tickets because we implement ticket key rotation based on the expiry time and sessions aren't actively purged from the stateful cache when they expire. Cached session state varies in size and nginx ends up writing errors to the log when clearing out a session fails to make room for a new one due to it being larger. It's best to finally get rid of this flawed approach to session resumption. TLSv1.3 provides the option of forward secrecy for resumed sessions and it's the only approach that's normally enabled so we don't need to worry about this anymore once TLSv1.2 is disabled as long as we never enable 0-RTT which weakens forward secrecy and other security properties.
532 lines
18 KiB
Nginx Configuration File
532 lines
18 KiB
Nginx Configuration File
# nginx 1.20.x
|
||
|
||
load_module modules/ngx_http_brotli_static_module.so;
|
||
|
||
worker_processes auto;
|
||
worker_rlimit_nofile 16384;
|
||
|
||
events {
|
||
worker_connections 4096;
|
||
}
|
||
|
||
http {
|
||
include mime.types;
|
||
default_type application/octet-stream;
|
||
|
||
charset utf-8;
|
||
charset_types text/css text/plain text/xml application/atom+xml application/javascript;
|
||
|
||
sendfile on;
|
||
sendfile_max_chunk 512k;
|
||
tcp_nopush on;
|
||
keepalive_timeout 3m;
|
||
server_tokens off;
|
||
msie_padding off;
|
||
|
||
client_max_body_size 1k;
|
||
client_body_buffer_size 1k;
|
||
client_header_buffer_size 1k;
|
||
large_client_header_buffers 4 4k;
|
||
http2_recv_buffer_size 128k;
|
||
|
||
client_body_timeout 30s;
|
||
client_header_timeout 30s;
|
||
send_timeout 30s;
|
||
|
||
http2_max_concurrent_streams 32;
|
||
limit_conn_status 429;
|
||
limit_conn_zone $binary_remote_addr zone=addr:10m;
|
||
limit_conn addr 256;
|
||
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256;
|
||
ssl_prefer_server_ciphers on;
|
||
ssl_conf_command Options PrioritizeChaCha;
|
||
|
||
ssl_certificate /etc/letsencrypt/live/grapheneos.org/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/grapheneos.org/privkey.pem;
|
||
|
||
# maintained by nginx-rotate-session-ticket-keys in ramfs
|
||
ssl_session_ticket_key session-ticket-keys/4.key;
|
||
ssl_session_ticket_key session-ticket-keys/3.key;
|
||
ssl_session_ticket_key session-ticket-keys/2.key;
|
||
ssl_session_ticket_key session-ticket-keys/1.key;
|
||
ssl_session_timeout 1d;
|
||
ssl_buffer_size 4k;
|
||
|
||
ssl_trusted_certificate /etc/letsencrypt/live/grapheneos.org/chain.pem;
|
||
ssl_stapling on;
|
||
ssl_stapling_verify on;
|
||
# maintained by certbot-ocsp-fetcher
|
||
ssl_stapling_file ocsp-cache/grapheneos.org.der;
|
||
|
||
log_format main '$remote_addr - $remote_user [$time_local] '
|
||
'"$request_method $scheme://$host$request_uri $server_protocol" $status $body_bytes_sent '
|
||
'"$http_referer" "$http_user_agent"';
|
||
access_log /var/log/nginx/access.log main buffer=64k flush=1m;
|
||
error_log syslog:server=unix:/dev/log,nohostname;
|
||
log_not_found off;
|
||
|
||
gzip_proxied any;
|
||
gzip_vary on;
|
||
|
||
if_modified_since before;
|
||
|
||
map $uri $preload_resources_uri {
|
||
/index.html ", </phone.png>; rel=preload; as=image, <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/faq.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/install/index.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/install/cli.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/install/web.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/releases.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
/usage.html ", <{{path|/js/redirect.js}}>; rel=modulepreload; integrity={{integrity|/js/redirect.js}}";
|
||
}
|
||
|
||
map $http_cookie $clear_legacy_push_cookie {
|
||
~__Host-preload= "__Host-preload=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0";
|
||
}
|
||
|
||
map $http_cookie $nopush {
|
||
~__Host-push=1 1;
|
||
default 0;
|
||
}
|
||
|
||
map $nopush $push_cookie {
|
||
0 "__Host-push=1; HttpOnly; Secure; SameSite=Lax; Path=/";
|
||
}
|
||
|
||
map $nopush $push_stylesheet {
|
||
0 "{{path|/main.css}}";
|
||
}
|
||
|
||
map $nopush $push_font_regular {
|
||
0 /fonts/roboto-v29-regular-latin.woff2;
|
||
}
|
||
|
||
map $nopush $push_font_bold {
|
||
0 /fonts/roboto-v29-bold-latin.woff2;
|
||
}
|
||
|
||
map $nopush $push_mask_icon {
|
||
0 "{{path|/mask-icon.svg}}";
|
||
}
|
||
|
||
map $nopush $push_phone {
|
||
0 /phone.png;
|
||
}
|
||
|
||
map $nopush $push_redirect_js {
|
||
0 "{{path|/js/redirect.js}}";
|
||
}
|
||
|
||
server {
|
||
listen 80 backlog=4096;
|
||
listen [::]:80 backlog=4096;
|
||
server_name grapheneos.org www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.dev www.grapheneos.dev grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page vanadium.app www.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
# use $host to set up HSTS for redirect domains
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name 0.grapheneos.org;
|
||
|
||
root /var/empty;
|
||
|
||
location /.well-known/acme-challenge/ {
|
||
root /srv/certbot;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://grapheneos.org$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2 backlog=4096;
|
||
listen [::]:443 ssl http2 backlog=4096;
|
||
server_name www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.dev www.grapheneos.dev grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page;
|
||
|
||
root /var/empty;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://grapheneos.org$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name vanadium.app www.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 302 https://github.com/GrapheneOS/Vanadium;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name grapheneos.org;
|
||
|
||
root /srv/grapheneos.org;
|
||
error_page 403 =404 /404;
|
||
error_page 404 /404;
|
||
|
||
open_file_cache max=2048 inactive=1d;
|
||
open_file_cache_valid 1d;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
gzip_static on;
|
||
brotli_static on;
|
||
|
||
if ($request_uri ~ ^[^?]+//) {
|
||
rewrite ^(.*)$ $1 permanent;
|
||
}
|
||
|
||
location = /, {
|
||
return 301 /;
|
||
}
|
||
|
||
location = /security.txt {
|
||
return 301 /.well-known/security.txt;
|
||
}
|
||
|
||
location = /bitcoin-address.png {
|
||
return 301 /donate-bitcoin.png;
|
||
}
|
||
|
||
location = /bitcoin-donation.png {
|
||
return 301 /donate-bitcoin.png;
|
||
}
|
||
|
||
location = /monero-donation.png {
|
||
return 301 /donate-monero.png;
|
||
}
|
||
|
||
location = /pdfviewer_privacy_policy {
|
||
return 301 /pdfviewer-privacy-policy;
|
||
}
|
||
|
||
# mangled backlinks to /install
|
||
location = /installMinimal {
|
||
return 301 /install/;
|
||
}
|
||
|
||
# mangled backlink to /faq
|
||
location = /fa {
|
||
return 301 /faq;
|
||
}
|
||
|
||
location = /FAQ {
|
||
return 301 /faq;
|
||
}
|
||
|
||
# mangled backlinks to /usage#updates
|
||
location = /updates {
|
||
return 301 /usage#updates;
|
||
}
|
||
|
||
# broken link (now fixed) on https://noagendaphone.com/ with UTF-8 object replacement character
|
||
location = "/" {
|
||
return 301 /;
|
||
}
|
||
|
||
location = /web-install {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /install-web {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /cli/install {
|
||
return 301 /install/cli;
|
||
}
|
||
|
||
location = /web/install {
|
||
return 301 /install/web;
|
||
}
|
||
|
||
location = /LICENSE {
|
||
return 301 /LICENSE.txt;
|
||
}
|
||
|
||
location = /generate_204 {
|
||
return 301 /faq#default-connections;
|
||
}
|
||
|
||
# redirect away from the old SVG favicon location
|
||
location = /mask-icon.svg {
|
||
return 301 /favicon.svg;
|
||
}
|
||
|
||
location = /404 {
|
||
internal;
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
include snippets/preload.conf;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /favicon.ico {
|
||
if ($http_accept ~ "image/svg\+xml") {
|
||
rewrite ^ /favicon.svg last;
|
||
}
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = /favicon.svg {
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = /bimi.svg {
|
||
include snippets/security-headers.conf;
|
||
# allow https://bimigroup.org/bimi-generator/ to hotlink the image
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location = {{path|/mask-icon.svg}} {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
}
|
||
|
||
location = /.well-known/matrix/client {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Access-Control-Allow-Origin "*";
|
||
default_type application/json;
|
||
}
|
||
|
||
location = /.well-known/matrix/server {
|
||
default_type application/json;
|
||
}
|
||
|
||
location = / {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_phone;
|
||
http2_push $push_redirect_js;
|
||
try_files /index.html =404;
|
||
}
|
||
|
||
location = /faq {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /install/ {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files /install/index.html =404;
|
||
}
|
||
|
||
location = /install/cli {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /install/web {
|
||
include snippets/security-headers-base.conf;
|
||
add_header Content-Security-Policy "default-src 'none'; child-src 'self'; connect-src 'self' https://releases.grapheneos.org/; font-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self'; style-src 'self'; form-action 'none'; frame-ancestors 'none'; block-all-mixed-content; base-uri 'none'" always;
|
||
add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), clipboard-read=(), clipboard-write=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), sync-xhr=(), xr-spatial-tracking=()" always;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /releases {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location = /usage {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
http2_push $push_redirect_js;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location ^~ /fonts/ {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
gzip_static off;
|
||
brotli_static off;
|
||
}
|
||
|
||
location ~ "/$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
try_files ${uri}index.html @noslash;
|
||
}
|
||
|
||
# redirect /path/ to /path if /path.html exists
|
||
location @noslash {
|
||
rewrite ^(.*)/$ $1;
|
||
if (-f $request_filename.html) {
|
||
rewrite ^(.*) $1 permanent;
|
||
}
|
||
return 404;
|
||
}
|
||
|
||
location ~ "\.(?:css|js|map|mjs)$" {
|
||
include snippets/security-headers-base.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
}
|
||
|
||
location ~ "\.webmanifest$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=604800";
|
||
}
|
||
|
||
location ~ "\.png$" {
|
||
include snippets/security-headers.conf;
|
||
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=31536000";
|
||
gzip_static off;
|
||
brotli_static off;
|
||
}
|
||
|
||
location ~ "\.(?:atom|pdf)$" {
|
||
include snippets/security-headers.conf;
|
||
# Chromium PDF range requests use wrong origin: https://bugs.chromium.org/p/chromium/issues/detail?id=1074261
|
||
# Thunderbird uses wrong origin for feeds: https://bugzilla.mozilla.org/show_bug.cgi?id=1698755
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Cache-Control "public, max-age=1800";
|
||
}
|
||
|
||
location ~ "\.(?:json|txt|xml)$" {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, max-age=1800";
|
||
}
|
||
|
||
# https://www.twipu.com/GrapheneOS doesn't handle links with fragments properly
|
||
location ~ "^/([^\s]*)<a href=$" {
|
||
return 301 /$1;
|
||
}
|
||
|
||
location ~ "/index|\.(?:br|gz|html)$" {
|
||
internal;
|
||
}
|
||
|
||
location / {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
add_header Cache-Control "public, no-cache";
|
||
include snippets/preload.conf;
|
||
try_files $uri.html $uri/ =404;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name mta-sts.grapheneos.org mta-sts.mail.grapheneos.org mta-sts.grapheneos.app mta-sts.grapheneos.ca mta-sts.grapheneos.com mta-sts.grapheneos.dev mta-sts.grapheneos.info mta-sts.grapheneos.net mta-sts.grapheneos.ovh mta-sts.grapheneos.page mta-sts.vanadium.app;
|
||
|
||
root /var/empty;
|
||
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name mta-sts.grapheneos.org mta-sts.mail.grapheneos.org mta-sts.grapheneos.app mta-sts.grapheneos.ca mta-sts.grapheneos.com mta-sts.grapheneos.dev mta-sts.grapheneos.info mta-sts.grapheneos.net mta-sts.grapheneos.ovh mta-sts.grapheneos.page mta-sts.vanadium.app;
|
||
|
||
root /srv/mta-sts;
|
||
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||
|
||
location = / {
|
||
return 301 https://grapheneos.org/articles/grapheneos-servers;
|
||
}
|
||
|
||
location = /.well-known/mta-sts.txt {}
|
||
|
||
location /.well-known/acme-challenge/ {
|
||
return 301 https://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen [::1]:81;
|
||
|
||
root /var/empty;
|
||
|
||
location = /nginx_status {
|
||
stub_status;
|
||
access_log off;
|
||
}
|
||
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
}
|