Environment_Infrastructure/roadmap/test-env/04-swag-nginx-configs.md
Murat ÖZDEMİR fd6a0b4f46 docs: fix roadmap inconsistencies between test-env and prod-env
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.
2026-05-16 16:52:48 +03:00

5.7 KiB

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)

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.

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.

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.

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.

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.

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)

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:

docker exec $(docker ps -q -f name=iklimco_swag) nginx -t

Check subdomains resolve (from outside the server):

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.