host-routing.sh: Complete rewrite - simplified, no VM/redsocks
Some checks failed
Test / test (push) Has been cancelled

- Hardcoded container IP (172.31.0.10) and bridge (br-rego-vpn)
- Simple start/stop/restart actions
- Removes stale routes before applying new ones
- Logs to /var/log/rego-routing.log
- Removed: redsocks, nft, VM subnet, container_apply
This commit is contained in:
2026-01-17 02:59:34 +00:00
parent 5e0004c0d8
commit 89e8f5cffc

View File

@@ -1,203 +1,107 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# Host routing script for rego-tunnel
# Routes TARGET_IP through the VPN container
#
set -euo pipefail set -euo pipefail
ACTION="${1:-start}" ACTION="${1:-start}"
APP_ENV="/etc/runtipi/app-data/runtipi/rego-tunnel/app.env" # Fixed configuration (we assigned these)
if [[ -f "$APP_ENV" ]]; then CONTAINER_IP="172.31.0.10"
# shellcheck disable=SC1090 BRIDGE_NAME="br-rego-vpn"
source "$APP_ENV"
fi
CONTAINER_NAME="${CONTAINER_NAME:-rego-tunnel_runtipi-rego-tunnel-1}"
# Prefer the user-config network (stable host bridge name) if it exists.
NET_NAME="${NET_NAME:-}"
NET_NAME_FALLBACK="${NET_NAME_FALLBACK:-rego-tunnel_runtipi_network}"
NET_NAME_PREFERRED="${NET_NAME_PREFERRED:-rego-tunnel_runtipi_vpn_static}"
TARGET_IP="${TARGET_IP:-10.35.33.230}" TARGET_IP="${TARGET_IP:-10.35.33.230}"
HOST_BRIDGE_ALIAS="${HOST_BRIDGE_ALIAS:-br-rego-tunnel}" LOG_FILE="/var/log/rego-routing.log"
VM_SUBNET="${VM_SUBNET:-100.100.0.0}"
TARGET_CIDR="$TARGET_IP/32"
VM_SUBNET_CIDR="${VM_SUBNET_CIDR:-${VM_SUBNET}/24}"
log() { log() {
echo "[rego-routing] $*" >&2 local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [rego-routing] $*"
echo "$msg" | tee -a "$LOG_FILE" >&2
} }
remove_stale_nft_redirects() { get_lan_interface() {
# Some previous setups may have installed a transparent proxy (REDSOCKS) ip route show default | awk '/default/ {for(i=1;i<=NF;i++) if($i=="dev") print $(i+1)}' | head -1
# redirecting TCP destined for TARGET_IP to a local port (e.g. 12345).
# That breaks the "single target IP via container" gateway model.
#
# We only remove rules that match our TARGET_IP in the ip nat/REDSOCKS chain.
if ! command -v nft >/dev/null 2>&1; then
return 0
fi
local handles
handles="$(nft -a list chain ip nat REDSOCKS 2>/dev/null | awk -v ip="$TARGET_IP" '/ip daddr/ && $0 ~ ip && /redirect to :/ {for(i=1;i<=NF;i++) if($i=="handle") print $(i+1)}' || true)"
if [[ -z "$handles" ]]; then
return 0
fi
while read -r h; do
[[ -n "$h" ]] || continue
nft delete rule ip nat REDSOCKS handle "$h" 2>/dev/null || true
done <<< "$handles"
} }
remove_stale_host_routes() { remove_routes() {
# The VM's internal subnet (default 100.100.0.0/24) should live only inside log "Removing stale routes for $TARGET_IP..."
# the rego-tunnel container/VM. If the host has a route for it (left over from
# older scripts), it can cause confusing routing. # Remove any existing route to TARGET_IP
ip -4 route del "$VM_SUBNET_CIDR" 2>/dev/null || true ip route del "$TARGET_IP" 2>/dev/null || true
ip route del "$TARGET_IP/32" 2>/dev/null || true
log "Stale routes removed"
} }
get_lan_if() { apply_routes() {
# Avoid early-exit pipelines (pipefail + SIGPIPE) by not exiting awk early. local lan_if
ip route show default 0.0.0.0/0 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1)}}' lan_if="$(get_lan_interface)"
}
choose_net_name() { log "Applying host routing rules..."
if [[ -n "${NET_NAME:-}" ]]; then log " Container IP: $CONTAINER_IP"
echo "$NET_NAME" log " Bridge: $BRIDGE_NAME"
return 0 log " Target IP: $TARGET_IP"
fi log " LAN interface: ${lan_if:-unknown}"
if docker network inspect "$NET_NAME_PREFERRED" >/dev/null 2>&1; then # Enable IP forwarding
echo "$NET_NAME_PREFERRED" echo 1 > /proc/sys/net/ipv4/ip_forward
return 0 log "IP forwarding enabled"
fi
echo "$NET_NAME_FALLBACK" # Add route to TARGET_IP via container
} ip route replace "$TARGET_IP/32" via "$CONTAINER_IP" dev "$BRIDGE_NAME"
log "Route added: $TARGET_IP via $CONTAINER_IP dev $BRIDGE_NAME"
get_bridge_and_container_ip() { # Allow forwarding in DOCKER-USER chain (if LAN interface detected)
local chosen net_id bridge_opt bridge cip if [[ -n "$lan_if" ]]; then
chosen="$(choose_net_name)" # Allow traffic from LAN to container for TARGET_IP
iptables -C DOCKER-USER -i "$lan_if" -o "$BRIDGE_NAME" -d "$TARGET_IP" -j ACCEPT 2>/dev/null || \
iptables -I DOCKER-USER 1 -i "$lan_if" -o "$BRIDGE_NAME" -d "$TARGET_IP" -j ACCEPT
net_id="$(docker network inspect -f '{{.Id}}' "$chosen" 2>/dev/null || true)" # Allow return traffic
if [[ -z "$net_id" ]]; then iptables -C DOCKER-USER -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
return 1 iptables -I DOCKER-USER 1 -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
fi
# If Docker network specifies an explicit bridge name, use it. log "DOCKER-USER iptables rules added for $lan_if <-> $BRIDGE_NAME"
bridge_opt="$(docker network inspect -f '{{index .Options "com.docker.network.bridge.name"}}' "$chosen" 2>/dev/null || true)" else
if [[ -n "$bridge_opt" && "$bridge_opt" != "<no value>" ]]; then log "WARN: Could not detect LAN interface, skipping DOCKER-USER rules"
bridge="$bridge_opt"
else
bridge="br-${net_id:0:12}"
fi
cip="$(docker inspect -f "{{(index .NetworkSettings.Networks \"$chosen\").IPAddress}}" "$CONTAINER_NAME" 2>/dev/null || true)"
if [[ -z "$cip" ]]; then
return 1
fi
echo "$bridge $cip"
}
set_bridge_alias() {
local bridge="$1"
# Docker creates the bridge name (br-<network id>); renaming it breaks Docker.
# Setting an alias is safe and improves readability in `ip link` output.
if [[ -n "${HOST_BRIDGE_ALIAS:-}" ]]; then
ip link set dev "$bridge" alias "$HOST_BRIDGE_ALIAS" 2>/dev/null || true
fi
}
container_apply() {
docker exec "$CONTAINER_NAME" sh -lc 'set -e
BR="${BRIDGE_NAME:-br-rego-vpn}"
VM="${VM_NET_IP:-100.100.0.2}"
TARGET="${TARGET_IP:-10.35.33.230}"
echo 1 > /proc/sys/net/ipv4/ip_forward
ip route replace "$TARGET/32" via "$VM" dev "$BR" 2>/dev/null || ip route replace "$TARGET" via "$VM" dev "$BR" 2>/dev/null || true
iptables -C FORWARD -d "$TARGET" -j ACCEPT 2>/dev/null || iptables -A FORWARD -d "$TARGET" -j ACCEPT
iptables -C FORWARD -s "$TARGET" -j ACCEPT 2>/dev/null || iptables -A FORWARD -s "$TARGET" -j ACCEPT
iptables -t nat -C POSTROUTING -o "$BR" -d "$TARGET" -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -o "$BR" -d "$TARGET" -j MASQUERADE
'
}
container_remove() {
docker exec "$CONTAINER_NAME" sh -lc 'set -e
BR="${BRIDGE_NAME:-br-rego-vpn}"
VM="${VM_NET_IP:-100.100.0.2}"
TARGET="${TARGET_IP:-10.35.33.230}"
ip route del "$TARGET/32" via "$VM" dev "$BR" 2>/dev/null || true
iptables -t nat -D POSTROUTING -o "$BR" -d "$TARGET" -j MASQUERADE 2>/dev/null || true
' || true
}
apply_host_rules() {
local lan_if bridge cip
remove_stale_nft_redirects
remove_stale_host_routes
lan_if="$(get_lan_if)"
if [[ -z "$lan_if" ]]; then
log "WARN: could not detect LAN default interface; skipping host iptables"
fi
read -r bridge cip < <(get_bridge_and_container_ip)
set_bridge_alias "$bridge"
echo 1 > /proc/sys/net/ipv4/ip_forward
ip route replace "$TARGET_CIDR" via "$cip" dev "$bridge"
if [[ -n "${lan_if:-}" ]]; then
iptables -C DOCKER-USER -i "$lan_if" -o "$bridge" -d "$TARGET_CIDR" -j ACCEPT 2>/dev/null || \
iptables -I DOCKER-USER 1 -i "$lan_if" -o "$bridge" -d "$TARGET_CIDR" -j ACCEPT
iptables -C DOCKER-USER -i "$bridge" -o "$lan_if" -s "$TARGET_CIDR" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
iptables -I DOCKER-USER 1 -i "$bridge" -o "$lan_if" -s "$TARGET_CIDR" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
fi
container_apply
log "OK: routing $TARGET_CIDR via $cip ($bridge)"
}
remove_host_rules() {
local lan_if bridge cip
lan_if="$(get_lan_if)"
if read -r bridge cip < <(get_bridge_and_container_ip); then
ip route del "$TARGET_CIDR" via "$cip" dev "$bridge" 2>/dev/null || true
if [[ -n "${lan_if:-}" ]]; then
iptables -D DOCKER-USER -i "$lan_if" -o "$bridge" -d "$TARGET_CIDR" -j ACCEPT 2>/dev/null || true
iptables -D DOCKER-USER -i "$bridge" -o "$lan_if" -s "$TARGET_CIDR" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
fi fi
fi
container_remove log "OK: Host routing applied - $TARGET_IP via $CONTAINER_IP ($BRIDGE_NAME)"
}
remove_all() {
local lan_if
lan_if="$(get_lan_interface)"
log "Removing all routing rules..."
# Remove route
ip route del "$TARGET_IP/32" via "$CONTAINER_IP" dev "$BRIDGE_NAME" 2>/dev/null || true
# Remove iptables rules
if [[ -n "$lan_if" ]]; then
iptables -D DOCKER-USER -i "$lan_if" -o "$BRIDGE_NAME" -d "$TARGET_IP" -j ACCEPT 2>/dev/null || true
iptables -D DOCKER-USER -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
fi
log "All routing rules removed"
} }
case "$ACTION" in case "$ACTION" in
start) start)
# Wait briefly for docker + container/network to be ready. remove_routes
for _ in {1..30}; do apply_routes
if get_bridge_and_container_ip >/dev/null 2>&1; then ;;
apply_host_rules stop)
exit 0 remove_all
fi ;;
sleep 2 restart)
done remove_all
log "ERROR: container/network not ready; no routing applied" sleep 1
exit 0 remove_routes
;; apply_routes
stop) ;;
remove_host_rules *)
;; echo "Usage: $0 {start|stop|restart}" >&2
*) exit 2
echo "Usage: $0 {start|stop}" >&2 ;;
exit 2
;;
esac esac