diff --git a/nginx/server.conf b/nginx/server.conf new file mode 100644 index 00000000..fee40bbc --- /dev/null +++ b/nginx/server.conf @@ -0,0 +1,121 @@ +server { + listen 80; + listen [::]:80; + + server_name connectivitycheck.grapheneos.org www.grapheneos.org grapheneos.org; + + root /var/empty; + + return 301 https://grapheneos.org$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name connectivitycheck.grapheneos.org www.grapheneos.org grapheneos.org; + + root /var/www/html; + + ssl_certificate /etc/letsencrypt/live/grapheneos.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/grapheneos.org/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + ssl_trusted_certificate /etc/letsencrypt/live/grapheneos.org/chain.pem; + ssl_stapling on; + ssl_stapling_verify on; + + if ($host != "grapheneos.org") { + return 301 https://grapheneos.org$request_uri; + } + + if ($request_uri ~ ^/(.*)\.html$) { + return 301 /$1; + } + + if ($request_uri ~ ^(.*)/index$) { + return 301 $1/; + } + + location = /security.txt { + return 301 /.well-known/security.txt; + } + + location = /graphene.png { + return 301 /logo.png; + } + + location = /pdfviewer_privacy_policy { + return 301 /pdfviewer-privacy-policy; + } + + location = /safari_pinned_tab_icon.svg { + return 301 /mask-icon.svg; + } + + location = /safari-pinned-tab-icon.svg { + return 301 /mask-icon.svg; + } + + location = /bitcoin_address.png { + return 301 /bitcoin-address.png; + } + + location = /generate_204 { + return 204; + } + + location = /LICENSE { + default_type text/plain; + } + + location ~ "\.(html|txt|xml)$" { + include /etc/nginx/snippets/security-headers.conf; + add_header Cache-Control "public, max-age=1800"; + } + + location ~ "\.(ico|webmanifest)$" { + include /etc/nginx/snippets/security-headers.conf; + add_header Cache-Control "public, max-age=604800"; + } + + location ~ "\.(css|js|png|svg|woff2)$" { + include /etc/nginx/snippets/security-headers.conf; + add_header Cache-Control "public, max-age=31536000"; + } + + try_files $uri $uri.html $uri/ =404; + include /etc/nginx/snippets/security-headers.conf; +} + +server { + listen 80; + listen [::]:80; + + server_name mta-sts.grapheneos.org; + + root /var/empty; + + return 301 https://mta-sts.grapheneos.org$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name mta-sts.grapheneos.org; + + root /var/www/mta-sts; + + ssl_certificate /etc/letsencrypt/live/grapheneos.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/grapheneos.org/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + ssl_trusted_certificate /etc/letsencrypt/live/grapheneos.org/chain.pem; + ssl_stapling on; + ssl_stapling_verify on; + + include /etc/nginx/snippets/security-headers.conf; +} diff --git a/nginx/snippets/security-headers.conf b/nginx/snippets/security-headers.conf new file mode 100644 index 00000000..06243ef5 --- /dev/null +++ b/nginx/snippets/security-headers.conf @@ -0,0 +1,9 @@ +add_header X-XSS-Protection "1; mode=block" always; +add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; +add_header X-Content-Type-Options "nosniff" always; +add_header X-Frame-Options "DENY" always; +add_header Referrer-Policy "no-referrer" always; +add_header Expect-CT "enforce, max-age=63072000, report-uri=\"https://danielmicay.report-uri.com/r/d/ct/enforce\"" always; +add_header Public-Key-Pins "max-age=2592000; pin-sha256=\"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\"; pin-sha256=\"sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=\"; pin-sha256=\"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=\"; pin-sha256=\"C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=\"; pin-sha256=\"mABt36m6Z2G6pnosx0K3dhXY+nUrZeZVWZlSW4GBZGs=\"; pin-sha256=\"v/9edLATMEdwyTJp0eXTft119lGXjZThpGSeX7t5Jv8=\"; pin-sha256=\"+f4CIotdnI56+v8CTfQvN88mYlQ1saQ1U68YvAwlotU=\"; pin-sha256=\"q3TMLLQkHOLGnFXKL1RytMd1Vr906gdK/ymgcBnk144=\"; pin-sha256=\"x3i8R25x59RNyo1LAunh4lGFvnIYXT5oTq2dOVgxTWU=\"; includeSubDomains; report-uri=\"https://danielmicay.report-uri.com/r/d/hpkp/enforce\"" always; +add_header Content-Security-Policy "default-src 'none'; 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'; report-uri https://danielmicay.report-uri.com/r/d/csp/enforce" always; +add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; wake-lock 'none'; xr-spatial-tracking 'none'" always;