# 04 — SWAG Nginx Proxy Configs (Test) ## Context SWAG nginx auto-includes only `site-confs/*.conf`. All proxy config templates live in `swag/site-confs/` in the repo and are rendered to `/opt/iklimco/swag/site-confs/` on the host at deploy time. Templates use `${VAR}` placeholders processed with `envsubst` at deploy time. ## Required env vars (in `.env` on storagebox `test/secrets/iklim.co/.env`) ```bash API_SUBDOMAIN=api-test.iklim.co APIGW_SUBDOMAIN=apigw-test.iklim.co RABBITMQ_SUBDOMAIN=rabbitmq-test.iklim.co GRAFANA_SUBDOMAIN=grafana-test.iklim.co # Comma-separated list of allowed CIDRs for IP-restricted subdomains RESTRICTED_IPS="78.187.87.109/32,95.70.151.248/32" ``` ## Files to create ### `swag/site-confs/default.conf` Default catch-all: HTTP→HTTPS redirect + 444 for unknown HTTPS hosts. ```nginx server { listen 80 default_server; listen [::]:80 default_server; server_name _; return 301 https://$host$request_uri; } server { listen 443 ssl http2 default_server; listen [::]:443 ssl http2 default_server; server_name _; include /config/nginx/ssl.conf; return 444; } ``` ### `swag/site-confs/api.conf.tpl` Public API gateway — no IP restriction. ```nginx server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${API_SUBDOMAIN}; include /config/nginx/ssl.conf; include /config/nginx/resolver.conf; client_max_body_size 50m; location / { include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app apisix; set $upstream_port 9080; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` ### `swag/site-confs/apigw.conf.tpl` APISIX Dashboard — IP restricted. ```nginx server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${APIGW_SUBDOMAIN}; include /config/nginx/ssl.conf; include /config/nginx/resolver.conf; client_max_body_size 0; location / { ${RESTRICTED_IPS_BLOCK} deny all; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app apisix-dashboard; set $upstream_port 9000; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` > `${RESTRICTED_IPS_BLOCK}` is generated at deploy time from `RESTRICTED_IPS` (comma-separated CIDRs) > as multi-line `allow` directives with `/32` suffix. See `07-deploy-pipeline-update.md` for the pipeline step. ### `swag/site-confs/rabbitmq.conf.tpl` RabbitMQ Management UI — IP restricted. ```nginx server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${RABBITMQ_SUBDOMAIN}; include /config/nginx/ssl.conf; include /config/nginx/resolver.conf; client_max_body_size 0; location / { ${RESTRICTED_IPS_BLOCK} deny all; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app rabbitmq; set $upstream_port 15672; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` ### `swag/site-confs/grafana.conf.tpl` Grafana — IP restricted. ```nginx server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${GRAFANA_SUBDOMAIN}; include /config/nginx/ssl.conf; include /config/nginx/resolver.conf; client_max_body_size 0; location / { ${RESTRICTED_IPS_BLOCK} deny all; include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app grafana; set $upstream_port 3000; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` ## Deploy step (handled by pipeline — see `07-deploy-pipeline-update.md`) ```bash set -a; . ./.env; . ./.env.secrets.swag; set +a docker run --rm -v /opt/iklimco/swag:/output alpine \ mkdir -p /output/dns-conf /output/site-confs envsubst < swag/dns-conf/godaddy.ini.tpl | docker run --rm -i \ -v /opt/iklimco/swag/dns-conf:/output \ alpine sh -c "cat > /output/godaddy.ini && chmod 600 /output/godaddy.ini" # RESTRICTED_IPS → multi-line allow block (indented 8 spaces per nginx style) export RESTRICTED_IPS_BLOCK="$(echo "$RESTRICTED_IPS" | tr ',' '\n' | sed 's|.*| allow &;|')" # Explicit var list prevents nginx $upstream_* from being substituted by envsubst SWAG_VARS='${API_SUBDOMAIN}${APIGW_SUBDOMAIN}${GRAFANA_SUBDOMAIN}${RABBITMQ_SUBDOMAIN}${RESTRICTED_IPS_BLOCK}' for tpl in swag/site-confs/*.conf.tpl; do fname=$(basename "${tpl%.tpl}") envsubst "$SWAG_VARS" < "$tpl" | docker run --rm -i \ -v /opt/iklimco/swag/site-confs:/output \ alpine sh -c "cat > /output/${fname}" echo "✅ ${fname}" done cat swag/site-confs/default.conf | docker run --rm -i \ -v /opt/iklimco/swag/site-confs:/output \ alpine sh -c "cat > /output/default.conf" ``` ## Verification After deploy, check SWAG nginx config is valid: ```bash docker exec $(docker ps -q -f name=iklimco_swag) nginx -t ``` Check subdomains resolve (from outside the server): ```bash curl -sk https://api-test.iklim.co/health # expects APISIX response curl -sk https://grafana-test.iklim.co # expects 403 Forbidden (wrong IP) ``` ## Notes - `include /config/nginx/resolver.conf` enables dynamic upstream resolution via Docker DNS — required for overlay service names like `apisix`, `grafana`, etc. - SWAG's `proxy.conf` already sets `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto` and WebSocket upgrade headers. No manual addition needed. - `*.iklim.co` cert covers both `api.iklim.co` and `api-test.iklim.co` subdomains — both test and prod servers can independently obtain and use it.