diff --git a/.drone.yml b/.drone.yml index 0fc8a25..93687ff 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,16 +1,176 @@ --- kind: pipeline type: docker -name: Verify +name: Build & deploy platform: os: linux arch: amd64 +clone: + disable: true + +environment: + NGINX_VERSION: 1.24.0 + +volumes: + - name: letsencrypt + temp: {} + - name: ssl + temp: {} + steps: - - name: Verify nginx confiy - image: nginx:1.25-bookworm + - name: Clone w/ submodules + image: alpine/git commands: - - nginx -t + - git clone $DRONE_REPO_LINK . + - git checkout $DRONE_COMMIT + - git submodule update --init --recursive + - name: Setup environment + image: alpine:3.18.4 + volumes: + - name: letsencrypt + path: /etc/letsencrypt + - name: ssl + path: /etc/ssl + commands: + - apk update + - apk add openssl + - cd $DRONE_WORKSPACE + - mkdir .ssl + - openssl req + -x509 + -nodes + -days 1 + -newkey rsa:4096 + -keyout .ssl/ssl-cert-snakeoil.key + -out .ssl/ssl-cert-snakeoil.pem + -batch + - grep -ro 'ssl_certificate[^;]*;' sites-available snippets | awk -F' ' '{print $2}' RS=';' | + while read -r file; do if [ ! -z $file ]; then mkdir -p $(dirname $file); fi; done + + - grep -ro 'ssl_certificate [^;]*;' sites-available snippets | + awk -F ' ' '{print $2}' RS=';' | + while read -r file; do if [ ! -z $file ]; then ln -sf $PWD/.ssl/ssl-cert-snakeoil.pem $file; fi; done + + - grep -ro 'ssl_certificate_key [^;]*;' sites-available snippets | + awk -F ' ' '{print $2}' RS=';' | + while read -r file; do if [ ! -z $file ]; then ln -sf $PWD/.ssl/ssl-cert-snakeoil.key $file; fi; done + +# - name: Verify config +# image: alpine:3.18.4 +# volumes: +# - name: letsencrypt +# path: /etc/letsencrypt +# - name: ssl +# path: /etc/ssl +# commands: +# - apk update +# - apk add nginx~$${NGINX_VERSION} +# - cd /etc/nginx +# - cp -r $DRONE_WORKSPACE/* . +# - cat nginx.conf | sed 's/load_module/#load_module/g' > nginx-module-less.conf +# - nginx -t -p $PWD -c nginx-module-less.conf +# - rm nginx-module-less.conf + + - name: Compile nginx & modules + image: ubuntu:22.04 + commands: + - mkdir -p $DRONE_WORKSPACE/nginx-build + - apt -q update + - apt -y -qq install -o Dpkg::Progress-Fancy="0" -o APT::Color="0" -o Dpkg::Use-Pty="0" + nginx + wget + build-essential + libpcre3 + libpcre3-dev + zlib1g + zlib1g-dev + libssl-dev + tree + - cd /tmp + - wget "http://nginx.org/download/nginx-$${NGINX_VERSION}.tar.gz" + - tar -xvzf nginx-$${NGINX_VERSION}.tar.gz + - cd nginx-$${NGINX_VERSION} + - ./configure + --prefix=$DRONE_WORKSPACE/nginx-build + --add-dynamic-module=$DRONE_WORKSPACE/modules-available/headers-more-nginx-module + --with-http_ssl_module + --with-http_v2_module + --with-http_stub_status_module + --with-http_gzip_static_module + --with-http_realip_module + --with-compat + - make modules + - make install + - cd $DRONE_WORKSPACE + - mv nginx-build/modules/* modules + - mv nginx-build/sbin . + - tree -I modules-available + + - name: Verify config w/ modules + image: ubuntu:22.04 + volumes: + - name: letsencrypt + path: /etc/letsencrypt + - name: ssl + path: /etc/ssl + commands: + - mkdir -p /var/log/nginx + - touch /var/log/nginx/error.log + - useradd nginx + - sbin/nginx -t -p $PWD -c nginx.conf -e /var/log/nginx/error.log + + - name: Setup credentials + image: alpine:3.18.4 + commands: + - mkdir .ssh + - echo $NGINX_DEPLOY_KEY | base64 -di > .ssh/id_ed25519 + - echo "" >> .ssh/id_ed25519 + - chmod 600 .ssh/id_ed25519 + environment: + NGINX_DEPLOY_KEY: + from_secret: NGINX_DEPLOY_KEY + when: + event: + include: + - push + exclude: + - pull_request + branch: + - main + + - name: Deploy + image: alpine:3.18.4 + commands: + - apk update + - apk add rsync openssh + - rsync + -avr + -e "ssh -i .ssh/id_ed25519 -o StrictHostKeyChecking=no" + --exclude=modules-available + --exclude=nginx-build + --exclude=".*" + --exclude="*_temp" + * $NGINX_USER@$NGINX_HOST:/etc/nginx/ + environment: + NGINX_USER: + from_secret: NGINX_USER + NGINX_HOST: + from_secret: NGINX_HOST + when: + event: + include: + - push + exclude: + - pull_request + branch: + - main + +--- +kind: signature +hmac: 7e392f769559ba043b923bbc35197ad955864d15a179979949528362731cbf29 + +... diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..657b89b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules-available/headers-more-nginx-module"] + path = modules-available/headers-more-nginx-module + url = https://github.com/openresty/headers-more-nginx-module diff --git a/README.md b/README.md index 255a7b7..3303fbb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ -# nginx -Nginx configuration files +# Nginx config + +Includes all nginx sites, snippets & required modules for homelab. + +# Modules + +Current configuration files require the following modules: + - [headers-more-nginx-module](#headers-more-nginx-module) + +We need to compile static modules like this from source (`modules-available/`) into `*.so` dynamic module files that can be loaded from `modules/`.[^1] + + +[^1]: [https://www.nginx.com/resources/wiki/extending/converting](https://www.nginx.com/resources/wiki/extending/converting) + +## headers-more-nginx-module + - [github.com/openresty/headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module) + +We want to add headers from snippets, and in `server` & `location` blocks, through snippets and define in `location` blocks. Nginx `add_header` directive by design only adds headers if not previously added, this means `server` & snippet headers will be removed from response if `location` block uses `add_header`. + +> There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.[^2] + +Instead we use module `headers-more` to replace directive `add_header 'key' 'value'` with `more_set_headers 'key value`. This will prevent removing previously defined headers and enable setting headers from snippets, and in `server` & `location` blocks. + +[^2]: [https://nginx.org/en/docs/http/ngx_http_headers_module.html](https://nginx.org/en/docs/http/ngx_http_headers_module.html) + +# CI/CD + +[Drone CI config file](https://github.com/kevinmidboe/nginx/blob/main/.drone.yml) defines compiling modules from source and transfering to nginx host during deploy step. + +**Setup environment:** +Current configs have references to SSL cert & key location. To validate nginx config with these references dummy cert & key files are created and symlinked to SSL cert & key locations defined in configs. + +**Compiling nginx binary & modules:** +It is setup to pull git submodules and build `modules-available/` from source. This requires us to pull latest nginx source-code, configure it, build and folder reference in `--add-dynamic-module` flag when compiling nginx. + +Since we compile nginx binary from source we need to enable flags for all common nginx modules we use in configs. View flags in [.circleci compile step](https://github.com/kevinmidboe/nginx/blob/main/.drone.yml). Flags currently set (prefix with `--with-{NGINX_MODULE_NAME}`): + + - [http_ssl_module](https://nginx.org/en/docs/http/ngx_http_ssl_module.html) + - [http_v2_module](https://nginx.org/en/docs/http/ngx_http_v2_module.html) + - [http_stub_status_module](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) + - [http_gzip_static_module](https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html) + - [http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html) + diff --git a/modules-available/headers-more-nginx-module b/modules-available/headers-more-nginx-module new file mode 160000 index 0000000..db45361 --- /dev/null +++ b/modules-available/headers-more-nginx-module @@ -0,0 +1 @@ +Subproject commit db45361256a665e4e076465249628bb4631c3354 diff --git a/modules/ngx_http_headers_more_filter_module.so b/modules/ngx_http_headers_more_filter_module.so new file mode 100755 index 0000000..006f7ca Binary files /dev/null and b/modules/ngx_http_headers_more_filter_module.so differ diff --git a/nginx.conf b/nginx.conf index d02521f..0d02224 100644 --- a/nginx.conf +++ b/nginx.conf @@ -4,14 +4,15 @@ worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; -include /etc/nginx/modules-enabled/*.conf; + +load_module modules/ngx_http_headers_more_filter_module.so; events { worker_connections 1024; } http { - include /etc/nginx/mime.types; + include mime.types; default_type text/plain; ################## @@ -34,7 +35,7 @@ http { # Headers # ################## - add_header X-Web-Entry "Bifrost" always; + more_set_headers 'X-Web-Entry Bifrost'; ################## # SSL settings # @@ -55,7 +56,7 @@ http { access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log; - include /etc/nginx/conf.d/*.conf; - include /etc/nginx/sites-enabled/*.conf; - include /etc/nginx/cloudflare; + include conf.d/*.conf; + include sites-enabled/*.conf; + include cloudflare; } diff --git a/sites-available/000-default.conf b/sites-available/000-default.conf index 5c90913..7e5f634 100644 --- a/sites-available/000-default.conf +++ b/sites-available/000-default.conf @@ -10,8 +10,8 @@ server { server_name _; - add_header X-Dead-End true; - add_header Content-Type text/plain; + more_set_headers 'X-Dead-End true'; + more_set_headers 'Content-Type text/plain'; return 200 ok; } @@ -24,8 +24,9 @@ server { server_name _; - add_header X-Dead-End true; - add_header Content-Type text/plain; + more_set_headers 'X-Dead-End true'; + more_set_headers 'Content-Type text/plain'; + return 425 "SSL not supported here."; ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; diff --git a/sites-available/adtech.conf b/sites-available/adtech.conf index f609232..13e1f05 100644 --- a/sites-available/adtech.conf +++ b/sites-available/adtech.conf @@ -20,7 +20,7 @@ server { proxy_pass http://adtech.schleppe:3000; client_max_body_size 2000M; - include /etc/nginx/snippets/proxy-params.conf; + include snippets/proxy-params.conf; } ssl_certificate /etc/letsencrypt/live/adtech.schleppe.cloud/fullchain.pem; # managed by Certbot diff --git a/sites-available/mc.conf b/sites-available/mc.conf index 38bf873..d48c0ff 100644 --- a/sites-available/mc.conf +++ b/sites-available/mc.conf @@ -12,8 +12,8 @@ server { ssl_certificate /etc/letsencrypt/live/mc.kevinmidboe.com-0001/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/mc.kevinmidboe.com-0001/privkey.pem; # managed by Certbot - include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot +# include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot +# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } @@ -29,4 +29,4 @@ server { return 404; # managed by Certbot -} \ No newline at end of file +} diff --git a/sites-available/planetposen.conf b/sites-available/planetposen.conf index 03299a1..2476289 100644 --- a/sites-available/planetposen.conf +++ b/sites-available/planetposen.conf @@ -24,7 +24,7 @@ server { server_name planetposen.no planet.schleppe.cloud; - add_header Upgrading Connection; + more_set_headers Upgrading Connection; return 302 https://$host$request_uri; } @@ -49,6 +49,8 @@ server { server_name planet.schleppe.cloud; + include snippets/proxy-params.conf; + location /ws { resolver 10.0.0.72; proxy_pass http://planetposen-ws; @@ -56,23 +58,17 @@ server { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /api/v1/images { resolver 10.0.0.72; proxy_pass http://planetposen-images/api/v1/images; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_request_buffering off; - add_header 'Access-Control-Allow-Origin' 'planet.schleppe.cloud'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'Content-Type'; + more_set_headers 'Access-Control-Allow-Origin planet.schleppe.cloud'; + more_set_headers 'Access-Control-Allow-Methods GET, POST, OPTIONS'; + more_set_headers 'Access-Control-Allow-Headers Content-Type'; client_max_body_size 5M; } @@ -81,27 +77,19 @@ server { resolver 10.0.0.72; proxy_pass http://planetposen-backend/api; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # add_header 'Access-Control-Allow-Origin' 'planet.schleppe.cloud'; - add_header 'Access-Control-Allow-Origin' '*'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'Content-Type'; + more_set_headers 'Access-Control-Allow-Origin *'; + more_set_headers 'Access-Control-Allow-Methods GET, POST, OPTIONS'; + more_set_headers 'Access-Control-Allow-Headers Content-Type'; } location / { resolver 10.0.0.72; proxy_pass http://planetposen-frontend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - add_header 'Access-Control-Allow-Origin' 'planet.schleppe.cloud'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'Content-Type'; + more_set_headers 'Access-Control-Allow-Origin planet.schleppe.cloud'; + more_set_headers 'Access-Control-Allow-Methods GET, POST, OPTIONS'; + more_set_headers 'Access-Control-Allow-Headers Content-Type'; } ssl_certificate /etc/letsencrypt/live/planet.schleppe.cloud/fullchain.pem; # managed by Certbot diff --git a/sites-available/request.conf b/sites-available/request.conf index 5410865..8636b55 100644 --- a/sites-available/request.conf +++ b/sites-available/request.conf @@ -19,7 +19,7 @@ server { resolver 10.0.0.72; proxy_pass http://seasoned.schleppe:31459; - include /etc/nginx/snippets/proxy-params.conf; + include snippets/proxy-params.conf; } @@ -27,7 +27,7 @@ server { resolver 10.0.0.72; proxy_pass http://seasoned.schleppe:5000; - include /etc/nginx/snippets/proxy-params.conf; + include snippets/proxy-params.conf; } ssl_certificate /etc/letsencrypt/live/request.movie-0001/fullchain.pem; # managed by Certbot @@ -44,7 +44,7 @@ server { resolver 10.0.0.72; proxy_pass http://seasoned.schleppe:31459; - include /etc/nginx/snippets/proxy-params.conf; + include snippets/proxy-params.conf; } ssl_certificate /etc/letsencrypt/live/api.request.movie/fullchain.pem; # managed by Certbot @@ -63,7 +63,7 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; - include /etc/nginx/snippets/proxy-params.conf; + include snippets/proxy-params.conf; } diff --git a/snippets/proxy-params.conf b/snippets/proxy-params.conf index 75d3c81..c9a4ab4 100644 --- a/snippets/proxy-params.conf +++ b/snippets/proxy-params.conf @@ -3,4 +3,4 @@ proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for; proxy_set_header Host $host; -add_header X-Proxy-Params Applied always; \ No newline at end of file +more_set_headers 'X-Proxy-Params Applied';