123
apps/cistech-tunnel/shared/host-routing.sh
Normal file
123
apps/cistech-tunnel/shared/host-routing.sh
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Host routing script for cistech-tunnel
|
||||
# Routes TARGET_IP through the VPN container
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
ACTION="${1:-start}"
|
||||
|
||||
# Fixed configuration (we assigned these)
|
||||
CONTAINER_IP="172.30.0.10"
|
||||
BRIDGE_NAME="br-cistech-vpn"
|
||||
TARGET_IP="${TARGET_IP:-10.3.1.0}"
|
||||
LAN_SUBNET="192.168.0.0/23"
|
||||
LAN_INTERFACES="eth0 eth1 wlan0"
|
||||
LOG_FILE="/var/log/cistech-routing.log"
|
||||
|
||||
log() {
|
||||
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [cistech-routing] $*"
|
||||
echo "$msg" | tee -a "$LOG_FILE" >&2
|
||||
}
|
||||
|
||||
get_lan_interface() {
|
||||
ip route show default | awk '/default/ {for(i=1;i<=NF;i++) if($i=="dev") print $(i+1)}' | head -1
|
||||
}
|
||||
|
||||
remove_routes() {
|
||||
log "Removing stale routes for $TARGET_IP..."
|
||||
|
||||
# Remove any existing route to TARGET_IP
|
||||
ip route del "$TARGET_IP" 2>/dev/null || true
|
||||
ip route del "$TARGET_IP/24" 2>/dev/null || true
|
||||
|
||||
log "Stale routes removed"
|
||||
}
|
||||
|
||||
apply_routes() {
|
||||
local lan_if
|
||||
lan_if="$(get_lan_interface)"
|
||||
|
||||
log "Applying host routing rules..."
|
||||
log " Container IP: $CONTAINER_IP"
|
||||
log " Bridge: $BRIDGE_NAME"
|
||||
log " Target IP: $TARGET_IP"
|
||||
log " LAN interface: ${lan_if:-unknown}"
|
||||
|
||||
# Enable IP forwarding
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
log "IP forwarding enabled"
|
||||
|
||||
# Add route to TARGET_IP via container
|
||||
ip route replace "$TARGET_IP/24" via "$CONTAINER_IP" dev "$BRIDGE_NAME"
|
||||
log "Route added: $TARGET_IP via $CONTAINER_IP dev $BRIDGE_NAME"
|
||||
|
||||
# Allow forwarding in DOCKER-USER chain for all LAN interfaces
|
||||
for lan_if in $LAN_INTERFACES; do
|
||||
# Check if interface exists
|
||||
if ip link show "$lan_if" &>/dev/null; then
|
||||
# Allow traffic from LAN to container for TARGET_IP
|
||||
iptables -C DOCKER-USER -i "$lan_if" -o "$BRIDGE_NAME" -d "$TARGET_IP" -j ACCEPT 2>/dev/null || \
|
||||
iptables -I DOCKER-USER 1 -i "$lan_if" -o "$BRIDGE_NAME" -d "$TARGET_IP" -j ACCEPT
|
||||
|
||||
# Allow return traffic
|
||||
iptables -C DOCKER-USER -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -I DOCKER-USER 1 -i "$BRIDGE_NAME" -o "$lan_if" -s "$TARGET_IP" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
log "DOCKER-USER iptables rules added for $lan_if <-> $BRIDGE_NAME"
|
||||
fi
|
||||
done
|
||||
|
||||
# Masquerade traffic from LAN subnet to VPN bridge (so return traffic routes correctly)
|
||||
# Use nft since iptables-nft backend doesn't support iptables -t nat commands
|
||||
if ! nft list chain ip nat POSTROUTING 2>/dev/null | grep -q "saddr $LAN_SUBNET.*oifname.*$BRIDGE_NAME.*masquerade"; then
|
||||
nft add rule ip nat POSTROUTING ip saddr "$LAN_SUBNET" oifname "$BRIDGE_NAME" counter masquerade
|
||||
log "NAT masquerade rule added for $LAN_SUBNET -> $BRIDGE_NAME"
|
||||
else
|
||||
log "NAT masquerade rule already exists for $LAN_SUBNET -> $BRIDGE_NAME"
|
||||
fi
|
||||
|
||||
log "OK: Host routing applied - $TARGET_IP via $CONTAINER_IP ($BRIDGE_NAME)"
|
||||
}
|
||||
|
||||
remove_all() {
|
||||
log "Removing all routing rules..."
|
||||
|
||||
# Remove route
|
||||
ip route del "$TARGET_IP/24" via "$CONTAINER_IP" dev "$BRIDGE_NAME" 2>/dev/null || true
|
||||
|
||||
# Remove iptables rules for all LAN interfaces
|
||||
for lan_if in $LAN_INTERFACES; do
|
||||
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
|
||||
done
|
||||
|
||||
# Remove masquerade rule (using nft)
|
||||
local handle
|
||||
handle=$(nft -a list chain ip nat POSTROUTING 2>/dev/null | grep "saddr $LAN_SUBNET.*oifname.*$BRIDGE_NAME.*masquerade" | grep -oP 'handle \K\d+' | head -1)
|
||||
if [ -n "$handle" ]; then
|
||||
nft delete rule ip nat POSTROUTING handle "$handle" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log "All routing rules removed"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
start)
|
||||
remove_routes
|
||||
apply_routes
|
||||
;;
|
||||
stop)
|
||||
remove_all
|
||||
;;
|
||||
restart)
|
||||
remove_all
|
||||
sleep 1
|
||||
remove_routes
|
||||
apply_routes
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
55
apps/cistech-tunnel/shared/install-host-services.sh
Normal file
55
apps/cistech-tunnel/shared/install-host-services.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install host-side systemd services for cistech-tunnel
|
||||
# Run this ONCE on the host after installing the app in Runtipi
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
APP_DATA_DIR="/etc/runtipi/app-data/runtipi/cistech-tunnel"
|
||||
|
||||
echo "Installing cistech-tunnel host services..."
|
||||
|
||||
# Create the path watcher unit
|
||||
cat << 'EOF' | sudo tee /etc/systemd/system/cistech-routing-watcher.path
|
||||
[Unit]
|
||||
Description=Watch for cistech-tunnel routing trigger
|
||||
|
||||
[Path]
|
||||
PathExists=/etc/runtipi/app-data/runtipi/cistech-tunnel/restart-routing
|
||||
Unit=cistech-routing-watcher.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Create the service unit
|
||||
cat << EOF | sudo tee /etc/systemd/system/cistech-routing-watcher.service
|
||||
[Unit]
|
||||
Description=Apply cistech-tunnel routing rules
|
||||
After=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=${SCRIPT_DIR}/host-routing.sh restart
|
||||
ExecStartPost=/bin/rm -f ${APP_DATA_DIR}/restart-routing
|
||||
ExecStartPost=/bin/bash -c 'echo "trigger cleared at \$(date)" >> ${APP_DATA_DIR}/watcher.log'
|
||||
EOF
|
||||
|
||||
# Make host-routing.sh executable
|
||||
sudo chmod +x "${SCRIPT_DIR}/host-routing.sh"
|
||||
|
||||
# Reload systemd and enable the watcher
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now cistech-routing-watcher.path
|
||||
|
||||
echo ""
|
||||
echo "Done! Services installed:"
|
||||
echo " - cistech-routing-watcher.path (watches for trigger file)"
|
||||
echo " - cistech-routing-watcher.service (applies routing rules)"
|
||||
echo ""
|
||||
echo "To check status:"
|
||||
echo " systemctl status cistech-routing-watcher.path"
|
||||
echo ""
|
||||
echo "To manually trigger routing:"
|
||||
echo " touch ${APP_DATA_DIR}/restart-routing"
|
||||
528
apps/cistech-tunnel/shared/openconnect-vpn
Normal file
528
apps/cistech-tunnel/shared/openconnect-vpn
Normal file
@@ -0,0 +1,528 @@
|
||||
#!/bin/bash
|
||||
|
||||
# OpenConnect-SSO VPN Connection Script
|
||||
# Usage: ./openconnect-vpn [-c|--connect] [-d|--disconnect] [-s|--status] [--setup-keyring] [--help]
|
||||
|
||||
# Credentials from environment variables (set by runtipi)
|
||||
VPN_EMAIL="${VPN_EMAIL:-}"
|
||||
VPN_PASSWORD="${VPN_PASSWORD:-}"
|
||||
VPN_TOTP_SECRET="${VPN_TOTP_SECRET:-}"
|
||||
VPN_HOST="${VPN_HOST:-}"
|
||||
TARGET_IP="${TARGET_IP:-}"
|
||||
VPN_INTERFACE="${VPN_INTERFACE:-tun0}"
|
||||
|
||||
|
||||
# Log directory
|
||||
LOG_DIR="/var/log/openconnect-vpn"
|
||||
LOG_RETENTION_DAYS=7
|
||||
mkdir -p "$LOG_DIR" 2>/dev/null
|
||||
|
||||
get_log_file() {
|
||||
echo "$LOG_DIR/$(date '+%Y-%m-%d').log"
|
||||
}
|
||||
|
||||
cleanup_old_logs() {
|
||||
find "$LOG_DIR" -name "*.log" -type f -mtime +$LOG_RETENTION_DAYS -delete 2>/dev/null
|
||||
}
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
GRAY='\033[0;90m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_banner() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN} OpenConnect-SSO VPN Connection ${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Flags
|
||||
DO_CONNECT=false
|
||||
DO_DISCONNECT=false
|
||||
DO_SETUP_KEYRING=false
|
||||
|
||||
# Logging
|
||||
log() {
|
||||
local level="$1"
|
||||
local msg="$2"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
local timestamp_short=$(date '+%H:%M:%S')
|
||||
local log_file=$(get_log_file)
|
||||
|
||||
local msg_plain=$(echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
echo "[$timestamp] [$level] $msg_plain" >> "$log_file"
|
||||
|
||||
case $level in
|
||||
INFO) echo -e "${GRAY}[$timestamp_short]${NC} ${GREEN}[INFO]${NC} $msg" ;;
|
||||
WARN) echo -e "${GRAY}[$timestamp_short]${NC} ${YELLOW}[WARN]${NC} $msg" ;;
|
||||
ERROR) echo -e "${GRAY}[$timestamp_short]${NC} ${RED}[ERROR]${NC} $msg" ;;
|
||||
DEBUG) echo -e "${GRAY}[$timestamp_short]${NC} ${CYAN}[DEBUG]${NC} $msg" ;;
|
||||
*) echo -e "${GRAY}[$timestamp_short]${NC} $msg" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_cmd() {
|
||||
local desc="$1"
|
||||
shift
|
||||
log DEBUG "$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
|
||||
}
|
||||
|
||||
# Fetch server certificate fingerprint from VPN host
|
||||
get_server_cert() {
|
||||
local host="$1"
|
||||
# Strip protocol and path to get just hostname:port
|
||||
local server=$(echo "$host" | sed -E 's|^https?://||; s|/.*$||')
|
||||
# Add default port if not specified
|
||||
[[ "$server" != *:* ]] && server="${server}:443"
|
||||
|
||||
log DEBUG "Fetching server certificate from $server..."
|
||||
|
||||
local cert_pin=$(echo | openssl s_client -connect "$server" 2>/dev/null \
|
||||
| openssl x509 -pubkey -noout 2>/dev/null \
|
||||
| openssl pkey -pubin -outform der 2>/dev/null \
|
||||
| openssl dgst -sha256 -binary \
|
||||
| openssl enc -base64)
|
||||
|
||||
if [[ -n "$cert_pin" ]]; then
|
||||
echo "pin-sha256:${cert_pin}"
|
||||
else
|
||||
log WARN "Could not fetch server certificate"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup keyring with credentials for openconnect-sso
|
||||
setup_keyring() {
|
||||
log INFO "Setting up keyring credentials..."
|
||||
|
||||
if [[ -z "$VPN_EMAIL" ]]; then
|
||||
log ERROR "VPN_EMAIL is not set"
|
||||
return 1
|
||||
fi
|
||||
|
||||
python3 << PYTHON
|
||||
import keyring
|
||||
from keyrings.alt.file import PlaintextKeyring
|
||||
|
||||
# Use plaintext keyring (works without desktop environment)
|
||||
keyring.set_keyring(PlaintextKeyring())
|
||||
|
||||
email = "$VPN_EMAIL"
|
||||
password = "$VPN_PASSWORD"
|
||||
totp_secret = "$VPN_TOTP_SECRET"
|
||||
|
||||
# Store password
|
||||
if password:
|
||||
keyring.set_password('openconnect-sso', email, password)
|
||||
print(f"Password stored in keyring for {email}")
|
||||
|
||||
# Store TOTP secret
|
||||
if totp_secret:
|
||||
keyring.set_password('openconnect-sso', f'totp/{email}', totp_secret.upper())
|
||||
print(f"TOTP secret stored in keyring for {email}")
|
||||
|
||||
print("Keyring setup complete")
|
||||
PYTHON
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log INFO "Keyring credentials configured"
|
||||
else
|
||||
log ERROR "Failed to setup keyring"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo -e "${CYAN}OpenConnect-SSO VPN Connection Script${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -c, --connect Connect to VPN"
|
||||
echo " -d, --disconnect Disconnect from VPN"
|
||||
echo " -s, --status Show VPN status"
|
||||
echo " -r, --routes Show routing table"
|
||||
echo " --setup-keyring Setup keyring credentials only"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Environment Variables:"
|
||||
echo " VPN_EMAIL Email/username for SSO"
|
||||
echo " VPN_PASSWORD Password for SSO"
|
||||
echo " VPN_TOTP_SECRET TOTP secret for 2FA"
|
||||
echo " VPN_HOST VPN server hostname"
|
||||
echo " TARGET_IP Target IP for connectivity test"
|
||||
echo " VPN_INTERFACE TUN interface name (default: tun0)"
|
||||
}
|
||||
|
||||
get_vpn_interface() {
|
||||
local iface=$(ip link show | grep -oP 'tun\d+(?=:.*UP)' | head -1)
|
||||
if [ -z "$iface" ]; then
|
||||
iface=$(ip link show | grep -oP 'tun\d+' | head -1)
|
||||
fi
|
||||
echo "$iface"
|
||||
}
|
||||
|
||||
get_container_ip() {
|
||||
ip addr show eth0 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
if echo "$line" | grep -qE "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_network_status() {
|
||||
log INFO "Current network status:"
|
||||
echo ""
|
||||
log DEBUG "Container Network Interfaces:"
|
||||
ip -4 addr show | grep -E "inet |^[0-9]+:" | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
|
||||
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
|
||||
|
||||
local container_ip=$(get_container_ip)
|
||||
if [ -n "$container_ip" ]; then
|
||||
log DEBUG "Container IP: $container_ip"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log DEBUG "Default gateway:"
|
||||
ip route show default | while IFS= read -r line; do
|
||||
echo -e " ${GRAY}│${NC} $line"
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
kill_vpn_processes() {
|
||||
log INFO "Killing VPN processes..."
|
||||
local killed=0
|
||||
|
||||
for pid in $(pgrep -x "openconnect" 2>/dev/null); do
|
||||
log DEBUG "Killing openconnect (PID $pid)"
|
||||
kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
|
||||
for pid in $(pgrep -f "openconnect-sso" 2>/dev/null); do
|
||||
log DEBUG "Killing openconnect-sso (PID $pid)"
|
||||
kill -9 "$pid" 2>/dev/null && ((killed++))
|
||||
done
|
||||
|
||||
if [ $killed -eq 0 ]; then
|
||||
log INFO "No VPN processes were running"
|
||||
else
|
||||
log INFO "Killed $killed process(es)"
|
||||
sleep 2
|
||||
fi
|
||||
}
|
||||
|
||||
disconnect_vpn() {
|
||||
log INFO "Disconnecting VPN..."
|
||||
kill_vpn_processes
|
||||
|
||||
if check_vpn_status; then
|
||||
log WARN "VPN still appears connected"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log INFO "VPN disconnected"
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_forwarding() {
|
||||
log INFO "Setting up IP forwarding rules..."
|
||||
|
||||
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 container_ip=$(get_container_ip)
|
||||
|
||||
log DEBUG "VPN interface: $vpn_iface"
|
||||
log DEBUG "VPN IP: $vpn_ip"
|
||||
log DEBUG "Container IP: $container_ip"
|
||||
|
||||
run_cmd "Enabling IP forwarding" sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# NAT masquerade for container network
|
||||
if ! iptables -t nat -C POSTROUTING -o "$vpn_iface" -j MASQUERADE 2>/dev/null; then
|
||||
run_cmd "Adding NAT masquerade" iptables -t nat -A POSTROUTING -o "$vpn_iface" -j MASQUERADE
|
||||
fi
|
||||
|
||||
log INFO "Forwarding rules configured"
|
||||
|
||||
# Trigger host routing service restart if available
|
||||
if [ -d /runtime ]; then
|
||||
touch /runtime/restart-routing 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
connect_vpn() {
|
||||
log INFO "=== Starting OpenConnect-SSO VPN ==="
|
||||
echo ""
|
||||
|
||||
# Kill any existing VPN processes
|
||||
kill_vpn_processes
|
||||
|
||||
# Validate required variables
|
||||
if [[ -z "$VPN_HOST" ]]; then
|
||||
log ERROR "VPN_HOST is not set"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Setup keyring credentials
|
||||
setup_keyring
|
||||
|
||||
# Build openconnect-sso command
|
||||
local sso_args=()
|
||||
sso_args+=("-s" "$VPN_HOST")
|
||||
|
||||
if [[ -n "$VPN_EMAIL" ]]; then
|
||||
sso_args+=("-u" "$VPN_EMAIL")
|
||||
fi
|
||||
|
||||
sso_args+=("--browser-display-mode" "hidden")
|
||||
|
||||
# Fetch server certificate
|
||||
log INFO "Fetching server certificate..."
|
||||
local servercert=$(get_server_cert "$VPN_HOST")
|
||||
|
||||
# Build openconnect args
|
||||
local oc_args=()
|
||||
oc_args+=("--protocol=anyconnect")
|
||||
oc_args+=("--interface" "$VPN_INTERFACE")
|
||||
oc_args+=("--script" "/usr/share/vpnc-scripts/vpnc-script")
|
||||
|
||||
if [[ -n "$servercert" ]]; then
|
||||
oc_args+=("--servercert" "$servercert")
|
||||
log DEBUG "Server cert: $servercert"
|
||||
fi
|
||||
|
||||
log INFO "Connecting to: $VPN_HOST"
|
||||
log DEBUG "Interface: $VPN_INTERFACE"
|
||||
|
||||
# Launch openconnect-sso
|
||||
log INFO "Launching openconnect-sso..."
|
||||
openconnect-sso "${sso_args[@]}" -- /usr/sbin/openconnect "${oc_args[@]}" &
|
||||
OC_PID=$!
|
||||
disown $OC_PID
|
||||
log DEBUG "openconnect-sso started with PID $OC_PID"
|
||||
|
||||
# Wait for VPN to connect
|
||||
log INFO "Waiting for VPN connection..."
|
||||
local wait_count=0
|
||||
local max_wait=300
|
||||
|
||||
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"
|
||||
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"
|
||||
|
||||
sleep 3
|
||||
setup_forwarding
|
||||
|
||||
# Test connection if TARGET_IP is set
|
||||
if [[ -n "$TARGET_IP" ]]; then
|
||||
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}"
|
||||
fi
|
||||
fi
|
||||
|
||||
log INFO "VPN setup complete"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main menu
|
||||
main_menu() {
|
||||
echo -e "${GREEN}Options:${NC}"
|
||||
echo -e " ${CYAN}1${NC} - Connect VPN"
|
||||
echo -e " ${CYAN}2${NC} - Disconnect VPN"
|
||||
echo -e " ${CYAN}3${NC} - Show VPN status"
|
||||
echo -e " ${CYAN}4${NC} - Setup IP forwarding"
|
||||
echo -e " ${CYAN}5${NC} - Test connection"
|
||||
echo -e " ${CYAN}6${NC} - Show network status"
|
||||
echo -e " ${CYAN}7${NC} - Show routing table"
|
||||
echo -e " ${CYAN}8${NC} - Setup keyring"
|
||||
echo -e " ${CYAN}q${NC} - Quit"
|
||||
echo ""
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-c|--connect)
|
||||
DO_CONNECT=true
|
||||
shift
|
||||
;;
|
||||
-d|--disconnect)
|
||||
DO_DISCONNECT=true
|
||||
shift
|
||||
;;
|
||||
-s|--status)
|
||||
print_banner
|
||||
check_vpn_status
|
||||
echo ""
|
||||
show_network_status
|
||||
exit 0
|
||||
;;
|
||||
-r|--routes)
|
||||
show_routes
|
||||
exit 0
|
||||
;;
|
||||
--setup-keyring)
|
||||
DO_SETUP_KEYRING=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Main
|
||||
parse_args "$@"
|
||||
cleanup_old_logs
|
||||
|
||||
echo "" >> "$(get_log_file)"
|
||||
echo "========================================" >> "$(get_log_file)"
|
||||
log INFO "openconnect-vpn script started"
|
||||
log DEBUG "VPN_EMAIL=$VPN_EMAIL"
|
||||
log DEBUG "VPN_HOST=$VPN_HOST"
|
||||
log DEBUG "TARGET_IP=$TARGET_IP"
|
||||
log DEBUG "VPN_TOTP_SECRET is $([ -n "$VPN_TOTP_SECRET" ] && echo 'set' || echo 'NOT SET')"
|
||||
|
||||
print_banner
|
||||
|
||||
if [ "$DO_SETUP_KEYRING" = "true" ]; then
|
||||
setup_keyring
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [ "$DO_DISCONNECT" = "true" ]; then
|
||||
disconnect_vpn
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [ "$DO_CONNECT" = "true" ]; then
|
||||
connect_vpn
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Interactive menu mode
|
||||
while true; do
|
||||
echo ""
|
||||
main_menu
|
||||
echo -ne "${CYAN}Choice: ${NC}"
|
||||
read -r choice
|
||||
echo ""
|
||||
|
||||
[[ -z "${choice// }" ]] && continue
|
||||
|
||||
case $choice in
|
||||
1) connect_vpn ;;
|
||||
2) disconnect_vpn ;;
|
||||
3) check_vpn_status ;;
|
||||
4) setup_forwarding ;;
|
||||
5) if [[ -n "$TARGET_IP" ]]; then
|
||||
log INFO "Testing connection to $TARGET_IP..."
|
||||
ping -c 3 "$TARGET_IP" && log INFO "Connection test: ${GREEN}SUCCESS${NC}" || log ERROR "Connection test: ${RED}FAILED${NC}"
|
||||
else
|
||||
log WARN "TARGET_IP not set"
|
||||
fi ;;
|
||||
6) show_network_status ;;
|
||||
7) show_routes ;;
|
||||
8) setup_keyring ;;
|
||||
q|Q) log INFO "Goodbye!"; exit 0 ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
24
apps/cistech-tunnel/shared/uninstall-host-services.sh
Normal file
24
apps/cistech-tunnel/shared/uninstall-host-services.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Uninstall host-side systemd services for cistech-tunnel
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
echo "Removing cistech-tunnel host services..."
|
||||
|
||||
# Stop and disable the watcher
|
||||
sudo systemctl stop cistech-routing-watcher.path 2>/dev/null || true
|
||||
sudo systemctl disable cistech-routing-watcher.path 2>/dev/null || true
|
||||
|
||||
# Remove routing rules
|
||||
/etc/runtipi/repos/runtipi/apps/cistech-tunnel/shared/host-routing.sh stop 2>/dev/null || true
|
||||
|
||||
# Remove systemd units
|
||||
sudo rm -f /etc/systemd/system/cistech-routing-watcher.path
|
||||
sudo rm -f /etc/systemd/system/cistech-routing-watcher.service
|
||||
|
||||
# Reload systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
echo ""
|
||||
echo "Done! Host services removed."
|
||||
39
apps/cistech-tunnel/shared/xstartup
Normal file
39
apps/cistech-tunnel/shared/xstartup
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# VNC xstartup - launches terminal with cisco-vpn script
|
||||
|
||||
unset SESSION_MANAGER
|
||||
unset DBUS_SESSION_BUS_ADDRESS
|
||||
|
||||
# Import environment variables from container (PID 1)
|
||||
# Systemd services don't inherit Docker env vars, so we source them here
|
||||
while IFS= read -r -d '' line; do
|
||||
export "$line"
|
||||
done < /proc/1/environ
|
||||
|
||||
export XDG_RUNTIME_DIR=/tmp/runtime-root
|
||||
mkdir -p $XDG_RUNTIME_DIR
|
||||
chmod 700 $XDG_RUNTIME_DIR
|
||||
|
||||
# GPU/WebKit workarounds for Cisco UI
|
||||
export GDK_BACKEND=x11
|
||||
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
|
||||
# Start dbus session
|
||||
[ -x /usr/bin/dbus-launch ] && eval $(dbus-launch --sh-syntax --exit-with-session)
|
||||
|
||||
# Start window manager
|
||||
openbox &
|
||||
sleep 2
|
||||
|
||||
# Disable screen blanking and power saving
|
||||
xset s off 2>/dev/null || true
|
||||
xset -dpms 2>/dev/null || true
|
||||
xset s noblank 2>/dev/null || true
|
||||
|
||||
# Make script executable and launch in terminal
|
||||
chmod +x /shared/openconnect-vpn 2>/dev/null || true
|
||||
xterm -fa 'Monospace' -fs 11 -bg black -fg white -geometry 130x45+10+10 \
|
||||
-title "Cistech VPN Terminal" \
|
||||
-e "bash -c '/shared/openconnect-vpn; exec bash'" &
|
||||
|
||||
wait
|
||||
Reference in New Issue
Block a user