fix(deploy): select verified proxy subnet
This commit is contained in:
+65
-2
@@ -97,16 +97,79 @@ resolve_existing_web_proxy_subnet() {
|
||||
fi
|
||||
|
||||
local project_name="${COMPOSE_PROJECT_NAME:-$(basename "$PWD")}" network_name subnet
|
||||
local labelled_networks labelled_network_id labelled_project
|
||||
network_name="${project_name}_web_proxy"
|
||||
if ! docker network inspect "$network_name" >/dev/null 2>&1; then
|
||||
return 0
|
||||
# Compose projects may have been created from a differently named checkout.
|
||||
# A unique network carrying Compose's logical `web_proxy` label is the same
|
||||
# production fact even when its generated resource name differs.
|
||||
labelled_networks=""
|
||||
if [ -z "${COMPOSE_PROJECT_NAME:-}" ]; then
|
||||
labelled_networks="$(docker network ls --filter label=com.docker.compose.network=web_proxy -q 2>/dev/null || true)"
|
||||
fi
|
||||
if [ "$(printf '%s\n' "$labelled_networks" | sed '/^$/d' | wc -l | tr -d '[:space:]')" = "1" ]; then
|
||||
labelled_network_id="$(printf '%s\n' "$labelled_networks" | sed '/^$/d')"
|
||||
labelled_project="$(docker network inspect -f '{{index .Labels "com.docker.compose.project"}}' "$labelled_network_id" 2>/dev/null || true)"
|
||||
if [[ "$labelled_project" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]]; then
|
||||
export COMPOSE_PROJECT_NAME="$labelled_project"
|
||||
network_name="$labelled_network_id"
|
||||
else
|
||||
network_name=""
|
||||
fi
|
||||
else
|
||||
network_name=""
|
||||
fi
|
||||
fi
|
||||
|
||||
subnet="$(docker network inspect -f '{{range .IPAM.Config}}{{println .Subnet}}{{end}}' "$network_name" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
|
||||
subnet=""
|
||||
if [ -n "$network_name" ]; then
|
||||
subnet="$(docker network inspect -f '{{range .IPAM.Config}}{{println .Subnet}}{{end}}' "$network_name" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
|
||||
fi
|
||||
if [[ "$subnet" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[12][0-9]|3[0-2])$ ]]; then
|
||||
export WEB_PROXY_SUBNET="$subnet"
|
||||
echo "Reusing WEB_PROXY_SUBNET from the existing ${network_name} Docker network."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# First creation of the isolated proxy network: use the documented default
|
||||
# only after proving it does not overlap any network currently on this host.
|
||||
local candidate="172.31.250.0/29" existing_output existing
|
||||
local candidate_start candidate_end existing_start existing_end
|
||||
local -a network_ids=()
|
||||
mapfile -t network_ids < <(docker network ls -q 2>/dev/null)
|
||||
if [ "${#network_ids[@]}" -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
if ! existing_output="$(docker network inspect -f '{{range .IPAM.Config}}{{println .Subnet}}{{end}}' "${network_ids[@]}" 2>/dev/null)"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
read -r candidate_start candidate_end < <(ipv4_cidr_bounds "$candidate") || return 0
|
||||
while IFS= read -r existing; do
|
||||
[[ "$existing" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[12][0-9]|3[0-2])$ ]] || continue
|
||||
read -r existing_start existing_end < <(ipv4_cidr_bounds "$existing") || continue
|
||||
if (( candidate_start <= existing_end && existing_start <= candidate_end )); then
|
||||
return 0
|
||||
fi
|
||||
done <<< "$existing_output"
|
||||
|
||||
export WEB_PROXY_SUBNET="$candidate"
|
||||
echo "Using non-overlapping WEB_PROXY_SUBNET ${candidate} after Docker network inventory."
|
||||
}
|
||||
|
||||
ipv4_cidr_bounds() {
|
||||
local cidr="$1" address prefix a b c d value ip size start
|
||||
address="${cidr%/*}"
|
||||
prefix="${cidr#*/}"
|
||||
IFS='.' read -r a b c d <<< "$address"
|
||||
for value in "$a" "$b" "$c" "$d"; do
|
||||
[[ "$value" =~ ^[0-9]{1,3}$ ]] && ((10#$value <= 255)) || return 1
|
||||
done
|
||||
[[ "$prefix" =~ ^[0-9]+$ ]] && ((10#$prefix <= 32)) || return 1
|
||||
ip=$(( (10#$a << 24) + (10#$b << 16) + (10#$c << 8) + 10#$d ))
|
||||
size=$(( 1 << (32 - 10#$prefix) ))
|
||||
start=$(( ip & ~(size - 1) ))
|
||||
printf '%s %s\n' "$start" "$((start + size - 1))"
|
||||
}
|
||||
|
||||
validate_deploy_config() {
|
||||
|
||||
@@ -32,7 +32,7 @@ No image was pulled and no production or persistent service was changed. Ephemer
|
||||
## Limitations and production gates
|
||||
|
||||
- No Traefik configuration exists in this repository. Verify its exact `Host()` rule, TLS route, replacement of forwarding headers, selected Docker network, and hostile-Host rejection on the operator host.
|
||||
- `WEB_PROXY_SUBNET` must be chosen after production Docker-network inventory; the example value is not a production fact. Deploys may recover a missing value only from the existing Compose `web_proxy` network, preserving the already-running production CIDR without guessing a new one.
|
||||
- `WEB_PROXY_SUBNET` must be chosen after production Docker-network inventory. Deploys recover a missing value from a uniquely labelled existing Compose `web_proxy` network. On its first creation, the documented `172.31.250.0/29` candidate is accepted only after a complete Docker network inventory proves it does not overlap; unreadable or overlapping inventory remains fail-closed.
|
||||
- Host firewall and `docker ps`/published-port state are unverified.
|
||||
- The exact `nginx:1.29.8-alpine` base image was not installed locally. Syntax was checked with the existing local nginx frontend image; approved CI must build the pinned Dockerfile.
|
||||
- A complete local proxy/browser smoke was not run because rebuilding the pinned container would require an unavailable base image/package access. No browser claim is made.
|
||||
|
||||
Reference in New Issue
Block a user