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
|
#!/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
|
||||||
|
|||||||
Reference in New Issue
Block a user