From 6fd57b0ce29e328eb1fabeaf948112fc73d16bbd Mon Sep 17 00:00:00 2001 From: Alex Zaw Date: Mon, 29 Dec 2025 06:59:52 +0000 Subject: [PATCH] feat(rego-tunnel): optional shared network via NIC2 --- apps/rego-tunnel/build/setup-network.sh | 50 +++++++++++++++++++---- apps/rego-tunnel/config.json | 54 +++++++++++++++++++++++++ apps/rego-tunnel/docker-compose.json | 24 +++++++++++ apps/rego-tunnel/docker-compose.yml | 6 +++ 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/apps/rego-tunnel/build/setup-network.sh b/apps/rego-tunnel/build/setup-network.sh index 1aa8b12..8a35fc9 100755 --- a/apps/rego-tunnel/build/setup-network.sh +++ b/apps/rego-tunnel/build/setup-network.sh @@ -21,6 +21,7 @@ TARGET_IP="${TARGET_IP:-10.35.33.230}" BRIDGE2_NAME="${BRIDGE2_NAME:-}" TAP2_NAME="${TAP2_NAME:-}" BRIDGE2_CIDR="${BRIDGE2_CIDR:-}" +BRIDGE2_UPLINK_IF="${BRIDGE2_UPLINK_IF:-}" if [[ "$BRIDGE_CIDR" != */* ]]; then BRIDGE_CIDR="$BRIDGE_CIDR/24" @@ -60,23 +61,58 @@ if [ -n "$BRIDGE2_NAME" ] || [ -n "$TAP2_NAME" ]; then if [ -z "$BRIDGE2_NAME" ] || [ -z "$TAP2_NAME" ]; then echo "[rego-tunnel] WARN: BRIDGE2_NAME and TAP2_NAME must both be set to enable the second bridge" else + # Optionally bridge an existing container uplink into BRIDGE2 (e.g. eth0/eth1) + # so the VM NIC shares the same L2 network as that uplink. + if [ -n "$BRIDGE2_UPLINK_IF" ]; then + if ! ip link show "$BRIDGE2_UPLINK_IF" &>/dev/null; then + echo "[rego-tunnel] WARN: BRIDGE2_UPLINK_IF=$BRIDGE2_UPLINK_IF not found; skipping uplink bridging" + BRIDGE2_UPLINK_IF="" + fi + fi + if ! ip link show "$BRIDGE2_NAME" &>/dev/null; then ip link add "$BRIDGE2_NAME" type bridge fi - if [ -n "$BRIDGE2_CIDR" ]; then - if [[ "$BRIDGE2_CIDR" != */* ]]; then - BRIDGE2_CIDR="$BRIDGE2_CIDR/24" - fi - ip addr show dev "$BRIDGE2_NAME" | grep -qF "$BRIDGE2_CIDR" || ip addr add "$BRIDGE2_CIDR" dev "$BRIDGE2_NAME" 2>/dev/null || true - fi ip link set "$BRIDGE2_NAME" up + # If an uplink interface is provided, move its IPv4 address to the bridge. + # This keeps the container reachable on that network while allowing the VM + # to participate in the same L2 broadcast domain. + if [ -n "$BRIDGE2_UPLINK_IF" ]; then + UPLINK_CIDR="$(ip -o -4 addr show dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{print $4}' | head -n1)" + UPLINK_DEFAULT_GW="$(ip route show default dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{print $3}' | head -n1)" + UPLINK_DEFAULT_METRIC="$(ip route show default dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="metric"){print $(i+1); exit}}')" + if [ -z "$BRIDGE2_CIDR" ] && [ -n "$UPLINK_CIDR" ]; then + BRIDGE2_CIDR="$UPLINK_CIDR" + fi + if [ -n "$BRIDGE2_CIDR" ]; then + if [[ "$BRIDGE2_CIDR" != */* ]]; then + BRIDGE2_CIDR="$BRIDGE2_CIDR/24" + fi + ip addr show dev "$BRIDGE2_NAME" | grep -qF "$BRIDGE2_CIDR" || ip addr add "$BRIDGE2_CIDR" dev "$BRIDGE2_NAME" 2>/dev/null || true + fi + # Remove IP from uplink and enslave it into the bridge + ip addr flush dev "$BRIDGE2_UPLINK_IF" 2>/dev/null || true + ip link set "$BRIDGE2_UPLINK_IF" master "$BRIDGE2_NAME" 2>/dev/null || true + ip link set "$BRIDGE2_UPLINK_IF" up 2>/dev/null || true + + # If the uplink carried the container default route, it may have been removed + # by the address flush. Restore it on the bridge. + if [ -n "$UPLINK_DEFAULT_GW" ]; then + if ip route show default 2>/dev/null | grep -q '^default '; then + ip route replace default via "$UPLINK_DEFAULT_GW" dev "$BRIDGE2_NAME" ${UPLINK_DEFAULT_METRIC:+metric "$UPLINK_DEFAULT_METRIC"} 2>/dev/null || true + else + ip route add default via "$UPLINK_DEFAULT_GW" dev "$BRIDGE2_NAME" ${UPLINK_DEFAULT_METRIC:+metric "$UPLINK_DEFAULT_METRIC"} 2>/dev/null || true + fi + fi + fi + if ! ip link show "$TAP2_NAME" &>/dev/null; then ip tuntap add "$TAP2_NAME" mode tap fi ip link set "$TAP2_NAME" master "$BRIDGE2_NAME" 2>/dev/null || true ip link set "$TAP2_NAME" up - echo "[rego-tunnel] Second bridge enabled: $BRIDGE2_NAME (tap $TAP2_NAME)" + echo "[rego-tunnel] Second bridge enabled: $BRIDGE2_NAME (tap $TAP2_NAME)${BRIDGE2_UPLINK_IF:+ uplink $BRIDGE2_UPLINK_IF}" fi fi diff --git a/apps/rego-tunnel/config.json b/apps/rego-tunnel/config.json index a3e5a7d..c56702e 100755 --- a/apps/rego-tunnel/config.json +++ b/apps/rego-tunnel/config.json @@ -76,6 +76,60 @@ "env_variable": "BRIDGE_CIDR", "default": "100.100.0.1" }, + { + "type": "text", + "label": "QEMU binary", + "hint": "Override the QEMU system binary (e.g. /usr/bin/qemu-system-amd64). Leave empty for default.", + "placeholder": "/usr/bin/qemu-system-amd64", + "required": false, + "env_variable": "QEMU_BIN", + "default": "" + }, + { + "type": "text", + "label": "TAP2 name (shared network)", + "hint": "Optional second TAP interface for a second VM NIC. Set to enable NIC2 (e.g. tap1).", + "placeholder": "tap1", + "required": false, + "env_variable": "TAP2_NAME", + "default": "" + }, + { + "type": "text", + "label": "Bridge2 name (shared network)", + "hint": "Optional second Linux bridge for NIC2 (e.g. br-app).", + "placeholder": "br-app", + "required": false, + "env_variable": "BRIDGE2_NAME", + "default": "" + }, + { + "type": "text", + "label": "Bridge2 uplink interface", + "hint": "If set, bridge NIC2 onto this container interface (e.g. eth0 for 10.x app network, eth1 for 172.x main network).", + "placeholder": "eth0", + "required": false, + "env_variable": "BRIDGE2_UPLINK_IF", + "default": "" + }, + { + "type": "text", + "label": "Bridge2 CIDR (optional)", + "hint": "Optional IPv4/CIDR for Bridge2. If empty and uplink is set, the bridge inherits the uplink IPv4 address.", + "placeholder": "10.128.14.2/24", + "required": false, + "env_variable": "BRIDGE2_CIDR", + "default": "" + }, + { + "type": "text", + "label": "VM MAC2 (shared network)", + "hint": "Optional MAC for NIC2 (e.g. 52:54:00:12:34:57).", + "placeholder": "52:54:00:12:34:57", + "required": false, + "env_variable": "VM_MAC2", + "default": "" + }, { "type": "ip", "label": "VM subnet", diff --git a/apps/rego-tunnel/docker-compose.json b/apps/rego-tunnel/docker-compose.json index fc44962..7947d30 100755 --- a/apps/rego-tunnel/docker-compose.json +++ b/apps/rego-tunnel/docker-compose.json @@ -21,6 +21,10 @@ "key": "TAP_NAME", "value": "${TAP_NAME}" }, + { + "key": "TAP2_NAME", + "value": "${TAP2_NAME}" + }, { "key": "BRIDGE_CIDR", "value": "${BRIDGE_CIDR}" @@ -41,6 +45,10 @@ "key": "VM_MAC", "value": "${VM_MAC}" }, + { + "key": "VM_MAC2", + "value": "${VM_MAC2}" + }, { "key": "DNS_SERVERS", "value": "${DNS_SERVERS}" @@ -52,6 +60,22 @@ { "key": "HOSTSHARE_DIR", "value": "${HOSTSHARE_DIR}" + }, + { + "key": "BRIDGE2_NAME", + "value": "${BRIDGE2_NAME}" + }, + { + "key": "BRIDGE2_CIDR", + "value": "${BRIDGE2_CIDR}" + }, + { + "key": "BRIDGE2_UPLINK_IF", + "value": "${BRIDGE2_UPLINK_IF}" + }, + { + "key": "QEMU_BIN", + "value": "${QEMU_BIN}" } ], "internalPort": 8006, diff --git a/apps/rego-tunnel/docker-compose.yml b/apps/rego-tunnel/docker-compose.yml index 0a437fa..50e5eab 100755 --- a/apps/rego-tunnel/docker-compose.yml +++ b/apps/rego-tunnel/docker-compose.yml @@ -12,14 +12,20 @@ services: VM_CPUS: ${WINDOWS_CPU_CORES} BRIDGE_NAME: ${BRIDGE_NAME} TAP_NAME: ${TAP_NAME} + TAP2_NAME: ${TAP2_NAME} BRIDGE_CIDR: ${BRIDGE_CIDR} VM_NET_IP: ${VM_NET_IP} VM_SUBNET: ${VM_SUBNET} TARGET_IP: ${TARGET_IP} VM_MAC: ${VM_MAC} + VM_MAC2: ${VM_MAC2} DNS_SERVERS: ${DNS_SERVERS} LEASE_TIME: ${LEASE_TIME} HOSTSHARE_DIR: ${HOSTSHARE_DIR} + BRIDGE2_NAME: ${BRIDGE2_NAME} + BRIDGE2_CIDR: ${BRIDGE2_CIDR} + BRIDGE2_UPLINK_IF: ${BRIDGE2_UPLINK_IF} + QEMU_BIN: ${QEMU_BIN} ports: - ${APP_PORT}:8006 volumes: