Corrects six documentation files to match the actual deployed pipeline behavior and align test/prod approaches where they share the same code. prod-env/02-godaddy-credentials.md - Step 1: correct secret file from .env.secrets.shared to .env.secrets.swag; add clarifying note that .env.secrets.shared holds AppRole/DB secrets and must not be used for GoDaddy credentials. - Step 4: document that GoDaddy A records are now managed automatically by the pipeline's 'Update DNS Records' step via the GoDaddy API; reference the Gitea variable PROD_FLOATING_IP that must be set once. prod-env/08-deploy-pipeline-update.md - Add Step 2 documenting the new 'Update DNS Records' pipeline step (GoDaddy API, idempotent check-before-update, requires jq and vars.PROD_FLOATING_IP). - Renumber subsequent steps 3-8 to accommodate the new step. - Fix DB hostnames in Step 7 (Run Database Init Scripts) from iklimco_postgresql/iklimco_mongodb to postgresql/mongodb, matching how Swarm overlay DNS resolves service names inside iklimco-net. - Update context block: correct DB hostname description, replace outdated storagebox path note with env-var approach, list new steps. - Update final step order to 24 steps including the DNS step and Release Deploy Lock; mark Wait for etcd as NEW. prod-env/09-verify.md - Insert check #2 for the precipitation image directory (/mnt/storagebox/precipitation/images) and iklimco_image-data volume bind mount, mirroring the equivalent check in test-env/08-verify.md. - Renumber all subsequent checks (3-12) to maintain sequential ordering. test-env/03-infra-stack-changes.md - Update SWAG service volume snippet: replace hardcoded paths (swag-vl:/config, /opt/iklimco/swag/dns-conf, /opt/iklimco/swag/site-confs) with env-var forms (${SWAG_CONFIG_DIR:-swag-vl}, ${SWAG_DNS_CONF_DIR:-...}, ${SWAG_SITE_CONFS_DIR:-...}) to match docker-stack-infra.yml. - Update cert-reloader volume snippet: replace swag-vl and /opt/iklimco/ssl with ${SWAG_CONFIG_DIR:-swag-vl} and ${SWAG_CERT_DIR:-/opt/iklimco/ssl}, enabling StorageBox override in prod without changing the base file. test-env/04-swag-nginx-configs.md - Replace RESTRICTED_IP_1/RESTRICTED_IP_2 individual env vars with RESTRICTED_IPS (comma-separated CIDR list) in the required-vars section, matching env-test/.env and the actual pipeline. - Update all three IP-restricted template examples (apigw, rabbitmq, grafana) from allow ${RESTRICTED_IP_1}; allow ${RESTRICTED_IP_2}; to ${RESTRICTED_IPS_BLOCK}, matching the actual .conf.tpl files in the repo. - Rewrite the deploy step section to match the real pipeline: docker run alpine for file writing, RESTRICTED_IPS_BLOCK generation via sed, and envsubst with explicit SWAG_VARS filter to protect nginx $upstream_* vars. test-env/07-deploy-pipeline-update.md - Step 2 (Prepare SWAG Directories): replace sudo-tee approach with the actual docker-run-alpine method used in deploy-test.yml; add nginx reload block; update notes to reflect RESTRICTED_IPS_BLOCK generation. - Step 4 (Re-order): correct step numbering to match actual pipeline (21 steps); mark 'Wait for etcd' as already present in pipeline rather than a new addition; add Bootstrap Vault TLS Placeholder which was missing from the documented order.
203 lines
5.7 KiB
Markdown
203 lines
5.7 KiB
Markdown
# 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.
|