AWS IoT Secure Tunneling Explained: A Comprehensive Guide

AWS IoT Secure Tunneling Explained: A Comprehensive Guide

Quick takeaway: AWS IoT Secure Tunneling lets you SSH into IoT devices behind firewalls without opening inbound ports. You create a tunnel via CLI or console, the device connects outbound over port 443, and you get a bidirectional session. This guide covers the CLI workflow, quick vs manual setup, local proxy configuration, and common troubleshooting scenarios.

When a Raspberry Pi in a factory starts sending bad temperature readings at 2 AM, you do not want to drive 200 miles to fix a config file. But the firewall blocks all inbound SSH — and your ops team cannot reach the device.

AWS IoT Secure Tunneling solves this. The device initiates an outbound WebSocket connection to AWS (port 443, already allowed by most firewalls). You open a tunnel from your side, and AWS brokers a secure, bidirectional session between you and the device. No inbound ports, no VPN, no jump host.

How AWS IoT Secure Tunneling Works

Three components:

  • Tunnel: created via the AWS IoT console or aws iotsecuretunneling open-tunnel CLI command. It has a source side (you) and a destination side (the remote device).
  • Local proxy: runs on both sides. On the destination device, it connects the tunnel to the local application (e.g., SSH on port 22). On your source machine, it maps a local port to the tunnel.
  • Access tokens: two tokens are generated when the tunnel opens — one for the source, one for the destination. Each side uses its token to authenticate with the AWS secure tunneling service.

The flow: the destination device’s local proxy connects to AWS over WebSocket (outbound, port 443). You start the source proxy, which also connects outbound. AWS matches them through the tunnel ID and tokens, then relays traffic in both directions. From your perspective, it feels like ssh -p 5555 localhost — but the actual device is a thousand miles away.

Quick Setup vs Manual Setup

CriteriaQuick SetupManual Setup
Tunnel creationDefault config, SSH onlyCustom config, any TCP service
Access tokensDestination token auto-delivered via MQTTManually download and manage both tokens
Local proxyBrowser-based, no installationRequires local proxy binary on both sides
Best forQuick one-off debuggingAutomated ops, non-SSH services, CI/CD

Prerequisites

  • AWS CLI v2 installed and configured with iotsecuretunneling permissions
  • Outbound port 443 allowed on the destination device’s firewall (non-negotiable — the tunnel uses WebSocket over TLS)
  • For manual setup: the local proxy binary from aws-samples/aws-iot-securetunneling-localproxy
  • For quick setup with auto token delivery: the device must be registered as an AWS IoT thing and connected via MQTT
  • IAM permissions: iotsecuretunneling:OpenTunnel, iotsecuretunneling:CloseTunnel, iotsecuretunneling:DescribeTunnel

Creating a Tunnel with the CLI

The aws iotsecuretunneling command group gives you full control. Here is the basic workflow.

Step 1: Open a tunnel

aws iotsecuretunneling open-tunnel \
    --description "Debug factory sensor" \
    --destination-config thingName=RemoteSensorA,services=SSH \
    --timeout-config maxLifetimeTimeoutMinutes=60Code language: JavaScript (javascript)

This creates a tunnel and returns two access tokens — one for the source (you) and one for the destination (the device). The destination token is automatically delivered to RemoteSensorA over the MQTT topic $aws/things/RemoteSensorA/tunnels/notify if the device is connected.

Response:

{
    "tunnelId": "01234567-89ab-cdef-0123-456789abcdef",
    "tunnelArn": "arn:aws:iot:us-east-1:123456789012:tunnel/01234567-...",
    "sourceAccessToken": "AQD...source-token...",
    "destinationAccessToken": "AQD...dest-token..."
}Code language: JSON / JSON with Comments (json)

Step 2: Start the local proxy on the destination

On the remote device, the IoT agent (e.g., AWS IoT Device Client) receives the token and starts the local proxy in destination mode:

./localproxy -r us-east-1 -d 22 -t DESTINATION_ACCESS_TOKEN

The -d 22 tells the proxy to forward tunnel traffic to localhost port 22 (SSH). You can use any port for other services — 3389 for RDP, 8080 for a web dashboard, etc.

Step 3: Start the local proxy on your source machine

./localproxy -r us-east-1 -s 5555 -t SOURCE_ACCESS_TOKEN

The -s 5555 maps the tunnel to your local port 5555. Once both proxies are connected:

ssh -p 5555 pi@localhostCode language: CSS (css)

You are now logged into the remote device through the tunnel. No inbound firewall rules were changed.

Managing Tunnels with the CLI

List active tunnels

aws iotsecuretunneling list-tunnelsCode language: PHP (php)

Describe a specific tunnel

aws iotsecuretunneling describe-tunnel \
    --tunnel-id 01234567-89ab-cdef-0123-456789abcdef

This shows the tunnel status (OPEN or CLOSED), creation time, and timeout configuration.

Rotate access tokens

If a token is leaked or a session is compromised, rotate it without closing the tunnel:

aws iotsecuretunneling rotate-tunnel-access-token \
    --tunnel-id 01234567-89ab-cdef-0123-456789abcdef \
    --client-mode SOURCE

Use --client-mode DESTINATION to rotate the device token. The old token stops working immediately.

Close a tunnel

aws iotsecuretunneling close-tunnel \
    --tunnel-id 01234567-89ab-cdef-0123-456789abcdef

Tunnel Lifecycle and Timeouts

A tunnel goes through these states:

  • OPEN: created and ready for connections
  • CLOSED: manually closed or reached maxLifetimeTimeoutMinutes

The default lifetime is 720 minutes (12 hours). You can set it as low as 1 minute or as high as 720 minutes with --timeout-config maxLifetimeTimeoutMinutes=N. There is no keepalive — if both proxies disconnect, the tunnel stays OPEN until the timeout expires. Always close tunnels you no longer need.

Quick Setup via AWS Console (No CLI)

For one-off debugging, the console quick setup skips the local proxy entirely.

  1. Go to AWS IoT → Tunnels hub
  2. Click Create tunnel → Quick setup
  3. Select the destination thing (e.g., RemoteSensorA)
  4. Choose SSH as the service
  5. Click Create — the token is sent to the device via MQTT
  6. Use the in-browser CLI that appears to SSH directly from the console

The browser-based SSH is convenient but limited. For scripting, CI/CD pipelines, or non-SSH services, use the manual setup with the local proxy.

Security Model

  • No inbound ports: the device never listens. Both sides connect outbound to AWS over TLS 1.2+.
  • Token-based auth: each side authenticates with a unique, single-use access token. Tokens can be rotated without closing the tunnel.
  • IAM policies: control who can open, close, describe, and list tunnels. Use resource-level permissions tied to specific thing names.
  • CloudTrail audit: every OpenTunnel, CloseTunnel, and RotateAccessToken call is logged.
  • No VPC dependency: the service is regional and does not require VPC endpoints, though interface VPC endpoints are supported if you need private connectivity.

Troubleshooting

“Connection refused” from the local proxy

The destination proxy cannot reach the local application. Check that the service (SSH, RDP, etc.) is actually running on the port you specified with -d. Verify with netstat -tlnp on the device.

Tunnel opens but SSH hangs

Port 443 outbound is the most common culprit. Both sides need outbound access on port 443 to data.tunneling.iot.<region>.amazonaws.com. Test with:

curl -v https://data.tunneling.iot.us-east-1.amazonaws.comCode language: JavaScript (javascript)

A 200 or 404 response means connectivity works. A timeout means the firewall is blocking it.

Destination token not delivered to device

The device must be connected to AWS IoT Core via MQTT and subscribed to the reserved topic: $aws/things/<ThingName>/tunnels/notify. If the device is offline when the tunnel is created, the token is not queued — you will need to create a new tunnel or deliver the token manually.

Tunnel timeout too short for long sessions

Increase maxLifetimeTimeoutMinutes up to 720 (12 hours). For sessions longer than 12 hours, create a new tunnel when the old one expires — there is no way to extend a tunnel’s lifetime.

FAQ

What is AWS IoT Secure Tunneling?

AWS IoT Secure Tunneling lets you establish a secure, bidirectional connection to a remote IoT device behind a firewall without opening inbound ports. The device connects outbound to AWS over port 443, and you connect from your side. AWS brokers the session. It is commonly used for SSH, remote debugging, and configuration updates on devices in the field.

What is the difference between quick setup and manual setup?

Quick setup creates a tunnel with default SSH configuration and handles tokens automatically via MQTT — no local proxy installation needed. Manual setup requires the local proxy binary on both sides and manual token management, but supports any TCP service (not just SSH) and works without MQTT-connected things.

How do I open a tunnel using the AWS CLI?

Use aws iotsecuretunneling open-tunnel –destination-config thingName=MyThing,services=SSH –timeout-config maxLifetimeTimeoutMinutes=60. This returns source and destination access tokens. Start the local proxy on both sides with the appropriate token, then connect through the mapped local port.

How much does AWS IoT Secure Tunneling cost?

There is no additional charge for using secure tunneling. You pay only for the messages delivered over MQTT to send access tokens (standard AWS IoT Core messaging rates apply). If you send one tunnel-open notification per device per day, the cost is negligible for most fleets.

Can I rotate access tokens without closing the tunnel?

Yes. Use aws iotsecuretunneling rotate-tunnel-access-token –tunnel-id –client-mode SOURCE (or DESTINATION). The old token stops working immediately. This is useful when a token is accidentally exposed in logs or shared with the wrong person.

Bottom Line

AWS IoT Secure Tunneling replaces the VPN-and-jump-host dance with a single CLI command and a local proxy. It costs nothing extra, works through firewalls without changes, and gives you SSH — or any TCP service — to devices in the field.

The core workflow: open-tunnel → start local proxy on both sides → connect through localhost. Rotate tokens when needed, close tunnels when done. For more IoT operations context, see the AWS IoT Secure Tunneling developer guide and the official demo on GitHub.

Nathan Cole Avatar

Leave a Reply

Your email address will not be published. Required fields are marked *