
This switches to a fully custom log format instead of using a variant of the standard combined format since we don't use any tools requiring the logs to be a standard format. This provides a cleaner format, allows us to freely add new fields and gets rid of legacy/redundant fields. The redundant timestamp already provided as the syslog timestamp is dropped along with the legacy identd field always set to a dash. This adds the connection serial number for identifying requests coming from the same connection. TLS version is added as a replacement for our previous addition of the URI scheme. This also adds the total request length and total bytes sent to the client instead of only the body bytes sent.
442 lines
15 KiB
Nginx Configuration File
442 lines
15 KiB
Nginx Configuration File
# nginx 1.22.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/javascript text/plain text/xml application/atom+xml;
|
||
|
||
sendfile on;
|
||
sendfile_max_chunk 256k;
|
||
tcp_nopush on;
|
||
keepalive_requests 256;
|
||
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 2 1k;
|
||
http2_chunk_size 4k;
|
||
|
||
client_body_timeout 15s;
|
||
client_header_timeout 15s;
|
||
send_timeout 30s;
|
||
|
||
resolver [::1];
|
||
resolver_timeout 15s;
|
||
|
||
http2_max_concurrent_streams 16;
|
||
limit_conn_status 429;
|
||
limit_conn_zone $binary_remote_addr zone=addr-limit:10m;
|
||
limit_conn addr-limit 128;
|
||
|
||
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 '$connection $remote_addr $remote_user $ssl_protocol $server_protocol '
|
||
'$host "$request_uri" $status $request_length $body_bytes_sent/$bytes_sent '
|
||
'"$http_referer" "$http_user_agent"';
|
||
access_log syslog:server=unix:/dev/log,nohostname main;
|
||
error_log syslog:server=unix:/dev/log,nohostname;
|
||
log_not_found off;
|
||
|
||
gzip_proxied any;
|
||
gzip_vary on;
|
||
|
||
if_modified_since before;
|
||
|
||
aio threads;
|
||
aio_write on;
|
||
|
||
map $uri $preload_resources_uri {
|
||
/index.html ", <{{path|/pixel-7-pro.svg}}>; 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-push= "__Host-push=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0";
|
||
}
|
||
|
||
server {
|
||
listen 80 backlog=4096;
|
||
listen [::]:80 backlog=4096;
|
||
server_name grapheneos.org mta-sts.grapheneos.org www.grapheneos.org grapheneos.app mta-sts.grapheneos.app www.grapheneos.app grapheneos.ca mta-sts.grapheneos.ca www.grapheneos.ca grapheneos.com mta-sts.grapheneos.com www.grapheneos.com grapheneos.dev mta-sts.grapheneos.dev www.grapheneos.dev grapheneos.info mta-sts.grapheneos.info www.grapheneos.info grapheneos.net mta-sts.grapheneos.net www.grapheneos.net grapheneos.ovh mta-sts.grapheneos.ovh www.grapheneos.ovh grapheneos.page mta-sts.grapheneos.page www.grapheneos.page vanadium.app mta-sts.vanadium.app www.vanadium.app mta-sts.mail.grapheneos.org;
|
||
|
||
root /var/empty;
|
||
|
||
location /.well-known/acme-challenge/ {
|
||
return 301 http://0.grapheneos.org$request_uri;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
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 / {
|
||
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 / {
|
||
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 = "/legal/Micay_ Copperhead_ Statement of Defendant and Counterclaim.pdf" {
|
||
return 301 /history/copperheados;
|
||
}
|
||
|
||
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 = /allowed_signers {}
|
||
location = /allowed_signers.sig {}
|
||
location = /allowed_signers.asc {}
|
||
|
||
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 = /.well-known/matrix/client {
|
||
include snippets/security-headers.conf;
|
||
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||
add_header Access-Control-Allow-Origin "*";
|
||
add_header Cache-Control "public, max-age=172800";
|
||
default_type application/json;
|
||
}
|
||
|
||
location = /.well-known/matrix/server {
|
||
add_header Cache-Control "public, max-age=172800";
|
||
default_type application/json;
|
||
}
|
||
|
||
location = /.well-known/traffic-advice {
|
||
default_type application/trafficadvice+json;
|
||
}
|
||
|
||
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=(), bluetooth=(), camera=(), clipboard-read=(), clipboard-write=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(), 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;
|
||
try_files $uri.html =404;
|
||
}
|
||
|
||
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 ~ "\.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 ~ "\.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$" {
|
||
include snippets/security-headers.conf;
|
||
# 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 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 / {
|
||
return 404;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen unix:/run/nginx/status.sock;
|
||
|
||
root /var/empty;
|
||
|
||
location = / {
|
||
stub_status;
|
||
access_log off;
|
||
}
|
||
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
}
|