亚马逊AWS官方博客

使用 AWS IoT secure tunneling 调试远程设备,增强安全并减少运营成本

应用场景

很多智能家居设备都部署在家用路由器保护的内部网络中,无法从外网直接访问。这给设备的远程维护带来很大不便。比如当设备出现故障时,无法远程登录进行故障排查;当需要更新设备固件时,也需要在本地操作。

当设备部署在远程的限制性防火墙后面时,您需要一种方式来访问这些设备以进行故障排除、配置更新和其他运维任务。使用安全隧道可以通过 AWS IoT 管理的安全连接在远程设备上建立双向通信。安全隧道不需要更新现有的入站防火墙规则,所以可以保持远程站点防火墙规则提供的同一安全级别。

例如,位于几百公里外工厂的传感器设备在测量工厂温度时遇到问题。您可以使用安全隧道快速打开并启动到该传感器设备的会话。在确定问题后(例如错误的配置文件),您可以通过同一会话修改文件并重新启动传感器设备。与更传统的故障排除(例如派遣技术人员到工厂调查传感器设备)相比,安全隧道减少了事件响应和恢复时间以及运营成本。

工作原理

AWS IoT Core 是 Amazon 提供的一站式物联网后端平台服务。它极大简化了将大规模 IoT 设备安全连接云端、管理和操作设备、实时处理和分析设备数据流等功能的开发,可快速构建可扩展、安全的 IoT 应用程序,并有效降低运营成本。

安全隧道基于 AWS IoT Core 的设备管理能力和基于浏览器的 SSH 的功能来实现远程登录设备。

工作流程参考下图:

  1. 创建一个 Tunnel,发送 token 到设备
  2. 运行在设备上的守护程序收到 token
  3. 启动 localproxy(以 Destination Mode)
  4. 使用集成在 AWS Management Console 中 Secure Shell(SSH),连接设备并登录
  5. 登录成功,正常执行命令

设定步骤

创建一个 AWS IoT Core Device

创建设备和关联证书

复制 AWS IoT Core 证书到设备侧

在本文中,我们使用一台 EC2 模拟远程设备

#设备采用 ubuntu 系统
ubuntu@ip-172-31-25-16:~$ uname -a
Linux ip-172-31-25-16 6.2.0-1012-aws #12~22.04.1-Ubuntu SMP Thu Sep 7 14:01:24 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

下载 SDK 和相关证书,检查设备与 IoT Core 的连接正常,可以做消息交互

下载路径:AWS IoT → Connect → Connect one device

选择设备和支持的语言, 这里我选择是 Java(采用 Java8 的版本)

设置完成后下载软件开发包,并上传到设备侧

验证设备与 AWS IoT Core 的连接

启动一个 EC2 来模拟设备,因为整个实验过程需要用到 unzip/java/maven/git 等工具,我们可以提前设置好

sudo apt install unzip openjdk-8-jdk maven -y
java -version # 1.8.x
mvn --version # 3.6.x

解压之前上传的 zip

#解压我们上传过来的连接包,里面包含设备证书
unzip connect_device_package.zip
#执行权限
chmod +x start.sh
#检查信息,脚本会下载 iot sdk 并尝试往测试 Topic sdk/test/java 发送测试消息
./start.sh

看到以下信息,证明设备已经成功连接 AWS Iot Core

在 AWS Manage Console,我们使用 AWS IoT → Test → MQTT test client 订阅 sdk/test/java 来查看消息是否正常从设备侧发过来了

如果这里能够收到消息,说明连通性没有问题。

在设备侧安装 localproxy

安装者编译 localproxy,具体请参考:https://github.com/aws-samples/aws-iot-securetunneling-localproxy/tree/main

编译相对比较复杂,需要一点耐心。

如果是 ubuntu 系列的,可以尝试使用我编译好的

在设备侧启动 Tunnel 消息监听程序

这个程序会作为守护程序运行,监听一个保留的 $aws/things/*/tunnels/notify,当 AWS IoT Core 创建 tunnel 或者刷新 token 的时候,会通过该 topic 将相关 token 发送到设备。

git clone https://github.com/nimysan/aws-iot-tunnelclient.git
cd aws-iot-tunnelclient
mvn clean package
./start.sh #编辑这个文件,使用您的设备相关的证书

启动成功,可以看到如下输出

到这里,设备侧的准备就完成了。接下来我们在 AWS IoT Core Management Console 开启一个 Tunnel 通道,并远程连接进来。

如果测试 topic 正常,但是无法订阅 tunnel 相关 topic,有可能是策略配置导致,具体解决方案请参考本文末尾的证书配置部分。

创建 Secure Tunnel 并使用 Secure Shell(SSH)连接设备

先在 AWS IoT Core 创建一条用于远程控制的 Tunnel

选择你 Tunnel 关联的设备

确认创建,创建后会将 Token 通过一个保留的 topic 发送到设备侧

我们可以 copy 这两个 token 备用

这个 Token 都是一次性的,如果我们连接失败或者需要再次连接进入,需要重新生成 Token,可以通过以下方式来生成

同步观察设备 Tunnel 守护进程的输出,可以看到如下信息

守护进程会在收到这个消息后在设备上以 Destination Mode 启动 localproxy。

如果你需要调试,也可以手动复制 token 到以下命令并启动 localproxy

localproxy \
-t your_token_here
-d localhost:22 \
-v 5

注意:设备是作为 localproxy 的 Destination Mode,需要使用 Access token for destination。

设备侧 localproxy 成功工作后,Secure Shell 所在页面状态状态如下

接下来,我们可以尝试去连接这个设备。

这里用你设备所在服务器的 username/password 或者 private key 文件即可

显示如下状态即连接成功了

使用常见工具来连接设备

在获得设备拥有着的授权后,维护人员已经可以通过 Secure Tunnel 远程登录设备开始维护工作。

在一些场景下,运营人员可能需要对大量的设备进行管理维护,他们需要使用一些熟练的 SSH 工具如 PuTTY、iterm 等来更高效率地完成管理工作。

借助 localproxy 的 Source  Mode 模式,可以实现通过终端工具远程访问 IoT 设备。当 localproxy 以 Source Mode 启动时,它将首先连接到服务,然后开始在指定的端口和绑定地址上监听新连接。

  1. 创建一个 Tunnel,发送 token 到设备
  2. 运行在设备上的守护程序收到 token
  3. 启动 localproxy(以 Destination Mode)
  4. 使用第一步的 Source Token,在你工作机器上启动 localproxy(以Source Mode)
  5. 使用工作机器的 terminal,连接到工作机器启动的 localproxy 的进程和端口
  6. 登录成功,正常执行命令

工作机器设置

我们同样需要在工作机器上安装 localproxy,具备步骤可参考前面设备处的实现。

在设备 tunnel 守护程序运行后,我们启动一个针对该设备的 tunnel(这一步可以通过管理人员来完成或者通过管理程序调用 AWS API 来完成)。

将 Access token for source 复制下来并分发给维护人员(需要登录远程设备的人员)

运维人员在工作机器上以 Source Mode 启动 localproxy,使用从管理员获得的 token

#Source mode 启动 localproxy
localproxy \
-r ap-southeast-1 \
-s 3389 \
-t your_token_here

正常启动后,可以看到类似输出:

[2023-12-08 09:57:30.660591] (0x00007f8264ebd840) [info] Starting proxy in source mode
[2023-12-08 09:57:30.662942] (0x00007f8264ebd840) [info] Attempting to establish web socket connection with endpoint wss://data.tunneling.iot.ap-southeast-1.amazonaws.com:443
[2023-12-08 09:57:30.714352] (0x00007f8264ebd840) [info] Web socket session ID: 0a1303fffee640a4-000006ca-00019cab-ab1c94f1a9c33db1-680ee7cc
[2023-12-08 09:57:30.714379] (0x00007f8264ebd840) [info] Successfully established websocket connection with proxy server: wss://data.tunneling.iot.ap-southeast-1.amazonaws.com:443
[2023-12-08 09:57:30.714476] (0x00007f8264ebd840) [info] Updated port mapping for v1 format:
[2023-12-08 09:57:30.714490] (0x00007f8264ebd840) [info] SSH = 3389
[2023-12-08 09:57:30.714510] (0x00007f8264ebd840) [info] calling setup from loop
[2023-12-08 09:57:30.714641] (0x00007f8264ebd840) [info] Listening for new connection on port 3389

同时,我们可以查看 tunnel 的状态,Source/Destination 应该都处于 Connected 的状态了

登录设备

两端都连接成功后,在工作机器启动终端,登录设备

# -i 是使用 key 登录,这个 key 登录最终设备的 key,host 为 localhost,port 是我们 localproxy 
# Source Mode 工作的端口

ssh -i /tmp/sig.pem ubuntu@localhost -p 3389

# 连接成功后,localproxy 会有类似如下输出
[2023-12-08 10:07:27.072707] (0x00007f525e0ce840) [info] creating tcp connection id 2
[2023-12-08 10:07:27.072768] (0x00007f525e0ce840) [info] Accepted tcp connection on port 3389 from 127.0.0.1:52358
[2023-12-08 10:07:27.072799] (0x00007f525e0ce840) [info]  sending connection start for service id: SSH connection id: 2

总结

通过这种方式,我们可以实现:

  1. 操作员和管理员分离,管理员负责创建 Tunnel,操作员拿到 tunnel token 远程登录;
  2. 操作员可以使用最熟悉的工具如 putty,iterm 来做远程管理;
  3. 结合 AWS API,我们可以将 tunnel 的操作交给设备拥有者,结合管理 app 等实现更多管理功能,更加合规安全地远程登录,帮助用户快速解决问题。

使用证书连接 AWS IoT Core 并订阅 Tunnel 相关 Topic

如果你在订阅 $aws/things/{thing_name}/tunnels/notify 看到如下错误

去检查该设备证书关联的 Policy 是否包含相关 Topic 的策略(请关注 tunnels/notify 策略)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:ap-southeast-1:{account_id}:topic/sdk/test/java",
        "arn:aws:iot:ap-southeast-1:{account_id}:topic/sdk/test/python",
        "arn:aws:iot:ap-southeast-1:{account_id}:topic/sdk/test/js",
        "arn:aws:iot:ap-southeast-1:{account_id}:topic/$aws/things/*/tunnels/notify"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": [
        "arn:aws:iot:ap-southeast-1:{account_id}:topicfilter/sdk/test/java",
        "arn:aws:iot:ap-southeast-1:{account_id}:topicfilter/sdk/test/python",
        "arn:aws:iot:ap-southeast-1:{account_id}:topicfilter/sdk/test/js",
        "arn:aws:iot:ap-southeast-1:{account_id}:topicfilter/$aws/things/*/tunnels/notify"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": [
        "arn:aws:iot:ap-southeast-1:{account_id}:client/sdk-java",
        "arn:aws:iot:ap-southeast-1:{account_id}:client/basicPubSub",
        "arn:aws:iot:ap-southeast-1:{account_id}:client/sdk-nodejs-*"
      ]
    }
  ]
}

注意策略中不同的 Prefix,Subscribe 使用的是 topicfilter,而 Publish/Receive 使用的是 topic。

按照上文提到的步骤,创建 tunnel 或者刷新 token,看到如下输出就说明配置成功

每台设备只会收到来自于本设备 Tunnel 的 Token。

参考资料

  1. AWS IoT secure tunneling:https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html
  2. AWS IoT Core – Reversed Topic:https://docs.aws.amazon.com/iot/latest/developerguide/reserved-topics.html
  3. 编译安装 localproxy 以及 Source Mode/Destination Mode 相关说明:https://github.com/aws-samples/aws-iot-securetunneling-localproxy
  4. AWS IoT Analytics 为您的 IoT 设备提供完全托管的可操作化分析:https://aws.amazon.com/cn/iot-analytics/

本篇作者

叶小微

亚马逊云科技解决方案架构师,曾就职于 IBM,后从事电商相关和企业数字化转型工作,拥有多年架构设计、研发、项目管理经验。在工作流、微服务、系统集成等方向有丰富的解决实际问题的经验。