host-routing.sh: Complete rewrite - simplified, no VM/redsocks
Some checks failed
Test / test (push) Has been cancelled
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:
@@ -1,203 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Host routing script for rego-tunnel
|
||||
# Routes TARGET_IP through the VPN container
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
ACTION="${1:-start}"
|
||||
|
||||
APP_ENV="/etc/runtipi/app-data/runtipi/rego-tunnel/app.env"
|
||||
if [[ -f "$APP_ENV" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
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}"
|
||||
# Fixed configuration (we assigned these)
|
||||
CONTAINER_IP="172.31.0.10"
|
||||
BRIDGE_NAME="br-rego-vpn"
|
||||
TARGET_IP="${TARGET_IP:-10.35.33.230}"
|
||||
HOST_BRIDGE_ALIAS="${HOST_BRIDGE_ALIAS:-br-rego-tunnel}"
|
||||
VM_SUBNET="${VM_SUBNET:-100.100.0.0}"
|
||||
|
||||
TARGET_CIDR="$TARGET_IP/32"
|
||||
VM_SUBNET_CIDR="${VM_SUBNET_CIDR:-${VM_SUBNET}/24}"
|
||||
LOG_FILE="/var/log/rego-routing.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() {
|
||||
# Some previous setups may have installed a transparent proxy (REDSOCKS)
|
||||
# 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"
|
||||
get_lan_interface() {
|
||||
ip route show default | awk '/default/ {for(i=1;i<=NF;i++) if($i=="dev") print $(i+1)}' | head -1
|
||||
}
|
||||
|
||||
remove_stale_host_routes() {
|
||||
# The VM's internal subnet (default 100.100.0.0/24) should live only inside
|
||||
# the rego-tunnel container/VM. If the host has a route for it (left over from
|
||||
# older scripts), it can cause confusing routing.
|
||||
ip -4 route del "$VM_SUBNET_CIDR" 2>/dev/null || true
|
||||
remove_routes() {
|
||||
log "Removing stale routes for $TARGET_IP..."
|
||||
|
||||
# Remove any existing route to TARGET_IP
|
||||
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() {
|
||||
# Avoid early-exit pipelines (pipefail + SIGPIPE) by not exiting awk early.
|
||||
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)}}'
|
||||
}
|
||||
apply_routes() {
|
||||
local lan_if
|
||||
lan_if="$(get_lan_interface)"
|
||||
|
||||
choose_net_name() {
|
||||
if [[ -n "${NET_NAME:-}" ]]; then
|
||||
echo "$NET_NAME"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if docker network inspect "$NET_NAME_PREFERRED" >/dev/null 2>&1; then
|
||||
echo "$NET_NAME_PREFERRED"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "$NET_NAME_FALLBACK"
|
||||
}
|
||||
|
||||
get_bridge_and_container_ip() {
|
||||
local chosen net_id bridge_opt bridge cip
|
||||
chosen="$(choose_net_name)"
|
||||
|
||||
net_id="$(docker network inspect -f '{{.Id}}' "$chosen" 2>/dev/null || true)"
|
||||
if [[ -z "$net_id" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# If Docker network specifies an explicit bridge name, use it.
|
||||
bridge_opt="$(docker network inspect -f '{{index .Options "com.docker.network.bridge.name"}}' "$chosen" 2>/dev/null || true)"
|
||||
if [[ -n "$bridge_opt" && "$bridge_opt" != "<no value>" ]]; then
|
||||
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"
|
||||
log "Applying host routing rules..."
|
||||
log " Container IP: $CONTAINER_IP"
|
||||
log " Bridge: $BRIDGE_NAME"
|
||||
log " Target IP: $TARGET_IP"
|
||||
log " LAN interface: ${lan_if:-unknown}"
|
||||
|
||||
# Enable IP forwarding
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
log "IP forwarding enabled"
|
||||
|
||||
ip route replace "$TARGET_CIDR" via "$cip" dev "$bridge"
|
||||
# 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"
|
||||
|
||||
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
|
||||
# Allow forwarding in DOCKER-USER chain (if LAN interface detected)
|
||||
if [[ -n "$lan_if" ]]; then
|
||||
# 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
|
||||
|
||||
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
|
||||
# Allow return traffic
|
||||
iptables -C DOCKER-USER -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -I DOCKER-USER 1 -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
log "DOCKER-USER iptables rules added for $lan_if <-> $BRIDGE_NAME"
|
||||
else
|
||||
log "WARN: Could not detect LAN interface, skipping DOCKER-USER rules"
|
||||
fi
|
||||
|
||||
container_apply
|
||||
|
||||
log "OK: routing $TARGET_CIDR via $cip ($bridge)"
|
||||
log "OK: Host routing applied - $TARGET_IP via $CONTAINER_IP ($BRIDGE_NAME)"
|
||||
}
|
||||
|
||||
remove_host_rules() {
|
||||
local lan_if bridge cip
|
||||
remove_all() {
|
||||
local lan_if
|
||||
lan_if="$(get_lan_interface)"
|
||||
|
||||
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
|
||||
log "Removing all routing rules..."
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
container_remove
|
||||
log "All routing rules removed"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
start)
|
||||
# Wait briefly for docker + container/network to be ready.
|
||||
for _ in {1..30}; do
|
||||
if get_bridge_and_container_ip >/dev/null 2>&1; then
|
||||
apply_host_rules
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
log "ERROR: container/network not ready; no routing applied"
|
||||
exit 0
|
||||
remove_routes
|
||||
apply_routes
|
||||
;;
|
||||
stop)
|
||||
remove_host_rules
|
||||
remove_all
|
||||
;;
|
||||
restart)
|
||||
remove_all
|
||||
sleep 1
|
||||
remove_routes
|
||||
apply_routes
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop}" >&2
|
||||
echo "Usage: $0 {start|stop|restart}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user