From daef0cf448af17357b552245f39067a9d340ce3d Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 27 Apr 2025 21:15:30 -0700 Subject: Waow --- .../outbound/templates/proxy/docker-compose.yml | 53 ++++++++++++++++---- .../templates/proxy/headscale/headscale-client.sh | 14 ++++++ .../outbound/templates/proxy/nginx/conf.d/bin.conf | 17 +++++++ .../templates/proxy/nginx/conf.d/default.conf | 11 +++++ .../outbound/templates/proxy/nginx/conf.d/idm.conf | 13 +++++ .../templates/proxy/nginx/conf.d/kanban.conf | 13 +++++ .../templates/proxy/nginx/conf.d/mail.conf | 15 ++++++ .../templates/proxy/nginx/conf.d/notes.conf | 13 +++++ .../proxy/nginx/dont-die-until-conn-closed.sh | 56 ++++++++++++++++++++++ .../outbound/templates/proxy/nginx/nginx.conf | 30 ++++++++++++ .../proxy/nginx/toplevel.conf.d/stream.conf | 56 ++++++++++++++++++++++ .../templates/proxy/nginx/wait-for-bridge.sh | 33 +++++++++++++ .../templates/proxy/sites-enabled/bin.conf | 17 ------- .../templates/proxy/sites-enabled/default.conf | 7 --- .../templates/proxy/sites-enabled/idm.conf | 13 ----- .../templates/proxy/sites-enabled/kanban.conf | 13 ----- .../templates/proxy/sites-enabled/mail.conf | 15 ------ .../templates/proxy/sites-enabled/notes.conf | 13 ----- .../templates/proxy/toplevel.conf.d/stream.conf | 38 --------------- 19 files changed, 314 insertions(+), 126 deletions(-) create mode 100755 playbooks/roles/outbound/templates/proxy/headscale/headscale-client.sh create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/bin.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/default.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/idm.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/kanban.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/mail.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/conf.d/notes.conf create mode 100755 playbooks/roles/outbound/templates/proxy/nginx/dont-die-until-conn-closed.sh create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/nginx.conf create mode 100644 playbooks/roles/outbound/templates/proxy/nginx/toplevel.conf.d/stream.conf create mode 100755 playbooks/roles/outbound/templates/proxy/nginx/wait-for-bridge.sh delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/bin.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/default.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/idm.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/kanban.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/mail.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/sites-enabled/notes.conf delete mode 100644 playbooks/roles/outbound/templates/proxy/toplevel.conf.d/stream.conf (limited to 'playbooks/roles/outbound/templates/proxy') diff --git a/playbooks/roles/outbound/templates/proxy/docker-compose.yml b/playbooks/roles/outbound/templates/proxy/docker-compose.yml index 9642d6a..c5aa3ac 100644 --- a/playbooks/roles/outbound/templates/proxy/docker-compose.yml +++ b/playbooks/roles/outbound/templates/proxy/docker-compose.yml @@ -1,36 +1,69 @@ +--- + services: headscale-client: image: tailscale/tailscale:latest environment: + - DEPLOYMENT_TIME={{ now() }} - TS_AUTHKEY={{ headscale_user_auth_key }} - TS_EXTRA_ARGS=--login-server=https://{{ headscale_host }} --accept-routes --accept-dns --stateful-filtering=false - TS_STATE_DIR=/var/lib/tailscale - TS_USERSPACE=false - TZ={{ timezone }} - - - VIRTUAL_HOST=*.{{ domain }},{{ domain }} - - VIRTUAL_PORT=80 - - LETSENCRYPT_HOST=*.{{ domain }},{{ domain }} - hostname: headscale-outbound restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "tailscale status"] + interval: 1s + timeout: 5s + retries: 10 + hostname: headscale-outbound cap_add: - NET_ADMIN - SYS_ADMIN volumes: - ./data:/var/lib/tailscale + - ./headscale/headscale-client.sh:/headscale-client.sh - /dev/net/tun:/dev/net/tun networks: - - proxy - proxy: + - internal_subnet_router # magic. + sysctls: + - net.ipv4.ip_forward=1 + entrypoint: ["/bin/sh"] + command: /headscale-client.sh + + {{ vpn_proxy_filter_container_name }}: image: nginx:latest - network_mode: service:headscale-client + entrypoint: ["/bin/sh"] + command: /wait-for-bridge.sh + pre_stop: + - command: /dont-die-until-conn-closed.sh + cap_add: + - NET_ADMIN # to modify the routing table + environment: + - DEPLOYMENT_TIME={{ now() }} + - VIRTUAL_HOST=*.{{ domain }},{{ domain }} + - VIRTUAL_PORT=80 + - LETSENCRYPT_HOST=*.{{ domain }},{{ domain }} + healthcheck: + test: ["CMD", "curl", "http://localhost/health"] + interval: 10s + timeout: 5s + retries: 3 depends_on: - headscale-client volumes: - - ./sites-enabled:/etc/nginx/conf.d - - ./toplevel.conf.d:/etc/nginx/toplevel.conf.d + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/conf.d:/etc/nginx/conf.d + - ./nginx/toplevel.conf.d:/etc/nginx/toplevel.conf.d + - ./nginx/wait-for-bridge.sh:/wait-for-bridge.sh + - ./nginx/dont-die-until-conn-closed.sh:/dont-die-until-conn-closed.sh + networks: + - proxy + - internal_subnet_router networks: + internal_subnet_router: + driver: bridge proxy: external: true diff --git a/playbooks/roles/outbound/templates/proxy/headscale/headscale-client.sh b/playbooks/roles/outbound/templates/proxy/headscale/headscale-client.sh new file mode 100755 index 0000000..1ce0acb --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/headscale/headscale-client.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +/usr/local/bin/containerboot & + +while ! tailscale status > /dev/null 2>&1; do + sleep 1 + echo '[+] Waiting for tailscale status to be up...' +done + +echo '[+] Tailscale is up. Enabling NAT...' +iptables -t nat -A POSTROUTING -o tailscale0 -j MASQUERADE +echo '[+] Done.' + +tail -f /dev/null diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/bin.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/bin.conf new file mode 100644 index 0000000..3c5682d --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/bin.conf @@ -0,0 +1,17 @@ +server { + listen 80; + server_name bin.liz.coffee; + client_max_body_size 200M; + + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/default.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/default.conf new file mode 100644 index 0000000..f4a8007 --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/default.conf @@ -0,0 +1,11 @@ +server { + listen 80 default_server; + + location / { + return 404; + } + + location /health { + return 200; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/idm.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/idm.conf new file mode 100644 index 0000000..c85ebcf --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/idm.conf @@ -0,0 +1,13 @@ +server { + listen 80; + server_name idm.liz.coffee; + + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + 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_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/kanban.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/kanban.conf new file mode 100644 index 0000000..b668310 --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/kanban.conf @@ -0,0 +1,13 @@ +server { + listen 80; + server_name kanban.liz.coffee; + + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + 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_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/mail.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/mail.conf new file mode 100644 index 0000000..c810f5a --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/mail.conf @@ -0,0 +1,15 @@ +server { + listen 80; + server_name mail.liz.coffee; + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/notes.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/notes.conf new file mode 100644 index 0000000..f7937dd --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/notes.conf @@ -0,0 +1,13 @@ +server { + listen 80; + server_name notes.liz.coffee; + + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + 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_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/dont-die-until-conn-closed.sh b/playbooks/roles/outbound/templates/proxy/nginx/dont-die-until-conn-closed.sh new file mode 100755 index 0000000..967c2c0 --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/dont-die-until-conn-closed.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +echo "[/] sleeping to wait for some time for container to be marked as stop." +# https://stackoverflow.com/a/45146086 +sleep 3 + +pid_file="/run/nginx.pid" +max_wait_seconds=30 + +if [ ! -f "$pid_file" ]; then + echo "[!] Nginx PID file not found at $pid_file. Assuming Nginx not running or already stopped." + exit 0 +fi + +PID=$(cat "$pid_file") + +# Validate PID +if [ -z "$PID" ] || ! [[ "$PID" =~ ^[0-9]+$ ]]; then + echo "[!] Invalid PID found in $pid_file: '$PID'" + exit 1 +fi + +# Check if the process actually exists before sending quit +# kill -0 PID checks if a signal can be sent. +if ! kill -0 "$PID" 2>/dev/null; then + echo "[!] Nginx process $PID not found or already stopped." + exit 0 # Exit successfully +fi + +echo "[/] sending signal to nginx (PID: $PID) to quit" +nginx -s quit + +start_time=$SECONDS +echo "[/] Waiting for Nginx (PID: $PID) to stop (max ${max_wait_seconds}s)..." + +while [ -d /proc/$PID ]; do + current_time=$SECONDS + elapsed_time=$((current_time - start_time)) + + if [ "$elapsed_time" -ge "$max_wait_seconds" ]; then + echo "[!] Timeout: Nginx process $PID did not stop within ${max_wait_seconds} seconds." + echo "[!] Sending SIGKILL to PID $PID." + kill -9 "$PID" 2>/dev/null + + exit 1 + fi + + sleep 0.5 + if (( $(echo "$elapsed_time % 5" | bc) == 0 )); then + echo "[/] Nginx (PID: $PID) still running (waited ${elapsed_time}s)..." + fi +done + +echo "[+] Nginx process $PID stopped gracefully." +echo "[+] done. goodbye." +exit 0 diff --git a/playbooks/roles/outbound/templates/proxy/nginx/nginx.conf b/playbooks/roles/outbound/templates/proxy/nginx/nginx.conf new file mode 100644 index 0000000..32feb3a --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/nginx.conf @@ -0,0 +1,30 @@ +user www-data; +worker_processes 4; +pid /run/nginx.pid; +# load_module modules/ndk_http_module.so; +# load_module modules/ngx_http_set_misc_module.so; + +events { + worker_connections 768; +} + +include /etc/nginx/toplevel.conf.d/*.conf; + +http { + charset utf-8; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + gzip on; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/toplevel.conf.d/stream.conf b/playbooks/roles/outbound/templates/proxy/nginx/toplevel.conf.d/stream.conf new file mode 100644 index 0000000..193e65a --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/toplevel.conf.d/stream.conf @@ -0,0 +1,56 @@ +stream { + log_format basic '$proxy_protocol_addr - [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time'; + upstream imaps { + server {{ loadbalancer_ip }}:993; + } + upstream smtps { + server {{ loadbalancer_ip }}:465; + } + upstream smtptls { + server {{ loadbalancer_ip }}:587; + } + upstream smtp { + server {{ loadbalancer_ip }}:25; + } + upstream managesieve { + server {{ loadbalancer_ip }}:4190; + } + + server { + set_real_ip_from {{ docker_network }}; + listen 993 proxy_protocol; + + proxy_pass imaps; + proxy_protocol on; + } + server { + set_real_ip_from {{ docker_network }}; + listen 25 proxy_protocol; + + proxy_pass smtp; + proxy_protocol on; + } + server { + set_real_ip_from {{ docker_network }}; + listen 587 proxy_protocol; + + proxy_pass smtptls; + proxy_protocol on; + } + server { + set_real_ip_from {{ docker_network }}; + listen 465 proxy_protocol; + + proxy_pass smtps; + proxy_protocol on; + } + server { + set_real_ip_from {{ docker_network }}; + listen 4190 proxy_protocol; + + proxy_pass managesieve; + proxy_protocol on; + } +} diff --git a/playbooks/roles/outbound/templates/proxy/nginx/wait-for-bridge.sh b/playbooks/roles/outbound/templates/proxy/nginx/wait-for-bridge.sh new file mode 100755 index 0000000..da273a9 --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/wait-for-bridge.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +set -e + +echo "[+] Waiting for headscale-client to be resolvable..." + +# Loop until headscale-client IP is found or timeout +timeout=30 +start_time=$(date +%s) + +HEADSCALE_IP="" +while [ -z "$HEADSCALE_IP" ]; do + HEADSCALE_IP=$(getent hosts headscale-client | awk '{ print $1 }' | head -n 1) + current_time=$(date +%s) + if [ $((current_time - start_time)) -ge $timeout ]; then + echo "[-] Timeout waiting for headscale-client DNS resolution." >&2 + exit 1 + fi + if [ -z "$HEADSCALE_IP" ]; then + sleep 1 + fi +done + +echo "[+] Found headscale-client IP: $HEADSCALE_IP" +echo "[+] Attempting to modify routing table..." + +apt update && apt install -y iproute2 +ip route del default || echo "[-] Warning: Failed to delete default route (maybe none existed)." +ip route add default via $HEADSCALE_IP +echo "[+] Default route set via $HEADSCALE_IP." + +echo "[+] Starting Nginx..." +nginx -g "daemon off;" diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/bin.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/bin.conf deleted file mode 100644 index 3c5682d..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/bin.conf +++ /dev/null @@ -1,17 +0,0 @@ -server { - listen 80; - server_name bin.liz.coffee; - client_max_body_size 200M; - - location / { - proxy_pass https://{{ loadbalancer_ip }}; - proxy_ssl_verify off; - proxy_http_version 1.1; - 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_set_header X-Forwarded-Proto $scheme; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/default.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/default.conf deleted file mode 100644 index d127cc5..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/default.conf +++ /dev/null @@ -1,7 +0,0 @@ -server { - listen 80 default_server; - - location / { - return 404; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/idm.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/idm.conf deleted file mode 100644 index c85ebcf..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/idm.conf +++ /dev/null @@ -1,13 +0,0 @@ -server { - listen 80; - server_name idm.liz.coffee; - - location / { - proxy_pass https://{{ loadbalancer_ip }}; - proxy_ssl_verify off; - 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_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/kanban.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/kanban.conf deleted file mode 100644 index b668310..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/kanban.conf +++ /dev/null @@ -1,13 +0,0 @@ -server { - listen 80; - server_name kanban.liz.coffee; - - location / { - proxy_pass https://{{ loadbalancer_ip }}; - proxy_ssl_verify off; - 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_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/mail.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/mail.conf deleted file mode 100644 index c810f5a..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/mail.conf +++ /dev/null @@ -1,15 +0,0 @@ -server { - listen 80; - server_name mail.liz.coffee; - location / { - proxy_pass https://{{ loadbalancer_ip }}; - proxy_ssl_verify off; - proxy_http_version 1.1; - 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_set_header X-Forwarded-Proto $scheme; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/sites-enabled/notes.conf b/playbooks/roles/outbound/templates/proxy/sites-enabled/notes.conf deleted file mode 100644 index f7937dd..0000000 --- a/playbooks/roles/outbound/templates/proxy/sites-enabled/notes.conf +++ /dev/null @@ -1,13 +0,0 @@ -server { - listen 80; - server_name notes.liz.coffee; - - location / { - proxy_pass https://{{ loadbalancer_ip }}; - proxy_ssl_verify off; - 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_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/playbooks/roles/outbound/templates/proxy/toplevel.conf.d/stream.conf b/playbooks/roles/outbound/templates/proxy/toplevel.conf.d/stream.conf deleted file mode 100644 index 68d5445..0000000 --- a/playbooks/roles/outbound/templates/proxy/toplevel.conf.d/stream.conf +++ /dev/null @@ -1,38 +0,0 @@ -stream { - upstream imaps { - server {{ loadbalancer_ip }}:993; - } - upstream smtps { - server {{ loadbalancer_ip }}:465; - } - upstream smtptls { - server {{ loadbalancer_ip }}:587; - } - upstream smtp { - server {{ loadbalancer_ip }}:25; - } - upstream managesieve { - server {{ loadbalancer_ip }}:4190; - } - - server { - listen 993; - proxy_pass imaps; - } - server { - listen 25; - proxy_pass smtp; - } - server { - listen 587; - proxy_pass smtptls; - } - server { - listen 465; - proxy_pass smtps; - } - server { - listen 4190; - proxy_pass managesieve; - } -} -- cgit v1.2.3-70-g09d2