🌐 网络工程 / 软考网工
OpenVPN Docker 容器故障排查与修复
- 手机通过 OpenVPN 连接后能访问 GitHub,但 Google/YouTube 无法访问
- 根本原因:
- DNS 配置错误: OpenVPN AS 推送的 DNS 是
127.0.0.11(Docker 内部 DNS),手机上不存在该地址 - NAT 缺失: VPN 子网
172.27.0.0/16没有做 MASQUERADE,包出去了但回不来
- DNS 配置错误: OpenVPN AS 推送的 DNS 是
- 解决方法:
- 修改容器
/etc/resolv.conf为公共 DNS(8.8.8.8+1.1.1.1) sacli设置vpn.client.routing.dns.0/1为公共 DNS- 容器内添加 iptables NAT:
iptables -t nat -A POSTROUTING -s 172.27.0.0/16 -o eth0 -j MASQUERADE - 修改 entrypoint 脚本使修复持久化
- 修改容器
- 关键知识点:
- Docker 容器的
127.0.0.11是内部 DNS 解析器,只在容器内有效 - OpenVPN AS 配置要用
sacli工具写入数据库,直接改config-local.json不生效 sacli ConfigPut→ 写入配置,sacli Start/Stop→ 重启服务- VPN 子网需要 NAT 才能访问外网,容器重启后 iptables 规则会丢失
- Docker 容器的
反向代理(Reverse Proxy)技术
正向代理 vs 反向代理
- 正向代理:代理客户端,帮客户端访问服务器(如 VPN、Fiddler)
- 反向代理:代理服务器,帮服务器接收客户端请求(如 Nginx、HAProxy)
核心作用
- 安全隐藏:客户端不知道真实服务器,反向代理是"面具",集中配置 WAF/防火墙
- 负载均衡:多台后端服务器,按策略分发请求(轮询、加权轮询、IP Hash、最少连接)
- SSL 终结(SSL Termination):客户端 HTTPS → Nginx 卸载 SSL → 后端明文 HTTP,减少加密开销
- 缓存加速:静态资源由 Nginx 直接响应,动态请求转发后端,减轻服务器压力
- 统一入口:多服务只暴露 80/443,按域名/路径分发(server_name + location + proxy_pass)
- 压缩与限流:Gzip 压缩响应、限制请求速率防 DDoS
常用 Nginx 指令
proxy_pass:转发请求到后端地址upstream:定义后端服务器组(负载均衡)location:匹配请求路径server_name:匹配域名
实际例子
客户端 → Nginx :443 (HTTPS) → :8000 (MkDocs)好处:加 HTTPS、绑域名、隐藏端口、统一管理
考试/面试要点
- 正向代理代表客户端,反向代理代表服务器
- 负载均衡四种算法:轮询、加权轮询、IP Hash、最少连接
- SSL 终结在代理层卸载加密
Nginx 负载均衡轮询配置
基础配置(默认轮询算法)
基础配置(默认轮询算法)
http {
upstream backend {
server web01;
server web02;
server web03;
}
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}关键点
upstream块定义后端服务器组- 默认算法就是轮询(round-robin):依次分配 web01 → web02 → web03
proxy_pass http://backend;中的backend对应upstream起的名字server后可加:port(如server web01:8080;),不写默认 80proxy_set_header把客户端真实信息传给后端
轮询变体
# 1. 加权重(默认 weight=1)
upstream backend {
server web01 weight=3;
server web02;
server web03;
}
# 2. ip_hash(同一客户端固定到同一后端)
upstream backend {
ip_hash;
server web01;
server web02;
server web03;
}
# 3. least_conn(最少连接数优先)
upstream backend {
least_conn;
server web01;
server web02;
server web03;
}
# 4. fair(按响应时间,第三方模块)
upstream backend {
fair;
server web01;
server web02;
server web03;
}常用参数
| 参数 | 作用 |
|---|---|
weight=N | 权重,默认为 1 |
max_fails=N | 失败次数上限(默认 1),配合 fail_timeout |
fail_timeout=10s | 失败超时时间(默认 10s) |
backup | 备用服务器,其他全挂才启用 |
down | 永久下线,不参与负载 |
max_conns=N | 限制最大并发连接数 |
upstream backend {
server web01 weight=3 max_fails=2 fail_timeout=10s;
server web02 weight=2;
server web03 weight=1;
server web04 backup; # 备用机
}操作命令
nginx -t # 检查配置语法
nginx -s reload # 重载配置(不中断服务)
systemctl restart nginx配置位置
- 主配置:
/etc/nginx/nginx.conf - 站点配置:
/etc/nginx/conf.d/或/etc/nginx/sites-enabled/ - 推荐
include /etc/nginx/conf.d/*.conf;拆分
Stream 模块(四层负载均衡)
适用 TCP/UDP 层(如 MySQL、Redis、游戏服务器),不是 HTTP。
适用:TCP/UDP 服务负载(MySQL、Redis、游戏服务器等)。
stream {
upstream web_cluster {
server web01:80;
server web02:80;
server web03:80;
}
server {
listen 80;
proxy_pass web_cluster;
proxy_connect_timeout 3s;
proxy_timeout 600s;
}
}stream 模块注意
- 没有
proxy_read_timeout/proxy_send_timeout(HTTP proxy_pass 才有的) proxy_timeout控制 nginx↔客户端 连接的空闲超时,不是后端响应时间- TCP 层发不出去就超时,没法定义"业务响应超时"
故障转移
server {
listen 80;
proxy_pass web_cluster;
proxy_connect_timeout 3s;
proxy_timeout 600s;
proxy_next_upstream on; # 默认就是 on,可不写
proxy_next_upstream_tries 2; # 总尝试次数
proxy_next_upstream_timeout 5s; # 整个重试超时时间
}注意: stream 的 proxy_next_upstream 只对连接阶段生效(握手失败、连接被拒、连接超时),不能像 HTTP proxy 一样根据 500/502/503 等业务返回码切换。
sing-box 代理部署(RainYun 服务器)
背景
- 服务器:RainYun R9(北京机房),4 核 8G 内存
- 系统:Debian 12 (bookworm)
- 目的:为 OpenRouter API (
openrouter.ai) 提供国内反向代理,解决 API 访问问题 - 方案:使用 sing-box 做反向代理(非 VPN/代理上网,仅代理特定 API 域名)
安装过程
1. 一键安装
wget -O /usr/local/bin/sing-box-as-a-service.sh https://raw.githubusercontent.com/SagerNet/sing-box/refs/heads/dev/docs/sing-box-as-a-service.sh
chmod +x /usr/local/bin/sing-box-as-a-service.sh
bash /usr/local/bin/sing-box-as-a-service.sh安装选项:1-安装;2-卸载;3-查看版本;5-安装为 systemd 服务(推荐)
2. 生成的文件
- 配置文件:
/etc/sing-box/config.json - 服务文件:
/usr/lib/systemd/system/sing-box.service
配置架构
Inbound(入站)— VLESS + Reality
{
"type": "vless",
"tag": "vless-in",
"listen": "::",
"listen_port": 443,
"users": [{
"name": "admin",
"uuid": "你的UUID",
"flow": "xtls-rprx-vision"
}],
"multiplex": {"enabled": false},
"tls": {
"enabled": true,
"server_name": "www.microsoft.com",
"reality": {
"enabled": true,
"handshake": {"server": "www.microsoft.com", "server_port": 443},
"private_key": "你的私钥",
"short_id": ["你的short_id"]
}
}
}关键概念:
- VLESS:轻量级代理协议,比 VMess 更高效
- Reality:TLS 伪装技术,流量看起来像访问正常网站
server_name:伪装成访问www.microsoft.comhandshake:实际与微软服务器进行 TLS 握手
- XTLS-Vision:流控技术,提高代理效率
- 端口 443:使用标准 HTTPS 端口,不被识别为代理
Outbound(出站)— 两种模式
- Direct(直连):请求直接发送到目标服务器
- Block(拦截):拒绝连接,用于阻止不必要的流量
路由规则
"route": {
"rules": [
{"domain_suffix": [".anthropic.com"], "outbound": "block"},
{"domain_suffix": [".openrouter.ai"], "outbound": "direct"}
],
"final": "direct"
}服务管理
systemctl enable --now sing-box
systemctl status sing-box
journalctl -u sing-box -fOpenClaw 配置代理
在 systemd drop-in 中设置 HTTPS_PROXY:
mkdir -p /etc/systemd/system/openclaw-gateway.service.d
cat > /etc/systemd/system/openclaw-gateway.service.d/proxy.conf << 'EOF'
[Service]
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
EOF
systemctl daemon-reload && systemctl restart openclaw-gateway注意事项
- sing-box 仅代理特定 API 域名,不影响服务器其他服务
- Reality 的
server_name需选真实存在的网站 - UUID 和密钥需妥善保管,泄露会导致未授权访问
客户端配置 — Clash Verge 远程导入
- 从 sing-box 配置中提取节点参数,生成 Clash Meta(mihomo)格式的
.yaml配置文件 - 服务器上开 HTTP 服务:
python3 -m http.server 8080 -d <配置文件目录> & - Clash Verge 导入 URL:
http://服务器IP:8080/配置文件.yaml - 优点:以后更新配置只需改服务器文件,Clash 端点刷新即可,不用每次本地导入
- ⚠️ 用完记得关 HTTP 服务:
kill $(lsof -t -i:8080) - 防火墙需放行对应端口:
firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload
Mac 生成 SSH 密钥
基本命令
ssh-keygen -t ed25519 -C "邮箱@example.com"默认保存到 ~/.ssh/id_ed25519,一路回车即可。
指定文件名
ssh-keygen -t ed25519 -f ~/.ssh/mykey -C "邮箱"传公钥到服务器
ssh-copy-id -i ~/.ssh/id_ed25519.pub 用户名@服务器IPMac 无 ssh-copy-id 的替代方案
cat ~/.ssh/id_ed25519.pub | ssh 用户名@服务器 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"注意
- ed25519 优于 RSA(更安全更快),除非老设备不支持
- 兼容老设备用
ssh-keygen -t rsa -b 4096 - 私钥别泄露,公钥可公开
ed25519 是什么
定义
ed25519 是一种非对称加密算法,具体是椭圆曲线数字签名算法(EdDSA)。
名称拆解
- Ed = Edwards 曲线(椭圆曲线变体)
- 25519 = Curve25519(曲线参数名,Daniel Bernstein 设计)
在 SSH 中的作用
生成公钥/私钥对。公钥放服务器验证身份,私钥自己留着证明身份。
与其他算法对比
- ed25519:密钥短、速度快、安全性高,首选
- RSA:老牌兼容性好,但需 4096 位才够安全
- ecdsa:比 RSA 好,有潜在随机数漏洞
优势
- 密钥短(公钥仅 68 字符 vs RSA 几百字符)
- 生成和验证速度快
- 抗侧信道攻击,不依赖随机数质量
- 256 位密钥 ≈ RSA 3072 位安全强度
SSH 公钥存放位置
服务器端路径
~/.ssh/authorized_keys
设置步骤
mkdir -p ~/.ssh && chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
cat 公钥.pub >> ~/.ssh/authorized_keysMac 一行命令传公钥
cat ~/.ssh/id_ed25519.pub | ssh 用户名@IP "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"权限要求
~/.ssh/→ 700(rwx------)authorized_keys→ 600(rw-------)- 权限不对 SSH 会忽略公钥文件
authorized_keys 放多个公钥
直接放(推荐)
~/.ssh/authorized_keys 里一行放一把公钥,SSH 依次匹配:
ssh-ed25519 AAAA...公钥1 用户1@电脑1
ssh-ed25519 AAAA...公钥2 用户2@电脑2吊销某把公钥直接删对应行即可。
分文件放(进阶)
在 /etc/ssh/sshd_config 中配置:
AuthorizedKeysFile .ssh/authorized_keys .ssh/keys/%u/%t/%k然后按用户/用途分目录存放公钥。一般没必要,直接塞 authorized_keys 最简单。
SSH 本地端口转发 -L(VNC 场景)
基本命令
ssh -N -L 5900:127.0.0.1:5900 bbben@192.168.31.60参数拆解
| 参数 | 含义 |
|---|---|
ssh | SSH 客户端 |
-N | 不执行远程命令,只建立隧道,适合纯端口转发 |
-L 本地端口:目标地址:目标端口 | 建立本地端口转发(Local forwarding) |
bbben@192.168.31.60 | SSH 登录用户和远程主机 |
完整语法:
-L [本地地址:]本地端口:目标地址:目标端口数据流向
本机 127.0.0.1:5900
↓ SSH 加密隧道
远程 SSH 主机
↓ 由远程主机发起连接
远程侧 127.0.0.1:5900这里的 127.0.0.1:5900 是从远程 SSH 主机的视角访问的目标,不是本机的回环地址。
VNC 应用场景
- 远程机上的 VNC Server 只监听
127.0.0.1:5900,不直接暴露到网络。 - 建立隧道后,本机访问
127.0.0.1:5900,等价于通过 SSH 访问远程机的127.0.0.1:5900。 - VNC Viewer 连接
127.0.0.1:5900即可。
三种 SSH 转发对比
| 类型 | 参数 | 数据流向 | 常见用途 |
|---|---|---|---|
| 本地转发 | -L | 本地端口 → 远程侧可访问的目标 | 访问远程内网服务、VNC、数据库 |
| 远程转发 | -R | 远程端口 → 本地侧可访问的目标 | 把本地服务提供给远程侧访问 |
| 动态转发 | -D | 本地 SOCKS 代理 → 按请求动态访问目标 | 临时代理、浏览远程网络 |
-N 与 -f
-N:不打开远程 Shell,也不执行命令,只保留转发连接。-f:认证完成后转入后台,常与-N搭配使用:
ssh -fN -L 5900:127.0.0.1:5900 bbben@192.168.31.60后台隧道需要额外考虑断线重连和进程管理;长期使用可交给 systemd 或 autossh 管理。
Tailscale 组网与局域网访问
纯命令行登录
- 安装:
curl -fsSL https://tailscale.com/install.sh | sh - 执行
sudo tailscale up,终端会输出一个授权 URL - 用任意能上网的设备(手机/电脑浏览器)打开该 URL,用 Tailscale 账号登录并授权
- 服务器终端会自动完成认证
- 其他方式:
--auth-key=tskey-auth-xxx(预共享密钥,适合自动化部署) - 本质:CLI 发起请求,浏览器完成授权,两步分离
Tailscale IP 与子网掩码
问题:Tailscale 分配的 IP 为什么只有第一段 100 相同,第二三段都不同?
解答:Tailscale 使用 100.64.0.0/10 网段,/10 表示前 10 位是网络位:
100 = 01100100(二进制)
/10 要求前 10 位固定:01100100 01xxxxxx ...
第二字节只有前 2 位固定(必须是 01),后 6 位可变所以第二字节范围 64-127,第三四字节完全自由,22 位主机位可分配约 419 万台
子网掩码对照:
/24(255.255.255.0):前 24 位固定,254 台主机/16(255.255.0.0):前 16 位固定,65534 台主机/10(255.192.0.0):前 10 位固定,约 419 万台主机
关键理解:不是前几位数字相同才算同一子网,而是按位数算。/10 只要求前 10 位一样,第一个字节相同 + 第二个字节前两位相同就够了
CIFS 挂载(客户端访问 Samba 共享)
命令:sudo mount -t cifs //100.x.x.x/共享名 /mnt/samba -o username=用户,password=***
逐项拆解:
sudo:挂载是系统级操作,需要 root 权限mount -t cifs:把远程 SMB/CIFS 共享映射到本地目录//IP/共享名:远程地址 + 共享名(//固定格式)/mnt/samba:本地挂载点
安全写法(密码写文件避免明文泄露):
echo "username=用户" | sudo tee /root/.smbpasswd
echo "password=密码" | sudo tee -a /root/.smbpasswd
chmod 600 /root/.smbpasswd
sudo mount -t cifs //IP/共享 /mnt/samba -o credentials=/root/.smbpasswd常用挂载选项:uid=1000(所有者), gid=1000(组), iocharset=utf8(中文乱码), vers=3.0(SMB协议版本), ro(只读)
卸载:sudo umount /mnt/samba,有进程在用时报 target is busy
服务器端安装客户端工具:
# smbclient 是命令行工具,cifs-utils 提供 mount -t cifs 支持
# 都是客户端工具,不监听端口,不需要管理服务
sudo apt install smbclient cifs-utils是否需要一直挂载?
- 挂载点本身几乎不占资源,但网络断了会卡死(进程等待、命令卡住)
- 偶尔用:手动
mount/umount - 经常用:用 autofs 自动挂载(访问时挂载,闲置 5 分钟自动卸载)
- autofs 配置:
echo "/mnt/samba -fstype=cifs,credentials=/root/.smbpasswd ://IP/共享" >> /etc/auto.smb