臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

原文地址

https://smc.im/post/macos-resolve-atrust-clash-tun-conflict-with-docker/#%e8%a7%a3%e5%86%b3%e6%80%9d%e8%b7%af%20%E8%BF%99%E4%B8%AA%E5%91%A2 蓬叶虫(金黄的样子)

在原作的基础上加了一些自己的制定化配置,我是网络小白,站在巨人的基础之上做了点小小的优化,请支持原作!

背景与起因

这篇文章记录了我在 macOS 上利用 OrbStack 和 Docker 隔离 aTrust VPN、配合 Clash Tun 模式实现内外网分流的过程,目标是同时流畅访问公司内网和 Google 等外部服务。

起因很简单:公司的网络限制严格到访问哪个网站都得登记,实在让人忍无可忍。在我的使用环境中,不开 Tun 模式,基本访问不了白名单以外的网站。Tun 模式与普通代理模式的区别会在下文介绍,感兴趣的小伙伴可以配置完再看。

最近在 macOS 上开发时,这两套工具的冲突尤其让我头疼。

访问公司内部资源,必须通过 aTrust 连接 VPN。它是深信服开发的 VPN 工具,EasyConnect 也是他们家的产品。而日常查资料、做开发,我又离不开 Clash 来访问外部站点。

我原本以为两者共存没什么大问题,后来使用 Google 的 AI 编辑器 Antigravity 才发现:在我的配置下,必须开启 Clash 的 Tun 模式,才能正常加载模型列表和发送问题。

这就陷入了一个死循环:

  1. 打开 Clash 的 Tun 模式:外部服务可以正常使用,但它与 aTrust 的路由配置发生冲突,导致公司内网断连。
  2. 关闭 Tun 模式:aTrust 恢复正常,但 Google 等外部服务又无法正常使用。

来回切换实在太折腾,干脆把 aTrust 隔离起来!

解决思路

既然两者在 macOS 上容易发生冲突,那就把 aTrust 单独隔离起来。docker-easyconnect 星光狮 这个项目正好提供了在 Docker 中运行 aTrust 的方案。

我采用的方案是将 aTrust 扔进 Docker 容器里运行。容器内部通过 Tun 连接公司内网,然后暴露一个 SOCKS5 端口给宿主机。这样在 macOS 看来,aTrust 不再是一个修改系统网络配置的 VPN 软件,而只是一个普通的本地代理服务(Proxy)。接下来,在宿主机的 Clash 中添加这个 SOCKS5 代理,再把公司内网访问的 IP 分流到这个代理上就行了。

最后,由宿主机的 Clash 统一接管流量:

  • 访问 公司内网 IP -> 转发给 Docker 容器的 SOCKS5 端口。
  • 访问 google.com 或其他外网 -> 转发给机场节点。
  • Clash 开启 Tun 模式 -> 解决 Antigravity 的流量劫持问题。

拓扑结构

在这个方案中,OrbStack 是个关键。相比 Docker Desktop,它的网络桥接更直观,容器可以直接通过 IP 或 mDNS 访问,省去了很多端口映射的麻烦。

整体流量走向如下:

macOS 上 aTrust 隔离与流量分流架构

打开交互式架构图 爆焰仔

架构理清了,下面记录一下具体的配置过程。

实战步骤

1. 准备工作

  • OrbStack: macOS 上 Docker Desktop 的最佳替代品,轻量且网络互通性极好。

  • VNC Viewer: 用于访问容器内的图形界面(aTrust 登录框)。 -我用的是TigerVNC https://tigervnc.org/ 疾光千兽

  • Clash: 我使用的是 FlClash,支持 Tun 模式即可。

2. 部署 aTrust 容器

这里使用 docker-easyconnect 窃光蚊 这个项目。相比于直接 docker run,我更习惯用 docker-compose 管理,方便后续调整参数。

在任意目录下创建 docker-compose.yml:

services:
  atrust:
    image: hagb/docker-atrust:latest
    container_name: atrust
    # [新增] 特权模式:解决路由表报错和网络不稳定的关键
    privileged: true
    restart: unless-stopped
    
    devices:
      - /dev/net/tun
    # 虽然有了 privileged,保留 cap_add 也没坏处
    cap_add:
      - NET_ADMIN
    
    ports:
      # SOCKS5 端口:Clash 将通过这个端口连接内网
      - "1080:1080"
      # VNC 端口
      - "5901:5901"
      # HTTP 端口备用,这里可以注释掉或留着
      - "8888:8888"
    
    environment:
      - PASSWORD=123456
      - URLWIN=1
    
    volumes:
      - ./data:/root
      
    sysctls:
      - net.ipv4.conf.default.route_localnet=1

进入保存yml的地址 打开终端 输入命令 启动容器:

在终端启动 aTrust 容器

docker-compose up -d

3. 登录 VPN

容器启动后,aTrust 已经在后台运行,但还没登录。

  • 下载 VNC Viewer 古钟蛇(本命年的样子),然后打开。
  • 地址栏输入:127.0.0.1:5901。
  • 输入密码(对应上面 yaml 里的 PASSWORD)。
  • 此时你应该能看到熟悉的 aTrust 登录界面。输入服务器地址、用户名、密码。
  • 登录成功后,不要关闭容器,直接关掉 VNC 窗口即可。

验证连接: 在终端里测试一下容器内部是否通了:

curl --max-time 10 \
  --proxy socks5h://127.0.0.1:1080 \
  http://platform.cic.inter/

如果能 curl 通,说明容器内的 VPN 隧道已经建立。

4. 配置 Clash 分流

这是最关键的一步。我们需要告诉宿主机的 Clash:“凡是公司 IP 的请求,都转发给本地 1080 端口”。

我使用 FlClash 这款软件,可以直接在 配置 页面添加脚本:

进入配置- 选择 ‘更多’

进入 FlClash 配置页面

第二步 选择 ‘覆写’

选择覆写配置

选择 ‘脚本’

选择脚本配置

选择 ‘前往配置脚本’

前往配置脚本

选择 ‘添加’

添加 FlClash 配置脚本

将下列脚本复制粘贴到里面并保存

编辑 FlClash 配置脚本

新增脚本,然后直接填写如下代码:

function main(config) {
  // =====================================================
  // 基础配置
  // =====================================================
 
  // 主力代理组名称。
  // 除公司内网之外,其他所有流量都会交给这个代理组。
  // 名称必须与 FlClash 中显示的代理组名称完全一致。
  const MAIN_GROUP_NAME = "FlyBit";
 
  // 公司内网 SOCKS5 节点名称。
  // 后续公司域名和公司 IP 规则都会使用这个名称。
  const COMPANY_PROXY_NAME = "🏢 公司内网";
 
  // 强制使用规则模式。
  // 全局模式会忽略下面的分流规则,因此不能使用全局模式。
  config.mode = "rule";
 
  // =====================================================
  // 公司内网 SOCKS5 代理
  // =====================================================
 
  // ATrust 在本机 1080 端口提供的 SOCKS5 服务。
  // 当前 SOCKS5 不需要用户名和密码。
  const COMPANY_PROXY = {
    name: COMPANY_PROXY_NAME,
    type: "socks5",
    server: "127.0.0.1",
    port: 1080
  };
 
  // =====================================================
  // 公司内网匹配规则
  // =====================================================
 
  const COMPANY_RULES = [
    // 所有以 cic.inter 结尾的域名走公司内网代理。
    // 例如:platform.cic.inter、esso.cic.inter。
    `DOMAIN-SUFFIX,cic.inter,${COMPANY_PROXY_NAME}`,
 
    // 整个 10.197.x.x 网段走公司内网代理。
    // /16 表示匹配 10.197.0.0 至 10.197.255.255。
    // no-resolve 表示匹配时不额外进行域名解析。
    `IP-CIDR,10.197.0.0/16,${COMPANY_PROXY_NAME},no-resolve`
  ];
 
  // =====================================================
  // 添加公司代理节点
  // =====================================================
 
  // 如果原配置没有 proxies 数组,则先创建空数组。
  if (!config.proxies) config.proxies = [];
 
  // 删除可能已经存在的同名节点,防止重复添加。
  config.proxies = config.proxies.filter(
    proxy => proxy.name !== COMPANY_PROXY_NAME
  );
 
  // 把公司代理节点添加到节点列表最前面。
  config.proxies.unshift(COMPANY_PROXY);
 
  // =====================================================
  // 调整规则顺序
  // =====================================================
 
  // 保存机场原有规则。
  const ORIGINAL_RULES = Array.isArray(config.rules)
    ? config.rules
    : [];
 
  config.rules = [
    // 第一优先级:公司域名和公司 IP 走公司内网代理。
    ...COMPANY_RULES,
 
    // 第二优先级:其余所有流量全部走 FlyBit。
    // MATCH 会匹配所有尚未被前面规则匹配的请求。
    `MATCH,${MAIN_GROUP_NAME}`,
 
    // 保留机场原有规则。
    // 由于 MATCH 已经匹配所有剩余请求,这些规则实际上不会执行,
    // 仅保留在最终配置中。
    ...ORIGINAL_RULES
  ];
 
  // 返回修改完成的 FlClash 配置。
  return config;
}

此时,你不需要开启 Tun 模式,浏览器应该已经可以同时访问 google.com 和公司内网 IP 了。

有些软件走代理很卡(要走直连的话可以参考)

在 COMPANY_RULES 后面增加钉钉进程规则,并放在 MATCH 前面:

⚠️ 平台注意:下面的钉钉进程规则仅适用于 macOS!

Windows 必须改用带 .exe 后缀的实际进程名,例如 DingTalk.exe。

  • macOS:PROCESS-NAME,DingTalk,DIRECT
  • Windows:PROCESS-NAME,DingTalk.exe,DIRECT

辅助进程以 Windows 版 FlClash“请求详情”中显示的实际名称为准,不能只给 macOS 的 DingTalk Helper 名称追加 .exe。

此处的平台差异针对钉钉进程规则,公司域名和 IP 的分流规则可以沿用。

// 钉钉直连规则
const DINGTALK_RULES = [
  "PROCESS-NAME,DingTalk,DIRECT",
  "PROCESS-NAME,DingTalk Helper,DIRECT",
  "PROCESS-NAME,DingTalk Helper (Renderer),DIRECT",
  "PROCESS-NAME,DingTalk Helper (GPU),DIRECT"
];

然后把规则部分改成:

config.rules = [
  // 公司内网优先走 ATrust
  ...COMPANY_RULES,
 
  // 钉钉的其他请求直连
  ...DINGTALK_RULES,
 
  // 剩余全部流量走 FlyBit
  `MATCH,${MAIN_GROUP_NAME}`,
 
  ...ORIGINAL_RULES
];

最终顺序是:

公司域名和 10.197.x.x → 🏢 公司内网
钉钉进程               → DIRECT
其他所有流量           → FlyBit

钉钉的实际进程名称可以在 FlClash“请求详情”的“进程”字段查看。如果显示的名称不在上面,就按实际名称补充一条:

"PROCESS-NAME,请求详情中显示的进程名,DIRECT"

如果希望钉钉访问公司域名时也强制直连,把 ...DINGTALK_RULES 放到 ...COMPANY_RULES 前面。

想省事可以直接粘我的配置

⚠️ Windows 用户请先修改,再复制使用!

下方完整脚本中的钉钉进程名是 macOS 版本,Windows 不能原样使用。

Windows 请将脚本中的 DINGTALK_RULES 替换为:

const DINGTALK_RULES = [
  "PROCESS-NAME,DingTalk.exe,DIRECT"
];

其他钉钉辅助进程需要直连时,再按 FlClash“请求详情”中的实际进程名补充,名称须包含 .exe 后缀。

function main(config) {
  // =====================================================
  // 基础配置
  // =====================================================
 
  // 主力代理组名称。
  // 除公司内网和钉钉外,其他流量全部走这个代理组。
  // 必须与 FlClash 中的代理组名称完全一致。
  const MAIN_GROUP_NAME = "FlyBit";
 
  // 公司内网代理节点名称。
  const COMPANY_PROXY_NAME = "🏢 公司内网";
 
  // 强制使用规则模式。
  // 全局模式会忽略自定义分流规则。
  config.mode = "rule";
 
  // =====================================================
  // 公司内网 SOCKS5 代理
  // =====================================================
 
  // ATrust 在本机提供的 SOCKS5 服务。
  // 127.0.0.1 表示本机,1080 是 SOCKS5 监听端口。
  // 当前服务不需要用户名和密码。
  const COMPANY_PROXY = {
    name: COMPANY_PROXY_NAME,
    type: "socks5",
    server: "127.0.0.1",
    port: 1080
  };
 
  // =====================================================
  // 公司内网规则
  // =====================================================
 
  const COMPANY_RULES = [
    // cic.inter 及其所有子域名走公司代理。
    // 例如:platform.cic.inter、esso.cic.inter。
    `DOMAIN-SUFFIX,cic.inter,${COMPANY_PROXY_NAME}`,
 
    // 整个 10.197.x.x 网段走公司代理。
    // /16 匹配 10.197.0.0 至 10.197.255.255。
    `IP-CIDR,10.197.0.0/16,${COMPANY_PROXY_NAME},no-resolve`
  ];
 
  // =====================================================
  // 钉钉直连规则
  // =====================================================
 
  // 钉钉主进程及其辅助进程不走 FlyBit,直接连接网络。
  // 实际进程名称可以在 FlClash 的请求详情中确认。
  const DINGTALK_RULES = [
    "PROCESS-NAME,DingTalk,DIRECT",
    "PROCESS-NAME,DingTalk Helper,DIRECT",
    "PROCESS-NAME,DingTalk Helper (Renderer),DIRECT",
    "PROCESS-NAME,DingTalk Helper (GPU),DIRECT"
  ];
 
  // =====================================================
  // 添加公司代理节点
  // =====================================================
 
  // 如果原配置没有代理节点数组,则创建空数组。
  if (!config.proxies) config.proxies = [];
 
  // 删除可能已经存在的同名公司节点,防止重复添加。
  config.proxies = config.proxies.filter(
    proxy => proxy.name !== COMPANY_PROXY_NAME
  );
 
  // 把公司代理节点加入代理列表最前面。
  config.proxies.unshift(COMPANY_PROXY);
 
  // =====================================================
  // 设置规则顺序
  // =====================================================
 
  // 保存机场原有规则。
  const ORIGINAL_RULES = Array.isArray(config.rules)
    ? config.rules
    : [];
 
  config.rules = [
    // 第一优先级:
    // 公司域名和公司 IP 始终走 ATrust SOCKS5。
    ...COMPANY_RULES,
 
    // 第二优先级:
    // 钉钉进程直连。
    // 因为公司规则在前,所以钉钉访问公司内网时仍走公司代理。
    ...DINGTALK_RULES,
 
    // 第三优先级:
    // 其他所有流量全部走 FlyBit。
    `MATCH,${MAIN_GROUP_NAME}`,
 
    // 保留机场原有规则。
    // 由于前面的 MATCH 已匹配所有剩余流量,
    // 这些规则实际上不会执行,仅保留在配置预览中。
    ...ORIGINAL_RULES
  ];
 
  // 返回修改后的 FlClash 配置。
  return config;
}

总结

这套方案把“脏乱差”的 VPN 客户端关进了 Docker,还给了 macOS 一个清爽的网络环境。 虽然前期配置稍微麻烦点(写 Compose、配 Clash),但一旦跑通,后续的使用体验就是无感的。不再需要手动开关软件,也不用担心路由表爆炸,这才是开发环境该有的样子。

注意:我在本文前面提到,我本来以为 aTrust 和 Clash Tun 这两者共存也没什么大问题,但是后来别人提醒,aTrust 只要启动了,无论是否退出,都会有进程在后台监控流量。虽然“在后台监控流量”这件事,我没有去验证,但是我确实发现,当完全退出 aTrust 后,在 macOS 自带的活动监视器中,发现仍然有名为 aTrust Agent 的进程在后台活跃,这就让我产生了疑心。

所以,我最终决定,无论是否使用 Clash Tun 模式,aTrust 这个垃圾软件,注定必须要使用 Docker 容器隔离了。

疑问

既然本机可以,那我把这个镜像装到公网的虚拟机上,别人不也可以访问了?

致谢