OpenVPN + TLJH + isso 从 Debian 迁移到 Ubuntu
2026-03-26 完成了从 Debian (jhub.dtype.info) 到 Ubuntu (mail) 的服务迁移记录。
一、OpenVPN 迁移
1.1 备份与传输
在源服务器上备份配置:
1
| sudo tar -czvf openvpn-backup.tar.gz /etc/openvpn/
|
1.2 恢复时的问题与解决
| 问题 |
原因 |
解决方案 |
/etc/openvpn/server.conf 不存在 |
配置文件实际在 /etc/openvpn/server/server.conf |
创建符号链接 |
Cannot pre-load keyfile (tc.key) |
相对路径解析错误 |
改用绝对路径 /etc/openvpn/server/tc.key |
Cannot assign requested address |
local 10.146.0.3 是旧服务器 IP |
改为新服务器 IP 160.251.141.121 |
1.3 修复后的配置
1 2 3 4 5 6 7 8 9 10
| sudo ln -s /etc/openvpn/server/server.conf /etc/openvpn/server.conf
ca /etc/openvpn/server/ca.crt cert /etc/openvpn/server/server.crt key /etc/openvpn/server/server.key dh /etc/openvpn/server/dh.pem tls-crypt /etc/openvpn/server/tc.key crl-verify /etc/openvpn/server/crl.pem
|
1.4 NAT 转发配置
OpenVPN 客户端连接后无法上网,需要添加 NAT 规则:
1 2 3 4 5 6
| sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo apt install iptables-persistent sudo iptables-save > /etc/iptables/rules.v4
|
1.5 防火墙放行
1 2 3
| sudo ufw allow 1194/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp
|
二、TLJH (The Littlest JupyterHub) 迁移
2.1 安装 TLJH
Ubuntu 上使用官方安装脚本:
1 2
| curl -sL https://raw.githubusercontent.com/jupyterhub/the-littlest-jupyterhub/main/bootstrap/bootstrap.py -o /tmp/bootstrap.py python3 /tmp/bootstrap.py
|
2.2 复制配置与状态
1 2
| ssh jhub "sudo tar -C /opt/tljh -czvf - config state" | ssh mail "sudo tar -xzvf - -C /opt/tljh/"
|
2.3 Traefik 配置问题
问题 1:端口 443 被 Tailscale Funnel 占用
1 2 3 4 5 6
| [entryPoints.http] address = "160.251.141.121:80"
[entryPoints.https] address = "160.251.141.121:443"
|
问题 2:traefik v3 不支持 toPort 参数(HTTP 重定向)
删除 traefik.toml 中的 toPort = 443 配置。
问题 3:缺少 HTTP 路由器定义
rules.toml 中需要显式定义路由器:
1 2 3 4
| [http.routers.router__2F] service = "service__2F" rule = "PathPrefix(`/`)" entryPoints = [ "https",]
|
2.4 防火墙放行
1 2
| sudo ufw allow 80/tcp sudo ufw allow 443/tcp
|
三、isso 评论系统迁移
3.1 安装 isso
1
| sudo pip3 install --break-system-packages isso gunicorn
|
3.2 创建 systemd 服务
1 2 3 4 5 6 7 8 9 10 11 12
| [Unit] Description=lightweight Disqus alternative
[Service] User=root Restart=always Environment="ISSO_SETTINGS=/etc/isso.conf" ExecStart=/usr/local/bin/gunicorn --bind 127.0.0.1:8000 --log-file /var/log/isso.log --preload isso.run
[Install] WantedBy=multi-user.target
|
3.3 配置文件与数据
1 2 3 4 5 6
| scp jhub:/etc/isso.conf mail:/tmp/ && ssh mail "sudo mv /tmp/isso.conf /etc/isso.conf"
scp jhub:/var/lib/isso/comments.db mail:/tmp/comments.db ssh mail "sudo mv /tmp/comments.db /var/lib/isso/comments.db"
|
3.4 Traefik 路由规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| [http.routers.isso] service = "isso" rule = "Host(`jhub.dtype.info`) && PathPrefix(`/isso/`)" entryPoints = [ "https",] middlewares = ["isso"]
[http.middlewares] [http.middlewares.isso.stripPrefix] prefixes = ["/isso"]
[http.services.isso.loadBalancer] passHostHeader = true [[http.services.isso.loadBalancer.servers]] url = "http://127.0.0.1:8000"
|
四、其他 Traefik 路由
4.1 LINE Webhook 转发
1 2 3 4 5 6 7 8 9 10
| [http.routers.line-webhook] service = "line-webhook" rule = "Host(`jhub.dtype.info`) && PathPrefix(`/line/`)" entryPoints = [ "https",]
[http.services.line-webhook.loadBalancer] passHostHeader = true [[http.services.line-webhook.loadBalancer.servers]] url = "http://100.94.198.8:18790"
|
五、服务状态
| 服务 |
端口 |
状态 |
| OpenVPN |
TCP 1194 |
✅ 运行中 |
| Traefik |
80/443 |
✅ 运行中 |
| JupyterHub |
15001 |
✅ 运行中 |
| isso |
8000 |
✅ 运行中 |
六、注意事项
- SSL 证书:acme.json 从旧服务器复制,Let’s Encrypt 证书可继续使用
- 客户端配置:OpenVPN 和 JupyterHub 客户端的
remote 地址需要更新为新服务器 IP
- DNS:确保
jhub.dtype.info 和 registry.dtype.info 指向新服务器 IP
- 二进制文件传输:使用
scp 而不是管道 cat,避免数据损坏
- iptables-persistent:替代 UFW 使用,更可靠地保存防火墙规则
Ubuntu Server + BCM4360 Wi-Fi + SSH + Tailscale + Claude Code + 免密 sudo
针对我的 MacBook Pro (Retina, 13-inch, Late 2013 / A1502) 按这次实际操作的顺序,整理成一份完整的关键命令清单。
1. 在 macOS 上确认机器和 USB
1 2 3
| system_profiler SPHardwareDataType diskutil list diskutil info /dev/disk4
|
我的机器确认是:
1 2 3
| MacBookPro11,1 MacBook Pro (Retina, 13-inch, Late 2013) Model A1502
|
2. Ubuntu 安装后确认网络接口
1 2
| ip link ls /sys/class/net
|
最初只有:
连接手机 USB 网络共享后出现:
3. 用手机 USB 网络临时联网
1 2
| sudo ip link set enxXXXXXXXXXXXX up sudo dhcpcd enxXXXXXXXXXXXX
|
确认 IPv4:
1
| ip -4 addr show enxXXXXXXXXXXXX
|
确认路由:
测试 Internet:
4. 安装 BCM4360 驱动
确认内核:
这次是:
确认驱动版本:
1
| apt policy bcmwl-kernel-source | grep Candidate
|
安装:
1 2
| sudo apt update sudo apt install bcmwl-kernel-source
|
5. 让 wl 接管 BCM4360
这台 Mac 的 BCM4360 最初被 b43/bcma 路径占用。
卸载 b43:
加载 Broadcom 专有驱动:
确认:
最终出现:
并且内核日志出现:
1 2
| wlan0: Broadcom BCM43a0 B02.11 Hybrid Wireless Controller 6.30.223.271 wl 0000:03:00.0 wlp3s0: renamed from wlan0
|
说明成功。
6. 启用 Wi-Fi
1
| sudo ip link set wlp3s0 up
|
确认:
7. 扫描 Wi-Fi
如果没有 iw:
扫描:
1
| sudo iw dev wlp3s0 scan | grep SSID
|
8. 配置 NetworkManager
1 2
| sudo apt install network-manager sudo systemctl enable --now NetworkManager
|
查看:
连接 Wi-Fi:
1
| nmcli device wifi connect "我的SSID" password "我的WiFi密码"
|
确认:
以及:
此时就可以拔掉手机 USB 网络共享。
9. 安装并启动 SSH
1
| sudo systemctl enable --now ssh
|
确认:
应该得到:
查看 Wi-Fi/LAN IP:
从另一台电脑:
如果因为重装系统出现:
1
| WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
|
执行:
1
| ssh-keygen -R 192.168.1.8
|
然后重新:
10. 安装 Tailscale
1
| curl -fsSL https://tailscale.com/install.sh | sh
|
登录:
查看:
1 2
| tailscale status tailscale ip
|
11. 安装 Claude Code
1
| curl -fsSL https://claude.ai/install.sh | bash
|
确认:
启动:
12. 设置 seii 免密 sudo
1
| sudo visudo -f /etc/sudoers.d/seii
|
加入:
1
| seii ALL=(ALL) NOPASSWD: ALL
|
保存时如果看到:
1
| File Name to Write: /etc/sudoers.d/seii.tmp
|
直接按:
然后:
检查:
测试:
应该直接输出:
现在这台 A1502 的状态已经是:Ubuntu Server + BCM4360 Wi-Fi + SSH + Tailscale + Claude Code + 免密 sudo。后续就可以基本脱离 HDMI 外接显示器,通过 SSH/Tailscale 管理这台 MacBook。
MacBook WiFi 服务器 SSH 断连排查与修复
适用机器:macbook-a1502(老款 MacBook,Broadcom BCM4360 无线网卡,用 WiFi 连接当服务器使用)。
症状
机器作为服务器运行时,NetworkManager 偶发进入异常状态,直接导致 SSH 断开。手动 sudo systemctl restart NetworkManager 可以恢复。
诊断方法
直接翻日志找证据:
1 2 3 4 5 6 7 8 9 10 11 12
| journalctl -u NetworkManager --no-pager -p warning
journalctl -u wpa_supplicant --no-pager
journalctl -k --no-pager | grep -iE "wl0|brcm|firmware|wlp3s0"
nmcli -f 802-11-wireless.powersave connection show "<连接名>" cat /etc/NetworkManager/conf.d/*.conf
|
根因
网卡驱动本身不稳定:这颗 BCM4360(PCI ID 14e4:43a0)用的是专有闭源 wl(broadcom-sta,DKMS 编译)驱动,跟现代内核 + wpa_supplicant 的 nl80211 接口配合不好。日志里能看到周期性(约 10~15 分钟一次)的本机自发 deauth:
1 2 3
| CTRL-EVENT-DISCONNECTED bssid=... reason=3 locally_generated=1 nl80211: deinit ifname=wlp3s0 ... (几秒后自动重新关联)
|
reason=3 + locally_generated=1 = 驱动自己主动断开重连,不是路由器或信号问题。多数情况下几秒内自愈。
偶发不能自愈的两种情况(这是需要手动重启 NM 的真正原因):
wpa_supplicant 直接从 D-Bus 掉线:Couldn't initialize supplicant interface: Failed to D-Bus activate wpa_supplicant service / Name owner lost
- 关联彻底超时:
link timed out / Activation: failed
这两种情况下 NetworkManager 状态机卡死,不会自己恢复。
次要因素:/etc/NetworkManager/conf.d/default-wifi-powersave-on.conf(network-manager 包自带)全局开着 wifi.powersave = 3,对这颗本就不稳的驱动可能是加剧因素。
已实施的缓解措施
1. 关闭 WiFi 省电模式
包自带的 default-wifi-powersave-on.conf 是 dpkg 管理的 conffile,不要直接改,加一个文件名排在它后面(按字母序覆盖):
1 2 3 4
| sudo tee /etc/NetworkManager/conf.d/zz-disable-wifi-powersave.conf > /dev/null <<'EOF' [connection] wifi.powersave = 2 EOF
|
2 = 禁用省电(0=用驱动默认,1=不改,3=启用)。
2. 自动看门狗
/usr/local/bin/network-watchdog.sh:每次被调用时 ping 默认网关,连续 3 次失败(间隔 5 秒,共约 15 秒)才判定为真掉线,5 分钟内不重复触发(防抖动):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #!/bin/bash set -euo pipefail STATE_FILE="/run/network-watchdog.last-restart" LOG_TAG="network-watchdog" COOLDOWN_SECS=300 RETRIES=3 RETRY_INTERVAL=5
GATEWAY=$(ip route show default 0.0.0.0/0 2>/dev/null | awk '/default/ {print $3; exit}') [ -z "$GATEWAY" ] && { logger -t "$LOG_TAG" "no default gateway found"; exit 0; }
fail_count=0 for _ in $(seq 1 "$RETRIES"); do ping -c1 -W2 "$GATEWAY" >/dev/null 2>&1 && exit 0 fail_count=$((fail_count + 1)); sleep "$RETRY_INTERVAL" done
now=$(date +%s) last_restart=0 [ -f "$STATE_FILE" ] && last_restart=$(cat "$STATE_FILE") if [ $((now - last_restart)) -lt "$COOLDOWN_SECS" ]; then logger -t "$LOG_TAG" "unreachable but restarted recently, skip"; exit 0 fi
logger -t "$LOG_TAG" "gateway $GATEWAY unreachable after $fail_count attempts, restarting NetworkManager" echo "$now" > "$STATE_FILE" systemctl restart NetworkManager
|
配套 network-watchdog.service(oneshot)+ network-watchdog.timer(每 2 分钟跑一次),sudo systemctl enable --now network-watchdog.timer 启用。
查看日志:journalctl -t network-watchdog -f
局限:ping 网关是反应式检测,测的是『本机到 AP 这一跳』,测不到自愈型的短暂 deauth(通常等不到检测窗口就已经好了),但恰好能抓住那两种真正需要重启才能恢复的情况。更精确的判据可以直接查 nmcli device status 里 wlp3s0 的状态或 busctl status fi.w1.wpa_supplicant1 看 wpa_supplicant 是否还在 D-Bus 上,比纯 ping 更贴近症状本身。