297 lines
11 KiB
Nix
297 lines
11 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
{
|
|
options.vpnProxy = {
|
|
enable = lib.mkEnableOption "VPN SOCKS proxy script";
|
|
|
|
vmUser = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "nate";
|
|
description = "Username for VM SSH connection";
|
|
};
|
|
|
|
vmIp = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "192.168.122.241";
|
|
description = "IP address of the VM";
|
|
};
|
|
|
|
socksPort = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 1080;
|
|
description = "Local SOCKS proxy port";
|
|
};
|
|
|
|
vpnTestUrl = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "https://doc-aut.app.vasionnow.com/";
|
|
description = "URL to test VPN connectivity";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf config.vpnProxy.enable {
|
|
home.packages = [
|
|
(pkgs.writeShellApplication {
|
|
name = "vpn-proxy";
|
|
runtimeInputs = with pkgs; [
|
|
openssh
|
|
procps
|
|
lsof
|
|
gnused
|
|
coreutils
|
|
curl
|
|
glib # for gsettings
|
|
nettools # for ss command
|
|
];
|
|
text = ''
|
|
VM_USER="${config.vpnProxy.vmUser}"
|
|
VM_IP="${config.vpnProxy.vmIp}"
|
|
SOCKS_PORT=${toString config.vpnProxy.socksPort}
|
|
PID_FILE="/tmp/vpn-socks.pid"
|
|
VPN_TEST_URL="${config.vpnProxy.vpnTestUrl}"
|
|
|
|
check_ssh_agent() {
|
|
# Check if SSH agent is available
|
|
if [ -z "$SSH_AUTH_SOCK" ]; then
|
|
echo "Error: SSH agent not running (SSH_AUTH_SOCK not set)"
|
|
return 1
|
|
fi
|
|
|
|
# Check if any SSH keys are loaded
|
|
if ! ssh-add -l >/dev/null 2>&1; then
|
|
echo "Error: No SSH keys are loaded in the agent"
|
|
echo "Please add your SSH key first with: ssh-add ~/.ssh/id_ed25519"
|
|
echo ""
|
|
echo "Agent status: SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
|
|
return 1
|
|
fi
|
|
|
|
# Show loaded keys for confirmation
|
|
echo "✓ SSH agent has loaded keys:"
|
|
ssh-add -l | sed 's/^/ /'
|
|
return 0
|
|
}
|
|
|
|
start_proxy() {
|
|
# Check if proxy is already running
|
|
if [ -f "$PID_FILE" ]; then
|
|
local pid
|
|
pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then
|
|
echo "Proxy already running (PID: $pid)"
|
|
return 1
|
|
else
|
|
echo "Stale PID file found, cleaning up..."
|
|
rm -f "$PID_FILE"
|
|
fi
|
|
fi
|
|
|
|
# Check SSH agent and keys
|
|
echo "Checking SSH agent..."
|
|
if ! check_ssh_agent; then
|
|
return 1
|
|
fi
|
|
echo ""
|
|
|
|
# Test SSH connectivity first
|
|
echo "Testing SSH connection to VM..."
|
|
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" exit 2>/dev/null; then
|
|
echo "Error: Cannot connect to VM: $VM_USER @ $VM_IP"
|
|
echo "Make sure:"
|
|
echo " 1. VM is running"
|
|
echo " 2. SSH service is running in VM"
|
|
echo " 3. SSH key authentication is set up"
|
|
return 1
|
|
fi
|
|
|
|
# Start SSH tunnel in background
|
|
echo "Starting SSH SOCKS proxy..."
|
|
ssh -D $SOCKS_PORT -f -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes "$VM_USER@$VM_IP"
|
|
local ssh_exit=$?
|
|
|
|
if [ $ssh_exit -ne 0 ]; then
|
|
echo "Error: SSH tunnel failed to start (exit code: $ssh_exit)"
|
|
return 1
|
|
fi
|
|
|
|
# Wait a moment for SSH to fully establish
|
|
sleep 1
|
|
|
|
# Find and save the SSH process PID
|
|
local pid
|
|
pid=$(pgrep -f "ssh -D $SOCKS_PORT.*$VM_USER@$VM_IP" | head -1)
|
|
if [ -z "$pid" ]; then
|
|
echo "Error: SSH process not found after startup"
|
|
return 1
|
|
fi
|
|
echo "$pid" > "$PID_FILE"
|
|
|
|
# Verify port is listening
|
|
echo "Verifying SOCKS proxy is listening on port $SOCKS_PORT..."
|
|
local retry=0
|
|
while [ $retry -lt 5 ]; do
|
|
if lsof -i :"$SOCKS_PORT" > /dev/null 2>&1 || ss -tln | grep -q ":$SOCKS_PORT "; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
retry=$((retry + 1))
|
|
done
|
|
|
|
if [ $retry -eq 5 ]; then
|
|
echo "Error: SOCKS proxy port $SOCKS_PORT is not listening"
|
|
pkill -P "$pid" 2>/dev/null
|
|
kill "$pid" 2>/dev/null
|
|
rm -f "$PID_FILE"
|
|
return 1
|
|
fi
|
|
|
|
# Configure system proxy
|
|
gsettings set org.gnome.system.proxy mode 'manual'
|
|
gsettings set org.gnome.system.proxy.socks host 'localhost'
|
|
gsettings set org.gnome.system.proxy.socks port $SOCKS_PORT
|
|
|
|
echo "✓ VPN proxy started successfully on localhost:$SOCKS_PORT (PID: $pid)"
|
|
|
|
# Test VPN connectivity
|
|
echo "Testing VPN connectivity..."
|
|
if curl -s -m 10 --socks5-hostname localhost:"$SOCKS_PORT" "$VPN_TEST_URL" > /dev/null 2>&1; then
|
|
echo "✓ VPN connection verified - can reach $VPN_TEST_URL"
|
|
else
|
|
echo "⚠ Warning: Could not reach $VPN_TEST_URL through proxy"
|
|
echo " The SOCKS proxy is running but VPN connection may not be active in the VM"
|
|
fi
|
|
}
|
|
|
|
stop_proxy() {
|
|
if [ ! -f "$PID_FILE" ]; then
|
|
echo "Proxy not running (no PID file)"
|
|
# Still try to clean up any orphaned processes
|
|
if pgrep -f "ssh -D $SOCKS_PORT" > /dev/null 2>&1; then
|
|
echo "Found orphaned SSH process, cleaning up..."
|
|
pkill -f "ssh -D $SOCKS_PORT"
|
|
fi
|
|
|
|
# Disable system proxy anyway
|
|
gsettings set org.gnome.system.proxy mode 'none'
|
|
return 1
|
|
fi
|
|
|
|
local pid
|
|
pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
|
|
# Kill the specific SSH process
|
|
if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then
|
|
echo "Stopping VPN proxy (PID: $pid)..."
|
|
kill "$pid" 2>/dev/null
|
|
|
|
# Wait for process to die
|
|
local retry=0
|
|
while [ $retry -lt 5 ] && ps -p "$pid" > /dev/null 2>&1; do
|
|
sleep 1
|
|
retry=$((retry + 1))
|
|
done
|
|
|
|
# Force kill if still alive
|
|
if ps -p "$pid" > /dev/null 2>&1; then
|
|
echo "Process didn't stop gracefully, force killing..."
|
|
kill -9 "$pid" 2>/dev/null
|
|
fi
|
|
else
|
|
echo "PID $pid not found, cleaning up..."
|
|
fi
|
|
|
|
# Also kill by pattern as backup
|
|
pkill -f "ssh -D $SOCKS_PORT.*$VM_USER@$VM_IP" 2>/dev/null
|
|
|
|
rm -f "$PID_FILE"
|
|
|
|
# Disable system proxy
|
|
gsettings set org.gnome.system.proxy mode 'none'
|
|
|
|
echo "✓ VPN proxy stopped"
|
|
}
|
|
|
|
status_proxy() {
|
|
if [ -f "$PID_FILE" ]; then
|
|
local pid
|
|
pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then
|
|
echo "✓ Proxy running on localhost:$SOCKS_PORT (PID: $pid)"
|
|
|
|
# Check if port is actually listening
|
|
if lsof -i :"$SOCKS_PORT" > /dev/null 2>&1 || ss -tln | grep -q ":$SOCKS_PORT "; then
|
|
echo "✓ Port $SOCKS_PORT is listening"
|
|
else
|
|
echo "⚠ Warning: Process running but port not listening"
|
|
fi
|
|
|
|
# Check system proxy settings
|
|
local proxy_mode
|
|
proxy_mode=$(gsettings get org.gnome.system.proxy mode 2>/dev/null)
|
|
if [ "$proxy_mode" = "'manual'" ]; then
|
|
echo "✓ System proxy configured"
|
|
else
|
|
echo "⚠ System proxy not configured (mode: $proxy_mode)"
|
|
fi
|
|
|
|
# Quick connectivity test
|
|
if curl -s -m 5 --socks5-hostname localhost:"$SOCKS_PORT" "$VPN_TEST_URL" > /dev/null 2>&1; then
|
|
echo "✓ VPN connectivity verified"
|
|
else
|
|
echo "⚠ Cannot reach VPN resources"
|
|
fi
|
|
|
|
return 0
|
|
else
|
|
echo "✗ Proxy not running (stale PID file)"
|
|
return 1
|
|
fi
|
|
else
|
|
echo "✗ Proxy not running"
|
|
|
|
# Check for orphaned processes
|
|
if pgrep -f "ssh -D $SOCKS_PORT" > /dev/null 2>&1; then
|
|
echo "⚠ Warning: Found SSH process without PID file"
|
|
echo " Run 'vpn-proxy stop' to clean up"
|
|
fi
|
|
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
case "''${1:-}" in
|
|
start)
|
|
start_proxy
|
|
;;
|
|
stop)
|
|
stop_proxy
|
|
;;
|
|
restart)
|
|
stop_proxy
|
|
sleep 1
|
|
start_proxy
|
|
;;
|
|
status)
|
|
status_proxy
|
|
;;
|
|
test)
|
|
# Quick VPN test without starting/stopping
|
|
if curl -s -m 10 --socks5-hostname localhost:"$SOCKS_PORT" "$VPN_TEST_URL" > /dev/null 2>&1; then
|
|
echo "✓ VPN connection working - can reach $VPN_TEST_URL"
|
|
exit 0
|
|
else
|
|
echo "✗ Cannot reach $VPN_TEST_URL through proxy"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: vpn-proxy {start|stop|restart|status|test}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
'';
|
|
})
|
|
];
|
|
};
|
|
}
|