new image structure for cisco-vpn and related scripts
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
817
apps/rego-tunnel/shared/cisco-vpn
Normal file
817
apps/rego-tunnel/shared/cisco-vpn
Normal file
@@ -0,0 +1,817 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Dover VPN Connection Script with Semi-Automation
|
||||
# Usage: ./cisco-vpn.sh [-c|--connect] [-d|--disconnect] [-m|--menu] [-r|--routes] [-h|--hosts] [--help]
|
||||
#
|
||||
# Options:
|
||||
# -c, --connect Start Cisco AnyConnect (optionally auto-login) and exit
|
||||
# -d, --disconnect Disconnect (kill Cisco processes) and exit
|
||||
# -m, --menu Skip auto-login, show menu directly
|
||||
# -r, --routes Show current routing table and exit
|
||||
# -h, --hosts Show /etc/hosts and exit
|
||||
# -s, --status Show VPN and network status and exit
|
||||
# --help Show this help message
|
||||
#
|
||||
# Keyboard shortcuts (global, work anywhere):
|
||||
# Ctrl+1 - Type email
|
||||
# Ctrl+2 - Type password
|
||||
# Ctrl+3 - Type TOTP code
|
||||
# Ctrl+4 - Type email + Tab + password (combo)
|
||||
# Ctrl+5 - Full sequence: email + Tab + password + Tab + TOTP + Enter
|
||||
|
||||
EMAIL="c-azaw@regoproducts.com"
|
||||
PASSWORD='Lz@83278327$$@@'
|
||||
TOTP_SECRET="rzqtqskdwkhz6zyr"
|
||||
VPN_HOST="vpn-ord1.dovercorp.com"
|
||||
TARGET_IP="10.35.33.230"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
GRAY='\033[0;90m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Flags
|
||||
SKIP_AUTO_LOGIN=false
|
||||
DO_CONNECT=false
|
||||
DO_DISCONNECT=false
|
||||
|
||||
# Logging function with timestamp
|
||||
log() {
|
||||
local level="$1"
|
||||
local msg="$2"
|
||||
local timestamp=$(date '+%H:%M:%S')
|
||||
case $level in
|
||||
INFO) echo -e "${GRAY}[$timestamp]${NC} ${GREEN}[INFO]${NC} $msg" ;;
|
||||
WARN) echo -e "${GRAY}[$timestamp]${NC} ${YELLOW}[WARN]${NC} $msg" ;;
|
||||
ERROR) echo -e "${GRAY}[$timestamp]${NC} ${RED}[ERROR]${NC} $msg" ;;
|
||||
DEBUG) echo -e "${GRAY}[$timestamp]${NC} ${CYAN}[DEBUG]${NC} $msg" ;;
|
||||
CMD) echo -e "${GRAY}[$timestamp]${NC} ${GRAY}[CMD]${NC} $msg" ;;
|
||||
*) echo -e "${GRAY}[$timestamp]${NC} $msg" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run command with logging
|
||||
run_cmd() {
|
||||
local desc="$1"
|
||||
shift
|
||||
log CMD "$desc: $*"
|
||||
output=$("$@" 2>&1)
|
||||
local rc=$?
|
||||
if [ -n "$output" ]; then
|
||||
echo "$output" | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
fi
|
||||
return $rc
|
||||
}
|
||||
|
||||
# Show help
|
||||
show_help() {
|
||||
echo -e "${CYAN}Dover VPN Connection Script${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -c, --connect Start Cisco AnyConnect and exit"
|
||||
echo " -d, --disconnect Disconnect (kill Cisco processes) and exit"
|
||||
echo " -m, --menu Skip auto-login, show menu directly"
|
||||
echo " -r, --routes Show current routing table and exit"
|
||||
echo " -h, --hosts Show /etc/hosts and exit"
|
||||
echo " -s, --status Show VPN and network status and exit"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Menu Options:"
|
||||
echo " 1 - Start Cisco AnyConnect (kill existing + launch)"
|
||||
echo " 2 - Copy credentials to clipboard (one by one)"
|
||||
echo " 3 - Show live TOTP"
|
||||
echo " 4 - Setup IP forwarding rules only"
|
||||
echo " 5 - Test connection to target"
|
||||
echo " 6 - Show network status"
|
||||
echo " 7 - Kill all Cisco processes"
|
||||
echo " 8 - Show routing table"
|
||||
echo " 9 - Show /etc/hosts"
|
||||
echo " e - Edit /etc/hosts"
|
||||
echo " q - Quit"
|
||||
}
|
||||
|
||||
# Show routing table
|
||||
show_routes() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Current Routing Table ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}IPv4 Routes:${NC}"
|
||||
ip -4 route show | while IFS= read -r line; do
|
||||
# Highlight VPN-related routes
|
||||
if echo "$line" | grep -qE "cscotun|tun[0-9]"; then
|
||||
echo -e " ${YELLOW}$line${NC}"
|
||||
elif echo "$line" | grep -qE "10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\."; then
|
||||
echo -e " ${CYAN}$line${NC}"
|
||||
else
|
||||
echo " $line"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo -e "${GREEN}VPN Interface:${NC}"
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
ip addr show "$vpn_iface" 2>/dev/null | grep -E "inet|link" | while IFS= read -r line; do
|
||||
echo " $line"
|
||||
done
|
||||
else
|
||||
echo -e " ${RED}No VPN interface found${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show /etc/hosts
|
||||
show_hosts() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} /etc/hosts ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
cat -n /etc/hosts
|
||||
}
|
||||
|
||||
# Edit /etc/hosts
|
||||
edit_hosts() {
|
||||
local editor="${EDITOR:-nano}"
|
||||
if command -v "$editor" &>/dev/null; then
|
||||
sudo "$editor" /etc/hosts
|
||||
else
|
||||
log ERROR "No editor found. Set EDITOR environment variable."
|
||||
log INFO "Try: sudo nano /etc/hosts"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get current TOTP
|
||||
get_totp() {
|
||||
oathtool --totp -b "$TOTP_SECRET"
|
||||
}
|
||||
|
||||
# Function to detect VPN tunnel interface dynamically
|
||||
get_vpn_interface() {
|
||||
# Look for cscotun* or tun* interfaces that are UP
|
||||
local iface=$(ip link show | grep -oP '(cscotun\d+|tun\d+)(?=:.*UP)' | head -1)
|
||||
if [ -z "$iface" ]; then
|
||||
# Fallback: any cscotun interface
|
||||
iface=$(ip link show | grep -oP 'cscotun\d+' | head -1)
|
||||
fi
|
||||
echo "$iface"
|
||||
}
|
||||
|
||||
# Function to get VM's IP on the bridge network (for container routing)
|
||||
get_vm_bridge_ip() {
|
||||
# Get IP from ens3 (main adapter with 100.100.0.x)
|
||||
ip addr show ens3 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1
|
||||
}
|
||||
|
||||
# Function to get container gateway IP
|
||||
get_container_gateway() {
|
||||
# The container bridge is at 100.100.0.1
|
||||
echo "100.100.0.1"
|
||||
}
|
||||
|
||||
# Function to get VPN tunnel IP
|
||||
get_vpn_ip() {
|
||||
local iface=$(get_vpn_interface)
|
||||
if [ -n "$iface" ]; then
|
||||
ip addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1
|
||||
fi
|
||||
}
|
||||
|
||||
# Start xbindkeys for keyboard macros
|
||||
start_xbindkeys() {
|
||||
log INFO "Starting keyboard macro listener (xbindkeys)..."
|
||||
|
||||
# Kill any existing xbindkeys
|
||||
pkill xbindkeys 2>/dev/null
|
||||
sleep 0.5
|
||||
|
||||
# Start xbindkeys
|
||||
xbindkeys -f ~/.xbindkeysrc 2>/dev/null &
|
||||
XBINDKEYS_PID=$!
|
||||
|
||||
if pgrep xbindkeys >/dev/null; then
|
||||
log DEBUG "xbindkeys started (PID: $(pgrep xbindkeys))"
|
||||
log INFO "Keyboard shortcuts active: Ctrl+1=email, Ctrl+2=pass, Ctrl+3=TOTP, Ctrl+4=combo, Ctrl+5=all"
|
||||
else
|
||||
log WARN "Failed to start xbindkeys"
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop xbindkeys
|
||||
stop_xbindkeys() {
|
||||
if pgrep xbindkeys >/dev/null; then
|
||||
log INFO "Stopping keyboard macro listener..."
|
||||
pkill xbindkeys 2>/dev/null
|
||||
log DEBUG "xbindkeys stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Kill all Cisco-related processes
|
||||
kill_cisco_processes() {
|
||||
local kill_agentd="${1:-false}"
|
||||
log INFO "Killing all Cisco-related processes..."
|
||||
|
||||
local killed=0
|
||||
local my_pid=$$
|
||||
local my_ppid=$(ps -o ppid= -p $$ | tr -d ' ')
|
||||
|
||||
# Kill vpnui specifically (not just any process with "vpn" in name)
|
||||
for pid in $(pgrep -x "vpnui" 2>/dev/null); do
|
||||
if [ "$pid" != "$my_pid" ] && [ "$pid" != "$my_ppid" ]; then
|
||||
log DEBUG "Killing vpnui (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Optionally kill vpnagentd (useful to clear stale state)
|
||||
if [ "$kill_agentd" = "true" ]; then
|
||||
for pid in $(pgrep -x "vpnagentd" 2>/dev/null); do
|
||||
if [ "$pid" != "$my_pid" ] && [ "$pid" != "$my_ppid" ]; then
|
||||
log DEBUG "Killing vpnagentd (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Kill Cisco-specific processes by exact path
|
||||
for proc in cstub cscan acwebsecagent vpndownloader; do
|
||||
for pid in $(pgrep -x "$proc" 2>/dev/null); do
|
||||
log DEBUG "Killing $proc (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
done
|
||||
|
||||
# Kill openconnect (exact match)
|
||||
for pid in $(pgrep -x "openconnect" 2>/dev/null); do
|
||||
log DEBUG "Killing openconnect (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
|
||||
if [ $killed -eq 0 ]; then
|
||||
log INFO "No Cisco processes were running"
|
||||
else
|
||||
log INFO "Killed $killed process(es)"
|
||||
sleep 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Disconnect VPN (best-effort)
|
||||
disconnect_vpn() {
|
||||
log INFO "Disconnecting Cisco AnyConnect..."
|
||||
|
||||
# If vpncli exists, attempt a clean disconnect first (ignore failures)
|
||||
if [ -x /opt/cisco/secureclient/bin/vpncli ]; then
|
||||
run_cmd "Attempting clean disconnect via vpncli" sudo /opt/cisco/secureclient/bin/vpncli -s <<'EOF' || true
|
||||
disconnect
|
||||
exit
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Always kill agent/UI processes afterwards
|
||||
kill_cisco_processes "true"
|
||||
|
||||
# Confirm status
|
||||
if check_vpn_status; then
|
||||
log WARN "VPN still appears connected (interface still up)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log INFO "VPN disconnected"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to setup iptables rules for forwarding
|
||||
setup_forwarding() {
|
||||
log INFO "Setting up IP forwarding rules for $TARGET_IP..."
|
||||
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -z "$vpn_iface" ]; then
|
||||
log ERROR "No VPN interface found! Is VPN connected?"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
local vm_bridge_ip=$(get_vm_bridge_ip)
|
||||
local container_gw=$(get_container_gateway)
|
||||
|
||||
log DEBUG "VPN interface: $vpn_iface"
|
||||
log DEBUG "VPN IP: $vpn_ip"
|
||||
log DEBUG "VM bridge IP: $vm_bridge_ip"
|
||||
log DEBUG "Container gateway: $container_gw"
|
||||
|
||||
# Enable IP forwarding
|
||||
run_cmd "Enabling IP forwarding" sudo sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# NAT masquerade for traffic from container network (100.100.0.0/24) going through VPN
|
||||
# This is the ONLY masquerade rule needed - source-based, not destination-based
|
||||
if ! sudo iptables -t nat -C POSTROUTING -s 100.100.0.0/24 -o "$vpn_iface" -j MASQUERADE 2>/dev/null; then
|
||||
run_cmd "Adding NAT masquerade for container network -> VPN" sudo iptables -t nat -A POSTROUTING -s 100.100.0.0/24 -o "$vpn_iface" -j MASQUERADE
|
||||
else
|
||||
log DEBUG "NAT masquerade for container network already exists"
|
||||
fi
|
||||
|
||||
# Forward rules
|
||||
if ! sudo iptables -C FORWARD -d "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (to target)" sudo iptables -A FORWARD -d "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (to target) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C FORWARD -s "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (from target)" sudo iptables -A FORWARD -s "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (from target) already exists"
|
||||
fi
|
||||
|
||||
# Accept forwarding from container network
|
||||
if ! sudo iptables -C FORWARD -s 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (from container network)" sudo iptables -A FORWARD -s 100.100.0.0/24 -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (from container network) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C FORWARD -d 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (to container network)" sudo iptables -A FORWARD -d 100.100.0.0/24 -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (to container network) already exists"
|
||||
fi
|
||||
|
||||
# Cisco VPN chain bypass (insert at top if chain exists)
|
||||
if sudo iptables -L ciscovpn -n &>/dev/null; then
|
||||
if ! sudo iptables -C ciscovpn -o "$vpn_iface" -d "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (outbound)" sudo iptables -I ciscovpn 1 -o "$vpn_iface" -d "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Ciscovpn bypass (outbound) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C ciscovpn -i "$vpn_iface" -s "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (inbound)" sudo iptables -I ciscovpn 2 -i "$vpn_iface" -s "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Ciscovpn bypass (inbound) already exists"
|
||||
fi
|
||||
|
||||
# Also allow container network through ciscovpn chain
|
||||
if ! sudo iptables -C ciscovpn -s 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (container network)" sudo iptables -I ciscovpn 3 -s 100.100.0.0/24 -j ACCEPT
|
||||
fi
|
||||
else
|
||||
log DEBUG "ciscovpn chain does not exist (yet)"
|
||||
fi
|
||||
|
||||
log INFO "Forwarding rules configured"
|
||||
echo ""
|
||||
log INFO "Container should now be able to reach $TARGET_IP through this VM"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Copy credentials to clipboard as alternative
|
||||
copy_to_clipboard() {
|
||||
log INFO "Starting clipboard credential rotation..."
|
||||
echo ""
|
||||
|
||||
log INFO "Copying EMAIL to clipboard"
|
||||
echo "$EMAIL" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}Email ready: $EMAIL${NC}"
|
||||
echo -e " Paste now (Ctrl+V), then press ${GREEN}Enter${NC} here for password..."
|
||||
read -r
|
||||
|
||||
log INFO "Copying PASSWORD to clipboard"
|
||||
echo "$PASSWORD" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}Password ready${NC}"
|
||||
echo -e " Paste now (Ctrl+V), then press ${GREEN}Enter${NC} here for TOTP..."
|
||||
read -r
|
||||
|
||||
TOTP=$(get_totp)
|
||||
log INFO "Copying TOTP to clipboard"
|
||||
echo "$TOTP" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}TOTP ready: $TOTP${NC}"
|
||||
echo -e " Paste now (Ctrl+V)"
|
||||
}
|
||||
|
||||
# Print current TOTP with countdown
|
||||
show_totp() {
|
||||
log INFO "Starting live TOTP display (Ctrl+C to stop)"
|
||||
echo ""
|
||||
while true; do
|
||||
TOTP=$(get_totp)
|
||||
SECONDS_LEFT=$((30 - ($(date +%s) % 30)))
|
||||
echo -ne "\r ${CYAN}Current TOTP:${NC} ${GREEN}$TOTP${NC} (expires in ${YELLOW}${SECONDS_LEFT}s${NC}) "
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# Show network status
|
||||
show_network_status() {
|
||||
log INFO "Current network status:"
|
||||
|
||||
# VM IPs
|
||||
echo ""
|
||||
log DEBUG "VM Network Interfaces:"
|
||||
ip -4 addr show | grep -E "inet |^[0-9]+:" | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
|
||||
# VPN status
|
||||
echo ""
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log INFO "VPN Status: ${GREEN}CONNECTED${NC}"
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
else
|
||||
log WARN "VPN Status: ${RED}NOT CONNECTED${NC}"
|
||||
fi
|
||||
|
||||
# Bridge IP (for container routing)
|
||||
local vm_bridge_ip=$(get_vm_bridge_ip)
|
||||
if [ -n "$vm_bridge_ip" ]; then
|
||||
log DEBUG "VM IP on bridge: $vm_bridge_ip"
|
||||
fi
|
||||
|
||||
# Container gateway
|
||||
local container_gw=$(get_container_gateway)
|
||||
log DEBUG "Container gateway: $container_gw"
|
||||
|
||||
# Default gateway
|
||||
echo ""
|
||||
log DEBUG "Default gateway:"
|
||||
ip route show default | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main menu
|
||||
main_menu() {
|
||||
echo -e "${GREEN}Options:${NC}"
|
||||
echo -e " ${CYAN}1${NC} - Start Cisco AnyConnect (kill existing + launch)"
|
||||
echo -e " ${CYAN}2${NC} - Copy credentials to clipboard (one by one)"
|
||||
echo -e " ${CYAN}3${NC} - Show live TOTP"
|
||||
echo -e " ${CYAN}4${NC} - Setup IP forwarding rules only"
|
||||
echo -e " ${CYAN}5${NC} - Test connection to $TARGET_IP"
|
||||
echo -e " ${CYAN}6${NC} - Show network status"
|
||||
echo -e " ${CYAN}7${NC} - Kill all Cisco processes"
|
||||
echo -e " ${CYAN}8${NC} - Show routing table"
|
||||
echo -e " ${CYAN}9${NC} - Show /etc/hosts"
|
||||
echo -e " ${CYAN}e${NC} - Edit /etc/hosts"
|
||||
echo -e " ${CYAN}q${NC} - Quit"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check if VPN is already connected
|
||||
check_vpn_status() {
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log INFO "VPN is ${GREEN}CONNECTED${NC}"
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
return 0
|
||||
else
|
||||
log WARN "VPN is ${RED}NOT CONNECTED${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Focus on Cisco AnyConnect window
|
||||
focus_vpn_window() {
|
||||
local win_id=$(xdotool search --name "Cisco" 2>/dev/null | head -1)
|
||||
if [ -n "$win_id" ]; then
|
||||
xdotool windowactivate --sync "$win_id" 2>/dev/null
|
||||
sleep 0.3
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Auto-login sequence using xdotool (no auto-focus, types to active window)
|
||||
auto_login() {
|
||||
log INFO "Starting automated login sequence..."
|
||||
|
||||
# Wait for UI to fully load
|
||||
log DEBUG "Waiting 5s for UI to load..."
|
||||
sleep 5
|
||||
|
||||
# Press Enter to initiate connection
|
||||
log DEBUG "Pressing Enter to start connection..."
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type email
|
||||
log DEBUG "Typing email..."
|
||||
xdotool type --delay 50 "$EMAIL"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type password
|
||||
log DEBUG "Typing password..."
|
||||
xdotool type --delay 50 "$PASSWORD"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type TOTP
|
||||
log DEBUG "Typing TOTP..."
|
||||
local totp=$(oathtool --totp -b "$TOTP_SECRET")
|
||||
log DEBUG "TOTP: $totp"
|
||||
xdotool type --delay 50 "$totp"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Extra enters for any confirmation dialogs
|
||||
log DEBUG "Sending confirmation enters..."
|
||||
xdotool key Return
|
||||
sleep 2
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
xdotool key Return
|
||||
|
||||
log INFO "Auto-login sequence completed"
|
||||
}
|
||||
|
||||
# Start Cisco AnyConnect with logging
|
||||
start_anyconnect() {
|
||||
local do_auto_login="$1"
|
||||
log INFO "=== Starting Cisco AnyConnect VPN ==="
|
||||
echo ""
|
||||
|
||||
# Kill existing processes first
|
||||
# Always restart vpnagentd for a clean session
|
||||
kill_cisco_processes "true"
|
||||
|
||||
# Start vpnagentd if not running
|
||||
if ! pgrep -x vpnagentd >/dev/null; then
|
||||
log INFO "Starting vpnagentd..."
|
||||
sudo /opt/cisco/secureclient/bin/vpnagentd &
|
||||
log DEBUG "Waiting for vpnagentd to initialize..."
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
# Show credentials
|
||||
log INFO "Credentials for SSO login:"
|
||||
echo -e " ${CYAN}Email: $EMAIL${NC}"
|
||||
echo -e " ${CYAN}Password: $PASSWORD${NC}"
|
||||
TOTP=$(get_totp)
|
||||
echo -e " ${CYAN}TOTP: $TOTP${NC}"
|
||||
echo ""
|
||||
|
||||
# Start AnyConnect with GPU/WebKit workarounds
|
||||
log INFO "Launching Cisco AnyConnect UI..."
|
||||
export GDK_BACKEND=x11
|
||||
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
/opt/cisco/secureclient/bin/vpnui &
|
||||
VPNUI_PID=$!
|
||||
log DEBUG "vpnui started with PID $VPNUI_PID"
|
||||
|
||||
if [ "$do_auto_login" = "true" ]; then
|
||||
# Run auto-login in background
|
||||
auto_login &
|
||||
AUTO_LOGIN_PID=$!
|
||||
log DEBUG "Auto-login started with PID $AUTO_LOGIN_PID"
|
||||
else
|
||||
log INFO "Manual login mode - use keyboard shortcuts or menu option 2 for credentials"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait for VPN to connect
|
||||
log INFO "Waiting for VPN connection..."
|
||||
local wait_count=0
|
||||
local max_wait=300 # 5 minutes
|
||||
while [ -z "$(get_vpn_interface)" ]; do
|
||||
sleep 2
|
||||
((wait_count+=2))
|
||||
if [ $((wait_count % 10)) -eq 0 ]; then
|
||||
log DEBUG "Still waiting for VPN... (${wait_count}s)"
|
||||
fi
|
||||
if [ $wait_count -ge $max_wait ]; then
|
||||
log ERROR "Timeout waiting for VPN connection after ${max_wait}s"
|
||||
stop_xbindkeys
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
log INFO "VPN connected!"
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
|
||||
# Wait a bit for routes to stabilize
|
||||
log DEBUG "Waiting for routes to stabilize..."
|
||||
sleep 3
|
||||
|
||||
# Setup forwarding
|
||||
setup_forwarding
|
||||
|
||||
# Test connection
|
||||
log INFO "Testing connection to $TARGET_IP..."
|
||||
if ping -c 2 -W 3 "$TARGET_IP" &>/dev/null; then
|
||||
log INFO "Connection test: ${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
log WARN "Connection test: ${RED}FAILED${NC} (may need manual route on Windows)"
|
||||
fi
|
||||
|
||||
# Monitor and auto-reconnect
|
||||
log INFO "Monitoring VPN connection (will auto-reconnect if disconnected)..."
|
||||
while true; do
|
||||
sleep 30
|
||||
if [ -z "$(get_vpn_interface)" ]; then
|
||||
log WARN "VPN disconnected! Reconnecting in 5 seconds..."
|
||||
sleep 5
|
||||
start_anyconnect "$do_auto_login"
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-c|--connect)
|
||||
DO_CONNECT=true
|
||||
shift
|
||||
;;
|
||||
-d|--disconnect)
|
||||
DO_DISCONNECT=true
|
||||
shift
|
||||
;;
|
||||
-m|--menu)
|
||||
SKIP_AUTO_LOGIN=true
|
||||
shift
|
||||
;;
|
||||
-r|--routes)
|
||||
show_routes
|
||||
exit 0
|
||||
;;
|
||||
-h|--hosts)
|
||||
show_hosts
|
||||
exit 0
|
||||
;;
|
||||
-s|--status)
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} VPN and Network Status ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
check_vpn_status
|
||||
echo ""
|
||||
show_network_status
|
||||
exit 0
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$DO_CONNECT" = "true" ] && [ "$DO_DISCONNECT" = "true" ]; then
|
||||
echo "Error: --connect and --disconnect are mutually exclusive"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main
|
||||
parse_args "$@"
|
||||
|
||||
if [ "$DO_DISCONNECT" = "true" ]; then
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Dover VPN Connection Script ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
disconnect_vpn
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [ "$DO_CONNECT" = "true" ]; then
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Dover VPN Connection Script ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
if [ "$SKIP_AUTO_LOGIN" = "true" ]; then
|
||||
start_anyconnect "false"
|
||||
else
|
||||
start_anyconnect "true"
|
||||
fi
|
||||
exit $?
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Dover VPN Connection Script ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
log INFO "Script started"
|
||||
echo ""
|
||||
|
||||
# Check current status
|
||||
if check_vpn_status; then
|
||||
echo ""
|
||||
log INFO "VPN already connected. Setting up forwarding..."
|
||||
setup_forwarding
|
||||
elif [ "$SKIP_AUTO_LOGIN" = "true" ]; then
|
||||
echo ""
|
||||
log INFO "Menu mode - skipping auto-login"
|
||||
else
|
||||
echo ""
|
||||
log INFO "Auto-starting VPN connection..."
|
||||
echo ""
|
||||
start_anyconnect "true"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
main_menu
|
||||
|
||||
while true; do
|
||||
echo -ne "${CYAN}Choice: ${NC}"
|
||||
read -r choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
echo ""
|
||||
if [ "$SKIP_AUTO_LOGIN" = "true" ]; then
|
||||
start_anyconnect "false"
|
||||
else
|
||||
start_anyconnect "true"
|
||||
fi
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
2)
|
||||
echo ""
|
||||
copy_to_clipboard
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
3)
|
||||
echo ""
|
||||
show_totp
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
4)
|
||||
echo ""
|
||||
setup_forwarding
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
5)
|
||||
echo ""
|
||||
log INFO "Testing connection to $TARGET_IP..."
|
||||
if ping -c 3 "$TARGET_IP"; then
|
||||
log INFO "Connection test: ${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
log ERROR "Connection test: ${RED}FAILED${NC}"
|
||||
fi
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
6)
|
||||
echo ""
|
||||
show_network_status
|
||||
main_menu
|
||||
;;
|
||||
7)
|
||||
echo ""
|
||||
kill_cisco_processes
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
8)
|
||||
echo ""
|
||||
show_routes
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
9)
|
||||
echo ""
|
||||
show_hosts
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
e|E)
|
||||
echo ""
|
||||
edit_hosts
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
q|Q)
|
||||
log INFO "Goodbye!"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log ERROR "Invalid choice"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
7
apps/rego-tunnel/shared/cisco-vpn-connect.desktop
Normal file
7
apps/rego-tunnel/shared/cisco-vpn-connect.desktop
Normal file
@@ -0,0 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Cisco VPN Auto-Connect
|
||||
Comment=Auto-connect Cisco VPN on login
|
||||
Exec=/usr/bin/cisco-vpn --connect
|
||||
Terminal=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
15
apps/rego-tunnel/shared/cisco-vpn-connect.service.user
Normal file
15
apps/rego-tunnel/shared/cisco-vpn-connect.service.user
Normal file
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Cisco VPN auto-connect (user session)
|
||||
After=graphical-session.target
|
||||
Wants=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=DISPLAY=:0
|
||||
ExecStart=/usr/bin/cisco-vpn --connect
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
729
apps/rego-tunnel/shared/cisco-vpn.sh.orig-20251228-143916
Normal file
729
apps/rego-tunnel/shared/cisco-vpn.sh.orig-20251228-143916
Normal file
@@ -0,0 +1,729 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Dover VPN Connection Script with Semi-Automation
|
||||
# Usage: ./cisco-vpn.sh [-m|--menu] [-r|--routes] [-h|--hosts] [--help]
|
||||
#
|
||||
# Options:
|
||||
# -m, --menu Skip auto-login, show menu directly
|
||||
# -r, --routes Show current routing table and exit
|
||||
# -h, --hosts Show /etc/hosts and exit
|
||||
# -s, --status Show VPN and network status and exit
|
||||
# --help Show this help message
|
||||
#
|
||||
# Keyboard shortcuts (global, work anywhere):
|
||||
# Ctrl+1 - Type email
|
||||
# Ctrl+2 - Type password
|
||||
# Ctrl+3 - Type TOTP code
|
||||
# Ctrl+4 - Type email + Tab + password (combo)
|
||||
# Ctrl+5 - Full sequence: email + Tab + password + Tab + TOTP + Enter
|
||||
|
||||
EMAIL="c-azaw@regoproducts.com"
|
||||
PASSWORD='Lz@83278327$$@@'
|
||||
TOTP_SECRET="rzqtqskdwkhz6zyr"
|
||||
VPN_HOST="vpn-ord1.dovercorp.com"
|
||||
TARGET_IP="10.35.33.230"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
GRAY='\033[0;90m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Flags
|
||||
SKIP_AUTO_LOGIN=false
|
||||
|
||||
# Logging function with timestamp
|
||||
log() {
|
||||
local level="$1"
|
||||
local msg="$2"
|
||||
local timestamp=$(date '+%H:%M:%S')
|
||||
case $level in
|
||||
INFO) echo -e "${GRAY}[$timestamp]${NC} ${GREEN}[INFO]${NC} $msg" ;;
|
||||
WARN) echo -e "${GRAY}[$timestamp]${NC} ${YELLOW}[WARN]${NC} $msg" ;;
|
||||
ERROR) echo -e "${GRAY}[$timestamp]${NC} ${RED}[ERROR]${NC} $msg" ;;
|
||||
DEBUG) echo -e "${GRAY}[$timestamp]${NC} ${CYAN}[DEBUG]${NC} $msg" ;;
|
||||
CMD) echo -e "${GRAY}[$timestamp]${NC} ${GRAY}[CMD]${NC} $msg" ;;
|
||||
*) echo -e "${GRAY}[$timestamp]${NC} $msg" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run command with logging
|
||||
run_cmd() {
|
||||
local desc="$1"
|
||||
shift
|
||||
log CMD "$desc: $*"
|
||||
output=$("$@" 2>&1)
|
||||
local rc=$?
|
||||
if [ -n "$output" ]; then
|
||||
echo "$output" | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
fi
|
||||
return $rc
|
||||
}
|
||||
|
||||
# Show help
|
||||
show_help() {
|
||||
echo -e "${CYAN}Dover VPN Connection Script${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -m, --menu Skip auto-login, show menu directly"
|
||||
echo " -r, --routes Show current routing table and exit"
|
||||
echo " -h, --hosts Show /etc/hosts and exit"
|
||||
echo " -s, --status Show VPN and network status and exit"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Menu Options:"
|
||||
echo " 1 - Start Cisco AnyConnect (kill existing + launch)"
|
||||
echo " 2 - Copy credentials to clipboard (one by one)"
|
||||
echo " 3 - Show live TOTP"
|
||||
echo " 4 - Setup IP forwarding rules only"
|
||||
echo " 5 - Test connection to target"
|
||||
echo " 6 - Show network status"
|
||||
echo " 7 - Kill all Cisco processes"
|
||||
echo " 8 - Show routing table"
|
||||
echo " 9 - Show /etc/hosts"
|
||||
echo " e - Edit /etc/hosts"
|
||||
echo " q - Quit"
|
||||
}
|
||||
|
||||
# Show routing table
|
||||
show_routes() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Current Routing Table ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}IPv4 Routes:${NC}"
|
||||
ip -4 route show | while IFS= read -r line; do
|
||||
# Highlight VPN-related routes
|
||||
if echo "$line" | grep -qE "cscotun|tun[0-9]"; then
|
||||
echo -e " ${YELLOW}$line${NC}"
|
||||
elif echo "$line" | grep -qE "10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\."; then
|
||||
echo -e " ${CYAN}$line${NC}"
|
||||
else
|
||||
echo " $line"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo -e "${GREEN}VPN Interface:${NC}"
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
ip addr show "$vpn_iface" 2>/dev/null | grep -E "inet|link" | while IFS= read -r line; do
|
||||
echo " $line"
|
||||
done
|
||||
else
|
||||
echo -e " ${RED}No VPN interface found${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show /etc/hosts
|
||||
show_hosts() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} /etc/hosts ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
cat -n /etc/hosts
|
||||
}
|
||||
|
||||
# Edit /etc/hosts
|
||||
edit_hosts() {
|
||||
local editor="${EDITOR:-nano}"
|
||||
if command -v "$editor" &>/dev/null; then
|
||||
sudo "$editor" /etc/hosts
|
||||
else
|
||||
log ERROR "No editor found. Set EDITOR environment variable."
|
||||
log INFO "Try: sudo nano /etc/hosts"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get current TOTP
|
||||
get_totp() {
|
||||
oathtool --totp -b "$TOTP_SECRET"
|
||||
}
|
||||
|
||||
# Function to detect VPN tunnel interface dynamically
|
||||
get_vpn_interface() {
|
||||
# Look for cscotun* or tun* interfaces that are UP
|
||||
local iface=$(ip link show | grep -oP '(cscotun\d+|tun\d+)(?=:.*UP)' | head -1)
|
||||
if [ -z "$iface" ]; then
|
||||
# Fallback: any cscotun interface
|
||||
iface=$(ip link show | grep -oP 'cscotun\d+' | head -1)
|
||||
fi
|
||||
echo "$iface"
|
||||
}
|
||||
|
||||
# Function to get VM's IP on the bridge network (for container routing)
|
||||
get_vm_bridge_ip() {
|
||||
# Get IP from ens3 (main adapter with 100.100.0.x)
|
||||
ip addr show ens3 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1
|
||||
}
|
||||
|
||||
# Function to get container gateway IP
|
||||
get_container_gateway() {
|
||||
# The container bridge is at 100.100.0.1
|
||||
echo "100.100.0.1"
|
||||
}
|
||||
|
||||
# Function to get VPN tunnel IP
|
||||
get_vpn_ip() {
|
||||
local iface=$(get_vpn_interface)
|
||||
if [ -n "$iface" ]; then
|
||||
ip addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1
|
||||
fi
|
||||
}
|
||||
|
||||
# Start xbindkeys for keyboard macros
|
||||
start_xbindkeys() {
|
||||
log INFO "Starting keyboard macro listener (xbindkeys)..."
|
||||
|
||||
# Kill any existing xbindkeys
|
||||
pkill xbindkeys 2>/dev/null
|
||||
sleep 0.5
|
||||
|
||||
# Start xbindkeys
|
||||
xbindkeys -f ~/.xbindkeysrc 2>/dev/null &
|
||||
XBINDKEYS_PID=$!
|
||||
|
||||
if pgrep xbindkeys >/dev/null; then
|
||||
log DEBUG "xbindkeys started (PID: $(pgrep xbindkeys))"
|
||||
log INFO "Keyboard shortcuts active: Ctrl+1=email, Ctrl+2=pass, Ctrl+3=TOTP, Ctrl+4=combo, Ctrl+5=all"
|
||||
else
|
||||
log WARN "Failed to start xbindkeys"
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop xbindkeys
|
||||
stop_xbindkeys() {
|
||||
if pgrep xbindkeys >/dev/null; then
|
||||
log INFO "Stopping keyboard macro listener..."
|
||||
pkill xbindkeys 2>/dev/null
|
||||
log DEBUG "xbindkeys stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Kill all Cisco-related processes
|
||||
kill_cisco_processes() {
|
||||
log INFO "Killing all Cisco-related processes..."
|
||||
|
||||
local killed=0
|
||||
local my_pid=$$
|
||||
local my_ppid=$(ps -o ppid= -p $$ | tr -d ' ')
|
||||
|
||||
# Kill vpnui specifically (not just any process with "vpn" in name)
|
||||
for pid in $(pgrep -x "vpnui" 2>/dev/null); do
|
||||
if [ "$pid" != "$my_pid" ] && [ "$pid" != "$my_ppid" ]; then
|
||||
log DEBUG "Killing vpnui (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Note: Don't kill vpnagentd - we need it running
|
||||
|
||||
# Kill Cisco-specific processes by exact path
|
||||
for proc in cstub cscan acwebsecagent vpndownloader; do
|
||||
for pid in $(pgrep -x "$proc" 2>/dev/null); do
|
||||
log DEBUG "Killing $proc (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
done
|
||||
|
||||
# Kill openconnect (exact match)
|
||||
for pid in $(pgrep -x "openconnect" 2>/dev/null); do
|
||||
log DEBUG "Killing openconnect (PID $pid)"
|
||||
sudo kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
|
||||
if [ $killed -eq 0 ]; then
|
||||
log INFO "No Cisco processes were running"
|
||||
else
|
||||
log INFO "Killed $killed process(es)"
|
||||
sleep 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to setup iptables rules for forwarding
|
||||
setup_forwarding() {
|
||||
log INFO "Setting up IP forwarding rules for $TARGET_IP..."
|
||||
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -z "$vpn_iface" ]; then
|
||||
log ERROR "No VPN interface found! Is VPN connected?"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
local vm_bridge_ip=$(get_vm_bridge_ip)
|
||||
local container_gw=$(get_container_gateway)
|
||||
|
||||
log DEBUG "VPN interface: $vpn_iface"
|
||||
log DEBUG "VPN IP: $vpn_ip"
|
||||
log DEBUG "VM bridge IP: $vm_bridge_ip"
|
||||
log DEBUG "Container gateway: $container_gw"
|
||||
|
||||
# Enable IP forwarding
|
||||
run_cmd "Enabling IP forwarding" sudo sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# NAT masquerade for traffic from container network (100.100.0.0/24) going through VPN
|
||||
# This is the ONLY masquerade rule needed - source-based, not destination-based
|
||||
if ! sudo iptables -t nat -C POSTROUTING -s 100.100.0.0/24 -o "$vpn_iface" -j MASQUERADE 2>/dev/null; then
|
||||
run_cmd "Adding NAT masquerade for container network -> VPN" sudo iptables -t nat -A POSTROUTING -s 100.100.0.0/24 -o "$vpn_iface" -j MASQUERADE
|
||||
else
|
||||
log DEBUG "NAT masquerade for container network already exists"
|
||||
fi
|
||||
|
||||
# Forward rules
|
||||
if ! sudo iptables -C FORWARD -d "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (to target)" sudo iptables -A FORWARD -d "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (to target) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C FORWARD -s "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (from target)" sudo iptables -A FORWARD -s "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (from target) already exists"
|
||||
fi
|
||||
|
||||
# Accept forwarding from container network
|
||||
if ! sudo iptables -C FORWARD -s 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (from container network)" sudo iptables -A FORWARD -s 100.100.0.0/24 -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (from container network) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C FORWARD -d 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding forward rule (to container network)" sudo iptables -A FORWARD -d 100.100.0.0/24 -j ACCEPT
|
||||
else
|
||||
log DEBUG "Forward rule (to container network) already exists"
|
||||
fi
|
||||
|
||||
# Cisco VPN chain bypass (insert at top if chain exists)
|
||||
if sudo iptables -L ciscovpn -n &>/dev/null; then
|
||||
if ! sudo iptables -C ciscovpn -o "$vpn_iface" -d "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (outbound)" sudo iptables -I ciscovpn 1 -o "$vpn_iface" -d "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Ciscovpn bypass (outbound) already exists"
|
||||
fi
|
||||
|
||||
if ! sudo iptables -C ciscovpn -i "$vpn_iface" -s "$TARGET_IP" -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (inbound)" sudo iptables -I ciscovpn 2 -i "$vpn_iface" -s "$TARGET_IP" -j ACCEPT
|
||||
else
|
||||
log DEBUG "Ciscovpn bypass (inbound) already exists"
|
||||
fi
|
||||
|
||||
# Also allow container network through ciscovpn chain
|
||||
if ! sudo iptables -C ciscovpn -s 100.100.0.0/24 -j ACCEPT 2>/dev/null; then
|
||||
run_cmd "Adding ciscovpn bypass (container network)" sudo iptables -I ciscovpn 3 -s 100.100.0.0/24 -j ACCEPT
|
||||
fi
|
||||
else
|
||||
log DEBUG "ciscovpn chain does not exist (yet)"
|
||||
fi
|
||||
|
||||
log INFO "Forwarding rules configured"
|
||||
echo ""
|
||||
log INFO "Container should now be able to reach $TARGET_IP through this VM"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Copy credentials to clipboard as alternative
|
||||
copy_to_clipboard() {
|
||||
log INFO "Starting clipboard credential rotation..."
|
||||
echo ""
|
||||
|
||||
log INFO "Copying EMAIL to clipboard"
|
||||
echo "$EMAIL" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}Email ready: $EMAIL${NC}"
|
||||
echo -e " Paste now (Ctrl+V), then press ${GREEN}Enter${NC} here for password..."
|
||||
read -r
|
||||
|
||||
log INFO "Copying PASSWORD to clipboard"
|
||||
echo "$PASSWORD" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}Password ready${NC}"
|
||||
echo -e " Paste now (Ctrl+V), then press ${GREEN}Enter${NC} here for TOTP..."
|
||||
read -r
|
||||
|
||||
TOTP=$(get_totp)
|
||||
log INFO "Copying TOTP to clipboard"
|
||||
echo "$TOTP" | xclip -selection clipboard
|
||||
echo -e " ${CYAN}TOTP ready: $TOTP${NC}"
|
||||
echo -e " Paste now (Ctrl+V)"
|
||||
}
|
||||
|
||||
# Print current TOTP with countdown
|
||||
show_totp() {
|
||||
log INFO "Starting live TOTP display (Ctrl+C to stop)"
|
||||
echo ""
|
||||
while true; do
|
||||
TOTP=$(get_totp)
|
||||
SECONDS_LEFT=$((30 - ($(date +%s) % 30)))
|
||||
echo -ne "\r ${CYAN}Current TOTP:${NC} ${GREEN}$TOTP${NC} (expires in ${YELLOW}${SECONDS_LEFT}s${NC}) "
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# Show network status
|
||||
show_network_status() {
|
||||
log INFO "Current network status:"
|
||||
|
||||
# VM IPs
|
||||
echo ""
|
||||
log DEBUG "VM Network Interfaces:"
|
||||
ip -4 addr show | grep -E "inet |^[0-9]+:" | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
|
||||
# VPN status
|
||||
echo ""
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log INFO "VPN Status: ${GREEN}CONNECTED${NC}"
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
else
|
||||
log WARN "VPN Status: ${RED}NOT CONNECTED${NC}"
|
||||
fi
|
||||
|
||||
# Bridge IP (for container routing)
|
||||
local vm_bridge_ip=$(get_vm_bridge_ip)
|
||||
if [ -n "$vm_bridge_ip" ]; then
|
||||
log DEBUG "VM IP on bridge: $vm_bridge_ip"
|
||||
fi
|
||||
|
||||
# Container gateway
|
||||
local container_gw=$(get_container_gateway)
|
||||
log DEBUG "Container gateway: $container_gw"
|
||||
|
||||
# Default gateway
|
||||
echo ""
|
||||
log DEBUG "Default gateway:"
|
||||
ip route show default | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main menu
|
||||
main_menu() {
|
||||
echo -e "${GREEN}Options:${NC}"
|
||||
echo -e " ${CYAN}1${NC} - Start Cisco AnyConnect (kill existing + launch)"
|
||||
echo -e " ${CYAN}2${NC} - Copy credentials to clipboard (one by one)"
|
||||
echo -e " ${CYAN}3${NC} - Show live TOTP"
|
||||
echo -e " ${CYAN}4${NC} - Setup IP forwarding rules only"
|
||||
echo -e " ${CYAN}5${NC} - Test connection to $TARGET_IP"
|
||||
echo -e " ${CYAN}6${NC} - Show network status"
|
||||
echo -e " ${CYAN}7${NC} - Kill all Cisco processes"
|
||||
echo -e " ${CYAN}8${NC} - Show routing table"
|
||||
echo -e " ${CYAN}9${NC} - Show /etc/hosts"
|
||||
echo -e " ${CYAN}e${NC} - Edit /etc/hosts"
|
||||
echo -e " ${CYAN}q${NC} - Quit"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check if VPN is already connected
|
||||
check_vpn_status() {
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
if [ -n "$vpn_iface" ]; then
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log INFO "VPN is ${GREEN}CONNECTED${NC}"
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
return 0
|
||||
else
|
||||
log WARN "VPN is ${RED}NOT CONNECTED${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Focus on Cisco AnyConnect window
|
||||
focus_vpn_window() {
|
||||
local win_id=$(xdotool search --name "Cisco" 2>/dev/null | head -1)
|
||||
if [ -n "$win_id" ]; then
|
||||
xdotool windowactivate --sync "$win_id" 2>/dev/null
|
||||
sleep 0.3
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Auto-login sequence using xdotool (no auto-focus, types to active window)
|
||||
auto_login() {
|
||||
log INFO "Starting automated login sequence..."
|
||||
|
||||
# Wait for UI to fully load
|
||||
log DEBUG "Waiting 5s for UI to load..."
|
||||
sleep 5
|
||||
|
||||
# Press Enter to initiate connection
|
||||
log DEBUG "Pressing Enter to start connection..."
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type email
|
||||
log DEBUG "Typing email..."
|
||||
xdotool type --delay 50 "$EMAIL"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type password
|
||||
log DEBUG "Typing password..."
|
||||
xdotool type --delay 50 "$PASSWORD"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Type TOTP
|
||||
log DEBUG "Typing TOTP..."
|
||||
local totp=$(oathtool --totp -b "$TOTP_SECRET")
|
||||
log DEBUG "TOTP: $totp"
|
||||
xdotool type --delay 50 "$totp"
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
|
||||
# Extra enters for any confirmation dialogs
|
||||
log DEBUG "Sending confirmation enters..."
|
||||
xdotool key Return
|
||||
sleep 2
|
||||
xdotool key Return
|
||||
sleep 5
|
||||
xdotool key Return
|
||||
|
||||
log INFO "Auto-login sequence completed"
|
||||
}
|
||||
|
||||
# Start Cisco AnyConnect with logging
|
||||
start_anyconnect() {
|
||||
local do_auto_login="$1"
|
||||
log INFO "=== Starting Cisco AnyConnect VPN ==="
|
||||
echo ""
|
||||
|
||||
# Kill existing processes first
|
||||
kill_cisco_processes
|
||||
|
||||
# Start vpnagentd if not running
|
||||
if ! pgrep -x vpnagentd >/dev/null; then
|
||||
log INFO "Starting vpnagentd..."
|
||||
sudo /opt/cisco/secureclient/bin/vpnagentd &
|
||||
log DEBUG "Waiting for vpnagentd to initialize..."
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
# Show credentials
|
||||
log INFO "Credentials for SSO login:"
|
||||
echo -e " ${CYAN}Email: $EMAIL${NC}"
|
||||
echo -e " ${CYAN}Password: $PASSWORD${NC}"
|
||||
TOTP=$(get_totp)
|
||||
echo -e " ${CYAN}TOTP: $TOTP${NC}"
|
||||
echo ""
|
||||
|
||||
# Start AnyConnect with GPU/WebKit workarounds
|
||||
log INFO "Launching Cisco AnyConnect UI..."
|
||||
export GDK_BACKEND=x11
|
||||
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
/opt/cisco/secureclient/bin/vpnui &
|
||||
VPNUI_PID=$!
|
||||
log DEBUG "vpnui started with PID $VPNUI_PID"
|
||||
|
||||
if [ "$do_auto_login" = "true" ]; then
|
||||
# Run auto-login in background
|
||||
auto_login &
|
||||
AUTO_LOGIN_PID=$!
|
||||
log DEBUG "Auto-login started with PID $AUTO_LOGIN_PID"
|
||||
else
|
||||
log INFO "Manual login mode - use keyboard shortcuts or menu option 2 for credentials"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait for VPN to connect
|
||||
log INFO "Waiting for VPN connection..."
|
||||
local wait_count=0
|
||||
local max_wait=300 # 5 minutes
|
||||
while [ -z "$(get_vpn_interface)" ]; do
|
||||
sleep 2
|
||||
((wait_count+=2))
|
||||
if [ $((wait_count % 10)) -eq 0 ]; then
|
||||
log DEBUG "Still waiting for VPN... (${wait_count}s)"
|
||||
fi
|
||||
if [ $wait_count -ge $max_wait ]; then
|
||||
log ERROR "Timeout waiting for VPN connection after ${max_wait}s"
|
||||
stop_xbindkeys
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
log INFO "VPN connected!"
|
||||
local vpn_iface=$(get_vpn_interface)
|
||||
local vpn_ip=$(get_vpn_ip)
|
||||
log DEBUG " Interface: $vpn_iface"
|
||||
log DEBUG " VPN IP: $vpn_ip"
|
||||
|
||||
# Wait a bit for routes to stabilize
|
||||
log DEBUG "Waiting for routes to stabilize..."
|
||||
sleep 3
|
||||
|
||||
# Setup forwarding
|
||||
setup_forwarding
|
||||
|
||||
# Test connection
|
||||
log INFO "Testing connection to $TARGET_IP..."
|
||||
if ping -c 2 -W 3 "$TARGET_IP" &>/dev/null; then
|
||||
log INFO "Connection test: ${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
log WARN "Connection test: ${RED}FAILED${NC} (may need manual route on Windows)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-m|--menu)
|
||||
SKIP_AUTO_LOGIN=true
|
||||
shift
|
||||
;;
|
||||
-r|--routes)
|
||||
show_routes
|
||||
exit 0
|
||||
;;
|
||||
-h|--hosts)
|
||||
show_hosts
|
||||
exit 0
|
||||
;;
|
||||
-s|--status)
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} VPN and Network Status ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
check_vpn_status
|
||||
echo ""
|
||||
show_network_status
|
||||
exit 0
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Main
|
||||
parse_args "$@"
|
||||
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} Dover VPN Connection Script ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
log INFO "Script started"
|
||||
echo ""
|
||||
|
||||
# Check current status
|
||||
if check_vpn_status; then
|
||||
echo ""
|
||||
log INFO "VPN already connected. Setting up forwarding..."
|
||||
setup_forwarding
|
||||
elif [ "$SKIP_AUTO_LOGIN" = "true" ]; then
|
||||
echo ""
|
||||
log INFO "Menu mode - skipping auto-login"
|
||||
else
|
||||
echo ""
|
||||
log INFO "Auto-starting VPN connection..."
|
||||
echo ""
|
||||
start_anyconnect "true"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
main_menu
|
||||
|
||||
while true; do
|
||||
echo -ne "${CYAN}Choice: ${NC}"
|
||||
read -r choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
echo ""
|
||||
if [ "$SKIP_AUTO_LOGIN" = "true" ]; then
|
||||
start_anyconnect "false"
|
||||
else
|
||||
start_anyconnect "true"
|
||||
fi
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
2)
|
||||
echo ""
|
||||
copy_to_clipboard
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
3)
|
||||
echo ""
|
||||
show_totp
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
4)
|
||||
echo ""
|
||||
setup_forwarding
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
5)
|
||||
echo ""
|
||||
log INFO "Testing connection to $TARGET_IP..."
|
||||
if ping -c 3 "$TARGET_IP"; then
|
||||
log INFO "Connection test: ${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
log ERROR "Connection test: ${RED}FAILED${NC}"
|
||||
fi
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
6)
|
||||
echo ""
|
||||
show_network_status
|
||||
main_menu
|
||||
;;
|
||||
7)
|
||||
echo ""
|
||||
kill_cisco_processes
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
8)
|
||||
echo ""
|
||||
show_routes
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
9)
|
||||
echo ""
|
||||
show_hosts
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
e|E)
|
||||
echo ""
|
||||
edit_hosts
|
||||
echo ""
|
||||
main_menu
|
||||
;;
|
||||
q|Q)
|
||||
log INFO "Goodbye!"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log ERROR "Invalid choice"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
55
apps/rego-tunnel/shared/fix-netplan-ens3.sh
Normal file
55
apps/rego-tunnel/shared/fix-netplan-ens3.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
MAC="52:54:00:12:34:56"
|
||||
IP_CIDR="100.100.0.2/24"
|
||||
GW="100.100.0.1"
|
||||
DNS1="8.8.8.8"
|
||||
DNS2="1.1.1.1"
|
||||
|
||||
backup_dir="/etc/netplan/backup-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# Back up all current netplan YAMLs
|
||||
cp -a /etc/netplan/*.yaml "$backup_dir"/ 2>/dev/null || true
|
||||
|
||||
# Move any NetworkManager-generated netplan snippets out of the way
|
||||
if ls /etc/netplan/90-NM-*.yaml >/dev/null 2>&1; then
|
||||
mv /etc/netplan/90-NM-*.yaml "$backup_dir"/ || true
|
||||
fi
|
||||
|
||||
cat > /etc/netplan/00-installer-config.yaml <<EOF
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
lan0:
|
||||
match:
|
||||
macaddress: ${MAC}
|
||||
set-name: ens3
|
||||
dhcp4: false
|
||||
addresses:
|
||||
- ${IP_CIDR}
|
||||
routes:
|
||||
- to: default
|
||||
via: ${GW}
|
||||
nameservers:
|
||||
addresses:
|
||||
- ${DNS1}
|
||||
- ${DNS2}
|
||||
EOF
|
||||
|
||||
chmod 600 /etc/netplan/*.yaml 2>/dev/null || true
|
||||
|
||||
netplan generate
|
||||
netplan apply
|
||||
|
||||
sleep 2
|
||||
|
||||
echo "==== Interfaces (ens3/ens4) ===="
|
||||
ip -br a | grep -E '^(ens3|ens4)\b' || true
|
||||
|
||||
echo "==== Routes ===="
|
||||
ip route | sed -n '1,10p'
|
||||
|
||||
echo "==== Ping ===="
|
||||
ping -c 1 -W 2 1.1.1.1
|
||||
29
apps/rego-tunnel/shared/generate-traefik-basicauth.sh
Normal file
29
apps/rego-tunnel/shared/generate-traefik-basicauth.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Generates a Traefik BasicAuth users string using Apache MD5 (apr1).
|
||||
# Output format matches Traefik label: basicauth.users="user:hash"
|
||||
#
|
||||
# Usage:
|
||||
# ./generate-traefik-basicauth.sh <username> <password>
|
||||
#
|
||||
# Recommended to paste output into:
|
||||
# /etc/runtipi/app-data/runtipi/rego-tunnel/app.env
|
||||
# as:
|
||||
# NOVNC_BASIC_AUTH_USERS='<output>'
|
||||
|
||||
user="${1:-}"
|
||||
pass="${2:-}"
|
||||
|
||||
if [[ -z "$user" || -z "$pass" ]]; then
|
||||
echo "Usage: $0 <username> <password>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "openssl not found; cannot generate apr1 hash" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
hash="$(openssl passwd -apr1 "$pass")"
|
||||
printf "%s:%s\n" "$user" "$hash"
|
||||
203
apps/rego-tunnel/shared/host-routing.sh
Normal file
203
apps/rego-tunnel/shared/host-routing.sh
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
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}"
|
||||
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() {
|
||||
echo "[rego-routing] $*" >&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"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)}}'
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
container_remove
|
||||
}
|
||||
|
||||
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
|
||||
;;
|
||||
stop)
|
||||
remove_host_rules
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
34
apps/rego-tunnel/shared/install-cisco-vpn-autostart.sh
Normal file
34
apps/rego-tunnel/shared/install-cisco-vpn-autostart.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Installs an XDG autostart entry so Cisco VPN auto-connect runs when the GUI user logs in.
|
||||
# Intended to be executed inside the VM.
|
||||
#
|
||||
# Usage:
|
||||
# ./install-cisco-vpn-autostart.sh [username]
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DESKTOP_FILE_SRC="$SCRIPT_DIR/cisco-vpn-connect.desktop"
|
||||
|
||||
if [[ ! -f "$DESKTOP_FILE_SRC" ]]; then
|
||||
echo "Missing $DESKTOP_FILE_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_USER="${1:-${SUDO_USER:-$(id -un)}}"
|
||||
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||
|
||||
if [[ -z "$TARGET_HOME" || ! -d "$TARGET_HOME" ]]; then
|
||||
echo "Could not determine home directory for user: $TARGET_USER" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AUTOSTART_DIR="$TARGET_HOME/.config/autostart"
|
||||
DEST_FILE="$AUTOSTART_DIR/cisco-vpn-connect.desktop"
|
||||
|
||||
sudo -n mkdir -p "$AUTOSTART_DIR"
|
||||
sudo -n install -m 0644 "$DESKTOP_FILE_SRC" "$DEST_FILE"
|
||||
sudo -n chown "$TARGET_USER:$TARGET_USER" "$DEST_FILE"
|
||||
|
||||
echo "Installed autostart entry: $DEST_FILE"
|
||||
echo "It will run on next GUI login for user: $TARGET_USER"
|
||||
14
apps/rego-tunnel/shared/install-cisco-vpn-connect-service.sh
Normal file
14
apps/rego-tunnel/shared/install-cisco-vpn-connect-service.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Installs and enables the cisco-vpn systemd unit on the machine where you run this.
|
||||
# Intended to be executed inside the VM.
|
||||
|
||||
UNIT_SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
UNIT_NAME="cisco-vpn-connect.service"
|
||||
|
||||
sudo install -m 0644 "$UNIT_SRC_DIR/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME"
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now "$UNIT_NAME"
|
||||
|
||||
sudo systemctl --no-pager --full status "$UNIT_NAME" || true
|
||||
25
apps/rego-tunnel/shared/install-cisco-vpn-user-service.sh
Normal file
25
apps/rego-tunnel/shared/install-cisco-vpn-user-service.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Installs a systemd *user* service to run Cisco VPN auto-connect in the logged-in GUI session.
|
||||
# Intended to be executed inside the VM as the desktop user (not root).
|
||||
#
|
||||
# Usage:
|
||||
# ./install-cisco-vpn-user-service.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
UNIT_SRC="$SCRIPT_DIR/cisco-vpn-connect.service.user"
|
||||
UNIT_NAME="cisco-vpn-connect.service"
|
||||
|
||||
if [[ ! -f "$UNIT_SRC" ]]; then
|
||||
echo "Missing $UNIT_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$HOME/.config/systemd/user"
|
||||
install -m 0644 "$UNIT_SRC" "$HOME/.config/systemd/user/$UNIT_NAME"
|
||||
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now "$UNIT_NAME"
|
||||
|
||||
systemctl --user --no-pager --full status "$UNIT_NAME" || true
|
||||
149
apps/rego-tunnel/shared/setup-network.sh
Normal file
149
apps/rego-tunnel/shared/setup-network.sh
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/bin/bash
|
||||
# Setup TAP/Bridge networking for QEMU VM
|
||||
# Defaults:
|
||||
# BRIDGE_NAME=br-rego-vpn
|
||||
# TAP_NAME=tap0
|
||||
# BRIDGE_CIDR=100.100.0.1/24
|
||||
# VM_NET_IP=100.100.0.2
|
||||
# VM_SUBNET=100.100.0.0/24
|
||||
|
||||
set -e
|
||||
|
||||
BRIDGE_NAME="${BRIDGE_NAME:-br-rego-vpn}"
|
||||
TAP_NAME="${TAP_NAME:-tap0}"
|
||||
BRIDGE_CIDR="${BRIDGE_CIDR:-100.100.0.1}"
|
||||
VM_NET_IP="${VM_NET_IP:-100.100.0.2}"
|
||||
VM_SUBNET="${VM_SUBNET:-100.100.0.0}"
|
||||
TARGET_IP="${TARGET_IP:-10.35.33.230}"
|
||||
|
||||
# Optional second bridge/tap for a second VM NIC (pure L2 with the container).
|
||||
# This is opt-in: set BRIDGE2_NAME and TAP2_NAME (and optionally BRIDGE2_CIDR).
|
||||
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"
|
||||
fi
|
||||
|
||||
if [[ "$VM_SUBNET" != */* ]]; then
|
||||
VM_SUBNET="$VM_SUBNET/24"
|
||||
fi
|
||||
|
||||
# Pick the outbound interface based on the container's default route.
|
||||
# (In Docker, this is not always eth1 when multiple networks are attached.)
|
||||
WAN_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); exit}}')"
|
||||
if [ -z "${WAN_IF}" ]; then
|
||||
WAN_IF="eth1"
|
||||
fi
|
||||
|
||||
# Ensure bridge exists
|
||||
if ! ip link show "$BRIDGE_NAME" &>/dev/null; then
|
||||
ip link add "$BRIDGE_NAME" type bridge
|
||||
fi
|
||||
|
||||
# Ensure bridge has address and is up
|
||||
ip addr show dev "$BRIDGE_NAME" | grep -qF "$BRIDGE_CIDR" || ip addr add "$BRIDGE_CIDR" dev "$BRIDGE_NAME" 2>/dev/null || true
|
||||
ip link set "$BRIDGE_NAME" up
|
||||
|
||||
# Ensure TAP exists
|
||||
if ! ip link show "$TAP_NAME" &>/dev/null; then
|
||||
ip tuntap add "$TAP_NAME" mode tap
|
||||
fi
|
||||
|
||||
# Ensure TAP is attached and up
|
||||
ip link set "$TAP_NAME" master "$BRIDGE_NAME" 2>/dev/null || true
|
||||
ip link set "$TAP_NAME" up
|
||||
|
||||
# Optional second bridge/tap (no NAT rules are applied here)
|
||||
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
|
||||
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)${BRIDGE2_UPLINK_IF:+ uplink $BRIDGE2_UPLINK_IF}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enable IP forwarding
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
# Setup NAT/masquerade for outbound traffic from VM
|
||||
iptables -t nat -C POSTROUTING -s "$VM_SUBNET" -o "$WAN_IF" -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s "$VM_SUBNET" -o "$WAN_IF" -j MASQUERADE
|
||||
|
||||
# Ensure forwarding between the VM bridge and outbound interface
|
||||
iptables -C FORWARD -i "$BRIDGE_NAME" -o "$WAN_IF" -j ACCEPT 2>/dev/null || \
|
||||
iptables -A FORWARD -i "$BRIDGE_NAME" -o "$WAN_IF" -j ACCEPT
|
||||
iptables -C FORWARD -i "$WAN_IF" -o "$BRIDGE_NAME" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A FORWARD -i "$WAN_IF" -o "$BRIDGE_NAME" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Forward traffic destined for VPN networks to VM (TARGET_IP defaults to IBM i)
|
||||
# The VM will route this through its VPN tunnel
|
||||
iptables -C FORWARD -d "$TARGET_IP" -j ACCEPT 2>/dev/null || iptables -A FORWARD -d "$TARGET_IP" -j ACCEPT
|
||||
iptables -C FORWARD -s "$TARGET_IP" -j ACCEPT 2>/dev/null || iptables -A FORWARD -s "$TARGET_IP" -j ACCEPT
|
||||
|
||||
# Route to TARGET_IP through VM
|
||||
ip route add "$TARGET_IP" via "$VM_NET_IP" 2>/dev/null || true
|
||||
|
||||
echo "Network setup complete"
|
||||
echo "Bridge: $BRIDGE_NAME = $BRIDGE_CIDR"
|
||||
echo "TAP: $TAP_NAME attached to $BRIDGE_NAME"
|
||||
echo "Route: $TARGET_IP via $VM_NET_IP (VM)"
|
||||
echo "Outbound interface: ${WAN_IF}"
|
||||
|
||||
if [ -n "$BRIDGE2_NAME" ] && [ -n "$TAP2_NAME" ]; then
|
||||
echo "Bridge2: $BRIDGE2_NAME${BRIDGE2_CIDR:+ = $BRIDGE2_CIDR}"
|
||||
echo "TAP2: $TAP2_NAME attached to $BRIDGE2_NAME"
|
||||
fi
|
||||
38
apps/rego-tunnel/shared/start-dnsmasq.sh
Normal file
38
apps/rego-tunnel/shared/start-dnsmasq.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
BRIDGE_NAME="${BRIDGE_NAME:-br-rego-vpn}"
|
||||
BRIDGE_CIDR="${BRIDGE_CIDR:-100.100.0.1}"
|
||||
VM_NET_IP="${VM_NET_IP:-100.100.0.2}"
|
||||
VM_MAC="${VM_MAC:-52:54:00:12:34:56}"
|
||||
|
||||
LEASE_TIME="${LEASE_TIME:-12h}"
|
||||
DNS_SERVERS="${DNS_SERVERS:-1.1.1.1,8.8.8.8}"
|
||||
|
||||
if [[ "$BRIDGE_CIDR" != */* ]]; then
|
||||
BRIDGE_CIDR="$BRIDGE_CIDR/24"
|
||||
fi
|
||||
|
||||
GATEWAY_IP="${BRIDGE_CIDR%%/*}"
|
||||
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
|
||||
cat > /etc/dnsmasq.d/rego.conf <<EOF
|
||||
interface=${BRIDGE_NAME}
|
||||
bind-interfaces
|
||||
except-interface=lo
|
||||
|
||||
dhcp-authoritative
|
||||
log-dhcp
|
||||
|
||||
dhcp-range=${VM_NET_IP},${VM_NET_IP},${LEASE_TIME}
|
||||
dhcp-option=option:router,${GATEWAY_IP}
|
||||
dhcp-option=option:dns-server,${DNS_SERVERS}
|
||||
|
||||
# Static lease for the VM
|
||||
# (VM must use DHCP for this to take effect)
|
||||
dhcp-host=${VM_MAC},${VM_NET_IP}
|
||||
EOF
|
||||
|
||||
echo "[rego-tunnel] dnsmasq on ${BRIDGE_NAME} gateway=${GATEWAY_IP} lease ${VM_MAC} -> ${VM_NET_IP}"
|
||||
exec dnsmasq --no-daemon --conf-file=/etc/dnsmasq.d/rego.conf
|
||||
116
apps/rego-tunnel/shared/start-vm.sh
Normal file
116
apps/rego-tunnel/shared/start-vm.sh
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# If provided, extract ssh.zip to /root/.ssh (not baked into the image)
|
||||
SSH_ZIP_PATH="/shared/ssh.zip"
|
||||
SSH_ZIP_DEST="/root/.ssh"
|
||||
|
||||
if [ -f "$SSH_ZIP_PATH" ]; then
|
||||
mkdir -p "$SSH_ZIP_DEST"
|
||||
chmod 700 "$SSH_ZIP_DEST"
|
||||
|
||||
echo "[rego-tunnel] Extracting $SSH_ZIP_PATH -> $SSH_ZIP_DEST"
|
||||
# Exclude editor swap/backup files; overwrite existing.
|
||||
7z x -y -aoa -o"$SSH_ZIP_DEST" "$SSH_ZIP_PATH" \
|
||||
-x!*.swp -x!*.swo -x!*.swx -x!*~ -x!.DS_Store >/dev/null
|
||||
|
||||
find "$SSH_ZIP_DEST" -type d -exec chmod 700 {} \;
|
||||
find "$SSH_ZIP_DEST" -type f -exec chmod 600 {} \;
|
||||
else
|
||||
echo "[rego-tunnel] No $SSH_ZIP_PATH found; skipping SSH zip extraction"
|
||||
fi
|
||||
|
||||
# Wait for network setup
|
||||
sleep 2
|
||||
|
||||
TAP_NAME="${TAP_NAME:-tap0}"
|
||||
|
||||
# Optional: provide a dedicated 9p export for host app-data (bind-mounted into the container at /shared/app-data)
|
||||
TSCLIENT_PATH="/hostshare"
|
||||
TSCLIENT_TAG="${TSCLIENT_TAG:-TSCLIENT}"
|
||||
SHARED_TAG="${SHARED_TAG:-shared}"
|
||||
|
||||
# Ensure the VM auto-mounts the 9p shares without manual steps.
|
||||
# This edits the QCOW2 from the outside (idempotent) before QEMU boots.
|
||||
AUTO_MOUNT_9P="${AUTO_MOUNT_9P:-1}"
|
||||
if [ "$AUTO_MOUNT_9P" = "1" ]; then
|
||||
QCOW2_PATH="/vm/linux-vm.qcow2"
|
||||
NBD_DEV="${NBD_DEV:-/dev/nbd0}"
|
||||
VMROOT_MNT="/mnt/vmroot"
|
||||
|
||||
if [ -e "$QCOW2_PATH" ] && [ -e "$NBD_DEV" ]; then
|
||||
echo "[rego-tunnel] Ensuring guest fstab mounts 9p tags ($SHARED_TAG, $TSCLIENT_TAG)"
|
||||
modprobe nbd max_part=16 >/dev/null 2>&1 || true
|
||||
qemu-nbd --disconnect "$NBD_DEV" >/dev/null 2>&1 || true
|
||||
qemu-nbd --connect "$NBD_DEV" "$QCOW2_PATH"
|
||||
sleep 1
|
||||
|
||||
# In containers, the kernel may create sysfs partition entries but not
|
||||
# auto-create the corresponding /dev/nbd0p* nodes. Create them if missing.
|
||||
base_dev="$(basename "$NBD_DEV")"
|
||||
for sysfs_dev in /sys/class/block/${base_dev}p*; do
|
||||
[ -e "$sysfs_dev" ] || continue
|
||||
part_name="$(basename "$sysfs_dev")"
|
||||
devnode="/dev/$part_name"
|
||||
[ -e "$devnode" ] && continue
|
||||
if [ -r "$sysfs_dev/dev" ]; then
|
||||
IFS=: read -r major minor < "$sysfs_dev/dev" || true
|
||||
if [ -n "${major:-}" ] && [ -n "${minor:-}" ]; then
|
||||
mknod "$devnode" b "$major" "$minor" 2>/dev/null || true
|
||||
chmod 660 "$devnode" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$VMROOT_MNT"
|
||||
ROOT_PART=""
|
||||
for part in "${NBD_DEV}"p*; do
|
||||
[ -e "$part" ] || continue
|
||||
# Try mount and detect a Linux root by presence of /etc/fstab and /etc/os-release
|
||||
if mount "$part" "$VMROOT_MNT" >/dev/null 2>&1; then
|
||||
if [ -d "$VMROOT_MNT/etc" ] && { [ -f "$VMROOT_MNT/etc/os-release" ] || [ -f "$VMROOT_MNT/usr/lib/os-release" ] || [ -f "$VMROOT_MNT/usr/share/os-release" ]; }; then
|
||||
ROOT_PART="$part"
|
||||
break
|
||||
fi
|
||||
umount "$VMROOT_MNT" >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$ROOT_PART" ]; then
|
||||
# already mounted from loop above
|
||||
mkdir -p "$VMROOT_MNT/shared" "$VMROOT_MNT/hostshare"
|
||||
|
||||
FSTAB="$VMROOT_MNT/etc/fstab"
|
||||
# Add entries only if missing
|
||||
grep -qE "^[[:space:]]*${SHARED_TAG}[[:space:]]+" "$FSTAB" || echo "${SHARED_TAG} /shared 9p trans=virtio,version=9p2000.L,msize=262144,_netdev,nofail,x-systemd.automount 0 0" >> "$FSTAB"
|
||||
grep -qE "^[[:space:]]*${TSCLIENT_TAG}[[:space:]]+" "$FSTAB" || echo "${TSCLIENT_TAG} /hostshare 9p trans=virtio,version=9p2000.L,msize=262144,_netdev,nofail,x-systemd.automount 0 0" >> "$FSTAB"
|
||||
|
||||
umount "$VMROOT_MNT" >/dev/null 2>&1 || true
|
||||
else
|
||||
echo "[rego-tunnel] WARN: could not locate guest root partition; skipping auto-mount setup"
|
||||
lsblk -fp "$NBD_DEV" 2>/dev/null || true
|
||||
blkid "$NBD_DEV"* 2>/dev/null || true
|
||||
fi
|
||||
|
||||
qemu-nbd --disconnect "$NBD_DEV" >/dev/null 2>&1 || true
|
||||
else
|
||||
echo "[rego-tunnel] WARN: missing $QCOW2_PATH or $NBD_DEV; skipping auto-mount setup"
|
||||
fi
|
||||
fi
|
||||
|
||||
exec qemu-system-x86_64 \
|
||||
-enable-kvm \
|
||||
-cpu host \
|
||||
-m ${VM_RAM:-8G} \
|
||||
-smp ${VM_CPUS:-4} \
|
||||
-hda /vm/linux-vm.qcow2 \
|
||||
-fsdev local,id=fsdev0,path=/shared,security_model=none,multidevs=remap \
|
||||
-device virtio-9p-pci,fsdev=fsdev0,mount_tag="$SHARED_TAG" \
|
||||
-fsdev local,id=fsdev1,path="$TSCLIENT_PATH",security_model=none,multidevs=remap \
|
||||
-device virtio-9p-pci,fsdev=fsdev1,mount_tag="$TSCLIENT_TAG" \
|
||||
-netdev tap,id=net0,ifname="$TAP_NAME",script=no,downscript=no \
|
||||
-device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56 \
|
||||
-vnc :0 \
|
||||
-vga virtio \
|
||||
-usb \
|
||||
-device usb-tablet
|
||||
46
apps/rego-tunnel/shared/supervisord.conf
Normal file
46
apps/rego-tunnel/shared/supervisord.conf
Normal file
@@ -0,0 +1,46 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/var/log/supervisord.log
|
||||
|
||||
[program:network-setup]
|
||||
command=/usr/local/bin/setup-network.sh
|
||||
autostart=true
|
||||
autorestart=false
|
||||
startsecs=0
|
||||
priority=1
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:dnsmasq]
|
||||
command=/usr/local/bin/start-dnsmasq.sh
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=5
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:sshd]
|
||||
command=/usr/sbin/sshd -D
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=10
|
||||
|
||||
[program:qemu]
|
||||
command=/usr/local/bin/start-vm.sh
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=20
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:novnc]
|
||||
command=/usr/bin/websockify --web /usr/share/novnc 8006 localhost:5900 --auth-plugin BasicHTTPAuth --auth-source alexz:Az@83278327$$@@
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=30
|
||||
Reference in New Issue
Block a user