当前位置:首页 > 未分类

绿联云NAS Docker透明代理完全指南

Ray5小时前未分类424

绿联云 NAS Docker 透明代理完全指南:从入门到精通

设备环境:绿联云 DH4300PLUS(ARM64)· UGOS Pro(Debian 定制)· Docker · v2rayA 2.2.7.5 + Xray 26.1.23

目标:在绿联云 NAS 上部署 v2rayA 透明代理,实现局域网设备(手机、电脑、游戏主机等)仅修改网关即可科学上网,同时支持 IPv6 双栈和安全加固。

本文是四次深度排障实战的完整记录,涵盖 Docker 网络模式选型、macvlan 网络配置、IPv6 SLAAC 获取、透明代理模式选择、DNS 防污染、安全加固等全部核心话题。


目录

  1. 背景与目标
  2. 踩坑之路:从 bridge 到绿联云面板 macvlan 再到命令行 macvlan
  3. 正确创建 Macvlan 网络(命令行方式)
  4. v2rayA 容器部署与 IPv4 验证
  5. IPv6 双栈配置:ULA + SLAAC 方案
  6. 透明代理模式选择:tproxy / redirect / TUN
  7. v2rayA 面板配置详解
  8. 安全加固:封锁公网 IPv6 暴露
  9. Macvlan 宿主机通信:vlink 桥接
  10. 其他容器走代理(青龙面板等)
  11. 持久化与开机自启
  12. 完整命令参考与 docker-compose
  13. 常见问题与排障速查

1. 背景与目标

家庭网络中,想让所有设备(手机、平板、电视、游戏主机)无感科学上网,最优雅的方案是旁路由 / 透明网关

  • 路由器负责基本的 DHCP 和 WiFi;
  • NAS 运行代理容器,作为局域网设备的网关
  • 设备只需把网关指向 NAS 的 IP,即可自动走代理。

绿联云 NAS(UGOS Pro)底层基于 Debian,原生支持 Docker,硬件性能足以胜任透明代理的加解密负载。但由于 UGOS 在内核、Docker 配置、网络架构上做了大量定制,实际部署中会遇到诸多”绿联专属”的坑。本文将逐一记录并给出解决方案。


2. 踩坑之路:从 bridge 到绿联云面板 macvlan 再到命令行 macvlan

本节记录真实的实验路径。如果你只想看结论,可以直接跳到第 3 节

2.1 第一步:bridge / host 模式——此路不通

最初的想法很简单:用 Docker 默认的 bridge 网络或 host 模式跑 v2rayA。结果两条路都走不通:

模式 尝试结果
bridge(默认) 容器 IP 在 docker0 虚拟网段内,局域网设备无法直接访问容器 IP,无法用作网关
host v2rayA 启动透明代理时会执行 sysctl -w net.ipv4.ip_forward=0,直接关闭宿主机 IP 转发,导致 NAS 上所有 bridge 容器(qBittorrent、Jellyfin 等)瞬间断网

结论:透明代理需要容器拥有独立的局域网 IP,只能用 macvlan 模式。

2.2 第二步:用绿联云可视化面板创建 macvlan——掉入深坑

知道要用 macvlan 后,自然的想法是通过绿联云的 Docker 可视化管理界面来创建。然而面板在引导创建 Macvlan 时,会强制要求先创建一个虚拟网桥来桥接物理网卡、系统虚拟网卡和 Macvlan 网络。

创建后发现透明代理完全不工作——流量转发全部被拦截。尝试了大量排查:

  • 检查 iptables 规则,发现 FORWARD 链默认 DROP
  • 手动 iptables -F 清空规则后暂时能通,但 NAS 其他 Docker 服务又全挂了;
  • 怀疑是 v2rayA 配置问题,反复调整代理模式、DNS 设置,均无效。

经过反复排查,最终定位到根因

绿联云 UGOS 系统会将物理网卡 eth0 包裹在一个虚拟网桥 bridge0 中。UI 创建的 macvlan 实际上挂载在 bridge0 上而非 eth0 上,导致所有 macvlan 流量经过 bridge0 时被 bridge-nf-call-iptables 强制送入宿主机 iptables 链,被 Docker 的默认 FORWARD DROP 策略拦截。

eth0 → bridge0 (虚拟网桥) → 宿主机 IP
                  ↑
         macvlan 挂在这里 → 流量被 bridge-nf-call-iptables 拦截!
# ❌ UI 创建的 macvlan 等效于
docker network create -d macvlan -o parent=bridge0 ...

2.3 第三步:弃用面板,命令行直接绑定 eth0——跑通 IPv4

明白了根因后,操作分两步:

  1. 先在绿联云的 Docker 可视化面板中删除之前创建的 Macvlan 网络,然后进入 系统设置 → 网络删除 UI 自动创建的虚拟网桥(这一步是关键——删除网桥后 eth0 才会从 bridge0 中释放出来);
  2. 通过 SSH 命令行直接绑定物理网卡 eth0 重新创建 macvlan:
# ✅ 正确做法:命令行创建,直接绑定 eth0
docker network create -d macvlan \
  --subnet=192.168.31.0/24 \
  --gateway=192.168.31.1 \
  -o parent=eth0 \
  macvlan_v2raya

启动 v2rayA 容器,手机设置网关为容器 IP,IPv4 透明代理一次跑通

2.4 第四步:进一步跑通 IPv6 双栈

IPv4 成功后,继续攻克 IPv6(详见第 5 节)。

2.5 macvlan 的核心优势

回顾整个过程,macvlan 模式(正确绑定到 eth0)的核心优势:

  • 容器拥有独立的局域网 IP(如 192.168.31.2),手机可以直接把网关指向它;
  • 容器拥有独立的网络命名空间,iptables 规则只作用于容器内部,不影响宿主机和其他容器;
  • 直接绑定物理网卡时,完全绕过 bridge-nf-call-iptables 等内核参数干扰。

3. 正确创建 Macvlan 网络(命令行方式)

3.1 创建命令

# ✅ 直接绑定物理网卡 eth0
docker network create -d macvlan \
  --subnet=192.168.31.0/24 \
  --gateway=192.168.31.1 \
  -o parent=eth0 \
  macvlan_v2raya

3.2 释放 eth0:删除绿联云的虚拟网桥

绿联云系统默认将 eth0 包裹在虚拟网桥 bridge0 中。如果 eth0 仍被 bridge0 占用(brctl show bridge0 可查看),macvlan 将无法直接绑定 eth0。需要先释放:

方法一(推荐):通过绿联云系统 UI 删除

进入绿联云管理界面 → 系统设置 → 网络,找到之前创建的虚拟网桥并删除。删除后系统会自动将网络切换回物理网卡 eth0 直连,无需手动配置 IP 和路由。

注意:删除网桥的瞬间 NAS 管理连接会短暂中断,刷新页面即可恢复。

方法二(备选):通过命令行手动拆除

如果 UI 中找不到删除选项,可以通过 SSH 手动操作:

# 查看 bridge0 是否包含 eth0
brctl show bridge0

# 如果包含,将 eth0 从 bridge0 中移除
ip link set bridge0 down
brctl delif bridge0 eth0
ip link del bridge0

# 将 IP 迁移到 eth0
ip addr add 192.168.31.178/24 dev eth0
ip link set eth0 up
ip route add default via 192.168.31.1

⚠️ 风险提示:命令行操作时 NAS 的管理 IP 会暂时中断,建议通过物理连接或 SSH 多开一个会话操作。此方式未经本文作者实测,仅作为理论参考。

3.3 替代方案:关闭 bridge-nf-call-iptables

⚠️ 未实测:本文作者未实际尝试此方案,仅作为理论备选。

如果不想拆 bridge0,可以关闭内核参数:

sysctl -w net.bridge.bridge-nf-call-iptables=0
sysctl -w net.bridge.bridge-nf-call-ip6tables=0

这样 bridge 上的流量不再被送入 iptables,但可能影响 Docker 的其他网络隔离功能。


4. v2rayA 容器部署与 IPv4 验证

命令行创建 macvlan 后(第 2.3 步),下一步就是启动 v2rayA 容器,验证 IPv4 透明代理是否跑通。如果你计划直接部署 IPv6 双栈版本,可以跳到第 5 节第 12 节的终极命令。

4.1 启动容器

假设已按第 3 节创建好 macvlan 网络:

docker run -d \
  --name v2raya \
  --restart=always \
  --privileged \
  --network=macvlan_v2raya \
  --ip=192.168.31.2 \
  --sysctl net.ipv4.ip_forward=1 \
  -v /volume1/docker/v2raya/etc:/etc/v2raya \
  mzz2017/v2raya

关键参数说明:

参数 作用
--privileged 授予完整内核权限,透明代理需要操作 iptables/路由表
--network=macvlan_v2raya 使用 macvlan 网络
--ip=192.168.31.2 指定固定 IP,作为局域网网关地址
--sysctl net.ipv4.ip_forward=1 容器内启用 IP 转发
-v ...:/etc/v2raya 持久化配置文件

4.2 访问 Web 面板

浏览器打开 http://192.168.31.2:2017,首次访问需设置管理员密码。

4.3 客户端配置

手机/电脑的 WiFi 设置:

  • IP:手动或自动(保持局域网段即可)
  • 网关192.168.31.2(v2rayA 容器 IP)
  • DNS192.168.31.2 或保持默认均可(v2rayA 会劫持 DNS 流量)

5. IPv6 双栈配置:ULA + SLAAC 方案

IPv4 透明代理跑通后(第 2.3 步),下一个目标就是让 IPv6 也走代理。这对应踩坑之路的第四步,也是整个部署中最”反直觉”的环节——Docker 对 IPv6 的支持远不如 IPv4 开箱即用,需要一套”ULA 激活 + SLAAC 获取”的组合拳。

5.1 问题背景

家用宽带的 IPv6 前缀(如 2408:xxxx:xxxx:xxxx::/64)是动态的,每隔 48-72 小时或重启路由器后会变化。如果在 macvlan 网络中写死公网 IPv6 前缀,前缀变化后容器 IPv6 立刻失效。

5.2 解决方案:ULA 激活 + SLAAC 获取公网地址

核心思路:

  1. 使用 ULA 地址(fd00:8::/64 作为 macvlan 的 IPv6 子网,仅用于激活 Docker 的 IPv6 协议栈;
  2. 通过 SLAAC(accept_ra=2 让容器自动从路由器获取公网 IPv6 地址;
  3. 删除 Docker 自动生成的 ULA 默认路由,避免路由冲突。

5.3 创建双栈 macvlan 网络

docker network create -d macvlan \
  --subnet=192.168.31.0/24 \
  --gateway=192.168.31.1 \
  --ipv6 \
  --subnet=fd00:8::/64 \
  --gateway=fd00:8::1 \
  -o parent=eth0 \
  macvlan_v2raya

关键发现:Docker 默认不为 macvlan 接口启用 IPv6。必须在 docker network create 时加上 --ipv6 参数,否则容器网卡的 IPv6 协议栈不会被激活,无法接收 RA(Router Advertisement)。

5.4 daemon.json 配置(可选)

对于 macvlan 模式,通常不需要修改 daemon.json。以下配置仅在使用 bridge 网络的 IPv6 时需要:

{
    "data-root": "/volume1/@docker",
    "ipv6": true,
    "fixed-cidr-v6": "fd00:1::/64",
    "experimental": true,
    "ip6tables": true
}

5.5 启动带 IPv6 支持的容器

docker run -d \
  --name v2raya \
  --restart=always \
  --privileged \
  --network=macvlan_v2raya \
  --ip=192.168.31.2 \
  --sysctl net.ipv4.ip_forward=1 \
  --sysctl net.ipv6.conf.all.forwarding=1 \
  --sysctl net.ipv6.conf.all.accept_ra=2 \
  --sysctl net.ipv6.conf.default.accept_ra=2 \
  --sysctl net.ipv6.conf.eth0.accept_ra=2 \
  -v /volume1/docker/v2raya/etc:/etc/v2raya \
  --entrypoint sh \
  mzz2017/v2raya \
  -c "echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind; ip -6 route del default via fd00:8::1 2>/dev/null || true; ip6tables -I INPUT -p tcp --dport 2017 -j DROP; exec v2raya"

关键 sysctl 参数解析

参数 作用
net.ipv6.conf.all.forwarding=1 启用 IPv6 转发(旁路由必需)
net.ipv6.conf.all.accept_ra=2 值为 2 表示即使在 forwarding 开启时也接受 RA
net.ipv6.conf.eth0.accept_ra=2 对容器内 eth0 接口单独设置

为什么 accept_ra 必须是 2? Linux 内核的默认行为是:当 forwarding=1 时自动忽略 RA(因为转发节点不应该动态获取地址)。设为 2 可以强制覆盖这一行为。

entrypoint 启动脚本解析

# 1. 开启 ip_nonlocal_bind(tproxy 透明代理必需,否则 connect: invalid argument)
echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind

# 2. 删除 Docker 自动创建的 ULA 默认路由(避免 IPv6 流量走 fd00:8::1 黑洞)
ip -6 route del default via fd00:8::1 2>/dev/null || true

# 3. 在 IPv6 防火墙中拦截对 2017 端口的入站连接(安全加固,详见第 8 节)
ip6tables -I INPUT -p tcp --dport 2017 -j DROP

# 4. 启动 v2rayA 主进程
exec v2raya

5.6 验证 IPv6

# 进入容器
docker exec -it v2raya sh

# 查看网卡地址,应看到 2408 开头的公网 IPv6
ip -6 addr show eth0

# 测试 IPv6 连通性
ping6 ipv6.google.com

成功获取公网 IPv6 地址示例:

inet6 2408:xxxx:xxxx:xxxx:42:acff:fe12:3456/64 scope global dynamic
inet6 fd00:8::2/64 scope global        ← ULA 地址(仅用于激活协议栈)
inet6 fe80::42:c0ff:fea8:1f02/64 scope link  ← 链路本地地址

6. 透明代理模式选择:tproxy / redirect / TUN

IPv4 和 IPv6 网络层面都跑通后,接下来需要选择和调优透明代理的工作模式。看似只是 v2rayA 面板上选一个选项的事,实际上又踩了不少坑——tproxy 模式一度因为 connect: invalid argument 和 DNS 死锁被误判为”在 macvlan 上不可用”,最终经过深度排查才定位到 ip_nonlocal_bind 这个隐藏的内核参数。

6.1 三种模式对比

模式 工作层级 TCP UDP 性能 macvlan 兼容性
redirect iptables NAT 表 ✅ 好
tproxy iptables mangle 表 最高 ✅ 需正确配置
system tun 虚拟 TUN 网卡 中高 ✅ 好
gvisor tun 用户态 TUN 模拟 ✅ 最好

6.2 tproxy 在 macvlan 中的排障实录

在最初的测试中,tproxy 模式在 macvlan 容器内会遇到 connect: invalid argument 错误。经过多轮深度排查,最终定位到两个根因,并全部解决:

根因一:ip_nonlocal_bind 未开启(关键!)

tproxy 需要绑定非本机 IP(冒充被劫持流量的目标地址)。绿联云系统的 net.ipv4.ip_nonlocal_bind 默认为 0,这直接导致了 connect: invalid argument 错误。修复方法:

# 在容器内开启(必须在 v2rayA 启动前执行)
echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind
# 或
sysctl -w net.ipv4.ip_nonlocal_bind=1

这是整个 tproxy 问题的核心根因,并非 macvlan 与 tproxy 不兼容,而是缺少这个内核参数。

根因二:DNS 自拦截死锁

macvlan 容器拥有独立的网络命名空间,tproxy 的 iptables 规则会劫持容器自身发出的 DNS 请求,形成死循环:

v2rayA 需要解析代理节点域名 → DNS 请求被自己的 tproxy 规则拦截 → 再次交给 v2rayA 处理 → 无限循环

解决方案:

  • 代理节点配置使用 IP 地址而非域名;
  • DNS 服务器使用直连 IP(如 223.5.5.58.8.8.8),避免经过域名二次解析;
  • v2rayA 中将”防止 DNS 污染”设为关闭DoH(DoH 更安全但首屏可能稍慢);
  • DNS 设置中排除代理节点域名的解析。

内核兼容性验证

通过 ping -m 255ip rule add fwmark 测试,确认绿联云内核完整支持 SO_MARK 和策略路由(CONFIG_IP_MULTIPLE_TABLES=y)。实际验证中,tproxy 所需的 xt_TPROXY 模块、table 100 策略路由、fwmark 0x40/0xc0 标记规则均正常工作,iptables 链(TP_PRETP_OUTTP_RULETP_MARK)的包计数器持续增长,日志中出现 [transparent -> proxy] 记录,证明 tproxy 在 macvlan 环境中完全可用

6.3 最终推荐:tproxy 模式

经过完整排障,tproxy 在 macvlan 环境中完全可用,并且是性能最优的选择:

  • 完美支持 TCP + UDP(游戏主机联机无忧);
  • 内核级透明代理,性能最高,CPU 开销最低;
  • 相比 TUN 模式少约 10-20% 的 CPU 使用率(TUN 需要用户态 ↔ 内核态数据拷贝);
  • IPv4 和 IPv6 双栈 tproxy 均可正常工作。

tproxy 的前置条件(缺一不可):

# 1. 开启 ip_nonlocal_bind(核心!否则 connect: invalid argument)
sysctl -w net.ipv4.ip_nonlocal_bind=1

# 2. 开启 IP 转发
sysctl -w net.ipv4.ip_forward=1

# 3. 确保容器以 --privileged 运行(需要操作 iptables mangle 表和策略路由)

备选方案:如果因任何原因无法使用 tproxy(如内核缺少 xt_TPROXY 模块),可退而求其次选择 system tungvisor tun 模式,兼容性好但性能略低。


7. v2rayA 面板配置详解

7.1 核心设置

在 v2rayA 的 设置 (Settings) 页面:

设置项 推荐值 说明
透明代理/系统代理实现方式 tproxy 性能最优;如不可用改 system tun
透明代理/系统代理 启用:大陆白名单模式 国内直连,其余走代理
防止 DNS 污染 关闭DoH 关闭最稳;DoH 更安全但首屏可能稍慢
特殊模式 关闭 非软路由环境无需开启
TCPFastOpen 关闭或保持默认 部分线路不支持会导致连接失败
多路复用(Mux) 关闭 开启会增加延迟,仅在线路极差时考虑
流量嗅探 启用 关键!用于纠偏被污染的 DNS 结果

7.2 流量嗅探(Sniffing)的重要性

嗅探功能是 v2rayA 透明代理的”灵魂”:

  1. 手机向路由器发起 DNS 请求,可能拿到被污染的错误 IP;
  2. 手机向错误 IP 发起连接,流量经过 v2rayA;
  3. v2rayA 嗅探 TLS ClientHello 中的 SNI(域名信息),发现实际要访问 youtube.com
  4. v2rayA 丢弃错误 IP,在远端重新解析正确地址并建立连接。

必须开启 QUIC 嗅探:YouTube App 大量使用 QUIC(UDP 443)协议。如果不开启 QUIC 嗅探,App 可能无限转圈。

7.3 白名单模式注意事项

“大陆白名单”模式会在 iptables 中注入数千条中国 IP 段的 RETURN 规则。在 ARM 架构的 NAS 上,这可能导致:

  • 规则注入时间极长(数十秒甚至数分钟);
  • 每个数据包都要遍历所有规则,造成显著的 CPU 负载和延迟

如果遇到性能问题,建议改用 GFWList 模式(仅代理被墙域名),规则数量大幅减少,性能压力显著降低。(⚠️ 本文作者使用的是白名单模式,GFWList 模式未实测。)


8. 安全加固:封锁公网 IPv6 暴露

IPv6 双栈跑通、tproxy 模式调通之后,本以为大功告成。然而在一次偶然的测试中,用手机 5G 流量直接访问容器的公网 IPv6 地址,竟然打开了 v2rayA 管理面板——这意味着任何人都可以从公网控制你的代理服务。

8.1 问题发现

配置 IPv6 双栈后,v2rayA 容器获得了公网 IPv6 地址(如 2408:xxxx:xxxx:xxxx:42:acff:fe12:3456)。由于 v2rayA 默认监听所有网络接口,Web 管理面板直接暴露在公网

手机 5G 流量访问 http://[2408:...]:2017 → 直接打开管理面板!

8.2 尝试过的方案

方案 结果
设置环境变量 V2RAYA_ADDRESS=0.0.0.0:2017 ❌ 无效,v2rayA 仍监听所有地址
启动参数 --address 0.0.0.0:2017 ❌ 无效,Go 程序仍遍历所有 IP 并监听

v2rayA 基于 Go 语言开发,其监听逻辑会遍历所有网络接口并绑定。日志中打印的地址列表实际反映了真实的监听状态。

8.3 终极解决方案:ip6tables 内核级封锁

既然应用层无法控制,直接在容器内的 IPv6 防火墙拦截:

ip6tables -I INPUT -p tcp --dport 2017 -j DROP

将这条命令写入容器的 entrypoint 脚本,在 v2rayA 启动之前执行:

--entrypoint sh mzz2017/v2raya \
-c "echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind; \
    ip -6 route del default via fd00:8::1 2>/dev/null || true; \
    ip6tables -I INPUT -p tcp --dport 2017 -j DROP; \
    exec v2raya"

8.4 为什么这样做是安全的?

  • 只拦截 INPUT 入站:不影响容器通过 IPv6 出站连接代理节点;
  • 内核级拦截:数据包在到达 v2rayA 进程之前就被丢弃,效率极高;
  • 随容器自动生效:写在 entrypoint 中,容器重启后自动恢复。

8.5 验证

# 局域网 IPv4 访问(应成功)
curl http://192.168.31.2:2017

# 公网 IPv6 访问(应超时)
# 手机关闭 WiFi,使用 5G 流量
# 浏览器访问 http://[2408:xxxx:xxxx:xxxx:...]:2017
# 预期:连接超时

9. Macvlan 宿主机通信:vlink 桥接

⚠️ 未实测:本节内容为理论方案,作者尚未在自己的环境中实际配置 vlink。

代理功能完善、安全加固完成后,日常使用中又发现了一个问题:从 NAS 本机竟然访问不了 v2rayA 的 Web 面板,NAS 上的其他容器也无法通过 v2rayA 上网。这是 macvlan 的一个”设计特性”——宿主机与自己创建的 macvlan 容器天然隔离。

9.1 问题

Macvlan 有一个设计层面的限制:宿主机与其自身创建的 macvlan 容器无法直接通信。这意味着:

  • NAS 本机无法访问 192.168.31.2:2017(v2rayA 面板);
  • NAS 本机的应用(如青龙面板的脚本)无法通过 v2rayA 代理上网。

9.2 解决方案:创建 vlink 子接口

# 创建 macvlan 子接口
ip link add vlink link eth0 type macvlan mode bridge

# 分配一个局域网 IP(不要与其他设备冲突)
ip addr add 192.168.31.100/32 dev vlink

# 启用接口
ip link set vlink up

# 添加到 v2rayA 容器的路由
ip route add 192.168.31.2/32 dev vlink

现在宿主机可以通过 vlink 接口与 macvlan 容器通信了。

9.3 如果需要 NAS 本机也走代理

⚠️ 未实测 + 高风险:此操作未经作者实测,且风险较高,请谨慎操作。

# 将 vlink 设为默认网关(宿主机流量走 v2rayA)
ip route del default
ip route add default via 192.168.31.2 dev vlink

⚠️ 风险提示:如果 v2rayA 容器停止,宿主机将完全断网。建议仅在测试时使用,生产环境建议为 NAS 本机保留直连路由。


10. 其他容器走代理(青龙面板等)

⚠️ 未实测:本节两种方案均为理论指导,作者尚未实际将青龙等容器接入代理。

10.1 方案一:加入 macvlan 网络

将需要走代理的容器也加入同一个 macvlan 网络,并将网关指向 v2rayA:

docker run -d \
  --name qinglong \
  --network=macvlan_v2raya \
  --ip=192.168.31.3 \
  whyour/qinglong

容器内手动设置路由:

docker exec qinglong ip route replace default via 192.168.31.2

10.2 方案二:使用 HTTP 代理环境变量

如果不想改网络模式,可以在容器启动时注入代理环境变量:

docker run -d \
  --name qinglong \
  -e HTTP_PROXY=http://192.168.31.2:20171 \
  -e HTTPS_PROXY=http://192.168.31.2:20171 \
  -e ALL_PROXY=socks5://192.168.31.2:20170 \
  whyour/qinglong

注意:需要在 v2rayA 设置中确认 SOCKS5(20170)和 HTTP(20171)代理端口已开启。


11. 持久化与开机自启

11.1 Docker 自动重启

容器使用 --restart=always 参数,Docker 服务启动时会自动拉起容器。macvlan 网络和容器配置均持久化存储,重启 NAS 后自动恢复。

11.2 vlink 和内核参数的持久化

⚠️ 未实测:以下启动脚本为理论方案,作者尚未在绿联云上实际配置开机自启脚本。

Docker 容器内的 entrypoint 脚本(ip6tables 规则、路由删除)会随容器启动自动执行。但宿主机上的 vlink 和内核参数需要通过启动脚本持久化:

# /etc/rc.local 或绿联云的自启动脚本
#!/bin/bash

# 如果通过绿联云系统设置删除了虚拟网桥,系统会自动切回 eth0 直连,通常无需额外配置
# 如果是命令行手动拆除 bridge0,则需要手动恢复 eth0 的 IP:
# ip addr add 192.168.31.178/24 dev eth0
# ip route add default via 192.168.31.1

# 创建 vlink(如需宿主机访问 v2rayA)
ip link add vlink link eth0 type macvlan mode bridge
ip addr add 192.168.31.100/32 dev vlink
ip link set vlink up
ip route add 192.168.31.2/32 dev vlink

# 确保宿主机 ip_forward 开启(bridge 容器依赖)
sysctl -w net.ipv4.ip_forward=1
# 注意:ip_nonlocal_bind 无需在宿主机设置,已在容器 entrypoint 中配置(容器有独立的网络命名空间)

注意:绿联云系统升级可能覆盖自定义启动脚本。建议升级前备份。


12. 完整命令参考与 docker-compose

12.1 完整 Docker 命令(终极版)

# 1. 创建双栈 macvlan 网络
docker network create -d macvlan \
  --subnet=192.168.31.0/24 \
  --gateway=192.168.31.1 \
  --ipv6 \
  --subnet=fd00:8::/64 \
  --gateway=fd00:8::1 \
  -o parent=eth0 \
  macvlan_v2raya

# 2. 启动 v2rayA 容器(IPv6 + 安全加固)
docker run -d \
  --name v2raya \
  --restart=always \
  --privileged \
  --network=macvlan_v2raya \
  --ip=192.168.31.2 \
  --sysctl net.ipv4.ip_forward=1 \
  --sysctl net.ipv6.conf.all.forwarding=1 \
  --sysctl net.ipv6.conf.all.accept_ra=2 \
  --sysctl net.ipv6.conf.default.accept_ra=2 \
  --sysctl net.ipv6.conf.eth0.accept_ra=2 \
  -v /volume1/docker/v2raya/etc:/etc/v2raya \
  --entrypoint sh \
  mzz2017/v2raya \
  -c "echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind; ip -6 route del default via fd00:8::1 2>/dev/null || true; ip6tables -I INPUT -p tcp --dport 2017 -j DROP; exec v2raya"

12.2 docker-compose.yml

⚠️ 未实测:以下 docker-compose 配置由 docker run 命令等价转换而来,作者实际使用的是 docker run 命令行方式。

version: "3.9"

services:
  v2raya:
    image: mzz2017/v2raya
    container_name: v2raya
    restart: always
    privileged: true
    networks:
      macvlan_v2raya:
        ipv4_address: 192.168.31.2
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.all.accept_ra=2
      - net.ipv6.conf.default.accept_ra=2
      - net.ipv6.conf.eth0.accept_ra=2
    volumes:
      - /volume1/docker/v2raya/etc:/etc/v2raya
    entrypoint: sh
    command: >
      -c "echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind;
          ip -6 route del default via fd00:8::1 2>/dev/null || true;
          ip6tables -I INPUT -p tcp --dport 2017 -j DROP;
          exec v2raya"

networks:
  macvlan_v2raya:
    driver: macvlan
    driver_opts:
      parent: eth0
    enable_ipv6: true
    ipam:
      config:
        - subnet: 192.168.31.0/24
          gateway: 192.168.31.1
        - subnet: fd00:8::/64
          gateway: fd00:8::1

13. 常见问题与排障速查

Q1:v2rayA 启动后,NAS 上其他 Docker 容器全部断网

原因:v2rayA 以 host 模式运行,启动透明代理时关闭了 ip_forward

解决:改用 macvlan 模式部署 v2rayA。


Q2:手机设置网关后能上百度,但不能上 Google

排查步骤

  1. 检查手机是否关闭了 IPv6(IPv6 流量会绕过 IPv4 透明代理);
  2. 检查 v2rayA 日志是否有 [transparent -> proxy] 记录;
  3. 检查代理节点是否正常连通。

Q3:手机设置网关后百度和 Google 都打不开

常见原因

  • tproxy 模式下 ip_nonlocal_bind=0,导致直连流量报 invalid argument
  • DNS 死锁:代理节点使用域名,DNS 请求被代理自身拦截形成环路。

解决

  1. 确保 ip_nonlocal_bind=1sysctl -w net.ipv4.ip_nonlocal_bind=1);
  2. 将”防止 DNS 污染”设为关闭;
  3. 代理节点使用 IP 地址而非域名。

Q4:YouTube App 无限转圈,但浏览器正常

原因:YouTube App 使用 QUIC(UDP 443)协议,v2rayA 未嗅探 QUIC 流量。

解决:在 v2rayA 设置中开启 QUIC 嗅探


Q5:iptables -F 能”修复”透明代理,但 NAS 其他服务挂了

原因:macvlan 绑定在 bridge0 上,bridge-nf-call-iptables=1 导致 bridge 流量被 iptables FORWARD DROP 规则拦截。iptables -F 清空了 DROP 规则所以暂时通了,但也清掉了 Docker 的其他必要规则。

解决:macvlan 绑定到 eth0 而非 bridge0(参见第 2.2 和第 3 节)。


Q6:容器获取不到公网 IPv6 地址

排查清单

  1. macvlan 创建时是否加了 --ipv6 参数;
  2. 容器启动时是否设置了 accept_ra=2
  3. 是否删除了 ULA 默认路由(否则 SLAAC 的路由会被覆盖);
  4. 路由器是否开启了 IPv6 RA 广播。
# 容器内检查
ip -6 addr show eth0      # 查看地址
ip -6 route show           # 查看路由表,default 应指向路由器

Q7:v2rayA 面板可以通过公网 IPv6 访问(安全隐患)

解决:在容器 entrypoint 中添加 ip6tables -I INPUT -p tcp --dport 2017 -j DROP


Q8:DoH 开启后首屏加载变慢

原因:DoH 需要 TCP + TLS 握手,且请求还要经过代理隧道,首次解析延迟较高。后续请求受益于连接复用和 DNS 缓存,速度恢复正常。

优化方案

  • 使用 DNS 分流:国内域名走 UDP DNS(223.5.5.5),海外域名走 DoH;
  • 如果手机同时开了代理软件(Fake-IP 模式),NAS 上可以关闭 DoH,因为 DNS 解析已被手机代理接管。

Q9:宿主机访问不了 v2rayA 面板

原因:macvlan 的设计限制——宿主机与自身的 macvlan 容器隔离。

解决:创建 vlink 子接口(参见第 9 节)。


Q10:NAS 重启后 IPv6 前缀变化,容器 IPv6 失效

解决:使用 ULA + SLAAC 方案(参见第 5 节)。macvlan 使用固定的 ULA 子网 fd00:8::/64,公网 IPv6 通过 SLAAC 动态获取,无需手动更新。


总结

本文完整记录了在绿联云 NAS 上从零搭建 Docker 透明代理的全过程,核心结论如下:

  1. 网络模式:必须使用 macvlan,避免 host 模式破坏其他容器;
  2. macvlan 绑定:直接绑定到 eth0,避免 bridge0 带来的 iptables 干扰;
  3. IPv6 方案:ULA 激活协议栈 + SLAAC 获取公网地址 + 删除 ULA 默认路由;
  4. 透明代理模式:推荐 tproxy 模式,性能最优;关键前置条件为 ip_nonlocal_bind=1、DNS 直连 IP、--privileged 权限;
  5. 安全加固:ip6tables 封锁公网 IPv6 入站,防止管理面板暴露;
  6. DNS 策略:开启嗅探(含 QUIC),避免 DNS 死锁,按需开启 DoH/分流。

这套方案经过反复实测验证,可以稳定运行,实现全家设备仅改网关即可无感科学上网的终极目标。

相关文章

隐私政策5个月前 (09-22)