diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index a9e6199d..05109d07 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -70,6 +70,7 @@ services: - ./web/dist:/usr/share/nginx/html:ro - ./web/nginx.conf.template:/etc/nginx/templates/default.conf.template:ro environment: + NGINX_ENTRYPOINT_LOCAL_RESOLVERS: "1" SKILLHUB_API_UPSTREAM: http://server:8080 SKILLHUB_WEB_API_BASE_URL: "" SKILLHUB_PUBLIC_BASE_URL: "" diff --git a/scripts/tests/nginx-forwarded-proto-test.sh b/scripts/tests/nginx-forwarded-proto-test.sh index 01be85c3..05fbd3f5 100755 --- a/scripts/tests/nginx-forwarded-proto-test.sh +++ b/scripts/tests/nginx-forwarded-proto-test.sh @@ -42,10 +42,12 @@ wait_for_nginx() { start_proxy() { local container="$1" local trust_forwarded_proto="$2" + local backend_name="${3:-$BACKEND}" docker run --detach \ --name "$container" \ --network "$NETWORK" \ - --env "SKILLHUB_API_UPSTREAM=http://$BACKEND:8080" \ + --env "NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1" \ + --env "SKILLHUB_API_UPSTREAM=http://$backend_name:8080" \ --env "SKILLHUB_TRUST_FORWARDED_PROTO=$trust_forwarded_proto" \ --volume "$TEMPLATE:/etc/nginx/templates/default.conf.template:ro" \ "$NGINX_IMAGE" >/dev/null @@ -98,4 +100,80 @@ done assert_proto "$TRUSTED_PROXY" http assert_proto "$TRUSTED_PROXY" http "https,http" -echo "nginx-forwarded-proto-test passed" +DNS_BACKEND="${TEST_ID}-dns-backend" +DNS_OLD_BACKEND="${TEST_ID}-dns-old" +DNS_NEW_BACKEND="${TEST_ID}-dns-new" +DNS_STALE_BACKEND="${TEST_ID}-dns-stale" +DNS_PROXY="${TEST_ID}-dns-proxy" + +start_dns_backend() { + local container="$1" + local response="$2" + local alias="${3:-}" + local static_ip="${4:-}" + local config="$TMP_DIR/$container.conf" + cat >"$config" </dev/null + CONTAINERS+=("$container") +} + +container_ip() { + docker inspect --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" "$1" +} + +start_dns_backend "$DNS_OLD_BACKEND" first "$DNS_BACKEND" +start_proxy "$DNS_PROXY" false "$DNS_BACKEND" +assert_dns_response() { + local expected="$1" + local actual + actual="$(docker exec "$DNS_PROXY" wget -qO- http://127.0.0.1/api/dns)" \ + || fail "DNS proxy request failed; expected '$expected'" + [[ "$actual" == "$expected" ]] \ + || fail "DNS proxy returned '$actual', expected '$expected'" +} +assert_dns_response first + +old_ip="$(container_ip "$DNS_OLD_BACKEND")" +docker rm -f "$DNS_OLD_BACKEND" >/dev/null +start_dns_backend "$DNS_STALE_BACKEND" stale "" "$old_ip" +start_dns_backend "$DNS_NEW_BACKEND" second "$DNS_BACKEND" +new_ip="$(container_ip "$DNS_NEW_BACKEND")" +[[ "$old_ip" != "$new_ip" ]] || fail "replacement backend reused old IP $old_ip" + +refreshed=false +for attempt in {1..36}; do + if actual="$(docker exec "$DNS_PROXY" wget -qO- http://127.0.0.1/api/dns 2>/dev/null)" \ + && [[ "$actual" == second ]]; then + refreshed=true + break + fi + sleep 0.5 +done +[[ "$refreshed" == true ]] \ + || { docker logs "$DNS_PROXY" >&2 || true; fail "unchanged proxy did not reach replacement backend $new_ip"; } + +echo "nginx-forwarded-proto-test passed, including DNS refresh ($old_ip -> $new_ip)" diff --git a/scripts/tests/web-base-path-nginx-smoke-test.sh b/scripts/tests/web-base-path-nginx-smoke-test.sh index ba2aa809..58db1eaf 100755 --- a/scripts/tests/web-base-path-nginx-smoke-test.sh +++ b/scripts/tests/web-base-path-nginx-smoke-test.sh @@ -41,6 +41,7 @@ chmod +x "$entrypoint_d/20-base-path.sh" "$entrypoint_d/30-runtime-config.sh" if ! docker run -d --name "$name" \ -p "$port:80" \ + -e NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 \ -e SKILLHUB_API_UPSTREAM=http://127.0.0.1:9 \ -e SKILLHUB_TRUST_FORWARDED_PROTO=false \ -e SKILLHUB_WEB_BASE_PATH=/skillhub/ \ @@ -154,6 +155,7 @@ name_default="$name-default" port_default=18082 docker run -d --name "$name_default" \ -p "$port_default:80" \ + -e NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 \ -e SKILLHUB_API_UPSTREAM=http://127.0.0.1:9 \ -e SKILLHUB_TRUST_FORWARDED_PROTO=false \ -e SKILLHUB_WEB_BASE_PATH=/skillhub/ \ @@ -206,6 +208,7 @@ name_trusted="$name-trusted" port_trusted=18083 docker run -d --name "$name_trusted" \ -p "$port_trusted:80" \ + -e NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 \ -e SKILLHUB_API_UPSTREAM=http://127.0.0.1:9 \ -e SKILLHUB_TRUST_FORWARDED_PROTO=true \ -e SKILLHUB_WEB_BASE_PATH=/skillhub/ \ @@ -246,6 +249,7 @@ fixed_port=18081 docker run -d --name "$fixed_name" \ -p "$fixed_port:80" \ + -e NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 \ -e SKILLHUB_API_UPSTREAM=http://127.0.0.1:9 \ -e SKILLHUB_TRUST_FORWARDED_PROTO=false \ -e SKILLHUB_WEB_BASE_PATH= \ diff --git a/web/Dockerfile b/web/Dockerfile index bf8f1fb0..74a45aa4 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -13,6 +13,7 @@ RUN pnpm build FROM nginx:alpine ENV SKILLHUB_TRUST_FORWARDED_PROTO=false +ENV NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 # Record a fixed build-time base so the entrypoint defaults SKILLHUB_WEB_BASE_PATH # to it and generates matching Nginx routing without repeating the value at runtime. # Placeholder builds (the default) intentionally write no file and default to '/'. diff --git a/web/nginx.conf.template b/web/nginx.conf.template index 7bf1c1ee..b2b78d25 100644 --- a/web/nginx.conf.template +++ b/web/nginx.conf.template @@ -2,6 +2,8 @@ server_tokens off; server { listen 80; server_name _; + resolver ${NGINX_LOCAL_RESOLVERS} valid=10s; + set $skillhub_api_upstream "${SKILLHUB_API_UPSTREAM}"; root /usr/share/nginx/html; index index.html; @@ -34,7 +36,7 @@ server { } location /api/ { - proxy_pass ${SKILLHUB_API_UPSTREAM}; + proxy_pass $skillhub_api_upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -43,7 +45,7 @@ server { } location /oauth2/ { - proxy_pass ${SKILLHUB_API_UPSTREAM}; + proxy_pass $skillhub_api_upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -52,7 +54,7 @@ server { } location /login/oauth2/ { - proxy_pass ${SKILLHUB_API_UPSTREAM}; + proxy_pass $skillhub_api_upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -61,7 +63,7 @@ server { } location /.well-known/ { - proxy_pass ${SKILLHUB_API_UPSTREAM}; + proxy_pass $skillhub_api_upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;