See https://docs.docker.com/build/building/multi-stage/#use-an-external-image-as-a-stage The shared volume was making updates complicated, as the persisted volume data would not be overriden by the new container
208 lines
7.1 KiB
Plaintext
Executable File
208 lines
7.1 KiB
Plaintext
Executable File
# The INVENTAIRE_PORT and PUBLIC_HOSTNAME variables are set with nginx image function,
|
|
# which will extract environment variables before nginx starts
|
|
# See https://hub.docker.com/_/nginx
|
|
|
|
upstream inv {
|
|
server inventaire:${INVENTAIRE_PORT} fail_timeout=5s;
|
|
}
|
|
|
|
# Using error_page as a way to have a named location that can
|
|
# then be shared between several locations, see:
|
|
# https://serverfault.com/questions/908086/nginx-directly-send-from-location-to-another-named-location
|
|
# https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/#what-to-do-instead
|
|
# Contrary to what the documentation says, the HTTP verbs aren't all converted to GET
|
|
# http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page
|
|
error_page 543 = @invserver;
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
|
|
# Required to be able to run `certbot -w /var/www/html/`
|
|
location /.well-known/ {
|
|
root /var/www/html/;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
http2 on;
|
|
|
|
server_name ${PUBLIC_HOSTNAME};
|
|
|
|
ssl_certificate /etc/letsencrypt/live/${PUBLIC_HOSTNAME}/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/${PUBLIC_HOSTNAME}/privkey.pem;
|
|
|
|
include /etc/nginx/snippets/ssl.conf;
|
|
|
|
client_max_body_size 25M;
|
|
|
|
# Disabling compression to mitigate BREACH exploit
|
|
# https://en.wikipedia.org/wiki/BREACH_(security_exploit)#Mitigation
|
|
# http://security.stackexchange.com/questions/39925/breach-a-new-attack-against-http-what-can-be-done
|
|
# until we can confidently say that HTTP/2 solves the issue? https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2
|
|
gzip off;
|
|
|
|
# On-The-Fly Image Resizer
|
|
|
|
# URLs look like /img/users/300x1200/8185d4e039f52b4faa06a1c277133e9a8232551b
|
|
# for locally hosted images
|
|
# or /img/remote/300x1200/630022006?href=http%3A%2F%2Fescaped.url
|
|
# for remote images, with 630022006 being the hash of the passed href
|
|
# generated by [hashCode](https://git.inventaire.io/inventaire/blob/35b1e63/server/lib/utils/base.js#L69-L80)
|
|
|
|
# The hack: I couldn't make the proxy_store work: it never hits the cache, but
|
|
# it does put the resized images in /tmp/nginx/resize, so using a try_files
|
|
# directive instead
|
|
|
|
# Sometimes, for some unidentified reason, the cached files end up empty, so it can be useful to add a root cron to remove those files:
|
|
# 0 4 * * * /usr/bin/find /tmp/nginx -type f -size 0 -delete
|
|
|
|
# Do not remove the (.*) capture group as it seems to be required by the try_files
|
|
location ~ ^/img/(groups|users|entities|assets)/(.*) {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
root /tmp/nginx/resize;
|
|
default_type "image/jpeg";
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
add_header X-File-Cache "hit";
|
|
add_header Content-Security-Policy "sandbox";
|
|
try_files $uri @invimg;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
}
|
|
|
|
# Same as above, but without the immutable
|
|
location ~ ^/img/remote/(.*) {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
root /tmp/nginx/resize;
|
|
default_type "image/jpeg";
|
|
add_header X-File-Cache "hit";
|
|
add_header Content-Security-Policy "sandbox";
|
|
try_files $uri @invimg;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
}
|
|
|
|
location ~ ^/img/ {
|
|
return 404;
|
|
}
|
|
|
|
location @invimg {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
default_type "image/jpeg";
|
|
# add_header Content-Type "image/jpeg";
|
|
add_header X-File-Cache "miss";
|
|
add_header Content-Security-Policy "sandbox";
|
|
proxy_temp_path /tmp/nginx/tmp;
|
|
proxy_store /tmp/nginx/resize$uri;
|
|
proxy_store_access user:rw group:rw all:r;
|
|
proxy_http_version 1.1;
|
|
proxy_pass http://inv;
|
|
}
|
|
|
|
root /opt/inventaire/client;
|
|
location /public {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
gzip_static on;
|
|
# Let resources that can't be cache busted
|
|
# - such as opensearch.xml or robots.txt -
|
|
# out of this caching policy
|
|
if ($uri ~ "^/public/(dist|fonts)/" ) {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
# All headers that aren't in the last block won't be taken in account
|
|
# thus the need to have CORS headers here too
|
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
add_header 'Access-Control-Allow-Methods' 'GET' always;
|
|
}
|
|
}
|
|
|
|
# Pass the request to the node.js server
|
|
# with some correct headers for proxy-awareness
|
|
location /api {
|
|
return 543;
|
|
}
|
|
|
|
location /.well-known/webfinger {
|
|
return 543;
|
|
}
|
|
|
|
# Let the API server handle all but /public JSON and RSS requests
|
|
location ~ "^/[^p].*\.(json|rss)$" {
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
return 543;
|
|
}
|
|
|
|
location @invserver {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
# Let the server decide when CORS headers should be added
|
|
proxy_set_header Host $http_host;
|
|
proxy_set_header X-Forwarded-Proto https;
|
|
proxy_set_header Host $host;
|
|
|
|
# Set a large value to let the API determine the appropriate
|
|
# timeout per endpoint
|
|
# http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
|
|
proxy_read_timeout 3600;
|
|
proxy_redirect off;
|
|
proxy_http_version 1.1;
|
|
proxy_pass http://inv;
|
|
}
|
|
|
|
location = /favicon.ico {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
try_files /public/$uri /public/images/$uri;
|
|
expires 30d;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
location = /robots.txt {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
gzip_static on;
|
|
try_files /public/$uri /$uri;
|
|
expires 1d;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
# Prevent exposing git folders such as /public/i18n/.git
|
|
# For why this rule takes precedence over location /public/
|
|
# see http://stackoverflow.com/a/34262192/3324977
|
|
location ~ /\.git {
|
|
deny all;
|
|
}
|
|
|
|
location ^~ '/.well-known/acme-challenge' {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
default_type "text/plain";
|
|
root /var/www/html;
|
|
}
|
|
|
|
location / {
|
|
include /etc/nginx/snippets/security_headers.conf;
|
|
gzip_static on;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
# index.html should always be fresh out of the server
|
|
# time is negative => “Cache-Control: no-cache”
|
|
# http://nginx.org/en/docs/http/ngx_http_headers_module.html
|
|
# Those headers should be set here and not at "location /" as they would be ignored (cf http://serverfault.com/a/786248)
|
|
expires -1;
|
|
# The remaining routes (/users, /entity, etc) should be handled by the client router
|
|
rewrite .* /public/index.html break;
|
|
}
|
|
}
|