From 34e97939f2b5466276724f4dc178b2083cca17e9 Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Sat, 6 Nov 2021 18:45:03 +0200 Subject: [PATCH] Nginx base image. --- .gitlab-ci.yml | 16 +++++++++++++ nginx/.dockerignore | 4 ++++ nginx/Dockerfile | 26 +++++++++++++++++++++ nginx/README.md | 3 +++ nginx/conf.d/default.conf | 13 +++++++++++ nginx/conf.d/global.conf | 13 +++++++++++ nginx/conf.d/status.conf | 7 ++++++ nginx/snippets/ads-txt.conf | 9 ++++++++ nginx/snippets/allow-ns1.conf | 1 + nginx/snippets/allow-ns4.conf | 1 + nginx/snippets/allow-private-ips.conf | 5 +++++ nginx/snippets/allow-shore-ips.conf | 3 +++ nginx/snippets/common-headers.conf | 6 +++++ nginx/snippets/ldap-auth.conf | 10 +++++++++ nginx/snippets/proxy-headers.conf | 8 +++++++ nginx/snippets/proxy-ssl.conf | 5 +++++ nginx/snippets/redirect-https.conf | 1 + nginx/snippets/redirect-www.conf | 1 + nginx/snippets/robots-allow-all.conf | 4 ++++ nginx/snippets/robots-disallow-all.conf | 4 ++++ nginx/snippets/security-txt.conf | 9 ++++++++ nginx/snippets/ssl.conf | 14 ++++++++++++ nginx/snippets/upgrade-secure.conf | 1 + nginx/snippets/vouch.conf | 30 +++++++++++++++++++++++++ nginx/snippets/websockets.conf | 3 +++ nginx/snippets/www-acme-challenge.conf | 1 + 26 files changed, 198 insertions(+) create mode 100644 nginx/.dockerignore create mode 100644 nginx/Dockerfile create mode 100644 nginx/README.md create mode 100644 nginx/conf.d/default.conf create mode 100644 nginx/conf.d/global.conf create mode 100644 nginx/conf.d/status.conf create mode 100644 nginx/snippets/ads-txt.conf create mode 100644 nginx/snippets/allow-ns1.conf create mode 100644 nginx/snippets/allow-ns4.conf create mode 100644 nginx/snippets/allow-private-ips.conf create mode 100644 nginx/snippets/allow-shore-ips.conf create mode 100644 nginx/snippets/common-headers.conf create mode 100644 nginx/snippets/ldap-auth.conf create mode 100644 nginx/snippets/proxy-headers.conf create mode 100644 nginx/snippets/proxy-ssl.conf create mode 100644 nginx/snippets/redirect-https.conf create mode 100644 nginx/snippets/redirect-www.conf create mode 100644 nginx/snippets/robots-allow-all.conf create mode 100644 nginx/snippets/robots-disallow-all.conf create mode 100644 nginx/snippets/security-txt.conf create mode 100644 nginx/snippets/ssl.conf create mode 100644 nginx/snippets/upgrade-secure.conf create mode 100644 nginx/snippets/vouch.conf create mode 100644 nginx/snippets/websockets.conf create mode 100644 nginx/snippets/www-acme-challenge.conf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a72788f..62ca6ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -151,3 +151,19 @@ push-webdav: needs: - job: build-webdav artifacts: true + +# nginx image: + +build-nginx: + extends: .container-build-base + variables: + CONTEXT: nginx + +push-nginx: + extends: .container-push-base + variables: + CONTEXT: nginx + IMAGE: nginx + needs: + - job: build-nginx + artifacts: true diff --git a/nginx/.dockerignore b/nginx/.dockerignore new file mode 100644 index 0000000..380e2e6 --- /dev/null +++ b/nginx/.dockerignore @@ -0,0 +1,4 @@ +* +!conf.d/ +!www/ +!snippets/ diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..41618e0 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,26 @@ +FROM nginx:1.21.3-alpine +# hadolint ignore=DL3018 +RUN rm -rf /etc/nginx/conf./* && \ + chmod 777 /run && \ + apk add --no-cache --update libcap openssl && \ + curl https://letsencrypt.org/certs/isrg-root-ocsp-x1.pem.txt > /etc/ssl/ocsp.pem && \ + mkdir /var/ssl &&\ + curl https://ssl-config.mozilla.org/ffdhe2048.txt > /var/ssl/dhparams &&\ + chmod 644 /var/ssl/dhparams && \ + install -d -m 755 -o root -g root /etc/nginx/snippets && \ + install -d -m 755 -o root -g root /var/ssl && \ + install -d -m 755 -o root -g root /var/www && \ + install -d -m 700 -o nginx -g nginx /var/cache/nginx && \ + openssl req -x509 \ + -newkey rsa:4096 \ + -keyout /var/ssl/site.key \ + -nodes \ + -out /var/ssl/site.crt \ + -batch && \ + setcap CAP_NET_BIND_SERVICE=+ep "$(command -v nginx)" && \ + chown nginx /var/ssl/site.* +COPY conf.d/ /etc/nginx/conf.d/ +COPY snippets/ /etc/nginx/snippets/ +USER nginx +RUN nginx -t +HEALTHCHECK CMD curl --fail --verbose --user-agent 'Docker health check' --header "Host: status" http://localhost/ || exit 1 diff --git a/nginx/README.md b/nginx/README.md new file mode 100644 index 0000000..630b37a --- /dev/null +++ b/nginx/README.md @@ -0,0 +1,3 @@ +# Nginx + +My tweaked version of the Nginx image. diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf new file mode 100644 index 0000000..f428ba9 --- /dev/null +++ b/nginx/conf.d/default.conf @@ -0,0 +1,13 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + include snippets/www-acme-challenge.conf; + location / { return 301 https://www.shore.co.il$request_uri; } +} + +server { + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + include snippets/ssl.conf; + location / { return 301 https://www.shore.co.il$request_uri; } +} diff --git a/nginx/conf.d/global.conf b/nginx/conf.d/global.conf new file mode 100644 index 0000000..608fe8d --- /dev/null +++ b/nginx/conf.d/global.conf @@ -0,0 +1,13 @@ +# The resolver for the Docker network. +resolver 127.0.0.11 valid=30s; +gzip on; +tcp_nopush on; +tcp_nodelay on; +server_tokens off; +include snippets/common-headers.conf; +# Validate proxied SSL connections. +proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; +proxy_ssl_verify on; +proxy_ssl_verify_depth 4; +# For proxying /validate on different hosts to Vouch. +map $host $vouch { default vouch; } diff --git a/nginx/conf.d/status.conf b/nginx/conf.d/status.conf new file mode 100644 index 0000000..6ecb7d8 --- /dev/null +++ b/nginx/conf.d/status.conf @@ -0,0 +1,7 @@ +server { + listen 80; + listen [::]:80; + server_name status; + location = / { stub_status; } + include snippets/allow-private-ips.conf; +} diff --git a/nginx/snippets/ads-txt.conf b/nginx/snippets/ads-txt.conf new file mode 100644 index 0000000..b074c08 --- /dev/null +++ b/nginx/snippets/ads-txt.conf @@ -0,0 +1,9 @@ +location = /ads.txt { + if ($scheme = http) { + return 301 https://$host$request_uri; + } + if ($scheme = https) { + add_header Content-Type "text/plain; charset=utf-8"; + return 200 "contact=webmaster@shore.co.il\n"; + } +} diff --git a/nginx/snippets/allow-ns1.conf b/nginx/snippets/allow-ns1.conf new file mode 100644 index 0000000..bdadb24 --- /dev/null +++ b/nginx/snippets/allow-ns1.conf @@ -0,0 +1 @@ +allow 62.219.131.121; # ns1.shore.co.il diff --git a/nginx/snippets/allow-ns4.conf b/nginx/snippets/allow-ns4.conf new file mode 100644 index 0000000..5e39f40 --- /dev/null +++ b/nginx/snippets/allow-ns4.conf @@ -0,0 +1 @@ +allow 163.172.74.36; # ns4.shore.co.il diff --git a/nginx/snippets/allow-private-ips.conf b/nginx/snippets/allow-private-ips.conf new file mode 100644 index 0000000..154262a --- /dev/null +++ b/nginx/snippets/allow-private-ips.conf @@ -0,0 +1,5 @@ +allow 127.0.0.0/8; +allow 10.0.0.0/8; +allow 192.168.0.0/16; +allow 172.16.0.0/12; +deny all; diff --git a/nginx/snippets/allow-shore-ips.conf b/nginx/snippets/allow-shore-ips.conf new file mode 100644 index 0000000..709b549 --- /dev/null +++ b/nginx/snippets/allow-shore-ips.conf @@ -0,0 +1,3 @@ +include snippets/allow-ns1.conf; +include snippets/allow-ns4.conf; +include snippets/allow-private-ips.conf; diff --git a/nginx/snippets/common-headers.conf b/nginx/snippets/common-headers.conf new file mode 100644 index 0000000..e97cb68 --- /dev/null +++ b/nginx/snippets/common-headers.conf @@ -0,0 +1,6 @@ +# add_headers are inherited from previous level if and only if there are no +# add_header directives defined on the current level. So any time there's an +# add_header directive there should be an `include snippets/common-headers.conf` +# directive as well. +add_header X-Frame-Options SAMEORIGIN always; +add_header Permissions-Policy interest-cohort=(); diff --git a/nginx/snippets/ldap-auth.conf b/nginx/snippets/ldap-auth.conf new file mode 100644 index 0000000..822c440 --- /dev/null +++ b/nginx/snippets/ldap-auth.conf @@ -0,0 +1,10 @@ +auth_request /validate; + +location = /validate { + proxy_pass https://auth.shore.co.il/validate; + proxy_http_version 1.1; + include snippets/proxy-ssl.conf; + internal; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; +} diff --git a/nginx/snippets/proxy-headers.conf b/nginx/snippets/proxy-headers.conf new file mode 100644 index 0000000..e142036 --- /dev/null +++ b/nginx/snippets/proxy-headers.conf @@ -0,0 +1,8 @@ +proxy_set_header X-Forwarded-Host $host; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_hide_header Strict-Transport-Security; +proxy_hide_header Public-Key-Pins; +proxy_hide_header Public-Key-Pins-Report-Only; diff --git a/nginx/snippets/proxy-ssl.conf b/nginx/snippets/proxy-ssl.conf new file mode 100644 index 0000000..b83886a --- /dev/null +++ b/nginx/snippets/proxy-ssl.conf @@ -0,0 +1,5 @@ +proxy_ssl_verify on; +proxy_ssl_verify_depth 3; +proxy_ssl_name auth.shore.co.il; +proxy_ssl_server_name on; +proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; diff --git a/nginx/snippets/redirect-https.conf b/nginx/snippets/redirect-https.conf new file mode 100644 index 0000000..991d593 --- /dev/null +++ b/nginx/snippets/redirect-https.conf @@ -0,0 +1 @@ +location / { return 301 https://$host$request_uri; } diff --git a/nginx/snippets/redirect-www.conf b/nginx/snippets/redirect-www.conf new file mode 100644 index 0000000..2d89d75 --- /dev/null +++ b/nginx/snippets/redirect-www.conf @@ -0,0 +1 @@ +location / { return 301 https://www.$host$request_uri; } diff --git a/nginx/snippets/robots-allow-all.conf b/nginx/snippets/robots-allow-all.conf new file mode 100644 index 0000000..627aee5 --- /dev/null +++ b/nginx/snippets/robots-allow-all.conf @@ -0,0 +1,4 @@ +location = /robots.txt { + add_header Content-Type "text/plain; charset=utf-8"; + return 200 "User-agent: *\nDisallow:\n"; +} diff --git a/nginx/snippets/robots-disallow-all.conf b/nginx/snippets/robots-disallow-all.conf new file mode 100644 index 0000000..03d5031 --- /dev/null +++ b/nginx/snippets/robots-disallow-all.conf @@ -0,0 +1,4 @@ +location = /robots.txt { + add_header Content-Type "text/plain; charset=utf-8"; + return 200 "User-agent: *\nDisallow: *\n"; +} diff --git a/nginx/snippets/security-txt.conf b/nginx/snippets/security-txt.conf new file mode 100644 index 0000000..c1f0d21 --- /dev/null +++ b/nginx/snippets/security-txt.conf @@ -0,0 +1,9 @@ +location = /.well-known/security.txt { + if ($scheme = http) { + return 301 https://$host$request_uri; + } + if ($scheme = https) { + add_header Content-Type "text/plain; charset=utf-8"; + return 200 "Contact: mailto:security@shore.co.il\nEncryption: https://www.shore.co.il/blog/static/nimrod.asc"; + } +} diff --git a/nginx/snippets/ssl.conf b/nginx/snippets/ssl.conf new file mode 100644 index 0000000..cb1f77f --- /dev/null +++ b/nginx/snippets/ssl.conf @@ -0,0 +1,14 @@ +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; +add_header Expect-CT "max-age=86400, enforce, report-uri=\"https://www.shore.co.il/about\""; +include snippets/common-headers.conf; +ssl_certificate /var/ssl/site.crt; +ssl_certificate_key /var/ssl/site.key; +ssl_dhparam /var/ssl/dhparams; +ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; +ssl_ciphers !AESCCM:!kRSA:!3DES:!RC4:!DES:!MD5:!aNULL:!NULL:AESGCM+ECDH:ECDH+CHACHA20:AES256+ECDH:AES128:CHACHA20:+SHA1; +ssl_prefer_server_ciphers on; +ssl_session_cache shared:SSL:50m; +ssl_session_timeout 5m; +ssl_stapling on; +ssl_stapling_verify on; +ssl_trusted_certificate /etc/ssl/ocsp.pem; diff --git a/nginx/snippets/upgrade-secure.conf b/nginx/snippets/upgrade-secure.conf new file mode 100644 index 0000000..2abc805 --- /dev/null +++ b/nginx/snippets/upgrade-secure.conf @@ -0,0 +1 @@ +if ($http_Upgrade-Insecure-Requests = 1) { return 301 https://$host$request_uri; } diff --git a/nginx/snippets/vouch.conf b/nginx/snippets/vouch.conf new file mode 100644 index 0000000..9571b80 --- /dev/null +++ b/nginx/snippets/vouch.conf @@ -0,0 +1,30 @@ +# send all requests to the `/validate` endpoint for authorization +auth_request /validate; + +location = /validate { + # forward the /validate request to Vouch Proxy + proxy_pass http://$vouch:9090/validate; + proxy_http_version 1.1; + internal; + include snippets/proxy-headers.conf; + + # Vouch Proxy only acts on the request headers + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + + # optionally add X-Vouch-User as returned by Vouch Proxy along with the request + auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; + + # these return values are used by the @error401 call + auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; + auth_request_set $auth_resp_err $upstream_http_x_vouch_err; + auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; +} + +# if validate returns `401 not authorized` then forward the request to the error401block +error_page 401 = @error401; + +location @error401 { + # redirect to Vouch Proxy for login + return 302 https://vouch.shore.co.il/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; +} diff --git a/nginx/snippets/websockets.conf b/nginx/snippets/websockets.conf new file mode 100644 index 0000000..64b7e37 --- /dev/null +++ b/nginx/snippets/websockets.conf @@ -0,0 +1,3 @@ +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection "Upgrade"; +proxy_read_timeout 36000s; diff --git a/nginx/snippets/www-acme-challenge.conf b/nginx/snippets/www-acme-challenge.conf new file mode 100644 index 0000000..ba3c0b7 --- /dev/null +++ b/nginx/snippets/www-acme-challenge.conf @@ -0,0 +1 @@ +location /.well-known/acme-challenge/ { root /var/www/www.shore.co.il; } -- GitLab