在我的工作中,需要同时负责多个项目的系统建设与运维,这意味着我要访问不同的内网服务器、数据库、网关、K8s 集群和应用系统。而这些单位都使用不同的 VPN,有的甚至使用同一家 VPN 厂商但版本不同。
当这些 VPN 同时安装在同一台电脑上时,各种问题开始出现:
-
连 VPN A,VPN B 就被强制断开
-
内网能访问了,但互联网完全打不开
-
DNS 被 VPN 改乱,GitHub、npm、VSCode 全都失效
-
路由、网卡被搞乱,网络最终只能靠"重启电脑"恢复
-
同款 VPN 客户端不同版本之间会反复升级/降级,甚至破坏网络驱动
如果你也遇到过这些问题,你就知道这是一个"多 VPN 共存需求"的典型痛点。
我已经在各个项目中见过很多VPN客户端,包括EasyConnect(经常多个客户单位都用这个但是存在版本不一致的情况),FortiClient,OpenVpn,ATrust(零信任),虎盾,iNode(也很容易有版本冲突问题)等。
鉴于我的工作笔记本资源配置比较充足,所以我决定把所有VPN都安装到Windows自带的Hyper-V创建的Windows虚拟机中,版本冲突的就安装到两台虚拟机中,实际场景大部分可以安装到同一台中,有些vpn甚至可以同时启用生效(根据VPN修改路由的情况决定)。此方案因为用到虚拟机,过程中会耗费一定系统资源,方案中使用的软件也有多种选择,有些可能需要破点小财 XD,下面我具体说说我的场景和实现方式。
PS. 值得一提的是,用这个方案顺带解决了WSL中使用大模型的问题。后边还会附带一个shell脚本用于在WSL中Ubuntu系统自动发现启用和停用Proxy配置。
方案用到的软件:
- CCProxy(免费),代理服务端,安装在虚拟机
- Proxifier (收费,花了**$39.95**, 买多个有优惠),代理分流,安装在宿主机
- Hyper-V(免费),虚拟机软件,安装在宿主机
- 其他上网客户端, 用于访问ClaudeCode(部分收费)安装在虚拟机
需要满足启动后允许本地网络访问,因为有些客户端启动后修改了路由 0.0.0.0 ,导致主机无法通过虚机内网ip通信。有些客户端有配置项可以开启"允许本地网络访问"/"Allow local(LAN) Access when using",有些则可能需要手动修改路由。
方案用到的硬件:
- 两个网卡( 如果你不需要WSL2中Ubuntu系统访问ClaudeCode或不需要通过各个单位VPN访问内部资源如Linux服务器,也可以忽略这个要求, 这主要是解决WSL内Ubuntu因为走了Nat网络,网关也被改过,如果虚机使用了主机相同的网卡,Ubuntu内无法联通虚机ip问题**)**
- 如果你的工作环境有Wifi和有线,那一般笔记本就满足(一般都可以同时接有限和无线网络)
- 如果你的工作环境只有无线,那需要花15-40块钱买一个USB无线网卡(购买时需要注意下,有些USB无线网卡不支持路由器的5G网络,如果你的无线网络没有非5G网络就浪费了)
核心思路:把 VPN"装进盒子里",让它们互不干扰
Hyper-V 本身是 Windows 自带、稳定成熟的虚拟化方案,并且对网络隔离非常友好。
结合我的设备配置比较充足(内存、CPU 都够),于是我设计了这样一个体系结构:
-
虚拟机负责跑 VPN(以及 CCProxy),我跑的Win10,内存2GB即可。
-
宿主机则永远不安装任何 VPN,或只安装常用VPN,比如自家公司的VPN客户端
-
所有访问 VPN 内网的流量都通过 Proxifier 精准分流到对应的虚拟机
软件操作和配置
Hyper-V
启用 Hyper-V 功能(图形界面方式)需要windows10,11 (非Home版本)
-
打开 控制面板 → 程序和功能 → 启用或关闭 Windows 功能
-
找到 Hyper-V,并勾选以下项目:
✔ Hyper-V 管理工具
✔ Hyper-V 平台
-
点击 确定,等待系统安装组件。
-
根据提示 重启电脑
Proxifier
1.配置Proxy Server,需要将虚机ip和虚机上启动CCProxy监听的端口配置到Proxifier中。需要注意,如果你带着笔记本在家,到单位或其他地点办公,虚机的ip会跟着变化,对应也要手动修改下下面server的ip配置。

- 配置代理规则,可以通过ip或者域名方式配置使用哪个代理进行访问

CCProxy(安装在虚机上)
开启下图中三项就够了,大多数时候其实也不需要SOCKS/MMS方式,但是因为我在能连接单位VPN网络后还需要本地访问堡垒机内RDP桌面,所以使用了SOCKS方式

为了安全性,点击上图"高级"按钮,配置禁止局域网外部用户使用此代理与管理员密码


同样为了安全,记得创建CCProxy用户用于连接代理服务


以上配置完成后就可以在本地电脑上访问多个VPN连接的网络了。
另外说下WSL下Ubuntu通过此方式访问AI大模型(如Claude)的配置
- 基本思路一样,但是WSL2内Ubuntu的网络流量不受Proxifier控制,所以需要单独配置Ubuntu使用CCProxy的代理(具体方式在下面第3点说明),需要注意的是虚机内科学客户端需要支持本地网络访问,否则开启后Ubuntu无法连通虚机ip,也就连不上CCProxy,因为一般这类客户端都会配置0.0.0.0的路由规则,劫持了所有流量。一般不同软件的开启本地网络访问的配置关键词可能是下面这些:
-
Allow LAN
-
Allow Local Network
-
Bypass LAN
-
仅代理远程流量,不代理本地流量
- 如果想要WSL2内Ubuntu 访问的到 Hyper-v 虚机的CCProxy,需要配置虚机使用的网卡不能是主机默认使用的网卡,这也是为什么我们需要第二块网卡。(使用同一块网卡我看到的现象是,从Ubuntu内通过nc -zv ip port 来测试ccproxy监听提示找不到路由,怀疑和Ubuntu给配置的Nat网关有关系), 若有同学能找到"不使用第二块网卡仍可实现 WSL2 访问虚机"的方式,非常欢迎分享!
以下是我的Hyper-V中网卡设置:

无线连接配置这里可以切换网卡分别连接到无线网络

3.修改Ubuntu内代理配置,来使网络流量走CCProxy代理, 因为在电脑在不同办公地点接入不同网络的时候虚机的ip会有变化(默认使用DHCP自动获取ip),所以每次我到了一个新的网络环境后都要修改其代理配置,有些麻烦,为了更方便我准备了一个shell脚本,用于自动检测,开启,禁用,刷新代理配置。
4.使用Ubuntu内管理代理配置的脚本
使用效果


将下方脚本保存到一个文件,命名proxy-manager.sh,放到Ubuntu内任意位置,我是放到了/root/shell/claude_shell/proxy-manager.sh,enable命令可以扫描指定网段的CCProxy监听端口,扫描到后将代理环境变量写入~/.bashrc文件,disable命令则删除代理配置。
- 脚本开头的变量PROXY_PORT是ccproxy监听的端口,因为可能有多个虚机同时开启,所以不同虚机的ccproxy端口尽量设置不一样区分开。
- PROXY_USER 和PROXY_PASS是CCProxy的用户密码
- INTERNAL_SUBNETS是你常用的网络的ip段信息
- PROIORITY_IPS是你最可能被分配到的ip第四位数字,用于提高扫描命中速度
bash
#!/bin/bash
# Proxy Manager Script for Ubuntu WSL
# Automatically detects and configures proxy settings based on internal subnets
PROXY_PORT=809
PROXY_USER="proxy(用户名需要修改)"
PROXY_PASS="234233密码需要修改"
APT_PROXY_CONF="/etc/apt/apt.conf.d/95proxies"
SCRIPT_NAME="$(basename "$0")"
MAX_PARALLEL_JOBS=20
FOUND_PROXY_FILE="/tmp/proxy_found_$$"
# 虚机常用网络位置会分配的网段,脚本会用于自动扫描的网段定义
INTERNAL_SUBNETS=(
"192.168.0" # Most common home/office subnet
"192.168.2" # Secondary common subnet
"10.0.0" # Corporate networks
)
# 经常会被分配到的ip或固定ip最后一位,会被优先扫描,提高扫描速度,如果不确定这里只保留一个反倒会提高网络速度,因为后续其他ip是并行扫描的,下面这里定义的是逐条扫描
PRIORITY_IPS=(15 16 17 18 123 113 )
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1" >&2
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
show_progress() {
local current=$1
local total=$2
local message=$3
local percent=$(( current * 100 / total ))
local filled=$(( percent / 2 ))
local empty=$(( 50 - filled ))
printf "\r${BLUE}[%3d%%]${NC} [" "$percent" >&2
printf "%*s" $filled | tr ' ' '=' >&2
printf "%*s" $empty | tr ' ' '-' >&2
printf "] %s" "$message" >&2
}
check_proxy_server() {
local ip=$1
local timeout=1
# Validate IP address format
if ! [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
return 1
fi
# Try to connect to proxy port
if timeout $timeout bash -c "echo >/dev/tcp/$ip/$PROXY_PORT" 2>/dev/null; then
return 0
else
return 1
fi
}
check_proxy_server_parallel() {
local ip=$1
local job_id=$2
if check_proxy_server "$ip"; then
# Test if proxy actually works
local test_url="http://www.google.com"
if curl -s --connect-timeout 3 --proxy "http://${PROXY_USER}:${PROXY_PASS}@${ip}:${PROXY_PORT}" "$test_url" >/dev/null 2>&1; then
echo "WORKING:$ip" > "$FOUND_PROXY_FILE"
else
echo "RESPONDING:$ip" > "$FOUND_PROXY_FILE"
fi
# Kill other background jobs
jobs -p | xargs -r kill 2>/dev/null
return 0
fi
return 1
}
detect_proxy_server() {
log_info "Starting optimized proxy server detection..."
local proxy_found=""
# Clean up any existing temp files
rm -f "$FOUND_PROXY_FILE" 2>/dev/null
# Check if we have network connectivity first
if ! ping -c 1 -W 2 223.5.5.5 >/dev/null 2>&1 && ! ping -c 1 -W 119.29.29.29 >/dev/null 2>&1; then
log_warning "No internet connectivity detected - continuing with proxy detection"
fi
log_info "Using parallel scanning with $MAX_PARALLEL_JOBS concurrent connections"
log_info "Scanning ${#INTERNAL_SUBNETS[@]} subnet(s): ${INTERNAL_SUBNETS[*]}"
for subnet in "${INTERNAL_SUBNETS[@]}"; do
log_info "Scanning subnet ${subnet}.x..."
# First check priority IPs (common gateway addresses)
log_info "Checking priority IPs first..."
for priority_ip in "${PRIORITY_IPS[@]}"; do
local ip="${subnet}.${priority_ip}"
echo -n "Priority check $ip:$PROXY_PORT... " >&2
if check_proxy_server "$ip"; then
echo "FOUND!" >&2
log_success "Proxy server found at $ip:$PROXY_PORT"
# Test if proxy actually works
log_info "Testing proxy connectivity..."
local test_url="http://www.google.com"
if curl -s --connect-timeout 5 --proxy "http://${PROXY_USER}:${PROXY_PASS}@${ip}:${PROXY_PORT}" "$test_url" >/dev/null 2>&1; then
log_success "Proxy server is functional"
proxy_found="$ip"
echo "$proxy_found"
return 0
else
log_warning "Proxy server at $ip:$PROXY_PORT responds but may not be functional"
proxy_found="$ip" # Still use it, might be filtering specific sites
echo "$proxy_found"
return 0
fi
else
echo "no response" >&2
fi
done
# If no priority IP worked, do parallel scan of remaining IPs
log_info "Priority IPs not found, starting parallel scan..."
local jobs_started=0
local total_checked=0
for i in {1..254}; do
# Skip priority IPs already checked
local skip=false
for priority_ip in "${PRIORITY_IPS[@]}"; do
if [[ $i -eq $priority_ip ]]; then
skip=true
break
fi
done
[[ $skip == true ]] && continue
# Wait if we have too many background jobs
while [[ $(jobs -r | wc -l) -ge $MAX_PARALLEL_JOBS ]]; do
sleep 0.1
# Check if proxy was found
if [[ -f "$FOUND_PROXY_FILE" ]]; then
break 2
fi
done
# Check if proxy was found by any background job
if [[ -f "$FOUND_PROXY_FILE" ]]; then
break
fi
local ip="${subnet}.${i}"
check_proxy_server_parallel "$ip" $jobs_started &
((jobs_started++))
((total_checked++))
# Show progress every 10 IPs
if (( total_checked % 10 == 0 )); then
echo -n "." >&2
fi
done
# Wait for all background jobs to complete
echo >&2 # New line
log_info "Waiting for parallel scans to complete..."
wait
# Check if proxy was found
if [[ -f "$FOUND_PROXY_FILE" ]]; then
local result=$(cat "$FOUND_PROXY_FILE")
local status=${result%:*}
proxy_found=${result#*:}
rm -f "$FOUND_PROXY_FILE"
if [[ $status == "WORKING" ]]; then
log_success "Found working proxy server at $proxy_found:$PROXY_PORT"
else
log_success "Found responding proxy server at $proxy_found:$PROXY_PORT"
log_warning "Proxy may not be fully functional but will be used"
fi
echo "$proxy_found"
return 0
fi
done
log_error "No proxy server found on port $PROXY_PORT in internal subnets"
log_info "Checked subnets: ${INTERNAL_SUBNETS[*]}"
return 1
}
set_environment_proxy() {
local proxy_server=$1
local proxy_url="http://${PROXY_USER}:${PROXY_PASS}@${proxy_server}:${PROXY_PORT}"
log_info "Setting environment proxy variables..."
echo "export http_proxy='$proxy_url'"
echo "export https_proxy='$proxy_url'"
echo "export ftp_proxy='$proxy_url'"
echo "export HTTP_PROXY='$proxy_url'"
echo "export HTTPS_PROXY='$proxy_url'"
echo "export FTP_PROXY='$proxy_url'"
echo "export no_proxy='localhost,127.0.0.1,::1,.local'"
echo "export NO_PROXY='localhost,127.0.0.1,::1,.local'"
# Remove existing proxy settings from bashrc first to avoid duplicates
if [[ -f ~/.bashrc ]]; then
# Create backup
cp ~/.bashrc ~/.bashrc.bak.$(date +%Y%m%d_%H%M%S) 2>/dev/null || true
# Remove existing proxy lines
sed -i '/^export [A-Za-z_]*[Pp][Rr][Oo][Xx][Yy]/d' ~/.bashrc
fi
# Add proxy settings marker and variables to bashrc
{
echo ""
echo "# Proxy settings - managed by $SCRIPT_NAME"
echo "export http_proxy='$proxy_url'"
echo "export https_proxy='$proxy_url'"
echo "export ftp_proxy='$proxy_url'"
echo "export HTTP_PROXY='$proxy_url'"
echo "export HTTPS_PROXY='$proxy_url'"
echo "export FTP_PROXY='$proxy_url'"
echo "export no_proxy='localhost,127.0.0.1,::1,.local'"
echo "export NO_PROXY='localhost,127.0.0.1,::1,.local'"
echo "# End proxy settings"
} >> ~/.bashrc
log_success "Environment proxy variables set and saved to ~/.bashrc"
}
set_apt_proxy() {
local proxy_server=$1
local proxy_url="http://${PROXY_USER}:${PROXY_PASS}@${proxy_server}:${PROXY_PORT}"
log_info "Configuring APT proxy..."
# Create APT proxy configuration
sudo tee "$APT_PROXY_CONF" > /dev/null << EOF
Acquire::http::Proxy "$proxy_url";
Acquire::https::Proxy "$proxy_url";
Acquire::ftp::Proxy "$proxy_url";
EOF
log_success "APT proxy configured"
}
enable_proxy() {
log_info "Enabling proxy settings..."
local proxy_server
proxy_server=$(detect_proxy_server)
if [[ $? -eq 0 && -n "$proxy_server" ]]; then
set_environment_proxy "$proxy_server"
set_apt_proxy "$proxy_server"
log_success "Proxy enabled successfully with server: $proxy_server:$PROXY_PORT"
else
log_error "Failed to detect proxy server. Proxy not enabled."
return 1
fi
}
disable_proxy() {
log_info "Disabling proxy settings..."
# Unset environment variables
unset http_proxy https_proxy ftp_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY no_proxy NO_PROXY
# Remove from bashrc using better pattern matching
if [[ -f ~/.bashrc ]]; then
# Create backup
cp ~/.bashrc ~/.bashrc.bak.$(date +%Y%m%d_%H%M%S) 2>/dev/null || true
# Remove proxy settings block completely
sed -i '/^# Proxy settings - managed by/,/^# End proxy settings/d' ~/.bashrc
# Remove any remaining individual proxy lines (fallback)
sed -i '/^export [A-Za-z_]*[Pp][Rr][Oo][Xx][Yy]/d' ~/.bashrc
sed -i '/^export [nN][oO]_[Pp][Rr][Oo][Xx][Yy]/d' ~/.bashrc
sed -i '/^export NO_PROXY/d' ~/.bashrc
# Remove any empty lines that might be left behind
sed -i '/^$/N;/^\n$/d' ~/.bashrc
fi
# Remove APT proxy configuration
if [[ -f "$APT_PROXY_CONF" ]]; then
if sudo rm -f "$APT_PROXY_CONF" 2>/dev/null; then
log_info "Removed APT proxy configuration"
else
log_warning "Failed to remove APT proxy configuration (permission denied?)"
fi
fi
log_success "Proxy settings disabled"
echo "unset http_proxy"
echo "unset https_proxy"
echo "unset ftp_proxy"
echo "unset HTTP_PROXY"
echo "unset HTTPS_PROXY"
echo "unset FTP_PROXY"
echo "export no_proxy='localhost,127.0.0.1,::1,.local'"
echo "export NO_PROXY='localhost,127.0.0.1,::1,.local'"
}
refresh_proxy() {
log_info "Refreshing proxy settings for new network location..."
disable_proxy
sleep 1
enable_proxy
}
show_status() {
log_info "Current proxy status:"
if [[ -n "$http_proxy" ]]; then
echo -e " Environment proxy: ${GREEN}ENABLED${NC}"
echo " http_proxy: $http_proxy"
echo " https_proxy: $https_proxy"
echo " no_proxy: $no_proxy"
else
echo -e " Environment proxy: ${RED}DISABLED${NC}"
fi
if [[ -f "$APT_PROXY_CONF" ]]; then
echo -e " APT proxy: ${GREEN}ENABLED${NC}"
echo " Config file: $APT_PROXY_CONF"
else
echo -e " APT proxy: ${RED}DISABLED${NC}"
fi
}
show_help() {
cat << EOF
$SCRIPT_NAME - Proxy Manager for Ubuntu WSL
Usage: $SCRIPT_NAME [OPTION]
OPTIONS:
enable Enable proxy settings (auto-detect proxy server)
disable Disable all proxy settings
refresh Refresh proxy settings (disable then enable)
status Show current proxy status
help Show this help message
DESCRIPTION:
This script automatically detects proxy servers on internal networks
and configures system-wide proxy settings including environment
variables and APT proxy configuration.
Proxy server detection:
- Scans common internal subnets for proxy servers on port $PROXY_PORT
- Uses authentication: $PROXY_USER/$PROXY_PASS
- Shows progress during detection
EXAMPLES:
$SCRIPT_NAME enable # Auto-detect and enable proxy
$SCRIPT_NAME disable # Disable all proxy settings
$SCRIPT_NAME refresh # Refresh when changing networks
$SCRIPT_NAME status # Check current settings
EOF
}
main() {
case "${1:-help}" in
enable)
enable_proxy
;;
disable)
disable_proxy
;;
refresh)
refresh_proxy
;;
status)
show_status
;;
help|--help|-h)
show_help
;;
*)
log_error "Unknown option: $1"
echo
show_help
exit 1
;;
esac
}
# Check if running as root for APT configuration
check_sudo() {
if [[ "$1" == "enable" || "$1" == "refresh" ]]; then
if ! sudo -n true 2>/dev/null; then
log_warning "This script requires sudo access for APT proxy configuration"
log_info "You may be prompted for your password"
fi
fi
}
# Main execution
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
check_sudo "$1"
main "$@"
fi
修改~/.bashrc文件新增一段proxy()函数,代码如下(注意:下面代码中proxy-manager.sh脚本路径要根据实际位置修改 /root/shell/claude_shell/proxy-manager.sh),增加这个函数配置是为了可以让上方脚本中增加的环境变量直接生效,而不用手动执行source ~/.bashrc或新开终端才能生效。也方便在终端中任何目录下直接调用。
bash
proxy() {
local valid_args=("enable" "disable" "refresh" "status" "help")
# check if $1 is valid
local is_valid=false
for arg in "${valid_args[@]}"; do
if [[ "$1" == "$arg" ]]; then
is_valid=true
break
fi
done
# If invalid argument, run help
if [[ "$is_valid" == false ]]; then
/root/shell/claude_shell/proxy-manager.sh help
return 1
fi
# status is special: do NOT eval
if [[ "$1" == "status" ]]; then
/root/shell/claude_shell/proxy-manager.sh status
return
fi
# help also does not need eval
if [[ "$1" == "help" ]]; then
/root/shell/claude_shell/proxy-manager.sh help
return
fi
# other commands must eval to apply env variables
eval "$(/root/shell/claude_shell/proxy-manager.sh "$@")"
}