从内网穿透到舵机失控:开源桌面陪伴机器人的网络安全加固落地复盘
在复现开源桌面陪伴机器人(比如基于 Open Duck Mini 改版的语音交互鸭,或是基于 Hugging Face LeRobot 的遥操作机械臂)时,很多人的第一反应是先把模型跑通、舵机调顺、图传拉流成功。
但只要你想在办公室摸鱼遥控家里的机器人,或者给它接上公网的大语言模型与语音接口,网络安全就不再是服务器运维的专属词汇。几个月前,我为了远程调试一台基于树莓派 5 + 双目鱼眼相机的桌面机器鸭,通过 FRP 把本地控制端口和视频流打到了公网 VPS 上,结果隔天看日志,发现被脚本扫描爆破,甚至有人尝试向我的总线舵机端口发送裸指令。
今天复盘一次桌面机器人在“图传、控制流、底层系统”三个层面的安全落地加固过程,附带具体配置和防御代码,避免你的机器鸭在公网上被人当成肉鸡或者直接烧舵机。
一、 涉及环境与组件(BOM & Setup)
在做任何加固前,先理清这台桌面机器人的软硬件拓扑。攻击面往往出现在“跨网络的数据转发点”。
- 计算平台:Raspberry Pi 5 (8GB) / Raspberry Pi OS 64-bit (Bookworm)
- 通信与下位机:Waveshare 串口总线舵机驱动板 + STS3215 串行总线舵机
- 软件协议栈:
- 图传层:MediaMTX (RTSP/WebRTC)
- 控制层:基于 Python FastAPI + WebSockets 的 LeRobot 简化远程遥操作服务
- 语音层:本地部署的 Whisper.cpp + 云端 LLM API
- 网络通道:Tailscale(虚拟局域网)替代原先的裸 FRP 映射
二、 暴露的风险点:机器人特有的网络物理安全
传统 Web 渗透最多导致数据泄露或被植入挖矿脚本,但桌面机器人连着物理执行机构。
- 未鉴权的 WebSocket 控制流:最初为了降低延迟,控制舵机角度的 WebSocket 接口未加任何认证。攻击者只要探测到端口,发送几组
{"id": 1, "angle": 180}就可以强制舵机打死角,几秒钟内就能烧毁堵转的舵机甚至融化 3D 打印支架。 - RTSP 视频流裸奔:桌面上对准私人空间的摄像头,很多开源推流工具默认未开启 Digest 认证,极易变成公开的监控探头。
- Root 权限滥用:为了方便读写
/dev/ttyUSB0和 GPIO,直接用 root 权限运行整套 Python 机器人服务,一旦服务存在远程代码执行(RCE)漏洞,整台主控立刻沦陷。
三、 落地加固步骤
1. 物理层防御:驱动级软限位与急停熔断
不能把物理安全寄托在网络协议层。在串口下发控制指令的底层类中,必须硬编码物理行程约束与发令频率限制,即使网络层被完全攻破,舵机也不会越界自毁。
# safety_controller.py
import time
import logging
class SafeServoController:
def __init__(self, raw_serial_driver):
self.driver = raw_serial_driver
# 硬编码物理安全界限,覆盖掉网络下发的任何极端值
self.LIMITS = {
1: (45.0, 135.0), # 头部俯仰角度限位
2: (10.0, 170.0), # 身体旋转角度限位
}
self.last_cmd_time = 0.0
self.MIN_INTERVAL = 0.02 # 限制下发频率,防止高频指令打崩串口或使舵机震颤
def move_servo(self, servo_id: int, target_angle: float):
current_time = time.time()
if current_time - self.last_cmd_time < self.MIN_INTERVAL:
logging.warning("指令频率过高,丢弃该帧")
return False
if servo_id not in self.LIMITS:
logging.error(f"未授权的舵机 ID: {servo_id}")
return False
min_ang, max_ang = self.LIMITS[servo_id]
# 强制截断到安全范围
clamped_angle = max(min_ang, min(target_angle, max_ang))
self.last_cmd_time = current_time
return self.driver.write_angle(servo_id, clamped_angle)
2. 控制层防御:WebSocket 增加 HMAC 时间戳防重放鉴权
摒弃无状态裸连接,改用简易的 HMAC-SHA256 签名机制。客户端发送控制帧时带上毫秒时间戳与签名,服务端在 500 ms 窗口外的数据直接判定为失效。
# auth_gateway.py
import hmac
import hashlib
import json
import time
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
app = FastAPI()
SHARED_SECRET = b"your_offline_provisioned_secret_key" # 部署时烧录的预共享密钥
def verify_signature(payload: dict, client_signature: str) -> bool:
# 检查时间戳,防止重放攻击 (窗口设为 500ms)
ts = payload.get("timestamp", 0)
now_ms = int(time.time() * 1000)
if abs(now_ms - ts) > 500:
return False
# 重构签名串
message = json.dumps(payload, sort_keys=True).encode("utf-8")
expected_sig = hmac.new(SHARED_SECRET, message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected_sig, client_signature)
@app.websocket("/ws/control")
async def control_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
raw_data = await websocket.receive_text()
packet = json.loads(raw_data)
payload = packet.get("payload")
signature = packet.get("sig")
if not payload or not signature or not verify_signature(payload, signature):
await websocket.send_json({"error": "Unauthorized or replay attack"})
await websocket.close()
break
# 鉴权通过,执行运动解算并下发
servo_id = payload.get("id")
angle = payload.get("angle")
# safe_controller.move_servo(servo_id, angle)
except WebSocketDisconnect:
pass
3. 网络与系统层收敛:废弃裸公网穿透,收拢访问控制
放弃把端口直接映射到公网 IPv4 的做法。机器人这类高私密性、低并发设备,更适合零信任网络(Zero-Trust Network Access)。
-
引入 Tailscale / WireGuard:树莓派主控与遥控客户端加入同一个 Tailscale 网状局域网,通信直接走加密通道,主控不再需要监听公网接口。
-
主控权限收敛(Udev 规则):普通用户运行控制脚本,无需 root 即可读写串口。
在树莓派
/etc/udev/rules.d/99-robot-serial.rules中添加:bashKERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", GROUP="dialout"将运行机器人的专用系统用户
robot_runner加入dialout用户组,实现服务降权。 -
开启系统级防火墙:
bashsudo apt install ufw sudo ufw default deny incoming sudo ufw default allow outgoing # 仅允许 Tailscale 虚拟网卡传入流量 sudo ufw allow in on tailscale0 to any port 8000 proto tcp sudo ufw enable
四、 避坑与翻车点排查
- 时钟不同步导致鉴权全部失效:树莓派断电且无 RTC(实时时钟)电池时,如果开机没连上外网导致 NTP 未同步,时间戳校验会直接把所有合法握手判定为过期。解决办法:加装 DS3231 I2C 硬件 RTC 模块,或者在网络就绪前推迟 WebSocket 握手。
- HMAC 序列化顺序问题:Python 的
json.dumps必须显式加上sort_keys=True。否则不同操作系统或客户端解析出来的字典键值顺序不一致,会导致签名验证莫名其妙失败。 - 加密算法对单片机/主控延时的影响:不要在每次 50 Hz(20 ms)的高频控制帧上反复做高强度的握手解密。合理的策略是连接握手阶段做强身份认证并换取短效 Session Token,高频控制通道只校验轻量级的 HMAC 或基于已建立的 TLS / WireGuard 隧道裸跑低延迟 UDP。
五、 结语
在 DIY 桌面机器人的过程中,让它动起来、学会视觉跟踪、说出第一句话往往让人很有成就感;但要让它在家里 7x24 小时开机并安全连接外网,安全闭环是绕不过去的底线。把驱动限位写死、废除端口直连、降权运行系统进程,这三件事做好,基本能抵御绝大部分外部脚本的扫描与非授权控制。
License: CC BY-NC 4.0
Updated 3 hours ago
Was this article helpful? Give it a like.
0 comments


