fix: bug: edge-control add_route targets non-existent Caddy server edge — registration succeeds in registry but traffic never routes (#789)
- install.sh: use Caddy `servers { name edge }` global option so the
emitted Caddyfile produces a predictably-named server
- lib/caddy.sh: add `_discover_server_name` that queries the admin API
for the first server listening on :80/:443 — add_route and remove_route
use dynamic discovery instead of hardcoding `/servers/edge/`
- lib/caddy.sh: add_route, remove_route, and reload_caddy now check HTTP
status codes (≥400 → return 1 with error message) instead of only
checking curl exit code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
02e86c3589
commit
987413ab3a
2 changed files with 73 additions and 22 deletions
|
|
@ -225,13 +225,19 @@ EOF
|
||||||
chmod 600 "$GANDI_ENV"
|
chmod 600 "$GANDI_ENV"
|
||||||
|
|
||||||
# Create Caddyfile with admin API and wildcard cert
|
# Create Caddyfile with admin API and wildcard cert
|
||||||
|
# The "servers" global option names the auto-generated server "edge" so that
|
||||||
|
# lib/caddy.sh (which discovers the server dynamically) finds a predictable
|
||||||
|
# name — defense-in-depth alongside the dynamic discovery in add_route.
|
||||||
CADDYFILE="/etc/caddy/Caddyfile"
|
CADDYFILE="/etc/caddy/Caddyfile"
|
||||||
cat > "$CADDYFILE" <<EOF
|
cat > "$CADDYFILE" <<'CADDYEOF'
|
||||||
# Caddy configuration for edge control plane
|
# Caddy configuration for edge control plane
|
||||||
# Admin API enabled on 127.0.0.1:2019
|
# Admin API enabled on 127.0.0.1:2019
|
||||||
|
|
||||||
{
|
{
|
||||||
admin localhost:2019
|
admin localhost:2019
|
||||||
|
servers {
|
||||||
|
name edge
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Default site (reverse proxy for edge tunnels will be added dynamically)
|
# Default site (reverse proxy for edge tunnels will be added dynamically)
|
||||||
|
|
@ -240,7 +246,7 @@ cat > "$CADDYFILE" <<EOF
|
||||||
dns gandi {env.GANDI_API_KEY}
|
dns gandi {env.GANDI_API_KEY}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
CADDYEOF
|
||||||
|
|
||||||
# Start Caddy
|
# Start Caddy
|
||||||
systemctl restart caddy 2>/dev/null || {
|
systemctl restart caddy 2>/dev/null || {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,24 @@ CADDY_ADMIN_URL="${CADDY_ADMIN_URL:-http://127.0.0.1:2019}"
|
||||||
# Domain suffix for projects
|
# Domain suffix for projects
|
||||||
DOMAIN_SUFFIX="${DOMAIN_SUFFIX:-disinto.ai}"
|
DOMAIN_SUFFIX="${DOMAIN_SUFFIX:-disinto.ai}"
|
||||||
|
|
||||||
|
# Discover the Caddy server name that listens on :80/:443
|
||||||
|
# Usage: _discover_server_name
|
||||||
|
_discover_server_name() {
|
||||||
|
local server_name
|
||||||
|
server_name=$(curl -sS "${CADDY_ADMIN_URL}/config/apps/http/servers" \
|
||||||
|
| jq -r 'to_entries | map(select(.value.listen[]? | test(":(80|443)$"))) | .[0].key // empty') || {
|
||||||
|
echo "Error: could not query Caddy admin API for servers" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$server_name" ]; then
|
||||||
|
echo "Error: could not find a Caddy server listening on :80/:443" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$server_name"
|
||||||
|
}
|
||||||
|
|
||||||
# Add a route for a project
|
# Add a route for a project
|
||||||
# Usage: add_route <project> <port>
|
# Usage: add_route <project> <port>
|
||||||
add_route() {
|
add_route() {
|
||||||
|
|
@ -26,6 +44,9 @@ add_route() {
|
||||||
local port="$2"
|
local port="$2"
|
||||||
local fqdn="${project}.${DOMAIN_SUFFIX}"
|
local fqdn="${project}.${DOMAIN_SUFFIX}"
|
||||||
|
|
||||||
|
local server_name
|
||||||
|
server_name=$(_discover_server_name) || return 1
|
||||||
|
|
||||||
# Build the route configuration (partial config)
|
# Build the route configuration (partial config)
|
||||||
local route_config
|
local route_config
|
||||||
route_config=$(cat <<EOF
|
route_config=$(cat <<EOF
|
||||||
|
|
@ -58,16 +79,21 @@ add_route() {
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
# Append route using POST /config/apps/http/servers/edge/routes
|
# Append route via admin API, checking HTTP status
|
||||||
local response
|
local response status body
|
||||||
response=$(curl -s -X POST \
|
response=$(curl -sS -w '\n%{http_code}' -X POST \
|
||||||
"${CADDY_ADMIN_URL}/config/apps/http/servers/edge/routes" \
|
"${CADDY_ADMIN_URL}/config/apps/http/servers/${server_name}/routes" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "$route_config" 2>&1) || {
|
-d "$route_config") || {
|
||||||
echo "Error: failed to add route for ${fqdn}" >&2
|
echo "Error: failed to add route for ${fqdn}" >&2
|
||||||
echo "Response: ${response}" >&2
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
status=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
if [ "$status" -ge 400 ]; then
|
||||||
|
echo "Error: Caddy admin API returned ${status}: ${body}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Added route: ${fqdn} → 127.0.0.1:${port}" >&2
|
echo "Added route: ${fqdn} → 127.0.0.1:${port}" >&2
|
||||||
}
|
}
|
||||||
|
|
@ -78,31 +104,45 @@ remove_route() {
|
||||||
local project="$1"
|
local project="$1"
|
||||||
local fqdn="${project}.${DOMAIN_SUFFIX}"
|
local fqdn="${project}.${DOMAIN_SUFFIX}"
|
||||||
|
|
||||||
# First, get current routes
|
local server_name
|
||||||
local routes_json
|
server_name=$(_discover_server_name) || return 1
|
||||||
routes_json=$(curl -s "${CADDY_ADMIN_URL}/config/apps/http/servers/edge/routes" 2>&1) || {
|
|
||||||
|
# First, get current routes, checking HTTP status
|
||||||
|
local response status body
|
||||||
|
response=$(curl -sS -w '\n%{http_code}' \
|
||||||
|
"${CADDY_ADMIN_URL}/config/apps/http/servers/${server_name}/routes") || {
|
||||||
echo "Error: failed to get current routes" >&2
|
echo "Error: failed to get current routes" >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
status=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
if [ "$status" -ge 400 ]; then
|
||||||
|
echo "Error: Caddy admin API returned ${status}: ${body}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Find the route index that matches our fqdn using jq
|
# Find the route index that matches our fqdn using jq
|
||||||
local route_index
|
local route_index
|
||||||
route_index=$(echo "$routes_json" | jq -r "to_entries[] | select(.value.match[]?.host[]? == \"${fqdn}\") | .key" 2>/dev/null | head -1)
|
route_index=$(echo "$body" | jq -r "to_entries[] | select(.value.match[]?.host[]? == \"${fqdn}\") | .key" 2>/dev/null | head -1)
|
||||||
|
|
||||||
if [ -z "$route_index" ] || [ "$route_index" = "null" ]; then
|
if [ -z "$route_index" ] || [ "$route_index" = "null" ]; then
|
||||||
echo "Warning: route for ${fqdn} not found" >&2
|
echo "Warning: route for ${fqdn} not found" >&2
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delete the route at the found index
|
# Delete the route at the found index, checking HTTP status
|
||||||
local response
|
response=$(curl -sS -w '\n%{http_code}' -X DELETE \
|
||||||
response=$(curl -s -X DELETE \
|
"${CADDY_ADMIN_URL}/config/apps/http/servers/${server_name}/routes/${route_index}" \
|
||||||
"${CADDY_ADMIN_URL}/config/apps/http/servers/edge/routes/${route_index}" \
|
-H "Content-Type: application/json") || {
|
||||||
-H "Content-Type: application/json" 2>&1) || {
|
|
||||||
echo "Error: failed to remove route for ${fqdn}" >&2
|
echo "Error: failed to remove route for ${fqdn}" >&2
|
||||||
echo "Response: ${response}" >&2
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
status=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
if [ "$status" -ge 400 ]; then
|
||||||
|
echo "Error: Caddy admin API returned ${status}: ${body}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Removed route: ${fqdn}" >&2
|
echo "Removed route: ${fqdn}" >&2
|
||||||
}
|
}
|
||||||
|
|
@ -110,13 +150,18 @@ remove_route() {
|
||||||
# Reload Caddy to apply configuration changes
|
# Reload Caddy to apply configuration changes
|
||||||
# Usage: reload_caddy
|
# Usage: reload_caddy
|
||||||
reload_caddy() {
|
reload_caddy() {
|
||||||
local response
|
local response status body
|
||||||
response=$(curl -s -X POST \
|
response=$(curl -sS -w '\n%{http_code}' -X POST \
|
||||||
"${CADDY_ADMIN_URL}/reload" 2>&1) || {
|
"${CADDY_ADMIN_URL}/reload") || {
|
||||||
echo "Error: failed to reload Caddy" >&2
|
echo "Error: failed to reload Caddy" >&2
|
||||||
echo "Response: ${response}" >&2
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
status=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
if [ "$status" -ge 400 ]; then
|
||||||
|
echo "Error: Caddy reload returned ${status}: ${body}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Caddy reloaded" >&2
|
echo "Caddy reloaded" >&2
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue