AnyLink 部署指南 · 群晖 DSM 7.2.2
基于 Docker 容器部署 AnyLink VPN 服务,支持 Cisco AnyConnect 协议客户端接入。
环境信息
| 项目 | 值 |
|---|---|
| 系统 | 群晖 DSM 7.2.2 |
| 机型 | SA6400 |
| 群晖 IP | 192.168.18.8 |
| 物理出口网卡 | ovs_eth4 |
| VPN 客户端网段 | 192.168.90.0/24 |
| VPN 网关 | 192.168.90.1 |
| 客户端 IP 范围 | 192.168.90.100 ~ 192.168.90.200 |
| 对外端口 | 4436(TCP/UDP) |
| 管理面板端口 | 8809 |
前置准备
1. 启用 SSH
控制面板 → 终端机与 SNMP → 启用 SSH 功能
2. 安装 Container Manager
套件中心 → 搜索 Container Manager → 安装
3. 确认内核模块
SSH 登录群晖后执行:
lsmod | grep tun
如无输出则手动加载:
sudo modprobe tun
目录结构
mkdir -p /volume1/docker/anylink/{conf,log}
cd /volume1/docker/anylink
最终结构:
/volume1/docker/anylink/
├── docker-compose.yml
├── conf/
│ ├── server.toml
│ ├── anylink.db
│ ├── profile.xml
│ ├── client_ca.pem
│ ├── client_ca.key
│ ├── files/
│ └── cert/
│ ├── anylink.ddns.vanjay.cn.pem
│ └── anylink.ddns.vanjay.cn.key
└── log/
docker-compose.yml
services:
anylink:
image: bjdgyc/anylink:latest
container_name: anylink
restart: unless-stopped
privileged: true
# cpus: 2
# mem_limit: 4g
ports:
- 4436:443
- 8809:8800
- 4436:443/udp
environment:
LINK_LOG_LEVEL: info
command:
- --conf=/app/conf/server.toml
volumes:
- ./conf:/app/conf
- ./log:/app/log
dns_search: .
server.toml 关键配置
# 其他配置文件,可以使用绝对路径
# 或者相对于 anylink 二进制文件的路径
# 数据文件
db_type = "sqlite3"
db_source = "./conf/anylink.db"
# 证书文件
cert_file = "./conf/cert/anylink.ddns.vanjay.cn.pem"
cert_key = "./conf/cert/anylink.ddns.vanjay.cn.key"
profile = "./conf/profile.xml"
files_path = "./conf/files"
# 日志目录,为空写入标准输出
log_path = "./log" # 添加日志文件路径
log_level = "info" # 修改日志等级为info
# 系统名称
issuer = "VanJay's Home VPN"
# 后台管理用户
admin_user = "VanJay"
admin_pass = "$2a$10$Jkfxxxxxxxx"
# 留空表示不开启 otp, 开启otp后密码为 pass + 6位otp
# 生成 ./anylink tool -o
admin_otp = ""
jwt_secret = "njKSeDiwKnypaFjepvVBUxxxxxxx"
# 服务监听地址
# server_addr = "443"
# 开启 DTLS, 默认关闭
# server_dtls = false
# server_dtls_addr = "443"
# 后台服务监听地址
# admin_addr = ":8800"
# 开启tcp proxy protocol协议
proxy_protocol = true
# 网络模式,只演示该模式,其他模式请自行参考github
link_mode = "tun"
# 客户端分配的ip地址池,docker 部署不需要
# ipv4_master = "eth0"
# ipv4_cidr = "192.168.100.0/24"
# pv4_gateway = "192.168.100.1"
# ipv4_start = "192.168.100.100"
# ipv4_end = "192.168.100.200"
# 最大客户端数量
max_client = 100
# 单个用户同时在线数量
max_user_client = 8
# IP租期(秒)
ip_lease = 86400
# 默认选择的组
default_group = "vip"
# 客户端失效检测时间(秒) dpd > keepalive
cstp_keepalive = 25
cstp_dpd = 30
mobile_keepalive = 85
mobile_dpd = 90
# 设置最大传输单元
mtu = 1460
# 要发布的默认域,可以多个,空格隔开
default_domain = "anylink.ddns.vanjay.cn"
# session过期时间,用于断线重连,0永不过期
session_timeout = 0
auth_timeout = 0
# 审计去重间隔(秒),-1代表关闭审计日志
audit_interval = 600
show_sql = false
# 是否自动添加nat
iptables_nat = true
# 启用压缩
compression = false
# 低于及等于多少字节不压缩
no_compress_limit = 256
# 客户端显示详细错误信息(线上环境慎开启)
display_error = false
HAProxy 配置(可选)
如果群晖上运行了 HAProxy 做前置代理,backend 配置如下:
backend anylink
mode tcp
option ssl-hello-chk
server anylink 127.0.0.1:4436 send-proxy-v2 check-send-proxy
使用
send-proxy-v2时,server.toml中必须设置proxy_protocol = true,否则客户端无法连接。
启动服务
cd /volume3/MyDocker/anylink
docker compose up -d
# 查看日志
docker logs -f anylink
网络配置(关键步骤)
群晖 Docker 环境下,VPN 流量需要手动处理两层 NAT:
问题分析
手机 → HAProxy → anylink容器(tun0) → 容器eth0 → 宿主机网桥 → ovs_eth4 → 公网
- 容器内:需要 MASQUERADE,将 VPN 客户端 IP(
192.168.90.x)转换为容器 eth0 IP - 宿主机:需要 FORWARD 放行 + MASQUERADE,将容器 IP 转换为群晖物理 IP 出公网
宿主机 iptables 规则
# 放行 VPN 客户端流量转发
iptables -I FORWARD -s 192.168.90.0/24 -j ACCEPT
iptables -I FORWARD -d 192.168.90.0/24 -j ACCEPT
# 出口 NAT(注意:出口网卡是 ovs_eth4,不是 eth0)
iptables -t nat -A POSTROUTING -s 192.168.90.0/24 -o ovs_eth4 -j MASQUERADE
容器内 iptables 规则(关键)
anylink 的
iptables_nat = true在群晖环境下不会自动生效(legacy/nftables 冲突),必须手动添加:
docker exec anylink iptables-legacy -t nat -A POSTROUTING \
-s 192.168.90.0/24 -o eth0 -j MASQUERADE
开机自启任务
群晖重启后内核模块和 iptables 规则会全部丢失,需要设置开机任务。
路径: 控制面板 → 任务计划 → 新增 → 触发的任务 → 用户定义的脚本
- 事件:开机
- 用户:root
确定网口
root@SA6400 ~$ ip route | grep default
default via 192.168.18.1 dev ovs_eth4 src 192.168.18.8
脚本内容:
#!/bin/bash
# 加载内核模块
modprobe tun
modprobe iptable_filter
modprobe iptable_nat
# 等待 Docker 和容器启动
sleep 30
# 宿主机 iptables 规则
iptables -I FORWARD -s 192.168.90.0/24 -j ACCEPT
iptables -I FORWARD -d 192.168.90.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.90.0/24 -o ovs_eth4 -j MASQUERADE
# 等容器完全就绪
sleep 10
# 容器内 NAT 规则(VPN 客户端流量从容器 eth0 出去)
docker exec anylink iptables-legacy -t nat -A POSTROUTING \
-s 192.168.90.0/24 -o eth0 -j MASQUERADE
验证
检查服务状态
docker ps | grep anylink
docker logs anylink --tail 20
检查规则是否生效
# 宿主机 NAT
iptables -t nat -L POSTROUTING -n -v | grep 192.168.90
# 宿主机 FORWARD
iptables -L FORWARD -n -v | grep 192.168.90
# 容器内 NAT(关键)
docker exec anylink iptables-legacy -t nat -L POSTROUTING -n -v | grep 192.168.90
三条都有输出则配置正确。
网络连通性测试
# 抓包确认 VPN 客户端流量
tcpdump -i any -n host 192.168.90.100
# 确认 NAT 出口
tcpdump -i ovs_eth4 -n src 192.168.18.8
客户端连接
| 平台 | 客户端 |
|---|---|
| iOS | App Store 搜索 Cisco Secure Client |
| Android | Play Store 搜索 Cisco Secure Client |
| macOS | App Store 搜索 Cisco Secure Client |
| Windows | 下载 Cisco AnyConnect 客户端 |
连接地址:anylink.ddns.vanjay.cn:4436
管理面板
http://192.168.18.8:8809
可查看在线用户、连接状态、IP 分配情况。
常见问题
tun 模块未加载
[error] Linux module tun is not loaded, please run `modprobe tun`
解决:
sudo modprobe tun
sudo modprobe iptable_filter
sudo modprobe iptable_nat
连接成功但无法上网
最常见原因:容器内缺少 MASQUERADE 规则。执行:
docker exec anylink iptables-legacy -t nat -A POSTROUTING \
-s 192.168.90.0/24 -o eth0 -j MASQUERADE
iptables nf_tables 警告
iptables v1.8.11 (nf_tables)
Warning: iptables-legacy tables present
这是正常现象,只要 docker-compose.yml 中设置了 IPTABLES_LEGACY: "on" 即可忽略。
proxy_protocol 导致连接失败
- 有 HAProxy 前置:
proxy_protocol = true - 直接暴露端口:
proxy_protocol = false
两者不能混用。
防火墙放行
控制面板 → 安全性 → 防火墙 → 新增规则:
| 端口 | 协议 | 用途 |
|---|---|---|
| 4436 | TCP | AnyConnect TLS 连接 |
| 4436 | UDP | DTLS 加速 |
| 8809 | TCP | Web 管理面板 |