学校的VPN从EasyConnect切换到aTrust,实质上换汤不换药,一些EasyConnect的不良行为在aTrust中依然存在。例如擅自签发最高权限CA证书攻击用户的HTTPS流量进行内容审计,系统后台进程强制开机自启并扫描监视系统其它进程和系统环境,搞乱系统路由表导致部分系统服务无法正常运行等等。为了所谓“合法合规”进行的此类措施,实际上是以牺牲用户隐私为代价的,对于用户而言,这是无法容忍的。本文主要介绍几种方法实现无副作用连接南京大学校园网。记录的是学习和踩坑的过程,略显繁琐。

效果

使用Clash管理

生成NJU代理组和NJUConnect“节点”和相应规则

访问本校资源示例(选课网站)

使用EZ4Connect连接

使用EZ4Connect客户端

访问本校资源示例(OJ网站)

容器化方法

类似于这个博客中的做法,可以在受控容器中运行aTrust客户端和socks5代理,使用容器连接校园网,再将此连接通过socks5端口暴露给宿主机使用,同时将aTrust客户端与宿主机相隔离。同时需要注意的是,aTrust客户端依赖图形界面进行登录和Web认证,因此需要通过VNC连接到容器中进行操作。

我们来考虑整个过程:

  1. 构建一个Docker镜像,需要包含基本的linux桌面环境、VNC服务端、aTrust客户端、socks5代理和chromium浏览器。VNC服务端用于远程访问容器的图形界面,aTrust客户端用于连接校园网,socks5代理用于将校园网连接暴露给宿主机,chromium浏览器用于进行Web认证。
  2. 通过VNC连接到容器的图形桌面,按照正常登陆流程输入密码、手机验证码等信息,完成校园网的连接。
  3. 在容器中启动socks5代理,将校园网连接通过socks5端口暴露给宿主机使用。
  4. 在宿主机上配置网络代理(这里利用我个人比较熟悉的Clash),将需要访问校园网资源的流量通过分流规则分流到socks5代理。

这里补充一些基本的网络知识,熟知基本网络协议的读者可以跳过。
网络分层模型(Claude生成)
关于这里为什么使用socks5代理而不是http(s)代理,从上面的网络分层模型可以看出,http(s)代理工作在应用层,而socks5代理工作在会话层。http(s)代理只能处理HTTP和HTTPS协议的流量,而socks5代理可以传输任意协议的流量,包括TCP和UDP。因此这里我们使用socks5代理可以更好地支持各种校园网资源的访问。

通过上网查找,找到了多个github项目,提供了类似的Docker镜像和配置文件。

  1. docker-easyconnect:使深信服开发的非自由的VPN软件EasyConnect和aTrust运行在docker或 podman中,并作为网关和/或提供 socks5、http 代理服务
  2. aTrustLogin:自动化登录深信服aTrust而无需人工干预。提供Docker镜像,可使得aTrust在路由器上无人交互地自动运行、登录
  3. Docker-aTrust:通用Docker-aTrust:在Docker中运行aTrust,将虚拟网卡侧访问转换为本地 SOCKS5/HTTP 代理端口,并通过 Clash Verge 全局扩展脚本做分流。

这里我是用了第三个,其它两个配置方法类似,但是参数较多。

简要声明一下我本地的环境。宿主机系统是Windows 11,安装有Clash Verge作为分流工具,Real VNC作为VNC客户端;WSL2使用Debian发行版,网络设置为默认的NAT转发,其上安装了较为轻量的Docker engine,并在/etc/systemd/system/docker.service.d/http-proxy.conf中配置了HTTP代理便于拉取镜像。

主要的步骤都在该仓库的README中有详细说明,这里我只记录一些踩坑的经验。

由于我的WSL2网络设置为NAT转发,不能直接通过localhost访问容器的socks5和VNC服务端口,需要找到WSL在本地网络中的IP地址。使用ip addr | grep eth0命令可以找到WSL2的IP地址,例如172.28.181.31。然后在宿主机上使用VNC客户端连接172.28.181.31:5901,即可访问容器的图形界面。同时,Clash的扩展脚本与需要将server参数设置为WSL2的IP地址。下面提供我的Clash全局扩展脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function main(config) {
const NJUProxyName = "NJUConnect";
const NJUproxy = { // 配置代理节点
name: NJUProxyName,
type: "socks5",
server: "172.28.181.31", // WSL2的IP地址
port: 1080, // socks5代理端口
udp: true,
};

if (!config.proxies) config.proxies = [];
config.proxies.push(NJUproxy); // 将代理节点推入订阅文件

const NJUProxyGroup = { // 配置代理组
name: "NJU",
type: "fallback",
proxies: [NJUproxy.name],
};

if (!config["proxy-groups"]) config["proxy-groups"] = [];
config["proxy-groups"].push(NJUProxyGroup); // 将代理组推入订阅文件

if (!config.rules) config.rules = [];
const NJURules = [ // 配置分流规则
`DOMAIN,vpn.nju.edu.cn,${NJUProxyGroup.name}`,
`DOMAIN,ztna.nju.edu.cn,${NJUProxyGroup.name}`,
`DOMAIN-SUFFIX,nju.edu.cn,${NJUProxyGroup.name}`,
];

config.rules.unshift(...NJURules); // 将分流规则推入订阅文件

return config;
}

如此便可以实现使用Clash管理校园网“节点”的效果。

但是使用容器有一些缺点:

  1. 启动和登陆繁琐,网络配置较为复杂
  2. 需要VNC客户端,操作不够便捷
  3. 镜像实测占用大约2G的空间,不够轻量
  4. 需要用到docker,WSL,VNC等工具,一般用户上手难度大

总之,容器化方法不够优雅轻便。下面介绍只需要在宿主机上进行操作的方法。

使用开源客户端EZ4Connect

EZ4Connect是一个开源的EasyConnect/aTrust客户端,使用Qt开发,内核基于zju-connect,支持Linux、Windows和macOS。

直接在Windows上下载它的压缩包并解压,双击EZ4Connect.exe即可运行。它的默认配置是哈工大的校园网,在 “文件”-“设置”-“核心” 页面中设置好服务器地址ztna.nju.edu.cn

如图配置

然后需要在“文件”-“设置”-“认证”中找到对的认证方式,我在这里犯了难。首先,qrcode这种方式需要通过扫二维码来进行认证,还要在手机上下载客户端,比较麻烦;剩下httpOauth“统一身份认证”和psw“LDAP认证”两种方式。

三种认证方式

首先,“LDAP”是什么?“统一身份认证”又是什么?看来我需要对这两种认证方式进行一些了解。问了Gemini。

LDAP认证是什么?

LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)认证是指利用 LDAP 协议来验证用户身份的一种机制,常用于企业和组织内部的统一身份管理。

  • 基本概念

LDAP 本身是一种协议,用来访问和维护分布式的目录信息服务——你可以把它理解成一个树状结构的数据库,专门用来存储”谁是谁、属于哪个组、有什么属性”这类信息(用户名、邮箱、部门、密码哈希等)。

“LDAP 认证”通常指的是:应用系统不自己维护用户名密码,而是把认证请求转发给 LDAP 服务器(比如 OpenLDAP、Microsoft Active Directory)去验证。

  • 认证的核心流程(Bind 操作)

最常见的方式叫 simple bind 认证,大致是这样:

  1. 用户输入用户名密码,提交给应用
  2. 应用先用一个服务账号(有查询权限的账号)连接 LDAP 服务器,搜索这个用户名对应的完整 DN(Distinguished Name,比如 uid=zhangsan,ou=people,dc=example,dc=com
  3. 找到 DN 后,应用再拿这个 DN 和用户输入的密码,向 LDAP 服务器发起一次 bind(绑定)请求
  4. LDAP 服务器尝试用这个 DN + 密码做认证:
    • 成功 → 说明密码正确,认证通过
    • 失败 → 拒绝

关键点是:应用本身从不知道也不存储用户的真实密码,密码校验完全由 LDAP 服务器完成。这也是它比”应用自己存密码哈希”更安全、更集中化的原因之一。

  • DN 的结构

LDAP 用树状结构组织条目,一个典型 DN 长这样:

1
uid=zhangsan,ou=Students,ou=People,dc=university,dc=edu,dc=cn

从右往左读,越往右越”顶层”(类似域名从右往左解析)。

  • 常见使用场景

  • 企业内网系统:员工用同一套 AD/LDAP 账号登录邮箱、VPN、内部平台

  • Linux 服务器集中认证:SSH 登录时通过 PAM 模块查 LDAP,而不是本地 /etc/passwd

  • Web 应用的 SSO 前置层:很多单点登录方案背后其实是 LDAP + SAML/OAuth 的组合

  • 和其他协议的关系

  • Active Directory:微软实现,底层用 LDAP 作为查询协议之一,但整体功能更丰富(还包括 Kerberos 认证、组策略等)

  • Kerberos:很多企业环境里,AD 同时支持 LDAP simple bind 和 Kerberos 票据认证,后者更安全(不在网络上传密码),但配置更复杂

  • LDAPS:LDAP over TLS/SSL,明文 LDAP(389端口)容易被抓包看到密码,生产环境应该用 LDAPS(636端口)或 STARTTLS

这和“统一身份认证”有什么区别?

你敏锐地抓住了核心。学校里网页端的“统一身份认证(Authserver)”和底层的 LDAP 确实都是为了实现“一个账号走天下”的目的,但它们处于完全不同的技术层级。

简单来说:LDAP 是底层的“数据源和验证官”,而 Authserver 是前端的“门面(协议代理)”。

为了看清它们的区别,我们可以从“角色定位”和“登录流程”两个维度来拆解。


    1. 角色与技术的本质区别
  • LDAP:底层的“花名册”
    它是一个专门存储用户数据(学号、姓名、学院、密码哈希)的目录数据库。它只认得底层的 LDAP 协议,不认识什么是浏览器,什么是 HTTP,更没有精美的网页登录界面。它只负责老老实实地回答其他系统提问:“这个学号和密码对不对?”
  • Authserver(统一身份认证):前端的“外交官”
    你看到的网页端(比如基于 CAS、OAuth2.0 或 OIDC 协议开发的认证中心),是一个Web 应用程序。它的主要工作是跟浏览器打交道,提供网页界面、处理验证码、支持短信验证、管理浏览器的 Cookie 和 Session。而它自己同样不存密码,当你在网页上输入密码后,它会转头去问底层的 LDAP。

    1. 登录流程上的核心区别

正因为一个是网页端,一个是底层协议,它们在运行机制和数据流向上有着显著的不同。

  • 流程 A:通过学校 Authserver 网页登录(以 CAS/OAuth2 协议为例)

这是最典型的“单点登录(SSO)”流程。它的核心特点是:应用系统(如教务系统)自始至终接触不到你的密码。

  1. 重定向: 你访问教务系统,教务系统发现你没登录,把你的浏览器重定向(Redirect)到 authserver.university.edu/login
  2. 网页交互: 你的浏览器打开了统一认证网页。你在网页上输入学号、密码,甚至滑块验证码。
  3. 后台比对(关键点): Authserver 后台收到你的密码,立刻通过内部网络向 LDAP 服务器 发起查询。LDAP 验证通过,返回“正确”。
  4. 颁发令牌: Authserver 在你的浏览器里写入一个全局 Cookie(证明你登录了学校大门),并生成一个临时票据(Ticket/Code)让浏览器带回给教务系统。
  5. 登录成功: 教务系统拿着票据去向 Authserver 确认无误后,准许你进入。

优点: 安全性极高。所有第三方系统(教务、图书馆、外卖平台)都碰不到你的密码,密码只在 Authserver 的网页上输入过一次。同时支持浏览器跨标签页的自动登录。


  • 流程 B:通过底层的 LDAP 直接登录(如 VPN 客户端、服务器 SSH)

很多客户端软件(如你上面 Dockerfile 里的 EasyConnect VPN 客户端、或者实验室的 Linux 服务器 SSH 登录)是桌面端软件或命令行,它们无法轻易跳转网页,因此采用的是直连 LDAP 认证

  1. 直接收集: 你在 VPN 客户端(客户端软件,非网页)的输入框里,直接输入学号和密码。
  2. 直接传输: VPN 客户端把密码通过网络加密发送给 VPN 服务器。
  3. 直连验证: VPN 服务器内部直接调用 LDAP 客户端库(比如 libldap),向学校的 LDAP 服务器 发起 Bind 请求。
  4. 放行: LDAP 返回成功,VPN 服务器直接在本地为你建立加密隧道。

特点: 这种方式下,VPN 服务器是能够短暂接触到你输入的明文密码的(尽管它不会保存)。它适用于不受浏览器环境限制的传统客户端软件、网络设备、基础设施的认证。


  • 总结:它们是如何协同工作的?

在学校的实际 IT 架构中,它们通常是一体两面的配合关系:

$$\text{你的浏览器} \xrightarrow{\text{输入密码}} \text{Authserver (网页认证中心)} \xrightarrow{\text{LDAP协议验证}} \text{LDAP 目录服务器 (账本)}$$

$$\text{VPN客户端} \xrightarrow{\text{输入密码}} \text{VPN 服务器} \xrightarrow{\text{LDAP协议验证}} \text{LDAP 目录服务器 (账本)}$$

学校只维护一套 LDAP 账本。当你要在浏览器里登教务系统时,学校给你派出了 Authserver 去站岗,用网页和 Token 保护你的密码;当你要登 VPN 或连接实验室服务器时,这些系统就跳过网页,拿着你的密码直接去翻底层的 LDAP 账本

如此说来,似乎使用LDAP认证更加直接,省去了Web登陆的步骤。于是我选择了psw“LDAP认证”方式,在“保存的凭据”一栏中填入账号和密码,点击“应用”保存配置。

认证方式配置

回到主窗口,点击“连接服务器”后,会直接弹出输入手机验证码的窗口,只要填入验证码即可连上校园网。
实际上,试了一下“统一身份认证”,确实能够弹出Web认证窗口,但是似乎缺失了接收手机验证码的环节,因而会弹出错误窗口,无法连接到服务器,因而不能使用。
如此,我们实现了上面的“使用EZ4Connect连接”的效果。

与此同时,EZ4Connect客户端也提供了socks5代理服务,默认端口为11080。我们可以在Clash中配置类似上面的全局扩展脚本,只需要将server参数改为127.0.0.1port参数改为11080,同时点击EZ4Connect客户端的“禁用系统代理”功能即可在Clash中使用该代理“节点”连接校园网,实现统一管理。

直接用zju-connect内核连接

既然EZ4Connect是基于zju-connect内核开发的,那么直接使用zju-connect内核连接校园网也是可行的。zju-connect是一个开源的EasyConnect/aTrust客户端,同样支持Linux、Windows和macOS,但是没有GUI渲染的成本。

下载zju-connect的Windows版本,解压后在命令行中查询认证方式后,输入以下命令:

1
./zju-connect -protocol atrust -server ztna.nju.edu.cn -username <你的学号> -password <你的密码> -login-domain <登录域,记得选择LDAP方式的> -auth-type "auth/psw" -disable-zju-config -phone <你的手机号码> -client-data-file client_data.json

查询认证方式

具体的参数说明见README

然后会在命令行中要求你输入SMS验证码。

命令行登录

即可连接。同时,zju-connect也提供了socks5代理服务,默认端口为1080。我们可以在Clash中配置类似上面的全局扩展脚本,只需要将server参数改为127.0.0.1,port参数改为1080,即可在Clash中使用该代理“节点”连接校园网了。

为了方便运行,可以将上边的指令去掉开头的./,并将其保存为一个.bat后缀的批处理文件,双击即可运行。

折腾了一天,终于搞好咯。最后来研究一下zju-connect是如何实现“无副作用”连接校园网的。通过LLM总结,该程序能够做以下几件事:

  1. 逆向和模拟了EasyConnect/aTrust的登陆行为,完成用户登录认证
  2. Sangfor后台共有三个进程对系统环境进行扫描,程序通过硬编码报文“骗过”了这些扫描
  3. 建立VPN加密连接,获取校园网的访问权限,将请求打包到一条隧道中传输
  4. 绕过非法证书的攻击,直接在VPN隧道中传输HTTPS流量,保证用户隐私不被泄露
  5. 提供socks5代理服务,将校园网连接端口暴露给本地使用

源码量巨大,我对网络编程也没有正式的了解和实践,但是通过LLM的帮助,我大致理解了zju-connect的工作原理。总之,zju-connect是一个非常优秀的开源项目,值得学习和使用。

2026年7月7日。