mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-11 10:54:46 +08:00
235 lines
7.7 KiB
Bash
Executable File
235 lines
7.7 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
. /usr/libexec/haproxy-manager/common.sh
|
|
umask 077
|
|
|
|
OUT="${1:-$TMP_CFG}"
|
|
is_safe_temp_path "$OUT" || { echo "Refusing unsafe generated config path: $OUT" >&2; exit 1; }
|
|
[ ! -d "$OUT" ] || { echo "Refusing generated config directory: $OUT" >&2; exit 1; }
|
|
WORK_OUT="$OUT.new.$$"
|
|
ENABLED="$(uci_get main enabled 0)"
|
|
HTTP_PORT="$(uci_get main http_port 80)"
|
|
HTTPS_PORT="$(uci_get main https_port 443)"
|
|
WAN_IP="$(wan_ip || true)"
|
|
|
|
if [ "$ENABLED" != 1 ]; then
|
|
echo "Managed HAProxy configuration is disabled." >&2
|
|
exit 2
|
|
fi
|
|
|
|
is_valid_port "$HTTP_PORT" || { echo "Invalid public HTTP port: $HTTP_PORT" >&2; exit 1; }
|
|
is_valid_port "$HTTPS_PORT" || { echo "Invalid public HTTPS port: $HTTPS_PORT" >&2; exit 1; }
|
|
|
|
if [ -z "$WAN_IP" ]; then
|
|
echo "Cannot detect WAN IPv4 address. Set the WAN bind address manually." >&2
|
|
exit 1
|
|
fi
|
|
is_valid_ip_or_host "$WAN_IP" || { echo "Invalid WAN bind address: $WAN_IP" >&2; exit 1; }
|
|
|
|
TMP_HTTP_RULES=/tmp/haproxy-manager-http-rules.$$
|
|
TMP_HTTP_BACKENDS=/tmp/haproxy-manager-http-backends.$$
|
|
TMP_TLS_RULES=/tmp/haproxy-manager-tls-rules.$$
|
|
TMP_TLS_BACKENDS=/tmp/haproxy-manager-tls-backends.$$
|
|
TMP_TCP_FRONTENDS=/tmp/haproxy-manager-tcp-frontends.$$
|
|
TMP_PORTS=/tmp/haproxy-manager-listen-ports.$$
|
|
: > "$TMP_HTTP_RULES"
|
|
: > "$TMP_HTTP_BACKENDS"
|
|
: > "$TMP_TLS_RULES"
|
|
: > "$TMP_TLS_BACKENDS"
|
|
: > "$TMP_TCP_FRONTENDS"
|
|
: > "$TMP_PORTS"
|
|
trap 'rm -f "$WORK_OUT" "$TMP_HTTP_RULES" "$TMP_HTTP_BACKENDS" "$TMP_TLS_RULES" "$TMP_TLS_BACKENDS" "$TMP_TCP_FRONTENDS" "$TMP_PORTS"' EXIT
|
|
|
|
HAS_HTTP=0
|
|
HAS_HTTPS=0
|
|
HAS_TCP=0
|
|
|
|
claim_port() {
|
|
local port="$1"
|
|
local owner="$2"
|
|
local existing
|
|
|
|
if grep -q "^${port}[[:space:]]" "$TMP_PORTS"; then
|
|
existing="$(sed -n "s/^${port}[[:space:]]*//p" "$TMP_PORTS" | head -1)"
|
|
echo "Public TCP port $port conflicts with ${existing:-another service}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf '%s\t%s\n' "$port" "$owner" >> "$TMP_PORTS"
|
|
}
|
|
|
|
add_http_route() {
|
|
local name="$1" host="$2" backend_host="$3" backend_port="$4"
|
|
local address
|
|
address="$(backend_address "$backend_host" "$backend_port")"
|
|
|
|
if [ "$HAS_HTTP" != 1 ]; then
|
|
claim_port "$HTTP_PORT" "the shared Web HTTP listener"
|
|
HAS_HTTP=1
|
|
fi
|
|
|
|
{
|
|
printf ' acl host_%s hdr(host),host_only -i %s\n' "$name" "$host"
|
|
printf ' use_backend be_http_%s if host_%s\n' "$name" "$name"
|
|
} >> "$TMP_HTTP_RULES"
|
|
{
|
|
printf 'backend be_http_%s\n' "$name"
|
|
printf ' mode http\n'
|
|
printf ' option httpchk GET /\n'
|
|
printf ' server srv1 %s check\n\n' "$address"
|
|
} >> "$TMP_HTTP_BACKENDS"
|
|
}
|
|
|
|
add_https_route() {
|
|
local name="$1" host="$2" backend_host="$3" backend_port="$4"
|
|
local address
|
|
address="$(backend_address "$backend_host" "$backend_port")"
|
|
|
|
if [ "$HAS_HTTPS" != 1 ]; then
|
|
claim_port "$HTTPS_PORT" "the shared Web HTTPS listener"
|
|
HAS_HTTPS=1
|
|
fi
|
|
|
|
printf ' use_backend be_tls_%s if { req.ssl_sni -i %s }\n' "$name" "$host" >> "$TMP_TLS_RULES"
|
|
{
|
|
printf 'backend be_tls_%s\n' "$name"
|
|
printf ' mode tcp\n'
|
|
printf ' server srv1 %s check\n\n' "$address"
|
|
} >> "$TMP_TLS_BACKENDS"
|
|
}
|
|
|
|
add_tcp_route() {
|
|
local name="$1" label="$2" backend_host="$3" public_port="$4" backend_port="$5"
|
|
local address bind_address
|
|
address="$(backend_address "$backend_host" "$backend_port")"
|
|
bind_address="$(backend_address "$WAN_IP" "$public_port")"
|
|
|
|
claim_port "$public_port" "service $label"
|
|
HAS_TCP=1
|
|
{
|
|
printf 'frontend fe_tcp_%s\n' "$name"
|
|
printf ' bind %s\n' "$bind_address"
|
|
printf ' mode tcp\n'
|
|
printf ' default_backend be_tcp_%s\n\n' "$name"
|
|
printf 'backend be_tcp_%s\n' "$name"
|
|
printf ' mode tcp\n'
|
|
printf ' server srv1 %s check\n\n' "$address"
|
|
} >> "$TMP_TCP_FRONTENDS"
|
|
}
|
|
|
|
for section in $(route_sections); do
|
|
enabled="$(uci_get "$section" enabled 1)"
|
|
[ "$enabled" = 1 ] || continue
|
|
|
|
kind="$(route_kind "$section")"
|
|
label="$(uci_get "$section" name)"
|
|
host="$(uci_get "$section" host)"
|
|
backend_host="$(uci_get "$section" backend_host)"
|
|
[ -n "$label" ] || label="$host"
|
|
[ -n "$label" ] || label="$kind"
|
|
name="$(route_name "${label}_${section}")"
|
|
|
|
is_valid_ip_or_host "$backend_host" || { echo "Invalid backend host for $label: $backend_host" >&2; exit 1; }
|
|
|
|
case "$kind" in
|
|
web)
|
|
is_valid_host "$host" || { echo "Invalid or missing domain for Web service $label: $host" >&2; exit 1; }
|
|
http_enabled="$(uci_get "$section" web_http 1)"
|
|
https_enabled="$(uci_get "$section" web_https 1)"
|
|
[ "$http_enabled" = 1 ] || [ "$https_enabled" = 1 ] || { echo "Web service $label has no enabled protocol." >&2; exit 1; }
|
|
|
|
if [ "$http_enabled" = 1 ]; then
|
|
backend_port="$(uci_get "$section" backend_http_port 80)"
|
|
is_valid_port "$backend_port" || { echo "Invalid HTTP backend port for $label: $backend_port" >&2; exit 1; }
|
|
add_http_route "$name" "$host" "$backend_host" "$backend_port"
|
|
fi
|
|
|
|
if [ "$https_enabled" = 1 ]; then
|
|
backend_port="$(uci_get "$section" backend_https_port 443)"
|
|
is_valid_port "$backend_port" || { echo "Invalid HTTPS backend port for $label: $backend_port" >&2; exit 1; }
|
|
add_https_route "$name" "$host" "$backend_host" "$backend_port"
|
|
fi
|
|
;;
|
|
http|https|both)
|
|
is_valid_host "$host" || { echo "Invalid or missing domain for legacy service $label: $host" >&2; exit 1; }
|
|
backend_port="$(uci_get "$section" backend_port)"
|
|
is_valid_port "$backend_port" || { echo "Invalid backend port for $label: $backend_port" >&2; exit 1; }
|
|
case "$kind" in http|both) add_http_route "$name" "$host" "$backend_host" "$backend_port" ;; esac
|
|
case "$kind" in https|both) add_https_route "$name" "$host" "$backend_host" "$backend_port" ;; esac
|
|
;;
|
|
ssh|rdp|custom|tcp)
|
|
map_index=0
|
|
maps="$(route_port_maps "$section" "$kind")"
|
|
[ -n "$maps" ] || { echo "No TCP port mappings configured for $label." >&2; exit 1; }
|
|
for map in $maps; do
|
|
is_valid_port_map "$map" || { echo "Invalid TCP port mapping for $label: $map" >&2; exit 1; }
|
|
public_port="${map%%:*}"
|
|
backend_port="${map#*:}"
|
|
map_index=$((map_index + 1))
|
|
add_tcp_route "${name}_${map_index}" "$label" "$backend_host" "$public_port" "$backend_port"
|
|
done
|
|
;;
|
|
*)
|
|
echo "Invalid service type for $label: $kind" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ "$HAS_HTTP" != 1 ] && [ "$HAS_HTTPS" != 1 ] && [ "$HAS_TCP" != 1 ]; then
|
|
echo "No enabled services define a HAProxy listener." >&2
|
|
exit 2
|
|
fi
|
|
|
|
{
|
|
printf '# Generated by luci-app-haproxy-manager. Manual changes may be overwritten.\n'
|
|
printf 'global\n'
|
|
printf ' log /dev/log local0\n'
|
|
printf ' maxconn 2048\n'
|
|
printf ' daemon\n\n'
|
|
printf 'defaults\n'
|
|
printf ' log global\n'
|
|
printf ' timeout connect 5s\n'
|
|
printf ' timeout client 60s\n'
|
|
printf ' timeout server 60s\n'
|
|
printf ' option dontlognull\n\n'
|
|
|
|
if [ "$HAS_HTTP" = 1 ]; then
|
|
http_bind="$(backend_address "$WAN_IP" "$HTTP_PORT")"
|
|
printf 'frontend fe_web_http\n'
|
|
printf ' bind %s\n' "$http_bind"
|
|
printf ' mode http\n'
|
|
printf ' option httplog\n'
|
|
cat "$TMP_HTTP_RULES"
|
|
printf ' default_backend be_not_found\n\n'
|
|
cat "$TMP_HTTP_BACKENDS"
|
|
fi
|
|
|
|
if [ "$HAS_HTTPS" = 1 ]; then
|
|
https_bind="$(backend_address "$WAN_IP" "$HTTPS_PORT")"
|
|
printf 'frontend fe_web_https\n'
|
|
printf ' bind %s\n' "$https_bind"
|
|
printf ' mode tcp\n'
|
|
printf ' tcp-request inspect-delay 5s\n'
|
|
printf ' tcp-request content accept if { req.ssl_hello_type 1 }\n'
|
|
cat "$TMP_TLS_RULES"
|
|
printf ' default_backend be_tls_not_found\n\n'
|
|
cat "$TMP_TLS_BACKENDS"
|
|
fi
|
|
|
|
cat "$TMP_TCP_FRONTENDS"
|
|
|
|
printf 'backend be_not_found\n'
|
|
printf ' mode http\n'
|
|
printf ' http-request return status 404 content-type text/plain string "No HAProxy route matched this host.\\n"\n\n'
|
|
printf 'backend be_tls_not_found\n'
|
|
printf ' mode tcp\n'
|
|
printf ' tcp-request content reject\n'
|
|
} > "$WORK_OUT"
|
|
|
|
# Replace the path itself so a stale symlink cannot redirect this root write.
|
|
rm -f "$OUT"
|
|
mv "$WORK_OUT" "$OUT"
|
|
|
|
printf '%s\n' "$OUT"
|