树莓派远程访问安全方案:Ngrok隧道与双因素认证实战
2026/6/4 14:32:00 网站建设 项目流程

1. 项目概述与核心需求解析

作为一名常年与树莓派打交道的嵌入式开发者和家庭实验室管理员,我深知远程访问这个小家伙的痛与乐。无论是检查一个长期运行的爬虫脚本状态,还是临时修改一下智能家居中枢的配置,能随时随地连上家里的树莓派,意味着自由和效率。但这份自由背后,是赤裸裸的安全风险——直接把树莓派的SSH端口暴露在公网上,无异于在自家门口贴上了“内有宝藏,欢迎尝试”的告示。所以,我的核心需求非常明确:在保证便捷远程访问的前提下,构筑一道坚固的安全防线。这不仅仅是技术实现,更是一种安全工程思维的实践。

这个方案的核心思路是“隧道+双锁”。Ngrok负责解决网络可达性问题,它像一个专业的信使,在复杂的公网环境中为你和树莓派之间建立一条加密的专属通道,无需折腾路由器端口转发或申请公网IP。而双因素认证(2FA)则是这道门上的第二把锁,即使你的SSH密码不幸泄露,攻击者没有你手机上实时生成的动态验证码,依然无法闯入。将这两者结合在树莓派上,我们得到的是一个“开箱即用”且“防御增强”的远程管理方案。它特别适合那些运行着智能家居中枢、NAS、个人博客或自动化脚本,但又对公网暴露心存顾虑的树莓派玩家和物联网开发者。

2. 工具链选型与安全架构设计

为什么是Ngrok和Google Authenticator?这个选择背后是经过权衡的。市面上内网穿透工具不少,像frp、花生壳等各有千秋。Ngrok的优势在于其极简的配置和出色的稳定性,它提供了官方的ARM版本,与树莓派的兼容性非常好,并且其云端管理界面能清晰展示隧道状态和访问日志,对于故障排查非常友好。更重要的是,它的免费套餐对于个人用户访问SSH这种低频、低流量应用完全足够,避免了早期成本投入。

在安全层面,双因素认证的方案也有很多,比如基于短信、邮件或硬件令牌。我选择Google Authenticator及其对应的PAM模块,主要基于三点:一是完全离线,验证码的生成和校验不依赖网络,避免了因网络问题导致的认证失败;二是开源且集成度高libpam-google-authenticator在树莓派系统源中即可安装,与Linux的PAM(可插拔认证模块)体系无缝结合,配置逻辑清晰;三是用户体验,手机App扫码绑定后,每次登录只需多看一眼手机并输入6位数字,流程顺畅,安全感的提升却是巨大的。

整个安全架构可以这样理解:Ngrok建立了一条从公网到树莓派22端口的加密隧道(TLS),这是第一层防护,确保了传输过程不被窃听。当连接请求抵达树莓派的SSH服务时,PAM模块介入,要求提供“你知道的”(SSH密码)和“你拥有的”(手机上的动态码)两种凭证,这是第二层防护。这种设计遵循了“纵深防御”原则,单一环节的失效不会导致整个系统被攻破。例如,即使Ngrok的隧道端点理论上存在被攻击的风险,或者你的密码因在其他网站泄露而被撞库,双因素认证依然能牢牢守住最后一道门。

注意:虽然Ngrok提供了传输加密,但我们的SSH密码和动态码仍在同一通道内传输。因此,务必确保你使用的Ngrok账户密码足够强壮,并且定期检查Ngrok仪表盘上的连接日志,关注是否有异常访问尝试。

3. 环境准备与Ngrok服务部署

3.1 树莓派系统基础配置

首先,确保你的树莓派系统是最新的。打开终端,执行更新操作是一个好习惯:

sudo apt update && sudo apt upgrade -y

接下来,我们需要确认SSH服务已经启用,因为整个远程访问都基于它。在树莓派OS的新版本中,SSH默认可能是关闭的。

# 启用SSH服务 sudo systemctl enable ssh # 立即启动SSH服务 sudo systemctl start ssh # 检查SSH服务状态,确认其为active (running) sudo systemctl status ssh

如果之前没有设置过静态IP,我强烈建议你在路由器后台为树莓派分配一个固定的局域网IP地址。这能避免DHCP租约到期后IP变化,导致后续服务配置失效。

3.2 获取并配置Ngrok客户端

Ngrok的安装过程非常直接。我们将在用户主目录下进行操作。

# 进入用户主目录 cd /home/pi/ # 下载Ngrok官方ARM版本压缩包 wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip # 解压压缩包 sudo unzip ngrok-stable-linux-arm.zip # (可选)删除下载的压缩包以节省空间 sudo rm ngrok-stable-linux-arm.zip

解压后,你会得到一个名为ngrok的可执行文件。此时,你可以通过./ngrok help测试一下是否能够运行。

3.3 关联Ngrok账户与认证令牌

Ngrok服务需要关联你的账户才能创建稳定的隧道。你需要前往 ngrok官网 注册一个免费账户。登录后,在侧边栏找到“Your Authtoken”(在“Auth”标签页下)。页面会显示一串长长的令牌字符串。

回到树莓派终端,使用以下命令配置令牌:

/home/pi/ngrok authtoken YOUR_AUTHTOKEN_HERE

请将YOUR_AUTHTOKEN_HERE替换为你从官网复制的真实令牌。命令成功后会显示 “Authtoken saved to configuration file”。这个令牌会被保存在~/.ngrok2/ngrok.yml配置文件中,这是Ngrok客户端的核心配置文件。

3.4 创建SSH隧道配置文件

默认的配置文件可能不包含我们需要的隧道定义。我们可以手动编辑它。

sudo nano /home/pi/.ngrok2/ngrok.yml

在文件中添加以下内容(如果文件已存在其他内容,可以追加在末尾):

version: "2" authtoken: YOUR_AUTHTOKEN_HERE # 通常上一步命令已自动写入,可核对或留空 tunnels: ssh: proto: tcp addr: 22 region: us # 可选:隧道区域,如us, eu, ap, au。选择离你近的延迟低。

这里定义了一个名为ssh的隧道,协议是TCP,将本地(树莓派)的22号端口(SSH默认端口)通过Ngrok的服务器暴露出去。region参数指定了Ngrok服务器的地区,选择离你物理位置近的通常能获得更低的延迟。

3.5 测试隧道并配置系统服务

现在,让我们进行一次手动测试,确保一切正常:

/home/pi/ngrok start ssh

如果配置正确,终端会输出类似以下的信息,其中tcp://0.tcp.ngrok.io:12345 -> localhost:22这一行是关键。它告诉你,Ngrok的公网地址是0.tcp.ngrok.io,端口是12345(这个端口是随机分配的,每次启动可能不同)。你可以暂时记下这个地址和端口。

测试成功后,我们需要让Ngrok在后台稳定运行,即配置为系统服务。这里我采用一个更通用和可控的方法,而不是依赖第三方脚本。创建服务文件:

sudo nano /etc/systemd/system/ngrok.service

将以下内容写入文件:

[Unit] Description=Ngrok Secure Tunnel for SSH After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi ExecStart=/home/pi/ngrok start --all --config /home/pi/.ngrok2/ngrok.yml Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

这个服务定义文件告诉系统:在网络就绪后,以pi用户身份,在/home/pi目录下,执行启动命令,并监控进程,如果失败则10秒后重启。

然后让系统加载这个新服务,并设置开机自启:

# 重新加载systemd配置 sudo systemctl daemon-reload # 启用ngrok服务(开机自启) sudo systemctl enable ngrok.service # 启动ngrok服务 sudo systemctl start ngrok.service # 查看服务运行状态,确认无报错 sudo systemctl status ngrok.service

现在,Ngrok隧道已经作为守护进程在后台运行了。你可以通过sudo journalctl -u ngrok.service -f来实时查看它的日志。

4. 集成Google Authenticator实现双因素认证

4.1 安装PAM认证模块

双因素认证的核心是PAM模块。首先安装它:

sudo apt install libpam-google-authenticator -y

这个包包含了google-authenticator命令行工具和PAM所需的库文件。

4.2 为当前用户生成认证密钥

运行以下命令来初始化配置:

google-authenticator

这是一个交互式配置过程,它会问你几个问题,并显示一个二维码。请务必在树莓派的本地终端(通过显示器或已有SSH会话)中进行此操作,而不是在未来的远程连接中做,否则你会把自己锁在外面。

  1. Do you want authentication tokens to be time-based (y/n): 输入y。这是默认且推荐的方式,基于时间生成动态码。
  2. 随后会显示一个大大的二维码,以及你的密钥(secret key)和紧急备用码(emergency scratch codes)。立即用手机上的Google Authenticator或Authy等App扫描这个二维码。备用码用于在手机丢失时应急,请务必安全保存(例如记在密码管理器里)。
  3. Do you want me to update your "/home/pi/.google_authenticator" file (y/n): 输入y,将配置保存到用户目录下。
  4. Do you want to disallow multiple uses of the same authentication token?: 输入y。这可以防止重放攻击。
  5. By default, tokens are good for 30 seconds... Do you want to do so? (y/n): 输入n。保持30秒的默认窗口期即可,增加容错窗口会略微降低安全性。
  6. Do you want to enable rate-limiting?: 输入y。这会在30秒内最多允许3次登录尝试,有效抵御暴力破解。

4.3 配置SSH服务以启用挑战应答认证

接下来,我们需要修改SSH服务的配置,使其在认证时询问动态验证码。

sudo nano /etc/ssh/sshd_config

找到并修改以下两行配置(使用Ctrl+W搜索):

# 将 ChallengeResponseAuthentication 的值改为 yes ChallengeResponseAuthentication yes # 确保 UsePAM 的值为 yes(通常默认就是) UsePAM yes

重要:我们在这里禁用密码认证(PasswordAuthentication)。双因素认证需要密码作为第一个因素。我们的流程将是:先通过PAM验证密码+动态码,然后再进行SSH本身的密码或密钥认证。这是一种串联验证方式。

4.4 配置PAM规则

现在,我们需要告诉PAM,在进行SSH认证时,使用Google Authenticator模块。

sudo nano /etc/pam.d/sshd

在文件的开头部分(通常在@include common-auth这一行之前)添加下面这行:

auth required pam_google_authenticator.so

这行配置的意思是:认证(auth)过程中,必须(required)通过Google Authenticator模块的检查required意味着如果这个模块认证失败,整个认证流程就会失败,但PAM会继续运行完其他模块才返回结果。将其放在开头是常见的做法。

4.5 重启服务并测试本地认证

在启用远程访问前,我们先在本地开一个新的终端窗口进行测试,以防配置错误导致所有连接中断。

# 重启SSH服务以加载新配置 sudo systemctl restart ssh

不要关闭你当前的SSH会话!新开一个终端,尝试从本地连接树莓派自己:

ssh pi@localhost

这时,登录流程应该发生了变化:

  1. 系统会首先提示:Password:(这是PAM的密码提示,对应你的树莓派用户pi的登录密码)。
  2. 输入正确密码后,会紧接着提示:Verification code:(这是Google Authenticator的6位动态验证码)。
  3. 输入你手机App上当前显示的6位数字。
  4. 如果两者都正确,才会进入熟悉的SSH密码或密钥认证环节(如果你设置了SSH密钥,可能还需要输入密钥密码)。

如果这个本地测试成功,说明双因素认证已经正确配置。如果失败,请检查之前的步骤,并务必保留当前的救命会话窗口。

5. 连接测试与远程访问实操

5.1 获取稳定的公网访问地址

由于我们将Ngrok配置成了系统服务,并且使用了--all参数启动所有在配置文件中定义的隧道,我们需要找到它为SSH隧道分配的公网地址。最可靠的方法是查看Ngrok的服务日志:

sudo journalctl -u ngrok.service --no-pager | grep “url\|tcp://”

在输出中,寻找包含tcp://的行,例如tcp://0.tcp.ap.ngrok.io:12345 -> localhost:22。这里的0.tcp.ap.ngrok.io是主机名,12345是端口号。这就是你从外网访问树莓派SSH的地址。

提示:免费版Ngrok的TCP隧道地址和端口在每次服务重启(或约2小时后)都会变化。如果你需要固定地址,可以考虑Ngrok的付费计划,或者使用脚本定期从日志中解析地址并发送到你的邮箱。

5.2 从外网进行SSH连接

现在,从任何可以访问互联网的电脑(比如你的办公电脑或手机热点下的笔记本),使用SSH客户端进行连接。命令格式如下:

ssh pi@0.tcp.ap.ngrok.io -p 12345

请将0.tcp.ap.ngrok.io12345替换为你实际获取到的主机名和端口。

  1. 首次连接时,可能会看到SSH主机密钥改变的警告(因为你是通过Ngrok的服务器中转的)。输入yes继续。
  2. 接着,你会看到熟悉的双因素认证流程:
    • 首先提示:Password:(输入树莓派用户pi的密码)。
    • 然后提示:Verification code:(输入手机Google Authenticator App上当前的6位动态码)。
  3. 认证通过后,即可成功登录树莓派。

5.3 连接流程与安全机制解析

让我们复盘一下整个连接过程中,安全机制是如何层层生效的:

  1. 隧道建立:你的SSH客户端连接到ngrok.io的服务器。连接本身是TLS加密的。
  2. 请求转发:Ngrok服务器根据你的Authtoken,识别出对应的隧道,并将流量转发到你树莓派上运行的Ngrok客户端。
  3. 本地送达:树莓派上的Ngrok客户端将流量解密后,转发给本地的127.0.0.1:22,即SSH服务。
  4. PAM双因素认证:SSH服务接收到连接请求,调用PAM。PAM首先通过pam_google_authenticator.so模块要求提供密码和动态码。只有两者都验证通过,PAM才会返回成功。
  5. SSH协议认证:PAM认证通过后,SSH协议自身的认证流程才开始(例如密码认证或公钥认证)。由于PAM已经验证了密码,SSH的密码认证通常会顺利通过。
  6. 会话建立:所有认证通过后,安全的SSH会话才正式建立。

这个流程确保了即使在最坏的情况下(Ngrok的端点被攻破,流量被拦截),攻击者得到的也是加密的TLS流。而即使TLS被破解(概率极低),他们还需要同时获得你的树莓派密码和手机的实时动态码才能入侵,难度极大。

6. 故障排查、优化与进阶配置

6.1 常见问题与解决方案

在实际部署中,你可能会遇到一些问题。下面是一个快速排查指南:

问题现象可能原因排查步骤与解决方案
SSH连接超时,无法建立连接1. Ngrok服务未运行。
2. 隧道地址/端口错误。
3. 本地SSH服务未运行或防火墙阻止。
1.sudo systemctl status ngrok.service检查状态,重启服务。
2.sudo journalctl -u ngrok.service重新获取最新地址。
3.sudo systemctl status ssh检查SSH服务,sudo ufw status检查防火墙(默认树莓派OS无激活防火墙)。
提示“Permission denied (publickey,password)”1. PAM双因素认证失败。
2. SSH密码认证被禁用。
1. 确认输入的密码和当前手机动态码正确。检查手机时间是否同步。
2. 确认/etc/ssh/sshd_configPasswordAuthenticationyes
只提示输入密码,不提示验证码PAM配置未生效或顺序不对。1. 检查/etc/pam.d/sshdpam_google_authenticator.so行是否添加。
2. 检查/etc/ssh/sshd_configChallengeResponseAuthentication是否为yes
3. 执行sudo systemctl restart ssh重启服务。
验证码正确但仍被拒绝1. 手机与树莓派时间不同步。
2. 之前配置时未允许令牌复用。
1. 在树莓派上运行sudo date检查时间。使用sudo apt install ntpdate同步时间。
2. 重新运行google-authenticator,在允许令牌复用的选项中选择y
Ngrok隧道频繁断开免费版连接限制或网络不稳定。免费版隧道约2小时重置。考虑使用autossh等工具监控并自动重启Ngrok服务,或升级付费计划。

6.2 服务稳定性优化

为了让服务更可靠,我们可以做两件事:

  1. 配置看门狗(Watchdog):修改Ngrok的systemd服务文件,利用Restart策略已经具备基本的故障恢复能力。我们可以进一步增加一个定时任务,定期检查隧道是否存活。
    # 创建一个检查脚本 sudo nano /home/pi/check_ngrok.sh
    脚本内容:
    #!/bin/bash # 检查Ngrok进程是否存在,且是否在监听端口 if ! pgrep -x "ngrok" > /dev/null; then echo "$(date): Ngrok process not found, restarting..." >> /home/pi/ngrok_watchdog.log sudo systemctl restart ngrok.service fi
    # 给脚本执行权限 sudo chmod +x /home/pi/check_ngrok.sh # 添加到crontab,每5分钟检查一次 (crontab -l 2>/dev/null; echo "*/5 * * * * /home/pi/check_ngrok.sh") | crontab -
  2. 获取固定域名(付费功能):对于生产环境,可以考虑Ngrok的付费套餐,它允许你为隧道保留固定的子域名(如yourname.ngrok.io)和端口,彻底解决地址变动问题。

6.3 安全强化建议

基础方案已经比较安全,但追求极致可以更进一步:

  • 禁用SSH密码登录,仅使用密钥:在双因素认证保护下,密码认证环节可以关闭,完全依赖更安全的SSH密钥对。修改/etc/ssh/sshd_config
    PasswordAuthentication no PubkeyAuthentication yes
    然后,你需要将你的公钥(id_rsa.pub)添加到树莓派的~/.ssh/authorized_keys文件中。这样,登录时先过PAM的密码+动态码关,再过SSH的密钥关,安全性最高。
  • 限制SSH监听接口:既然我们只通过Ngrok的本地转发(127.0.0.1)来访问,可以配置SSH只监听本地回环地址,阻止来自局域网的直接连接尝试。在/etc/ssh/sshd_config中设置:
    ListenAddress 127.0.0.1
  • 定期轮换认证密钥:每隔一段时间(如半年),可以重新运行google-authenticator命令生成新的密钥,并在手机App中重新绑定。同时,妥善保管好每次生成的紧急备用码。

6.4 扩展应用:暴露其他服务

Ngrok的配置非常灵活,你不仅可以暴露SSH(TCP 22),还可以暴露树莓派上运行的其他Web服务,比如一个本地的Web管理界面(端口80)或一个自定义的API服务(端口3000)。只需在~/.ngrok2/ngrok.yml文件中添加新的隧道定义即可:

tunnels: ssh: proto: tcp addr: 22 web-ui: proto: http addr: 80 host_header: "localhost" # 可选,用于设置Host头 my-app: proto: http addr: 3000

重启Ngrok服务后,HTTP隧道会提供一个固定的https://xxxxxx.ngrok.io地址,你可以直接在浏览器中访问树莓派上的Web服务。同样地,这些HTTP流量也会经过TLS加密和你的Ngrok账户认证。

经过以上步骤,你已经成功搭建了一个兼具便捷性与安全性的树莓派远程访问网关。这个方案的核心价值在于,它用较小的成本和复杂度,实现了接近企业级远程访问方案的安全水平。在实际使用中,最深的体会是“安全感”的提升——我知道我的树莓派暴露在公网上,但我也知道它被两把可靠的锁保护着。唯一需要养成的新习惯就是在每次登录前,自然地看一眼手机上的验证码。对于需要管理多个树莓派或物联网设备的朋友,这个模式完全可以复制,只需为每个设备创建独立的Ngrok配置文件和Google Authenticator密钥即可。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询