# V2Ray 详细资料归档

> 日常使用请看 `v2ray.md`。本文保留完整原理分析、诊断过程与踩坑记录。
> 归档于 2026-10-21，由原 v2ray.md + v2ray-pc.md 合并而成。

---

# 第一部分：服务端部署与 WS+TLS 改造


> 适用场景：在服务器上部署 V2Ray，供 iOS / PC 客户端连接使用。
> 文档基于实际部署验证（2026-10-20），可直接套用到其他服务器。

---

## 一、架构说明

本方案与已有的 Tailscale、Squid 并列运行，互不冲突：

```
手机 / PC
├── Tailscale exit node（WireGuard UDP）  —— 原有
├── Squid 代理（HTTP/HTTPS, 9999）        —— 原有
└── V2Ray                                —— 新增
    ├── VMess  : 10086  → 公网开放，手机客户端用
    ├── SOCKS5 : 1080   → 仅本机/内网，PC 用
    └── HTTP   : 1081   → 仅本机/内网，PC 用
```

**端口规划要点**：Apache/Nginx 通常已占用 80/443，V2Ray 需避开。本例 VMess 用 10086。

---

## 二、服务端部署

### 1. 生成 UUID

```bash
cat /proc/sys/kernel/random/uuid
```

记录输出，后续服务端和客户端都要用同一个值。

### 2. 创建配置文件

```bash
sudo mkdir -p /etc/v2ray
sudo tee /etc/v2ray/config.json > /dev/null <<'EOF'
{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "port": 1080,
      "listen": "0.0.0.0",
      "protocol": "socks",
      "settings": { "auth": "noauth", "udp": true },
      "tag": "socks-in"
    },
    {
      "port": 1081,
      "listen": "0.0.0.0",
      "protocol": "http",
      "settings": {},
      "tag": "http-in"
    },
    {
      "port": 10086,
      "listen": "0.0.0.0",
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "在此填入你生成的UUID",
            "alterId": 0,
            "security": "auto"
          }
        ]
      },
      "tag": "vmess-in"
    }
  ],
  "outbounds": [
    { "protocol": "freedom", "settings": {}, "tag": "direct" }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": []
  }
}
EOF
```

将 `在此填入你生成的UUID` 替换为上一步的 UUID。

### 3. 启动容器

```bash
sudo docker run -d \
  --name v2ray \
  --restart unless-stopped \
  -p 127.0.0.1:1080:1080 \
  -p 172.17.0.1:1080:1080 \
  -p 127.0.0.1:1081:1081 \
  -p 172.17.0.1:1081:1081 \
  -p 10086:10086 \
  -v /etc/v2ray/config.json:/etc/v2ray/config.json \
  v2fly/v2fly-core:latest \
  run -config /etc/v2ray/config.json
```

**关键点**：1080/1081 绑定到 `127.0.0.1` 和 `172.17.0.1`（Docker 网桥），**不绑 0.0.0.0**。详见下文安全章节。

### 4. 验证

```bash
# 容器状态与端口映射
sudo docker ps --filter name=v2ray --format '{{.Status}} | {{.Ports}}'

# 本地转发测试（应返回服务器公网 IP）
curl -s --socks5 127.0.0.1:1080 https://ifconfig.me

# 外部端口可达性
curl -s -X POST https://portchecker.io/api/v1/query \
  -H "Content-Type: application/json" \
  -d '{"host":"你的公网IP","ports":[1080,1081,10086]}'
```

期望结果：10086 为 `true`，1080/1081 为 `false`。

### 5. 查看日志排错

```bash
sudo docker logs v2ray --tail 20
```

---

## 三、客户端配置

### 参数对照表

| 项目 | 值 |
|---|---|
| 协议 | VMess |
| 地址 | 服务器公网 IP |
| 端口 | `10086` |
| UUID | 服务端配置的 UUID |
| alterId / 额外ID | `0` |
| 加密 | `auto` |
| 传输协议 | `tcp` |
| TLS | 关闭 |
| 伪装类型 | `none` |

### iOS 客户端选择

| App | 价格 | 说明 |
|---|---|---|
| **Shadowrocket** | $2.99 | 首选。全协议支持，分流规则强，教程最多 |
| **V2Box** | 免费 | 免费里易用性最好，实测可用 |
| **sing-box** | 免费 | 官方出品，JSON 配置，功能最新 |
| Stash | $3.99 | Clash 内核，适合用 Clash 订阅的场景 |

> 这类 App 在中国区 App Store 全部下架，需非中国区 Apple ID 下载。
> 中国区能搜到的同名应用多为低质量仿品，可能无法解析标准 `vmess://` 链接。

### 生成 vmess:// 导入链接

```bash
python3 -c "
import json, base64
cfg = {
  'v':'2','ps':'节点名称','add':'你的公网IP','port':'10086',
  'id':'你的UUID','aid':'0','scy':'auto',
  'net':'tcp','type':'none','host':'','path':'','tls':'','sni':''
}
s = json.dumps(cfg, separators=(',',':'))
print('vmess://' + base64.b64encode(s.encode()).decode())
"
```

V2Box / Shadowrocket 中选"从剪贴板导入"即可。

### PC 端使用

PC 装 Tailscale 后，代理地址填服务器的 Tailscale IP：

- SOCKS5：`<Tailscale IP>:1080`
- HTTP：`<Tailscale IP>:1081`

---

## 四、sing-box 配置（1.14+ 新语法）

sing-box 版本迭代快，旧配置会报弃用警告。以下配置已通过 **1.14.3** 实跑验证，无任何警告，兼容到 1.16。

### 智能分流版（国内直连，国外走代理）

```json
{
  "log": { "level": "info", "timestamp": true },
  "dns": {
    "servers": [
      { "type": "https", "tag": "dns-remote", "server": "1.1.1.1", "detour": "proxy" },
      { "type": "udp", "tag": "dns-direct", "server": "223.5.5.5" }
    ],
    "rules": [
      { "rule_set": "geosite-cn", "server": "dns-direct" }
    ],
    "final": "dns-remote",
    "strategy": "prefer_ipv4"
  },
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
      "mtu": 9000,
      "auto_route": true,
      "strict_route": false,
      "stack": "gvisor"
    }
  ],
  "outbounds": [
    {
      "type": "vmess",
      "tag": "proxy",
      "server": "你的公网IP",
      "server_port": 10086,
      "uuid": "你的UUID",
      "security": "auto",
      "alter_id": 0
    },
    { "type": "direct", "tag": "direct" }
  ],
  "route": {
    "rules": [
      { "action": "sniff" },
      { "protocol": "dns", "action": "hijack-dns" },
      { "ip_is_private": true, "outbound": "direct" },
      { "rule_set": ["geosite-cn", "geoip-cn"], "outbound": "direct" }
    ],
    "rule_set": [
      {
        "type": "remote",
        "tag": "geosite-cn",
        "format": "binary",
        "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-cn.srs",
        "http_client": { "detour": "proxy" }
      },
      {
        "type": "remote",
        "tag": "geoip-cn",
        "format": "binary",
        "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geoip@rule-set/geoip-cn.srs",
        "http_client": { "detour": "proxy" }
      }
    ],
    "final": "proxy",
    "default_domain_resolver": "dns-direct",
    "auto_detect_interface": true
  }
}
```

### 全局代理版

```json
{
  "log": { "level": "info", "timestamp": true },
  "dns": {
    "servers": [
      { "type": "udp", "tag": "dns-direct", "server": "223.5.5.5" }
    ],
    "final": "dns-direct"
  },
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "address": ["172.19.0.1/30"],
      "mtu": 9000,
      "auto_route": true,
      "strict_route": false,
      "stack": "gvisor"
    }
  ],
  "outbounds": [
    {
      "type": "vmess",
      "tag": "proxy",
      "server": "你的公网IP",
      "server_port": 10086,
      "uuid": "你的UUID",
      "security": "auto",
      "alter_id": 0
    },
    { "type": "direct", "tag": "direct" }
  ],
  "route": {
    "rules": [
      { "action": "sniff" },
      { "ip_is_private": true, "outbound": "direct" }
    ],
    "final": "proxy",
    "default_domain_resolver": "dns-direct",
    "auto_detect_interface": true
  }
}
```

### 导入方式

把 JSON 放到 Web 目录，在 sing-box 里 Profiles → `+` → **Remote** → 填 URL。

建议用随机文件名（如 `sb-full-4ca873ff0a3f.json`），避免被人扫到 UUID。

---

## 五、sing-box 版本迁移对照

版本升级时按此表改配置：

| 旧写法 | 新写法 | 废弃版本 | 移除版本 |
|---|---|---|---|
| `"address": "https://1.1.1.1/dns-query"` | `"type": "https", "server": "1.1.1.1"` | 1.12.0 | 1.14.0 |
| DNS server 的 `"detour": "direct"` | 删除该字段（默认即直连） | 1.12.0 | — |
| 缺少 `route.default_domain_resolver` | 显式添加，指向某个 DNS tag | 1.12.0 | 1.14.0 |
| `"independent_cache": true` | 直接删除，无替代 | 1.14.0 | 1.16.0 |
| `"download_detour": "proxy"` | `"http_client": { "detour": "proxy" }` | 1.14.0 | 1.16.0 |

### 配置校验方法

**重要**：`sing-box check` 只做静态语法检查，catch 不到运行时约束和弃用警告。必须实际运行才能发现全部问题。

```bash
# 下载对应版本二进制
cd /tmp && curl -sL -o sb.tar.gz \
  "https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz"
tar xzf sb.tar.gz
SB=/tmp/sing-box-1.14.3-linux-amd64/sing-box

# 把 tun 入站换成 socks（避免需要 root 建虚拟网卡），然后实跑
python3 -c "
import json
c = json.load(open('你的配置.json'))
c['inbounds'] = [{'type':'socks','tag':'st','listen':'127.0.0.1','listen_port':17890}]
json.dump(c, open('/tmp/runtest.json','w'), indent=2)
"
setsid \$SB run -c /tmp/runtest.json > /tmp/sb.log 2>&1 &
sleep 7

# 检查警告与启动状态
grep -iE 'deprecat|WARN|FATAL|removed' /tmp/sb.log || echo "无警告"
grep 'started' /tmp/sb.log
curl -s --socks5-hostname 127.0.0.1:17890 https://ifconfig.me
```

用**最新稳定版**校验，它的约束最严，能提前暴露未来版本的问题。

---

## 六、安全注意事项

### 1. 不要把 SOCKS5/HTTP 代理暴露到公网

无认证的 SOCKS5/HTTP 代理等于**开放代理**，几分钟内就会被扫描器发现并滥用，最终导致 IP 被各大服务拉黑。

实测日志（部署后约 10 分钟）：
```
tcp:68.183.43.132:44538 rejected proxy/socks: insufficient header
```
这是 DigitalOcean 的扫描机器人在探测 1080 端口。

**正确做法**：1080/1081 只绑 `127.0.0.1` 和 `172.17.0.1`，远程访问走 Tailscale。

如必须公网开放，一定加认证：
```json
{
  "protocol": "socks",
  "settings": {
    "auth": "password",
    "accounts": [{ "user": "用户名", "pass": "强密码" }],
    "udp": true
  }
}
```

### 2. 配置文件不要放在 Web 可访问目录

配置文件含 UUID，泄露即等于别人可以白用你的服务器。

**易踩的坑**：备份目录也可能在 DocumentRoot 下。本次部署就遇到这个问题——文件从 `share/` 删除后，备份放到 `data/back/`，结果该目录公网返回 HTTP 200，UUID 照样泄露。

正确做法：备份放到 Web 根目录之外，如 `/root/` 下并设 700 权限。

验证方法：
```bash
curl -s -o /dev/null -w "%{http_code}\n" http://你的域名/备份路径/文件名.json
# 期望 403 或 404
```

### 3. Tailscale authkey 泄露风险

`docker inspect` 可读到容器环境变量明文：
```bash
sudo docker inspect tailscaled --format '{{range .Config.Env}}{{println .}}{{end}}'
# TS_AUTHKEY=tskey-auth-xxxxx  ← 明文暴露
```

容器注册完成后该 key 已无用，建议去 Tailscale 后台吊销。

---

## 七、常见问题

### 客户端报 `invalid user`

服务端日志：
```
proxy/vmess/encoding: invalid user > proxy/vmess: Not Found
```

**说明网络是通的**，连接已到达服务器，纯粹是认证参数不匹配。检查顺序：

1. **UUID** 是否完全一致（建议复制粘贴，手输易漏字符，注意首尾空格）
2. **alterId** 是否为 `0`（部分旧客户端默认填 64，会导致认证算法不同）
3. **加密方式** 选 `auto` 或 `aes-128-gcm`，不要选 `none`

### 用浏览器访问 VMess 端口测试连通性

**这个方法无效**。VMess 不是 HTTP 协议，浏览器访问必然失败，这不代表端口不通。

正确的端口测试：
```bash
# 外部检测
curl -s -X POST https://portchecker.io/api/v1/query \
  -H "Content-Type: application/json" \
  -d '{"host":"你的IP","ports":[10086]}'

# 或看服务端日志有无连接记录
sudo docker logs v2ray --tail 20
```

### 容器启动了但端口不通

检查 `docker run` 是否漏了 `-p` 映射。容器内监听不等于宿主机发布：

```bash
sudo docker ps --filter name=v2ray --format '{{.Ports}}'
```

漏了需删除容器重建（改端口映射不能靠 restart）：
```bash
sudo docker stop v2ray && sudo docker rm v2ray
# 然后用完整 -p 参数重新 run
```

### sing-box 报 `detour to an empty direct outbound makes no sense`

1.12+ 不允许 DNS server 的 `detour` 指向空的 direct 出站。删掉该 `detour` 字段即可，默认行为就是直连。

### 脚本里用 pkill 导致 shell 退出

`pkill -f 'sing-box run'` 可能匹配到自身所在的 shell 进程，导致脚本中断（exit 144）。改用精确 PID：

```bash
setsid $SB run -c config.json > log.txt 2>&1 &
SBPID=$!
# ... 测试 ...
kill $SBPID
```

---

## 八、关于加速效果

### 当前配置的局限

本文档的配置是 **VMess + 裸 TCP**，没有 TLS 伪装。协议特征明显，如果慢的原因是运营商对代理流量做 QoS，裸 TCP 可能照样被限速甚至阻断。

### 真正能绕 QoS 的方案

**VMess/VLESS + WebSocket + TLS**，伪装成正常 HTTPS 流量。需要：

- 一个域名（已有则直接用）
- TLS 证书（Let's Encrypt 免费）
- 443 端口（若被 Apache/Nginx 占用，可用反向代理共存）

### 排查思路：先确认瓶颈在哪

部署任何方案前，先在服务器上直接测速：

```bash
pip install speedtest-cli
speedtest-cli
```

- 服务器带宽本身就低 → 任何协议都快不了，换协议无用
- 服务器带宽正常，但经代理慢 → 协议被限速，WS+TLS 值得上

### 另一个容易忽略的点

**国内流量不要走代理**。用智能分流（本文档的分流版配置）让国内网站直连，不绕服务器一圈，实际体感提升往往比换协议更明显。

---

## 九、维护命令速查

```bash
# 查看状态
sudo docker ps --filter name=v2ray --format '{{.Status}} | {{.Ports}}'

# 查看日志
sudo docker logs v2ray --tail 30

# 改配置后重启（仅改 config.json 内容时可用）
sudo docker restart v2ray

# 改端口映射需重建容器
sudo docker stop v2ray && sudo docker rm v2ray
# 再用完整 docker run 命令重新创建

# 查看当前生效的 UUID
sudo grep -o '"id": "[^"]*"' /etc/v2ray/config.json

# 更新镜像
sudo docker pull v2fly/v2fly-core:latest
sudo docker stop v2ray && sudo docker rm v2ray
# 再 docker run
```

---

## 十、本次部署实例参数

> 仅作参考，迁移到新服务器时需全部替换。
> **UUID 属敏感信息，请勿将本节内容公开。**

| 项目 | 值 |
|---|---|
| 服务器公网 IP | `43.156.23.211` |
| 域名 | `stargazer.org.cn` |
| Tailscale IP | `100.93.191.79` |
| VMess 端口 | `10086` |
| SOCKS5 / HTTP | `1080` / `1081`（仅内网） |
| 已验证客户端 | V2Box (iOS) — 连接成功 |
| 共存服务 | Apache (80/443)、Squid (9999)、Tailscale、DERP (33445) |

> ⚠️ 本节为裸 VMess 时期参数，10086 端口已于 2026-10-20 关闭。
> 当前生效参数见第十一节。

---

## 十一、WS + TLS 改造（2026-10-20）

### 改造动机

裸 VMess（无 TLS、无伪装）在 10086 端口运行一段时间后出现故障：客户端显示连接成功，但无法访问 Google / YouTube。

诊断过程：

```bash
# 1. 节点本身是否正常 —— 正常
curl -s -o /dev/null -w "%{http_code}\n" --max-time 10 https://www.google.com/generate_204
# 204，节点出口干净

# 2. 服务端日志 —— 发现关键线索
docker logs v2ray 2>&1 | grep rejected | tail
```

日志显示：

```
12:26:12  39.144.156.17:65145 accepted udp:142.251.151.6:443 [direct]     ← 最后一批正常流量
12:27:11  39.144.156.17:64960 rejected proxy/vmess/encoding:
          failed to read request header > read tcp ...: i/o timeout        ← 突然开始失败
```

**结论**：TCP 三次握手成功（所以客户端显示"连接成功"），但客户端发出的 VMess 加密头根本没到服务器。这是运营商侧针对该 `IP:端口` 的链路干扰——放过握手，丢弃真实数据。

另一个加剧因素，同一天日志里有 34 次来自 20+ 个国内 IP 的扫描探测：

```bash
docker logs v2ray 2>&1 | grep 'invalid user' | grep -oP '^\S+ \S+ \K[0-9.]+' | sort | uniq -c | sort -rn
```

裸 VMess 端口被扫描标记后限速或阻断，是国内网络环境下的常见结局。

### 改造后架构

```
客户端 ──443/TLS──> Apache (stargazer.org.cn)
                      ├── /ray （WebSocket 升级）──ws──> 127.0.0.1:10087  v2ray vmess+ws
                      └── 其他路径 ─────────────────────> /var/www/html 网站内容
```

对 GFW 和扫描器而言，这是一台跑着正常网站的 443 服务器。访问 `/ray` 以外的任何路径都返回真实网页内容，这是 WS+TLS 抗探测的核心——**伪装必须有真实回落**，否则反而更可疑。

### 步骤 1：备份

```bash
TS=$(date +%Y%m%d%H%M%S)
cp /etc/apache2/sites-enabled/https-redirect.conf /var/www/html/data/back/https-redirect.conf-bak-$TS
cp /etc/v2ray/config.json /var/www/html/data/back/v2ray-config.json-bak-$TS
# 容器完整 inspect，便于精确重建
docker inspect v2ray > /var/www/html/data/back/v2ray-inspect-bak-$TS.json
```

重建容器前**务必先存 inspect**，`PortBindings` / `Binds` / `RestartPolicy` 都在里面，否则重建时容易漏参数。

### 步骤 2：启用 Apache 模块

```bash
a2enmod proxy proxy_http proxy_wstunnel
```

`proxy_wstunnel` 是 WebSocket 反代的关键，仅启用 `proxy_http` 会导致升级请求失败。

### 步骤 3：v2ray 新增 ws inbound

在 `inbounds` 数组追加（保留原有 inbound，便于回退）：

```json
{
  "port": 10087,
  "listen": "0.0.0.0",
  "protocol": "vmess",
  "settings": {
    "clients": [
      { "id": "<与原有相同的 UUID>", "alterId": 0, "security": "auto" }
    ]
  },
  "streamSettings": {
    "network": "ws",
    "wsSettings": { "path": "/ray" }
  },
  "tag": "vmess-ws-in"
}
```

> **容器内必须监听 `0.0.0.0`**。docker-proxy 从宿主转发进来的流量源地址不是 127.0.0.1，若容器内填 `127.0.0.1` 将收不到连接。对外安全性由宿主侧端口映射保证（见下一步）。

### 步骤 4：重建容器

Docker 无法给运行中的容器热加端口，必须重建：

```bash
docker stop v2ray && docker rm v2ray
docker run -d --name v2ray --restart unless-stopped \
  -v /etc/v2ray/config.json:/etc/v2ray/config.json \
  -p 127.0.0.1:1080:1080 -p 172.17.0.1:1080:1080 \
  -p 127.0.0.1:1081:1081 -p 172.17.0.1:1081:1081 \
  -p 127.0.0.1:10087:10087 \
  v2fly/v2fly-core:latest run -config /etc/v2ray/config.json
```

关键是 `-p 127.0.0.1:10087:10087` —— **只绑本机**，公网扫不到这个端口，所有外部流量必须经 Apache 443 进入。

### 步骤 5：配置 Apache 443 vhost

```apache
<VirtualHost *:443>
    ServerName stargazer.org.cn
    ServerAlias www.stargazer.org.cn
    DocumentRoot /var/www/html

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/stargazer.org.cn/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/stargazer.org.cn/privkey.pem

    # WebSocket 升级请求 -> v2ray
    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule ^/ray$ ws://127.0.0.1:10087/ray [P,L]

    ProxyPreserveHost On

    # 沿用 80 端口原有目录策略
    <Directory /var/www/html/edgetts>
        Require all denied
    </Directory>
    <Directory /var/www/html/mydata/data>
        Require all denied
    </Directory>
    <Directory /var/www/html/video>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog  ${APACHE_LOG_DIR}/ssl-error.log
    CustomLog ${APACHE_LOG_DIR}/ssl-access.log combined
</VirtualHost>
```

要点：

- `RewriteCond %{HTTP:Upgrade} =websocket` 只拦截 WebSocket 升级请求，普通 HTTP 请求照常走网站。
- **不要加 HSTS、不要强制 http→https 跳转**，80 端口保持原样，避免破坏既有访问习惯。
- 目录保护策略要从 80 端口的 vhost 复制过来，否则 443 会绕过保护。

```bash
apache2ctl configtest    # 必须 Syntax OK
systemctl restart apache2   # 新启用模块需 restart，reload 不够
```

### 步骤 6：验证

**6.1 网站未受影响**

```bash
for u in http://www.stargazer.org.cn/ https://stargazer.org.cn/ \
         https://www.stargazer.org.cn/ https://stargazer.org.cn/share/; do
  printf "%-42s " "$u"
  curl -s -o /dev/null -w "%{http_code} verify=%{ssl_verify_result}\n" --max-time 10 "$u"
done
```

全部应为 `200 verify=0`（`verify=0` 表示证书校验通过）。

**6.2 WebSocket 升级握手**

```bash
curl -s -o /dev/null -D- --max-time 10 \
  -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  -H "Sec-WebSocket-Version: 13" \
  https://stargazer.org.cn/ray
```

期望 `HTTP/1.1 101 Switching Protocols`。

**6.3 伪装有效性**（非 WS 访问不应暴露代理）

```bash
curl -s -o /dev/null -w "%{http_code}\n" --max-time 10 https://stargazer.org.cn/ray
# 404 —— 和普通网页一样，不暴露代理存在
```

**6.4 端口暴露面**

```bash
ss -ltnp | grep 10087        # 应只绑 127.0.0.1
# 公网不可达验证
timeout 5 bash -c 'cat </dev/null >/dev/tcp/<公网IP>/10087' && echo "警告:公网可达!" || echo "OK"
```

**6.5 端到端实测**（最关键，用真实 vmess 客户端跑）

```bash
mkdir -p /tmp/wstest && cat > /tmp/wstest/client.json <<'EOF'
{
  "log": { "loglevel": "warning" },
  "inbounds": [{ "port": 10809, "listen": "127.0.0.1", "protocol": "socks",
    "settings": { "auth": "noauth", "udp": true } }],
  "outbounds": [{
    "protocol": "vmess",
    "settings": { "vnext": [{ "address": "stargazer.org.cn", "port": 443,
      "users": [{ "id": "<UUID>", "alterId": 0, "security": "auto" }] }]},
    "streamSettings": { "network": "ws", "security": "tls",
      "tlsSettings": { "serverName": "stargazer.org.cn" },
      "wsSettings": { "path": "/ray" } }
  }]
}
EOF

docker run -d --name wstest --network host \
  -v /tmp/wstest/client.json:/etc/v2ray/config.json \
  v2fly/v2fly-core:latest run -config /etc/v2ray/config.json
sleep 4

curl -s -o /dev/null -w "%{http_code}\n" --max-time 20 \
  -x socks5h://127.0.0.1:10809 https://www.google.com/generate_204
curl -s --max-time 15 -x socks5h://127.0.0.1:10809 https://ipinfo.io/json | grep '"ip"'

docker rm -f wstest && rm -rf /tmp/wstest   # 用完即清
```

前五项全过只说明链路搭对了，**只有 6.5 能证明代理真的能用**。

### 步骤 7：关闭旧的裸 VMess 端口

新链路稳定运行后再做。先确认旧端口已无流量：

```bash
docker logs v2ray 2>&1 | grep accepted | tail -5   # 看来源 IP 是否还有客户端在用
```

从配置移除 `vmess-in` inbound，并去掉端口映射重建容器：

```bash
python3 -c "
import json
d = json.load(open('/etc/v2ray/config.json'))
d['inbounds'] = [i for i in d['inbounds'] if i.get('tag') != 'vmess-in']
json.dump(d, open('/tmp/cfg.json','w'), indent=2, ensure_ascii=False)
"
cp /tmp/cfg.json /etc/v2ray/config.json
# 重建时不再带 -p 10086:10086
```

验证关闭：

```bash
ss -ltnp | grep 10086 || echo "本机无 10086 监听"
timeout 5 bash -c 'cat </dev/null >/dev/tcp/<公网IP>/10086' || echo "公网已关闭"
```

### 步骤 8：IPv4 Only 优化

服务器若为纯 IPv4 出口，先确认：

```bash
ip -6 addr show scope global | grep inet6 || echo "无全局 IPv6"
ip -6 route show default || echo "无 IPv6 默认路由"
```

给 `freedom` outbound 加 `domainStrategy`：

```json
{
  "protocol": "freedom",
  "settings": { "domainStrategy": "UseIPv4" },
  "tag": "direct"
}
```

**为什么需要**：默认 `AsIs` 会让 v2ray 用系统 DNS 解析，双栈站点返回 AAAA 后 v2ray 尝试连 IPv6 → 无路由 → 失败 → 回落 IPv4。虽然最终能通，但每次浪费一个失败的连接尝试，表现为偶发的首次加载变慢。

只改 `config.json` 内容时无需重建容器：

```bash
docker restart v2ray
```

### 顺带修好的 HTTPS

改造前 443 的配置是坏的：

```apache
<VirtualHost *:443>
    SSLCertificateFile /etc/ssl/certs/apache-selfsigned.crt   # 自签证书
    RewriteRule ^(.*)$ http://%{HTTP_HOST}$1 [R=301,L]        # 全部跳回 http
</VirtualHost>
```

实际表现：浏览器弹证书警告 → 用户点"继续" → 被 301 踢回 http。而 `/etc/letsencrypt/live/` 里躺着一张有效的正式证书却没被使用。

改造顺带把 HTTPS 修正了，并补签 `www` 子域：

```bash
# 先 dry-run 确认不会伤到现有证书
certbot certonly --webroot -w /var/www/html \
  -d stargazer.org.cn -d www.stargazer.org.cn \
  --cert-name stargazer.org.cn --keep-until-expiring --dry-run

# 正式签发（--expand 用于向现有证书追加域名）
certbot certonly --webroot -w /var/www/html \
  -d stargazer.org.cn -d www.stargazer.org.cn \
  --cert-name stargazer.org.cn --expand --non-interactive
```

检查续期机制：

```bash
systemctl list-timers | grep certbot
ls /etc/cron.d/ | grep certbot
certbot renew --cert-name <域名> --dry-run
```

> 443 正式启用后，证书到期会导致**网站和代理一起挂**，续期机制必须确认可用。

### 踩坑记录

**1. 容器内 listen 地址**

ws inbound 在容器内必须 `0.0.0.0`。docker-proxy 转发进来的流量源地址不是 127.0.0.1，填 `127.0.0.1` 会收不到连接。安全性靠宿主侧 `-p 127.0.0.1:10087:10087` 保证，不是靠容器内的 listen。

**2. 伪装域名 (host) 必填**

v2rayN 等客户端若 host 留空，部分版本发出的请求不带 `Host` 头，Apache vhost 匹配失败。表现为连接成功但无法上网，排查时容易误判为服务端问题。

**3. `a2enmod` 后要 restart**

新启用模块必须 `systemctl restart apache2`，`reload` 不加载新模块。

**4. certbot deploy-hook 会重启关联服务**

本机 deploy-hook 配置了 derper，签发证书时 derper 被自动重启。属预期行为，但要记得确认重启后状态正常：

```bash
docker ps --format '{{.Names}} | {{.Status}}'
```

**5. 排查时的一次虚警**

`certbot` 输出里出现：

```
ERROR: ld.so: object '/$LIB/libonion.so' from /etc/ld.so.preload cannot be preloaded
```

`ld.so.preload` 注入 + 文件疑似缺失 + `libonion.so` 这种命名，一度判断为 rootkit。查证结论：**是腾讯云主机安全（YunJing / CWP）的 exec 审计模块**，由 `/usr/local/qcloud/onion_check/` 安装，hook `execve` 系列上报进程创建事件，属正常 EDR 行为。

```bash
# 确认归属的方法
dpkg -S /usr/lib/x86_64-linux-gnu/libonion.so       # 不属任何包
nm -D --defined-only <so 文件>                       # 看到 execve/execl/execv 系列
ls /usr/local/qcloud/onion_check/                     # 腾讯云组件目录
```

警告本身是 `/$LIB` 路径解析的小瑕疵，无害，不影响 certbot 退出码。**不要删除**，那是云厂商组件，擅自删会触发安全告警。

### 回滚

```bash
# Apache
cp /var/www/html/data/back/https-redirect.conf-bak-<时间戳> \
   /etc/apache2/sites-available/https-redirect.conf
apache2ctl configtest && systemctl reload apache2

# v2ray
cp /var/www/html/data/back/v2ray-config.json-bak-<时间戳> /etc/v2ray/config.json
docker restart v2ray
# 若需恢复端口映射，参照备份的 inspect json 重建容器
```

改造期间**保留旧的 10086 inbound 作为回退保险**，新链路确认稳定后再关闭。这样最坏情况下客户端仍能用旧配置连上。

---

## 十二、当前生效参数（2026-10-20 改造后）

> **UUID 属敏感信息，请勿将本节内容公开。**

| 项目 | 值 |
|---|---|
| 连接地址 | `stargazer.org.cn:443` |
| 传输方式 | VMess + WebSocket + TLS |
| WS 路径 | `/ray` |
| 伪装域名 / SNI | `stargazer.org.cn` |
| UUID | `2c50f055-3c0d-4d6e-ae44-b637170211ec` |
| alterId / 加密 | `0` / `auto` |
| 证书 | Let's Encrypt，含 `www` 子域，到期 2027-01-18 |
| v2ray ws inbound | `127.0.0.1:10087`（仅本机） |
| 出站策略 | `domainStrategy: UseIPv4` |
| 服务器出口 | `43.156.23.211`（新加坡 / 腾讯云，纯 IPv4） |
| 旧裸 VMess 10086 | **已关闭** |
| 共存服务 | Apache (80/443)、Squid (9999)、Tailscale、DERP (33445) |

配置文件：

- v2rayN / 参数明细：`vmess.md`
- sing-box（推荐）：`sb-ws-tls-v2.json`
- sing-box（旧版）：`sb-ws-tls-20261020.json`

---

# 第二部分：PC 客户端详解

> 注意：本部分参数为裸 VMess 时期（10086 / tcp / 无 TLS），已失效。
> 当前参数见 `v2ray.md`。保留此节是为其中的 IPv6 分析与排查方法。


> 配套文档：`v2ray.md`（服务端部署）
> 版本信息核对于 2026-10-20：sing-box `1.14.3`、v2rayN `7.25.6`

---

## 连接参数

两个系统都用同一套参数：

| 项目 | 值 |
|---|---|
| 协议 | VMess |
| 地址 | `43.156.23.211` |
| 端口 | `10086` |
| UUID | 服务端配置的 UUID |
| alterId | `0` |
| 加密 | `auto` |
| 传输协议 | `tcp` |
| TLS | 关闭 |

---

# 一、Windows 10

## 方案 A：v2rayN（推荐，图形界面）

最省事的选择，带界面、支持系统代理一键切换、能导入 `vmess://` 链接。

### 安装步骤

1. 下载（160 MB，含 Xray/sing-box 内核）

   https://github.com/2dust/v2rayN/releases/latest
   https://github.com/2dust/v2rayN/releases/download/7.25.6/v2rayN-windows-64.zip

   选 **`v2rayN-windows-64.zip`**

   > 带 `-desktop` 后缀的是不含内核的精简版，需自行下载内核，不推荐新手用。
   > 32 位系统选 `v2rayN-windows-86.zip`，ARM 设备选 `arm64`。

2. 解压到一个固定目录，例如 `D:\v2rayN`

   **不要放在 `C:\Program Files`**，权限问题会导致配置写入失败。

3. 运行 `v2rayN.exe`

   首次启动 Windows Defender 可能报毒，这是代理软件的通用误报。需要在 Defender 里把该目录加入排除项。

4. 导入节点

   - 复制服务端生成的 `vmess://` 链接
   - v2rayN 里按 `Ctrl+V`，或点菜单 **服务器 → 从剪贴板导入批量URL**
   - 节点出现在列表后，双击选中

5. 开启代理

   - 右下角托盘图标右键 → **系统代理 → 自动配置系统代理**
   - 或主界面底部选 **系统代理：自动配置**

6. 验证

   浏览器访问 `https://ifconfig.me`，显示 `43.156.23.211` 即生效。

### 手动添加节点（不用链接）

菜单 **服务器 → 添加 VMess 服务器**，按上面的参数表填写。

### 常用设置

| 需求 | 操作 |
|---|---|
| 开机自启 | 设置 → 参数设置 → 勾选"开机自动启动" |
| 国内直连分流 | 路由设置 → 选预设规则"绕过大陆" |
| 查看实时日志 | 主界面下方日志区 |
| 本地监听端口 | 默认 SOCKS5 `10808`、HTTP `10809` |

---

## 方案 B：sing-box（命令行，配置可复用）

好处是能直接用服务端那份 JSON 配置，跟 iOS 保持一致。

### 安装步骤

1. 下载 **`sing-box-1.14.3-windows-amd64.zip`**

   https://github.com/SagerNet/sing-box/releases/latest
   https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-windows-amd64.zip
   > Windows 7 用 `-legacy-windows-7` 那个包。

2. 解压到 `D:\sing-box`

3. 把配置保存为 `D:\sing-box\config.json`

   用 `v2ray.md` 第四章的配置，或从服务器拉取：

   ```powershell
   curl -o D:\sing-box\config.json http://stargazer.org.cn/share/sb-full-4ca873ff0a3f.json
   ```

4. **以管理员身份**打开 PowerShell（tun 模式需建虚拟网卡）

   ```powershell
   cd D:\sing-box
   .\sing-box.exe run -c config.json
   ```

5. 验证

   另开一个窗口：
   ```powershell
   curl https://ifconfig.me
   ```

### 注册为系统服务（后台常驻）

管理员 PowerShell 执行：

```powershell
# 创建服务
New-Service -Name "sing-box" `
  -BinaryPathName "D:\sing-box\sing-box.exe run -c D:\sing-box\config.json" `
  -DisplayName "sing-box" `
  -StartupType Automatic

# 启动
Start-Service sing-box

# 查看状态
Get-Service sing-box
```

删除服务：
```powershell
Stop-Service sing-box
sc.exe delete sing-box
```

> tun 模式要求服务以管理员权限运行。`New-Service` 创建的服务默认用 LocalSystem 账户，权限足够。

---

# 二、Ubuntu

## 方案 A：sing-box（推荐）

### 手动安装二进制（首选）

```bash
cd /tmp
curl -fsSL -o sb.tar.gz \
  "https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz"
tar xzf sb.tar.gz
sudo install -m 755 sing-box-1.14.3-linux-amd64/sing-box /usr/local/bin/
sing-box version
```

> ARM 设备把 `amd64` 换成 `arm64`。
> 如遇 glibc 版本问题，用 `-musl` 那个包（静态链接，兼容性更好）。
> 代价：升级要手动换二进制，没有 `apt upgrade`。

### 官方 apt 源（国内网络下大概率不可用）

2026-10-20 实测（Ubuntu 22.04，南京移动）：`sing-box.app/gpg.key` 连接被重置，
`deb.sagernet.org` 返回 404，这条路走不通，直接用上面的手动安装。
网络环境允许时可以再试：

```bash
sudo mkdir -p /etc/apt/keyrings
sudo curl -fsSL -o /etc/apt/keyrings/sagernet.asc \
  https://sing-box.app/gpg.key
sudo chmod a+r /etc/apt/keyrings/sagernet.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
  sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null

sudo apt update
sudo apt install sing-box
```

### 配置

```bash
sudo mkdir -p /etc/sing-box
sudo curl -fsSL -o /etc/sing-box/config.json \
  http://stargazer.org.cn/share/sb-full-4ca873ff0a3f.json

# 校验语法
sing-box check -c /etc/sing-box/config.json
```

#### 必改：关掉 IPv6

服务端没有 IPv6 出口，但拉下来的配置默认是 `prefer_ipv4`。这个名字有误导性，
它只是「优先」IPv4，拿到 AAAA 记录照样会去连，结果是每条 IPv6 连接等 5 秒超时。
Google 的 AAAA 记录给得很全，Chrome 对 IPv6 又激进，直接表现为打不开。

改两处：

1. `dns.strategy`: `prefer_ipv4` → `ipv4_only`
2. `inbounds[0].address`: 删掉 IPv6 那行 `fdfe:dcba:9876::1/126`，只留 `172.19.0.1/30`

```bash
sudo sed -i 's/"prefer_ipv4"/"ipv4_only"/' /etc/sing-box/config.json
sudo sed -i '/fdfe:dcba:9876::1\/126/d' /etc/sing-box/config.json
# 上一行删掉后，172.19.0.1/30 末尾的逗号要去掉，否则 JSON 语法错
sudo sed -i 's#"172.19.0.1/30",#"172.19.0.1/30"#' /etc/sing-box/config.json

sing-box check -c /etc/sing-box/config.json
sudo systemctl restart sing-box
```

#### `dns.strategy` 的四个取值

| 取值 | 行为 |
|---|---|
| `prefer_ipv4` | 同时查 A 和 AAAA，优先用 A，但 AAAA 仍会返回给客户端 |
| `prefer_ipv6` | 同上，优先 AAAA |
| `ipv4_only` | 只查 A，AAAA 不返回 ← **本机用这个** |
| `ipv6_only` | 只查 AAAA |

同样这四个值还可用于 `dns.servers[].strategy`、`dns.rules[].strategy`（按规则细化，
例如只对 `geosite-google` 强制 `ipv4_only`）、`outbounds[].domain_strategy`。

#### 什么时候该改回 `prefer_ipv4`

**两个条件同时满足**才值得改回去：

1. 服务端有 IPv6 出口
2. tun 的 `address` 里保留了 IPv6 段（`fdfe:dcba:9876::1/126`）

缺一个就继续用 `ipv4_only`。只改 DNS 不改 tun 的话，AAAA 解析出来了 tun 也不接管，照样走不通。

两者的代价不对称，这是选 `ipv4_only` 的真正理由：

- `ipv4_only` 的代价温和 —— 只是失去纯 IPv6 站点（极少），IPv6 出口能力闲置
- `prefer_ipv4` 的代价不可预测 —— Chrome 反复试 IPv6、每条等 5 秒，直接表现为打不开网页

另外注意服务端地址的写法：`ipv4_only` 下 outbound 写 IP 字面量（含 IPv6 字面量）都不走 DNS，
不受影响；但若写成只有 AAAA 记录的**域名**，解析为空会直接连不上服务端。

> 本机 `/home/adminpc/v2ray/config.json` 已是改好的版本，可直接
> `sudo install -m 644 /home/adminpc/v2ray/config.json /etc/sing-box/config.json` 覆盖。

### 前台运行测试

```bash
sudo sing-box run -c /etc/sing-box/config.json
```

看到 `sing-box started` 即正常。另开终端验证：

```bash
curl -s https://ifconfig.me
```

`Ctrl+C` 停止。

### 配置 systemd 服务

手动安装的需自建（本机用的就是这份）：

```bash
sudo tee /etc/systemd/system/sing-box.service > /dev/null <<'EOF'
[Unit]
Description=sing-box service
Documentation=https://sing-box.sagernet.org
After=network.target nss-lookup.target

[Service]
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
ExecStart=/usr/local/bin/sing-box -D /var/lib/sing-box -C /etc/sing-box run
Restart=on-failure
RestartSec=10s
LimitNOFILE=infinity

[Install]
WantedBy=multi-user.target
EOF

sudo mkdir -p /var/lib/sing-box
sudo systemctl daemon-reload
sudo systemctl enable --now sing-box
sudo systemctl status sing-box
```

> 本机已按上述步骤装好，可复用 `install-sing-box.sh` 一键重装。
> apt 源装得上时自带 service 文件，直接 `systemctl enable --now sing-box` 即可。

### 日常命令

```bash
sudo systemctl start sing-box        # 启动
sudo systemctl stop sing-box         # 停止（回到直连）
sudo systemctl restart sing-box      # 重启（改完配置用）
sudo systemctl status sing-box       # 看状态
sudo journalctl -u sing-box -f       # 实时日志
sudo journalctl -u sing-box -n 50    # 最近 50 行
```

### 启停与开机自启

注意本机装的是 **sing-box**，没有 v2ray 服务，所以服务名是 `sing-box`
（`systemctl is-active v2ray` 会返回 `inactive`，那是方案 C 才有的）。

`enable`/`disable` 管的是**开机自启**，`start`/`stop` 管的是**当前运行**，两者独立：

```bash
sudo systemctl disable sing-box           # 只关自启，当前服务继续跑 ← 常用
sudo systemctl disable --now sing-box     # 关自启并立即停止（--now 才会停服务）
sudo systemctl stop sing-box              # 只停当前，下次开机仍会自启
sudo systemctl enable sing-box            # 只恢复自启，不立即启动
sudo systemctl enable --now sing-box      # 恢复自启并立即启动
sudo systemctl is-enabled sing-box        # 查自启状态
```

想「平时不自动起，用的时候手动开」就选第一条，之后用
`sudo systemctl start sing-box` / `stop sing-box` 手动控制。

改完核对，预期 `disabled` + `active`（自启已关、服务仍在跑）：

```bash
systemctl is-enabled sing-box; systemctl is-active sing-box
```

> **务必用 `systemctl stop`，不要 kill 进程或直接关终端。**
> tun 模式改了系统路由表，正常 stop 会清理干净，强杀则可能残留路由导致整机断网。
> 真断网了修复：`sudo systemctl restart NetworkManager`

停止后确认已回到直连：

```bash
curl -s https://icanhazip.com        # 应显示本地运营商 IP，不是 43.156.23.211
ip -brief addr show tun0             # 应查不到该网卡
```

### 查看状态

本机服务：

```bash
systemctl status sing-box            # 一屏看全
systemctl is-active sing-box         # 是否运行 → active
systemctl is-enabled sing-box        # 是否自启 → enabled / disabled
systemctl show sing-box -p ActiveEnterTimestamp --value   # 本次启动时间
```

日志：

```bash
sudo journalctl -u sing-box -f                   # 实时跟踪
sudo journalctl -u sing-box -n 50                # 最近 50 行
sudo journalctl -u sing-box --since "10 min ago" | grep -i error
```

> **坑**：`journalctl -u` 默认翻出历史全部记录，含服务重启前的。看到一堆 error 先别慌，
> 用 `--since` 限定到本次启动时间之后再数。

哪些日志可以忽略：

- `connection download closed: ... endpoint not connected` — 客户端主动断开，
  关网页、切页面都会产生，正常噪音
- 真正要警惕的是连 `43.156.23.211:10086` 的 `i/o timeout`，那是服务端连不上了

远端 VPS（服务端 Docker 部署，需 SSH 登录）：

```bash
ssh <用户>@43.156.23.211

docker ps                      # 容器是否在跑
docker logs v2ray --tail 50    # 服务端日志，认证失败会报 invalid user
docker stats --no-stream       # 容器资源占用
uptime; free -h; df -h         # 负载、内存、磁盘
```

不登录的粗略探测，只判断端口是否在听：

```bash
timeout 5 bash -c 'cat < /dev/null > /dev/tcp/43.156.23.211/10086' \
  && echo "端口可达" || echo "端口不可达"
```

> 其实 `curl -s https://icanhazip.com` 返回 `43.156.23.211` 就已经证明服务端活着且认证通过了。

---

## 方案 B：v2rayN for Linux（图形界面）

Ubuntu 桌面版可以用，操作方式和 Windows 版一致。

```bash
cd /tmp
curl -fsSL -o v2rayn.deb \
  "https://github.com/2dust/v2rayN/releases/latest/download/v2rayN-linux-64.deb"
sudo apt install ./v2rayn.deb
```

从应用菜单启动，或命令行 `v2rayN`。

> 仅适用于有桌面环境的系统。纯服务器环境用方案 A。

---

## 方案 C：v2ray-core 官方脚本（仅命令行代理）

只想要一个本地 SOCKS5/HTTP 代理端口，不需要全局 tun 的场景。

```bash
# 官方安装脚本
sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh)"
```

### 客户端配置

```bash
sudo tee /usr/local/etc/v2ray/config.json > /dev/null <<'EOF'
{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "port": 1080,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "settings": { "auth": "noauth", "udp": true }
    },
    {
      "port": 1081,
      "listen": "127.0.0.1",
      "protocol": "http",
      "settings": {}
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "43.156.23.211",
            "port": 10086,
            "users": [
              {
                "id": "在此填入UUID",
                "alterId": 0,
                "security": "auto"
              }
            ]
          }
        ]
      },
      "tag": "proxy"
    },
    { "protocol": "freedom", "tag": "direct" }
  ]
}
EOF

sudo systemctl enable --now v2ray
sudo systemctl status v2ray
```

### 使用

```bash
# 临时给当前终端设代理
export http_proxy=http://127.0.0.1:1081
export https_proxy=http://127.0.0.1:1081
curl https://ifconfig.me

# 单条命令用代理
curl --socks5 127.0.0.1:1080 https://ifconfig.me

# 取消
unset http_proxy https_proxy
```

### 让 apt 走代理

```bash
sudo tee /etc/apt/apt.conf.d/95proxy > /dev/null <<'EOF'
Acquire::http::Proxy "http://127.0.0.1:1081";
Acquire::https::Proxy "http://127.0.0.1:1081";
EOF
```

不需要时删掉该文件即可。

---

# 三、三种方案怎么选

| | v2rayN | sing-box | v2ray-core |
|---|---|---|---|
| 界面 | 有 | 无 | 无 |
| 全局代理（tun） | 支持 | 支持 | 不支持 |
| 配置复用 iOS | 否 | **是** | 否 |
| 节点切换 | 界面点选 | 改配置重启 | 改配置重启 |
| 适合 | Windows 桌面日常用 | 跨设备统一配置 | 服务器/命令行 |

**建议**：

- Windows 日常上网 → **v2rayN**
- Ubuntu 桌面想要全局代理 → **sing-box**
- Ubuntu 服务器只需命令行走代理 → **v2ray-core**（方案 C）
- 想和 iOS 用同一份配置、省心维护 → 两边都用 **sing-box**

---

# 四、常见问题

### Windows Defender 报毒

代理软件的通用误报。处理：Windows 安全中心 → 病毒和威胁防护 → 管理设置 → 排除项 → 添加程序所在文件夹。

### v2rayN 开了系统代理但浏览器不走

- Chrome/Edge 跟随系统代理，正常应该生效
- Firefox 默认用自己的代理设置：设置 → 网络设置 → 选"使用系统代理设置"
- 装过 SwitchyOmega 等插件会覆盖系统代理，检查插件状态

### tun 模式下 Chrome 打不开 Google，但 Firefox 可以

2026-10-20 本机实测遇到过，根因是 **IPv6**，不是 Chrome 设置问题。

现象：

- Chrome 控制台刷 `ssl_client_socket_impl.cc ... net_error -100`
- `curl https://www.google.com` 返回 200，Firefox 也正常
- sing-box 日志里失败目标全是 IPv6：
  `open connection to [2404:6800:...]:443 ... i/o timeout`
  （`2404:6800::/32`、`2001:4860::/32` 都是 Google 的 IPv6 段）

原因：服务端无 IPv6 出口，而 `prefer_ipv4` 仍会接受 AAAA 记录。curl 单次请求拿到 A
记录就走了所以没事；Firefox 的 Happy Eyeballs 回落快；Chrome 对 IPv6 和 QUIC 都激进，
反复重试 IPv6，每条等 5 秒，表现就是一直转圈打不开。

解决：按上面「必改：关掉 IPv6」改成 `ipv4_only` 并删掉 tun 的 IPv6 地址，重启服务，
然后**完全退出 Chrome 重开**（`pkill chrome`，关窗口不够，Chrome 只在启动时读代理配置）。

排查时可忽略的无关噪音（Linux 版 Chrome 常见，和网络无关）：

- `Installed VAAPI version is too old` — 硬件解码
- `Failed to decrypt token` — 账号密钥环
- `Fontconfig error: Cannot load default config file` — 字体配置

### tun 模式下要不要装 SwitchyOmega

**不要。** tun 工作在网络层，流量在进程之外就被 tun0 接管了，Chrome 根本不知道有代理，
代理设置保持「系统默认」即可。

SwitchyOmega 的原理是让 Chrome 把请求转发到一个本地代理端口，而 tun 模式不提供这种端口
（`ss -tlnp` 看不到 1080/1081/10808 之类的监听）。真装了并填端口，结果是连接被拒绝、反而上不去网。
建议直接卸载而非停用，插件停用状态下仍可能注入配置覆盖系统设置。

要「部分站点走代理、部分直连并随时手动切」的精细控制，才需要本地端口：给配置加一个
`mixed` 类型 inbound（和 tun 并存），或按方案 C 另装 v2ray-core。
但配置里已有 geosite-cn / geoip-cn 智能分流，一般没必要。

### Ubuntu 桌面 GNOME 系统代理要设成什么

tun 模式下应设为 **`none`**：

```bash
gsettings get org.gnome.system.proxy mode    # 应为 'none'
gsettings set org.gnome.system.proxy mode 'none'
```

坑：如果 mode 是 `auto` 而 `autoconfig-url` 为空，Chrome 会走 WPAD 自动发现去找 PAC 文件，
在 tun 环境下找不到，请求失败。注意 curl 不读 GNOME 设置，所以命令行正常不代表浏览器正常。

顺手检查有无遗留的旧代理地址：

```bash
gsettings list-recursively org.gnome.system.proxy | grep -E "host|port"
# 有残留就重置
gsettings reset org.gnome.system.proxy.http host
gsettings reset org.gnome.system.proxy.http port
```

### Ubuntu 上 sing-box 报权限错误

tun 模式需要创建虚拟网卡。前台运行必须加 `sudo`；systemd 服务要保留 service 文件里的 `CapabilityBoundingSet` 和 `AmbientCapabilities` 两行。

### Ubuntu 关掉 sing-box 后断网

tun 模式会改系统路由表，异常退出可能残留。修复：

```bash
sudo systemctl restart systemd-networkd
# 或重启 NetworkManager
sudo systemctl restart NetworkManager
```

正常用 `systemctl stop` 停止不会有这个问题。

### 连不上，报 invalid user

网络是通的，认证参数不对。检查 UUID 是否完整、alterId 是否为 0。服务端看日志确认：

```bash
sudo docker logs v2ray --tail 20
```

### 怎么确认代理真的生效了

```bash
# 不走代理
curl -s https://ifconfig.me          # 应显示你本地的 IP

# 走代理
curl -s --socks5 127.0.0.1:10808 https://ifconfig.me   # v2rayN 默认端口
curl -s https://ifconfig.me          # tun 模式下直接就是代理 IP
```

显示 `43.156.23.211` 说明流量已经过服务器。

> `ifconfig.me` 偶尔会抽风返回空，别急着判定代理坏了，换个端点复核：
> `curl -s https://icanhazip.com` 或 `curl -s https://api.ipify.org`。

Ubuntu 上一次性全面体检：

```bash
systemctl is-active sing-box                      # active
ip -brief addr show tun0                          # 172.19.0.1/30
curl -s https://icanhazip.com                     # 43.156.23.211（走代理）
curl -s https://www.cip.cc/ | head -3             # 国内 IP（直连分流正常）
gsettings get org.gnome.system.proxy mode         # 'none'
journalctl -u sing-box --since "5 min ago" | grep -ic error   # 0
```

国内站点显示你本地的运营商 IP 才是对的，说明 geosite-cn / geoip-cn 规则让国内流量直连、
没有绕道服务器。

---

# 五、下载地址汇总

| 软件 | 地址 |
|---|---|
| v2rayN | https://github.com/2dust/v2rayN/releases/latest |
| sing-box | https://github.com/SagerNet/sing-box/releases/latest |
| v2ray-core | https://github.com/v2fly/v2ray-core/releases/latest |
| sing-box 文档 | https://sing-box.sagernet.org/ |

GitHub 下载慢可以用加速镜像，在原链接前加 `https://ghproxy.net/`：

```bash
curl -fsSL -o sb.tar.gz \
  "https://ghproxy.net/https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz"
```
