服务器配置
- 如果你有服务器搭建 nginx 相关经验,可以忽略以下步骤,只需要进行这两步
- 将 command/nginx.conf 作为 nginx 配置文件,若为方案 B,将文件中的 5922 改成 10000
- 若为方案 B,在 secret.yaml 中填写 SERVER_IP、SERVER_USERNAME,并放置服务器密钥于 command/lrobot.pem 处
切记不要随意公布你的服务器 ip
配置参考
- 若本地有 ip,则忽略配置服务器步骤,直接从配置 nginx 开始
- 若不需要域名,则修改 nginx.conf 配置
- 若只有本地不想用服务器,可以参考其他
各平台连接方式及需求
- 其中 napcat 是基于本地的 http 协议推送和调用,bili 是基于浏览器 cookie 的 api 调用,均不涉及
ip/域名配置
| 平台 | qqbot | qqbot(botpy) | 微博 | QQ小程序 | |
|---|---|---|---|---|---|
| 连接协议 | https | websocket | http | http | https |
| 消息接收方式 | 主动推送到对应 ip(https) | websocket | 主动推送到对应 ip(http) | 主动推送到对应 ip(http) | request 请求(https)(域名) |
| 消息发送方式 | 白名单 ip 调用对应 api | websocket | 调用对应 api | 调用对应 api | request 请求返回 |
| 需求 | 支持 https 的域名/ip,公网 ip 发送 | 公网 ip 接收及发送 | 支持 http 的 ip | 支持 http 的 ip | 支持 https 的域名 |
配置总结
- 运行所有服务需要正向代理 + 反向代理 + 域名 + 备案
- 没有备案域名无法运行 qqapp(当前虽然不用 qqapp,但无备案域名也无法在 qq 上实现直接访问网站的效果)
- 没有域名无法运行 qqbot 和 QQ 小程序
- 考虑到校园网络环境,本项目中正向代理选择
服务器+socks的方法,反向代理选择服务器+nginx的方法 - 本项目中服务器 ip 可随时更换(只需要更改 secret.yaml 的配置即可)
- 其他正向及反向代理方法可参考下方
配置服务器
- 由于网页端的备案限制,本项目选择方案
国内的服务器+国内的域名(不需要备案域名参考此处) - 本项目服务器配置为:
CPU - 2核内存 - 2GB系统盘 - SSD云硬盘 50GB流量包 - 300GB/月(带宽 4Mbps)
价格参考
- 免费: 国内的域名大部分都收费,国外的域名有大型的免费域名提供商
- 试用期: 国内大厂服务器试用期只有 1-3 月,国外的服务器试用期大多为 1 年(但需要 visa 卡),国内小厂服务器极其不稳定
- 价格:
- 国内域名中,阿里价格最便宜,新用户参考此处可获得 79/年的服务器,域名在 169/年左右
- 腾讯的其次,368/三年的服务器+109/三年的域名
- 国外的微软的云服务价格大致为 22 元/月,一年 264 元

购买服务器+域名
- 以本项目腾讯云域名+服务器购买为例
- 进入优惠页面,选择轻量应用服务器,参考价格
368/三年 - 实名后选择域名,购买域名,参考价格
109/三年 - 选择右上角的备案,进行备案,等待腾讯云及信管局审核(7 天)
- 备案完成后,进入控制台,搜索 SSL 证书,选择申请免费证书,选择自动 DNS 验证
- 进入控制台,选择轻量应用型服务器,
SSH 密钥-创建密钥,绑定实例-同意强制关机-继续,私钥会自动下载,重命名为 lrobot.pem,放在 command 文件夹下 管理域名-添加域名解析勾选前两个,第三个输入*,确认,即使用www * @三条 A 记录
连接服务器
- ssh 连接
- 使用
ssh -i xxx\lrobot.pem username@ip连接服务器(记得替换命令中的密钥文件路径、管理员名称和服务器 ip)(腾讯管理员名称一般是 ubuntu) Are you sure you want to continue connecting (yes/no/[fingerprint])?选择 yes- 进入服务器命令行
- 图形界面连接
- 进入腾讯云,选择控制台,进入轻量应用服务器
- 选择对应服务器,点击登录
- 选择 OrcaTerm,使用图形界面登录
- 右侧是命令行,左侧可以打开文件夹快速传输文件
配置服务器端口
- 22 端口: ssh 连接,若使用方案 B,需要开启
- 80 和 443 端口: 通过服务器
ip/路径直接测试网页服务能否访问,排查连接错误时可开启- 测试结束后,关闭服务器的 80 及 443 端口,后续都直接通过域名访问
- 6099 端口:防火墙-来源全部ipv4-TCP-6099-允许,用于方案 A 中临时配置 napcat,使用结束后改为拒绝
域名证书
- 我的证书中可以下载证书(nginx 中需要)
- 使用免费证书,开启自动续费
- 不用开启托管,因为一般的服务器不满足条件
- 证书有效期为 3 个月,开启了在到期前 1 个月替换的话,可以每 2 个月替换证书(方法在下面);如果改成到期前 10 天的话可能替换的间隔久一点
配置 nginx
linux
- 参考教程或下文的安装命令在服务器上配置 nginx
- nginx 安装命令参考(可自行修改版本号为最新版本):
cd /opt
sudo wget http://nginx.org/download/nginx-1.28.0.tar.gz
sudo tar -zxvf nginx-1.28.0.tar.gz -C /usr/local
cd /usr/local/nginx-1.28.0
sudo apt-get update
sudo apt-get install -y zlib1g-dev libpcre3 libpcre3-dev build-essential libssl-dev certbot python3-certbot-nginx
sudo ./configure --with-http_v2_module --with-http_ssl_module
sudo make
sudo make install
- 启动 nginx 服务
cd /usr/local/nginx/sbin/sudo ./nginx(此步骤失败参考下方) - 将腾讯云下发的证书拷贝到服务器路径
/etc/nginx/ssl/whumystery.cn_bundle.pem和/etc/nginx/ssl/whumystery.cn.key处- 参考命令
ssh -i xx\lrobot.pem username@ip "sudo mkdir -p /etc/nginx/ssl/ && sudo chown username:username /etc/nginx/ssl/scp -i xx\lrobot.pem "xx\whumystery.cn_bundle.pem" username@ip:/etc/nginx/ssl/scp -i xx\lrobot.pem "xx\whumystery.cn.key" username@ip:/etc/nginx/ssl/- 输入
cd /usr/local/nginx/sbinsudo ./nginx -s reload来重启 nginx 服务
- 修改 nginx 配置,使用项目中的 nginx.conf(command/nginx.conf)(方案 B 更改 5922 为 10000)
- 传输方法:
- 在本地命令行使用命令
type xxxx\nginx.conf | ssh -i xxx\lrobot.pem username@ip "sudo tee /usr/local/nginx/conf/nginx.conf > /dev/null" - 以更改服务器上的 nginx 配置文件(记得替换 nginx 配置路径、密钥文件路径、管理员名称和服务器 ip)
- 随后通过 ssh 连接服务器
- 输入
cd /usr/local/nginx/sbinsudo ./nginx -s reload来重启 nginx 服务
windows
- 注意 windows 版本,windows 在 2016 以上才能安装 openssh
- 访问 https://nginx.org/en/download.html 下载 nginx
- 复制本地文件 nginx.conf 到 nginx/conf/nginx.conf
- 同样复制证书文件(需要修改 nginx.conf 中密钥文件路径为 windows 上的路径)
nginx 配置
- nginx.conf 文件说明
- 由于没有配置 AAAA 记录以及服务器无 IPV6 地址,故 nginx.conf 里不考虑 IPV6
- 配置开启 Gzip,并支持 WebSocket
- 禁止通过服务器 IP 直接访问,所有 HTTP 请求 301 重定向到 HTTPS,并将请求转发到本地 5922 端口
- 配置默认错误页面(需要根据路径自行在服务器上设置页面)
- nginx 配置在请求头处携带 ip,供后端分析原 ip
- 若是方案 B,使用 command 容器启用转发时,需要转发到 10000 端口,故把两处 5922 改为 10000
相关问题
Connection closed by xxx port 22
省流: 直接刷新代理
- 解决步骤如下:
- 步骤:观察到日志
/home/ubuntu/.ssh/authorized_keys:1: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
- 猜测:authorized_key 添加了限制参数,端口转发 -R 不在允许范围内
- 结果:
/home/ubuntu/.ssh/authorized_keys发现没有任何限制
- 步骤:猜测 ssh 可能不允许远程转发
- 执行
sudo grep -E "AllowTcpForwarding|GatewayPorts" /etc/ssh/sshd_config看 ssh 是否允许远程转发 - 结果:允许:
sudo vim /etc/ssh/sshd_config``AllowTcpForwarding yes``GatewayPorts yes``sudo systemctl restart ssh
- 步骤:日志
Remote: Forwarding listen address "localhost" overridden by server GatewayPorts
- 猜测:GatewayPorts 未生效?
- 执行:
sudo sshd -T | grep -E "allowtcpforwarding|gatewayports" gatewayports yes allowtcpforwarding yes已生效 - 结果:是正常日志,不用管()
- 步骤:猜测是客户端主动断开的连接
- 猜测:触发了容器连接失败的关闭机制
- 猜测:ssh_clean 未完成,ssh_run 便开始执行
- 猜测:await 只是开启了连接,ssh 连接后续的执行不归 await 管
- 执行:打印结果,对照两边日志,加 sleep,发现 ssh_clean 退出后才执行 ssh_run
- 结果:跟两条指令先后无关
- 步骤:删除退出重连机制,检测日志,本地只有两次认证,而服务器有三次
Accepted publickey for ubuntu from xx port xx
- 现象1:新ssh请求进入:
localhost sshd[286370]:Accepted publickey for ubuntu from xxx port 51623 - 现象2:清理进程登录成功:
localhost sshd[286370]: pam_unix(sshd:session): session opened for user ubuntu(uid=1000) by ubuntu(uid=0) - 现象3:旧10000端口进程被清理:
sshd[286205]: pam_unix(sshd:session): session closed for user ubuntu - 现象4:清理进程主动断开:
localhost sshd[286417]: Received disconnect from xx port xx:11: disconnected by user - 现象5:清理进程断开完成:
localhost sshd[286417]: Disconnected from user ubuntu xx port 51623 - 现象6:新ssh请求进入:
localhost sshd[286424]: Accepted publickey for ubuntu from xx port xx ssh2: RSA SHA256:... - 现象7:连接进程登录成功:
localhost sshd[286424]: pam_unix(sshd:session): session opened for user ubuntu(uid=1000) by ubuntu(uid=0) - 现象8:新ssh请求进入:
localhost sshd[286496]: Accepted publickey for ubuntu from xx port 51634 ssh2: RSA SHA256:... - 现象9:未知进程登录成功:
localhost sshd[286496]: pam_unix(sshd:session): session opened for user ubuntu(uid=1000) by ubuntu(uid=0) - 现象10:连接进程被关闭:
localhost sshd[286424]: pam_unix(sshd:session): session closed for user ubuntu - 现象11:未知进程被关闭:
localhost sshd[286543]: Received disconnect from xx port 51634:11: disconnected by user - 推测:由于ssh机制或者代理问题,清理进程和未知进程都被发送了两次,后一个清理进程关闭了前一个已经连接上的连接进程
- 结论:解决这个多次连接的问题,只有把两条命令合并
- 步骤:开始尝试合并指令
- 执行1:改成
ssh -f -v -N -C -D ... -R 10000:lrobot:5922 user@ip sh -c 'sudo lsof -t -i:10000 | xargs -r sudo kill -9; while sudo lsof -i:10000 >/dev/null; do sleep 0.5; done' - 结果:不行,由于是先连接后执行命令行,10000端口被占用时,连接会直接失败,命令不会执行
- 执行2:
ssh -i {pem_path} {username}@{ip} 'sudo lsof -t -i:10000 | xargs -r sudo kill -9; while sudo lsof -i:10000 >/dev/null; do sleep 0.5; done; exec ssh -f -N -C -D 0.0.0.0:5923 -R 10000:lrobot:5922 localhost' - 解释:先执行清除再执行连接
- 结果:ssh key 验证失败,服务器无 ssh key
- 执行3:使用 ssh-agent 把 pem 加载到服务器
set -e
# 启动 ssh-agent 并添加密钥
eval "$(ssh-agent -s)"
ssh-add /app/storage/lrobot.pem
# 执行原始 CMD
exec "$@"
- 解释:容器启动时加载 entrypoint.sh,命令使用
ssh -i pem user@remote 'sudo lsof -t -i:10000 | xargs ...; exec ssh -f -N -C -D ... -R ... localhost',自动加载 key 到服务器 - 结果:外层 ssh 一旦执行完了清理命令,内层 ssh 会被关闭,此方法不行
- 步骤:合并指令均失败,尝试自动清理 ssh 端口
- 执行1:使用 ForceCommand,在 sshd_config:
Match User your_username``ForceCommand /usr/local/bin/ssh_wrapper.sh
#!/bin/bash
# 清理10000端口占用
sudo lsof -t -i:10000 | xargs -r sudo kill -9
# 执行原始命令(ssh传入的)
if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
exec $SSH_ORIGINAL_COMMAND
else
exec bash
fi
- 执行1:编辑脚本 ssh_wrapper.sh
- 结果:ForceCommand 发生在用户 session 建立之前,10000 端口被占用时连接直接失败了,无法执行
- 执行2:使用 ~/.ssh/rc 来清除端口
#!/bin/bash
# 检查是否有 sshd 进程已经在尝试绑定10000端口(处于 LISTEN 状态)
if ss -lntp 2>/dev/null | grep -q ":10000"; then
sudo lsof -t -i:10000 | xargs -r sudo kill -9
fi
- 结论:~/.ssh/rc 是在 用户的 shell/session 已经建立后才执行,但连接 10000 端口已经被占用了,失败
- 步骤:清除端口失败,重新尝试使用 && 合并两个 ssh 命令
- 结论:仍存在这个问题
- 步骤:重新回到问题,本地只有两次 ssh 连接,服务器却收到三次 Accepted publickey
- 猜测:触发了 ssh 的额外机制
- 执行:本地打印 ssh 命令,只有两个
- 执行:使用 sudo ss -tnp | grep 183.219.193.87 抓已有连接的启动时间和参数
- 执行:抓取到异常的那次连接的参数:
ESTAB 0 0 [::ffff:10.0.12.2]:22 [::ffff:xxx]:52223 users:(("sshd",pid=441803,fd=4),("sshd",pid=441756,fd=4)) - 执行:
sudo ps -fp 441803拿到文件描述符 - 执行:
- 查看其是否建立隧道
sudo ss -lnp | grep -E '10000|5923'-> 同样建立 - 查看 exec 记录
sudo grep "441756" /var/log/auth.log | grep -E 'cmd=|session opened|session closed'存在记录
- 查看其是否建立隧道
- 结果:其就是一模一样的连接,参数、命令行同转发命令
- 结果:本地无日志,因为是绕过 docker 另一个进程发的
- 步骤:确认产生了新连接,排查新连接是否为 docker 产生
- 执行 docker logs command 2>&1 | findstr "ssh.*10000.*lrobot"
- 只有一条日志,不是 docker 产生
- 步骤:查找此时谁使用了对服务器的连接
- 执行
Get-NetTCPConnection -RemotePort 22 -RemoteAddress 175.24.19.228 -State Established |
Select LocalAddress,LocalPort,OwningProcess |
%{ $_ | Add-Member -NotePropertyName ProcessName -NotePropertyValue (Get-Process -Id $_.OwningProcess).ProcessName; $_ }
- 结果:发现只有当前的 ssh 与服务器的简单连接(纯 ssh 连接用于记录日志),还有 mihomo(梯子)
- 结果:本地只有一条早就有的 ssh 连接,没有新连接
- 步骤:推测为当前 ssh 的简单连接把其他两个命令行杀死了,并且重新连接
- 执行:退出当前 ssh 的连接,重试 docker
- 结果:仍然失败,ssh 简单连接不是异常连接
- 步骤:滚动捕获 ssh 连接
- 执行
while ($true) {
Get-NetTCPConnection -RemoteAddress xx -RemotePort 22 -EA SilentlyContinue |
Select LocalAddress,LocalPort,OwningProcess,
@{N='ProcessName';E={(Get-Process -Id $_.OwningProcess).ProcessName}},
@{N='CommandLine';E={(Get-WmiObject Win32_Process -Filter "ProcessId=$($_.OwningProcess)" | Select -Expand CommandLine)}}
Start-Sleep 1
}
- 结果: 55637,55636,55635,55634,55629,55628 五个端口循环出现,其中 55637 是 mihomo 出口
- 结果:与服务器上三条连接日志的端口不同(两条测试,一条正常)
- 结果:mihomo 的出口 snat 导致端口不同,无法将服务器端口与本地的端口对照
- 步骤:继上次错误排查后,正常转发了一会,第二天继续出问题,且总认证数从三次变成了四次
- 猜测:由于网络/代理异常,docker 和 windows 发送了两条连接,由于网络波动导致的时好时不好
- 结果:ssh 进程不同,则是独立的 tcp 连接
- 步骤:使用
sudo lsof -i:22查找有多少个 ssh 连接
- 结果:四个连接,192.168 是本地网卡,198.18 是 docker
TCP 192.168.1.13:53355 175.24.19.228:22 ESTABLISHED InHost TCP 192.168.1.13:61347 175.24.19.228:22 ESTABLISHED InHost TCP 198.18.0.1:53354 175.24.19.228:22 ESTABLISHED InHost TCP 198.18.0.1:61344 175.24.19.228:22 ESTABLISHED InHost
- 步骤:推测是执行了两次导致四个连接
- 结果:四个连接分别是正常情况下的连接+ssh 简单连接+两个 mihomo 转发
- 步骤:只需要知道 docker 的端口范围,在异常时查看异常端口是不是 docker 范围内的即可知道是不是 docker 产生的连接
- 执行:netsh int ipv4 show dynamicport tcp 为 49152–65535,异常端口和正常端口均不符合
- 步骤:继续捕获端口,实时捕获 TCP 连接查看进程
- 执行:
while ($true) {
netstat -ano | findstr ":22"
Start-Sleep -Seconds 1
}
- 结果:失败的 ssh 连接进程均为 0,无法直接捕获
- 步骤:推测可能是 ssh 的连接复用机制
- 执行:
cat ~/.ssh/config | grep -i control无连接复用配置
- 步骤:直接在容器中执行命令,排除程序干扰
- 执行
root@9f827cda1308:/app# ssh -i /app/storage/lrobot.pem ubuntu@xx \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
"sudo lsof -t -i:10000 | xargs -r sudo kill -9"
Warning: Permanently added 'xx' (ED25519) to the list of known hosts.
root@9f827cda1308:/app# ssh -i /app/storage/lrobot.pem \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-C -N -D 0.0.0.0:5923 \
-R 10000:lrobot:5922 \
ubuntu@xx &
[1] 36
root@9f827cda1308:/app# Warning: Permanently added 'xx' (ED25519) to the list of known hosts.
bind [0.0.0.0]:5923: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 5923
Could not request local forwarding.
Warning: remote port forwarding failed for listen port 10000
- 结果:仍然失败,显示端口 5923 已被占用
- 步骤:服务器监控每个 ssh 连接,看看连接是怎么产生的
- 执行:
sudo tail -f /var/log/auth.log | while read line; do
if echo "$line" | grep -q "Accepted publickey"; then
echo "--- 新连接 ---"
echo "$line"
# 尝试看到这个连接执行了什么命令
sleep 0.5
ps aux | grep sshd | tail -5
fi
done- 结果:发现四次 ssh 连接
- 结果:跟以前的排查步骤类似,用另一种方式验证了四次连接
- 步骤:推测是 IPV4 和 IPV6 各连了一次
- 执行:命令中用 ssh -4 强制 IPV4
- 结果:仍然四次连接
- 步骤:排查直接连接的 ssh 连接次数
echo "===== 测试SSH隧道 ====="
date +%H:%M:%S
ssh -vvv -4 -i /app/storage/lrobot.pem \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-C -N -D 0.0.0.0:5923 \
-R 10000:lrobot:5922 \
ubuntu@xx 2>&1 | tee /tmp/tunnel_debug.log &
TUNNEL_PID=$!
echo "隧道PID: $TUNNEL_PID"
# 等待5秒观察
sleep 5
# 查看debug日志中的关键信息
echo "===== 连接次数统计 ====="
grep -c "Connecting to" /tmp/tunnel_debug.log
grep "Connecting to" /tmp/tunnel_debug.log
echo "===== 认证次数统计 ====="
grep -c "Authenticating to" /tmp/tunnel_debug.log
grep "Authenticating to" /tmp/tunnel_debug.log
echo "===== 端口转发信息 ====="
grep -E "Local forwarding|Remote forwarding|bind|channel" /tmp/tunnel_debug.log | head -20
date +%H:%M:%S
echo "===== 测试结束 ====="
# 杀掉隧道进程
kill $TUNNEL_PID 2>/dev/null
- 结果:统计连接次数为 1,认证次数为 1,则 ssh 客户端只建立了一次连接
- 结果:一次 ssh 只建立了一次连接,是产生了多条 ssh 而不是单条
- 步骤:确认为,一次 ssh 的 forword 连接由于失败,尝试了 2~3 次连接,跟 clean 无关
- 在可以复现的网络环境下,只使用 ssh_run 指令,手动在服务器上清理 10000 端口,则可以成功转发;
- 关闭当前连接,重新执行 ssh_run,转发失败,服务器显示 3 次连接
- 故问题仅在于清理程序失效
- 步骤:回顾 AI 关键错误
- 使用 Claude 给出的方法统计出,一个 ssh 客户端只建立了一次连接
- chatgpt 直接认为,ssh 的尝试会在不同进程中尝试,且会有日志
- 结论:端口 5923 被占用才是关键,由于之前的端口被占用,所以 ssh 转发命令进行了 3 次尝试,有时是 2 次
- 结论:所以这些尝试才会使用相同的命令
- 最终结论:“重试”只是同一个 ssh 进程在 TCP/SSH 协议层的自动复连,没有新的进程、没有新的横幅,也没有新的错误回显,所以容器终端里看不到
- 步骤:修改清理命令
- 最终采用:
sudo lsof -i:10000; sudo fuser -k 10000/tcp;sudo lsof -i:10000; - 之前使用
sudo lsof -i:10000;sudo kill -9 …… - 以及还有其他几条命令,均失败
- 此命令仍有时候会失败
- 步骤:拆分重连
- 取消 command 的 restart:always
- 有时存在容器无日志、同时网页访问不了的情况,由于 ssh 连接日志阻塞,没有输出日志,也没有触发检测'closed by'的退出机制
- 改为在 lrobot 里定时检测网址,失败则使用 LR5921 向用户发送消息(但方案 A 中 Napcat 与 Lrobot 处于同一服务器,假设服务器崩溃可能也不会触发)
- 重启容器由 lrobot 中的指令触发命令行
docker restart完成,command 有时自动重启会失败,换成 lrobot 暂时没有出现失败情况 - 之后本系统直接从方案 B 换成方案 A 了,只观察了三天此方法
- 偶尔连不上刷新一下代理就好了
Invalid user dnsmasq from xxxxxx port 33570
- 在使用代理时,可能会出现此日志,dnsmasq 是使用代理后被更改的用户名,需要关闭代理
连接卡在 debug1: pledge: network
- 在同一电脑向服务器建立两个 ssh 连接时,如果开启 debug,有时会发现卡在此处,没找到解决方法,可能是服务器无法处理两个 ssh 连接的问题,使用网上的清理 btmp 文件、禁用 DNS 解析、重启 systemd-logind 服务三种方法,均未解决
端口占用处理
- 端口被占用:ssh 未正常关闭时发生
- 显示
Remote: Forwarding listen address "localhost" overridden by server GatewayPorts - 需要使用
sudo ss -lpn | grep :10000以及sudo kill -9 {pid}来杀死进程 - 已在 command 里面实现自动杀死进程
- 或者使用 netstat(部分不支持)
sudo netstat -tulnp | grep :80sudo kill -9 1234(占用进程的id)
错误调试
- 当配置 nginx 完成后却无法访问网站/发送消息显示不在白名单时可按照此步骤逐步排除配置错误
- 针对方案 B
- 本地运行后端代码开启测试端口(自行配置或者新建 test.py,复制下方代码)
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/test")
def read_test():
return Response(content="Hello World!", media_type="text/plain")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5922)
- 访问
127.0.0.1:5922/test成功返回 Hello World! - 命令行 1 输入
ssh -i xxx\lrobot.pem username@ip测试连接,命令行进入 linux 界面 - 命令行 2 输入
ssh -i xxx\lrobot.pem -v -N -R 10000:localhost:5922 username@ip显示remote forward success即为监听成功 - 命令行 1 分别用
curl http://localhost:10000/test和curl http://localhost:80/test测试端口是否转发成功、服务器是否配置成功 - 访问
服务器/test,域名/test测试反向代理、域名代理是否成功(如果服务器未开启 80/443 端口则不用测试服务器) - 关闭命令行 2
- 命令行 3 输入
ssh -v -N -D 5923 -i xxx\lrobot.pem username@ip - 显示
Remote: /home/lrobot/.ssh/authorized_keys:1: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding即为转发成功 - 命令行 1 输入
sudo nano /etc/ssh/sshd_config - 设置 AllowTcpForwarding yes、PermitTunnel yes、GatewayPorts no,重启服务
sudo systemctl restart ssh - 命令行 4 输入
curl -v --socks5-hostname 127.0.0.1:5923 http://ifconfig.me成功返回地址 - 若上一步错误
- 服务器(命令行 1)测试
curl http://ifconfig.me返回地址 ssh -D 5923 localhost- 接着
curl -v --socks5 127.0.0.1:5923 http://ifconfig.me - 正确返回服务器地址,则服务器本身配置正确
- 服务器(命令行 1)测试
uvicorn 解析失败
- 有时候会出现 uvicorn 服务在 0.0.0.0 无法访问的问题,解答可能是本地防火墙或安全组规则可能阻止了 127.0.0.1 的访问权限(尽管此情况较罕见)
ws 连接强制解析 ip
- 当使用校园网的时候,通过 ipconfig 获取的 ip 理应 是你的真实 ip,但把此 ip 加入 qq 的白名单后却无法建立 ws 连接,推测跟连接时的转发机制有关,请求到达校园服务器后,网关不会将请求直接映射到你的电脑上
- 当使用 socks5 的时候,将服务器 ip 加入白名单,向 qq 服务器建立 ws 连接,会连接失败,强制识别你的本机 ip(原理未知)
- 关于上一点
- 使用了查看 dns 解析,更改 aiohttp.session方法(storage/abandoned/aiohttp_test.py),更换 connector 的方法后,访问 ipconfig 返回的都是服务器的 ip
- 通过抓包工具(wireshark 和 tcpdump 监听)分析也是经过服务器转发的;请求头在本地打印后是空的,抓包的话需要解密
- 当使用 clash 的 TUN 模式时,由于是模拟网络层而不是应用层,可以绕开 ws 连接携带 ip 的限制(猜测),把代理服务器加入白名单后即可正确连接 qq
- 经过解析,可能需要启动 SNAT 技术来更改 websocket 请求中的源 ip 地址信息来实现 ws 的正向代理,clash 使用了 MASQUERADE(动态 snat)来完成
其他
免费域名+服务器申请
- 如果不使用备案域名,可以申请国外的免费域名+服务器试用
- 创建一个域名,以 cloudDNS 为例,进入官网,注册帐号并在邮箱激活
- 回到官网,选择是,跳转到 DNS 区域创建页面,在 DNS 托管中选择
创建区域-自由区,创建域名 - 删除域名网站的域名下所有记录,选择
添加新记录 - 进入 CloudFlare 并注册帐号,选择添加站点,输入你的域名
- 选择 Free 计划并确认,选择
继续,在更新名称服务器下面有两个名称服务器网址,复制 - 复制后回到域名网站,添加新记录,选择
NS,指向到粘贴名称服务器网址,其他不用填。重复两次 - 等待 cloudFlare 向你发送邮件,域名托管成功
- 创建一个服务器,以 Azure 为例
- 填写手机以及信用卡号(一定要有visa卡),注册成功(可以用微软帐号登录,也需要验证手机号和卡号)
- 选择
免费服务,创建 Linux 虚拟机,输入名称 - 选择
地区((Asia Pacific) East Asia 应该是香港,最快),大小选择B1s(免费服务应该只有 B1s),设置用户名 - SSH 选择
生成新密钥对,设置密钥对名称为 lrobot,选择查看+创建,选择下载密钥对并创建资源 - 选择
转到资源,点击概述里的公共 ip 地址,跳转到lrobot-ip 配置,选择设置-配置,IP 地址分配选择静态,点击保存 - 回到 lrobot,选择
网络-网络配置,创建端口规则-入站端口规则,服务分别选择HTTP和HTTPS,点击添加
- 如果没有免费服务,可以选择购买服务器
- 务必选择对应的域名和服务器,国外的域名对应国外的服务器,国内的域名对应国内的服务器
- 在 CloudDNS 官网上添加三条 A 记录,类型选择 A,指向到输入服务器公共 ip 地址,主机分别填写 (不填)、*、www (三条 A 记录放在 cloudFlare 上托管也行)
- 在配置 Cloudflare 时经常遇到无法转发的错误,所以使用 nginx 的 https 配置以及 Let's Encrypt 证书
- nginx 配置与上文相同
- 配置完成后,配置 Let's Encrypt SSL 证书与自动续期
sudo pkill nginxsudo certbot certonly --nginx -d 域名sudo certbot renew --dry-run,sudo crontab -e- 打开文件后在末尾添加
0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx" - 输入 ctrl+x 并输入 :qa 退出
cd /usr/local/nginx/sbin/,sudo ./nginx
- windows 中配置证书的操作为
- 访问 https://www.win-acme.com/ 下载 acme
- 运行 wacs,输入 n,2,域名,3,n,y,邮箱,成功配置
- 手动配置密钥生成路径
wacs.exe --target manual --host 域名 --store pemfiles --pemfilespath "C:\path\to\your\certs" - 需要每隔三个月运行 wacs.exe --renew 进行更新或者手动更新
其他正反向代理方法
clash
- 如果采用正向代理,下载 clash,自己获取一个机场,项目运行时开启 TUN 模式即可
- TUN 模式:模拟网络层设备,新建一个 TUN 虚拟网卡实现 DNS 劫持
- 服务模式:支持 TUN 模式,重启 clash 并在系统中创建一个服务管理网卡
- TAP 模式:模拟数据链路层设备,操作以太网帧
- 系统代理:配置系统中的代理,对于浏览器适用,对于其他软件可能不适用
- 可以设置一个长期的稳定 ip 针对 qq 服务器(sgroup.qq.com)的消息转发,配置为:
proxies:
- {name: xxx, server: xxx, port: 39091, type: ssr, cipher: chacha20-ietf, password: xxx, protocol: xxx, obfs: xxx, udp: true
rules:
- DOMAIN-SUFFIX,sgroup.qq.com,xx
- MATCH,DIRECT
小米球
- 反向代理可采用小米球
- 小米球使用 ngrok 服务让本地项目支持外网访问,可以自己申请隧道
- 申请好后将地址和密码写入 xiaomiqiu.conf 中,将小米球文件夹放在 lrobot 同级目录中
- 之后在 command 容器里面的 tasks 增加一项:run_xiaomiqiu(),如果需要日志格式转化则参考此处的方式
其他失败的方法
- 用 cloudflare 里的 zerotrust 的本地隧道穿透测试失败,能显示访问请求且结果为 NOERROR,tunnel 配置和 DNS 配置都是正常的,可能与网络有关
- DDNS-GO 可用于不存在网关的网络系统中。对于家庭组网路由,ipv4 和网关的 ipv4 相同,动态 DDNS 解析没有用,本质上还是获取你的固定的 ipv4 上报到类似于 cloudflared 的域名解析平台,访问提交给网关时,无法正确定位到你的电脑上
- 如果网关不是光猫而是路由器直接连接网线,可以尝试配置路由器分配固定 ip 的方法,即配置路由器某端口固定返回本机。光猫网关不支持分配端口
- 使用 ipv6 也许是可行的,但 Hurricane Electric 的 6in4 注册不了,可能是网络问题
相关知识
常用命令
目录操作
cd /path/to/dir进入目录cd ..返回上一级目录pwd查看当前路径ls列出当前目录下的文件ls -l以详细列表形式显示文件信息ls -a显示包括隐藏文件在内的所有文件mkdir 文件夹名创建文件夹rm 文件名删除文件rm -rf 文件夹名强制删除文件夹及其内容cp 源文件 目标路径复制文件mv 源文件 目标路径移动或重命名文件
文件操作
cat 文件名 查看文件内容
tail -f 文件名 实时查看文件日志输出
vim 文件名 使用 Vim 编辑文件
nano 文件名 使用 Nano 编辑文件
touch 文件名 创建空文件或更新修改时间
磁盘空间
df -h 查看磁盘分区使用情况
du -sh * 查看当前目录下每个文件夹大小
du -h --max-depth=1 / 查看根路径下一级目录的总大小
sudo journalctl --vacuum-size=100M 清理系统日志,限制总大小为100M
sudo apt autoremove 自动清理不再需要的软件包
sudo apt clean 清理 apt 缓存文件
sudo apt autoclean 删除无用的旧包缓存
系统操作
free -h 查看内存使用情况
uptime 查看系统运行时间与平均负载
ps aux 查看所有进程
kill 进程号 结束进程
kill -9 进程号 强制结束进程
网络相关
ping 域名或IP 测试网络连通性
curl http://example.com 请求网页内容
wget URL 下载文件
ss -tuln 查看端口占用情况
netstat -tulnp 查看监听端口
ifconfig 查看网卡信息
ip addr 查看 IP 地址
SSH相关
ssh 用户名@服务器IP 连接远程服务器
ssh -p 端口 用户名@IP 指定端口连接
sudo tail -f /var/log/auth.log grep sshd
scp 本地文件 用户名@远程IP:/目标路径 从本地上传文件到服务器
scp 用户名@远程IP:/远程文件 本地路径 从服务器下载文件到本地
软件相关
sudo apt update 更新软件包索引
sudo apt upgrade 升级所有可更新的软件包
sudo apt install 包名 安装软件
sudo apt remove 包名 卸载软件
apt list --installed 查看已安装软件
whereis 软件名 查找软件安装路径
用户相关
sudo su 切换为 root 用户
adduser 用户名 新建用户
deluser 用户名 删除用户
passwd 用户名 修改密码
chmod 权限 文件名 修改文件权限(如 chmod 755 script.sh)
chown 用户:用户组 文件名 修改文件拥有者
sudo 临时以管理员身份执行命令
名词解释
ip 与域名
- ip 包括 ip 地址(纯数字)和域名(字母)
- 域名是通过 DNS 解析成 ip 的
协议
- 协议: 传输层 tcp/udp,应用层 http/https/websocket
- 连接方式: (基于 tcp 协议)http1.0/http1.1/websocket
- 消息收发方式: 单向 request+ 单向 response/webhook,双向 websocket
- http 和 https 是两种不同的协议,分别关联服务器的 80 端口和 443 端口,https 需要证书
证书
- SSL 证书是一种数字证书,用于在客户端和服务器之间建立安全的加密通信通道
- 多个地方可以提供证书,腾讯提供收费和免费的证书,cloudflare、Let's Encrypt 提供免费的证书
备案
- 备案指所有国内的域名必须在 ICP 管理局进行备案,由于 QQ 小程序在 25 年年初停止了网址代备案服务,所以所有域名只能在服务器提供商处备案
- 国内的域名使用需要备案,国外的域名使用不需要备案
域名解析
- 域名必须要有一个域名托管处(DNS 解析跳转的地方)
- 免费域名的供应商不提供托管服务,可以使用 cloudflare 进行免费托管;收费的域名一般是域名提供商提供托管服务
- 域名托管服务器会根据记录将域名请求转发到你的服务器上
- 国内大企业服务器提供商无法解析国外的域名,只能解析国内域名(且需要审核);国内小厂服务器可解析所有域名(但非常容易跑路);国外的服务器可以解析国外及国内的域名
- 解析:
- A 记录: 将域名解析成一个 ipv4 地址
- CNAME 记录: 将你的域名解析成另一个域名
- DS 记录: 包含一个哈希值,用于验证 DNS 区域中下级区域的密钥
- 子记录:域名下的子域记录
- * : 所有未明确指定的子域名都会被解析到这个 IP 地址
- www:
www.域名会被解析到这个 IP 地址 - 域名本身: 仅域名本身会被解析到这个 IP 地址
服务器正向代理
- 正向代理(一般叫做代理)指的是客户端通过代理服务器访问目标服务器
- 服务器连接命令中的
-D 5923就是一种采用 socks5 的正向代理,把 5923 端口的请求转发到服务器 - 为什么不用 socks 而用 socks5?在测试 socks 代理时,使用
nslookup http://ifconfig.me返回服务器 unknown,与网关有关,必须强制在服务器上解析地址,--socks5-hostname 可以做到而 socks 不行;单独使用 --socks5 会报错
服务器反向代理
- 反向代理指的是服务器在接收到客户端请求后,将请求转发给真实的服务器,并将服务器的响应返回给客户端
- 服务器连接命令中如
-R localhost:10000:localhost:5922就是将本地的 5922 端口绑定到服务器的 10000 端口 - 同时配置 nginx 将 10000 端口转发到 80 端口和 443 端口,实现反向代理