Проблема IoT: устройства за NAT без публичного доступа

Raspberry Pi стоит на полке дома. ESP32-датчики разбросаны по складу. Контроллер умного дома сидит за CGNAT провайдера. Ни у одного из них нет статического IP и прямого доступа из интернета. Когда вам нужно зайти на Pi по SSH из офиса, отправить данные с датчика в облако или показать веб-интерфейс ESP32 коллеге – сеть не пускает.

Это фундаментальная проблема IoT: устройства должны быть доступны извне, но сетевая инфраструктура активно этому мешает. Туннелирование решает задачу – устройство само устанавливает исходящее соединение с публичным сервером и получает адрес, доступный откуда угодно.

IoT-устройства за NAT (нет публичного IP)
┌──────────────────────────────────────────────┐
│  Домашняя / офисная сеть                     │
│                                              │
│  ┌─────────────┐  ┌─────────────┐            │
│  │ Raspberry Pi│  │   ESP32     │            │
│  │ SSH :22     │  │ Web :80     │            │
│  │ Grafana:3000│  │ MQTT :1883  │            │
│  └──────┬──────┘  └──────┬──────┘            │
│         │                │                   │
│         └───────┬────────┘                   │
│                 │                            │
│         ┌───────▼────────┐                   │
│         │  NAT / Роутер  │                   │
│         │  (нет проброса)│                   │
│         └───────┬────────┘                   │
└─────────────────┼────────────────────────────┘
                  │ исходящее соединение (TLS)
                  ▼
        ┌──────────────────┐
        │  fxTunnel Server │
        │  tunnel.fxtun.dev│
        └────────┬─────────┘
                 │
                 ▼
        Публичные адреса:
        ssh → tunnel.fxtun.dev:41234
        web → https://rpi.fxtun.dev
        mqtt → tunnel.fxtun.dev:51883

Традиционные решения и их проблемы

Без туннеля остаются три классических варианта: проброс портов на роутере, VPN или аренда статического IP. Все три сложны в настройке и создают дополнительные проблемы. Подробнее о компромиссах – в статье «Как открыть localhost из интернета».

Проброс портов на роутере требует доступа к настройкам роутера, публичного IP (не CGNAT) и ручной конфигурации для каждого устройства. Порт открыт всему интернету без шифрования — это прямая угроза безопасности для IoT-устройств, которые часто имеют слабую защиту.

VPN (WireGuard, OpenVPN) создаёт зашифрованный канал, но требует сервера с публичным IP, настройки на каждом устройстве и поддержки. Для одного Raspberry Pi — это избыточное решение. Для ESP32 — часто невозможное из-за ограничений памяти и процессора.

Статический IP стоит денег, привязывает к провайдеру и всё равно требует проброса портов и настройки файрвола. Многие провайдеры не предоставляют статический IP для домашних тарифов.

Туннель — это одна команда на устройстве. Никаких настроек роутера, никакого VPN-сервера, никакого статического IP. Устройство устанавливает исходящее TLS-соединение, а fxTunnel выдаёт публичный адрес.

Как туннели решают проблему IoT-связности

Клиент на IoT-устройстве открывает исходящее TLS-соединение с сервером fxTunnel. Сервер выделяет публичный адрес (URL для HTTP или хост:порт для TCP/UDP) и маршрутизирует входящий трафик через это соединение на локальный порт устройства. NAT и файрвол не мешают, потому что соединение идёт изнутри сети.

Что делает fxTunnel особенно полезным для IoT – поддержка TCP и UDP. Большинство IoT-протоколов (MQTT, CoAP, Modbus TCP) работают поверх TCP, а некоторые (CoAP, mDNS) – поверх UDP. fxTunnel покрывает оба случая, в отличие от ngrok (только TCP/HTTP) или Cloudflare Tunnel (только HTTP на бесплатном тарифе).

Сценарий 1: Удалённый доступ к Raspberry Pi

Нужно зайти по SSH на Raspberry Pi, который стоит дома, а вы – в офисе? Это, пожалуй, самый распространённый IoT-сценарий для туннеля.

SSH через TCP-туннель

# На Raspberry Pi: пробрасываем SSH-порт
fxtunnel tcp 22
# → tunnel.fxtun.dev:41234
# С любого компьютера: подключаемся к Raspberry Pi
ssh -p 41234 pi@tunnel.fxtun.dev

Готово. Вы получили SSH-доступ к Raspberry Pi без статического IP, без проброса портов и без VPN.

Веб-интерфейс через HTTP-туннель

Если на Raspberry Pi работает веб-приложение (Home Assistant, OctoPrint, Pi-hole), откройте HTTP-туннель:

# Веб-интерфейс Home Assistant на порте 8123
fxtunnel http 8123
# → https://rpi-home.fxtun.dev

Публичный HTTPS-URL можно открыть в браузере с любого устройства — телефона, рабочего компьютера, планшета.

Сценарий 2: MQTT-брокер через TCP-туннель

MQTT — основной протокол для IoT-сенсоров и актуаторов. Брокер (Mosquitto, EMQX) обычно работает на Raspberry Pi или сервере в локальной сети. Чтобы IoT-устройства из других сетей могли публиковать и получать сообщения, нужен публичный доступ к брокеру.

# На машине с MQTT-брокером
# Mosquitto слушает порт 1883 (TCP)
fxtunnel tcp 1883
# → tunnel.fxtun.dev:51883
┌─────────────┐     ┌─────────────────────┐     ┌─────────────┐
│ ESP32 #1    │     │   fxTunnel Server   │     │ Raspberry Pi│
│ (датчик     │────▶│ tunnel.fxtun.dev    │◀────│ Mosquitto   │
│  температуры)│     │ :51883              │     │ :1883       │
└─────────────┘     └─────────────────────┘     └─────────────┘
                            ▲
┌─────────────┐             │
│ ESP32 #2    │─────────────┘
│ (датчик     │
│  влажности) │
└─────────────┘

Теперь ESP32 из любой сети может подключиться к брокеру:

// Arduino / ESP32 — подключение к MQTT через туннель
#include <WiFi.h>
#include <PubSubClient.h>

const char* mqtt_server = "tunnel.fxtun.dev";
const int mqtt_port = 51883;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  WiFi.begin("SSID", "password");
  while (WiFi.status() != WL_CONNECTED) delay(500);

  client.setServer(mqtt_server, mqtt_port);
  while (!client.connected()) {
    client.connect("esp32-sensor-01");
    delay(1000);
  }
}

void loop() {
  float temp = analogRead(34) * 0.1; // пример чтения датчика
  char payload[16];
  snprintf(payload, sizeof(payload), "%.1f", temp);
  client.publish("sensors/temperature", payload);
  client.loop();
  delay(5000);
}

Сценарий 3: Веб-сервер ESP32

ESP32 часто используется с встроенным веб-сервером для конфигурации и мониторинга. Проблема — веб-интерфейс доступен только в локальной сети. Через туннель вы можете открыть его для удалённого доступа.

Поскольку ESP32 не может запустить fxTunnel напрямую (ограничения ресурсов), используйте промежуточный хост — Raspberry Pi или любой компьютер в той же сети:

# На Raspberry Pi в той же сети, что и ESP32
# ESP32 имеет IP 192.168.1.50 и веб-сервер на порте 80
fxtunnel http 192.168.1.50:80
# → https://esp32-web.fxtun.dev

Теперь веб-интерфейс ESP32 доступен по публичному HTTPS-адресу. Это полезно для удалённой отладки, конфигурации устройства и демонстрации прототипов.

Сценарий 4: Дашборд мониторинга (Grafana / Node-RED)

На Raspberry Pi часто работают дашборды для мониторинга IoT-данных: Grafana (визуализация метрик), Node-RED (автоматизация потоков) или собственные веб-приложения. Чтобы дать доступ команде, откройте HTTP-туннель:

# Grafana на порте 3000
fxtunnel http 3000
# → https://iot-dashboard.fxtun.dev

# Node-RED на порте 1880
fxtunnel http 1880
# → https://nodered.fxtun.dev

Для сценария с несколькими сервисами на одном Raspberry Pi запустите отдельный туннель для каждого:

# Запускаем все туннели одновременно
fxtunnel tcp 22 &       # SSH-доступ
fxtunnel http 3000 &    # Grafana
fxtunnel http 1880 &    # Node-RED
fxtunnel tcp 1883 &     # MQTT-брокер

Установка fxTunnel на Raspberry Pi

fxTunnel написан на Go и поставляется с ARM-бинарниками из коробки. Установка одной командой работает на всех моделях Raspberry Pi – от Zero до Pi 5.

Установка

# Установка fxTunnel (автоматически определяет ARM-архитектуру)
curl -fsSL https://fxtun.dev/install.sh | bash

# Проверяем установку
fxtunnel --version

Автозапуск через systemd

Для IoT-устройств критично, чтобы туннель запускался автоматически при загрузке и перезапускался при сбоях. Создайте systemd-сервис:

# /etc/systemd/system/fxtunnel.service
[Unit]
Description=fxTunnel - IoT tunnel service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/fxtunnel tcp 22
Restart=always
RestartSec=5
User=pi
Environment=FXTUNNEL_TOKEN=your-auth-token-here

[Install]
WantedBy=multi-user.target

Активируйте сервис:

# Перечитываем конфигурацию systemd
sudo systemctl daemon-reload

# Включаем автозапуск при загрузке
sudo systemctl enable fxtunnel

# Запускаем сейчас
sudo systemctl start fxtunnel

# Проверяем статус
sudo systemctl status fxtunnel

Несколько туннелей через systemd

Если нужно пробросить несколько портов, создайте отдельный сервис для каждого:

# /etc/systemd/system/fxtunnel-ssh.service
[Unit]
Description=fxTunnel SSH tunnel
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/fxtunnel tcp 22
Restart=always
RestartSec=5
User=pi
Environment=FXTUNNEL_TOKEN=your-auth-token-here

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/fxtunnel-mqtt.service
[Unit]
Description=fxTunnel MQTT tunnel
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/fxtunnel tcp 1883
Restart=always
RestartSec=5
User=pi
Environment=FXTUNNEL_TOKEN=your-auth-token-here

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable fxtunnel-ssh fxtunnel-mqtt
sudo systemctl start fxtunnel-ssh fxtunnel-mqtt

Безопасность IoT-туннелей

IoT-устройства — частая цель атак из-за слабых паролей, устаревшего ПО и отсутствия мониторинга. Туннель шифрует трафик через TLS, но безопасность устройства — ваша ответственность.

Аутентификация и токены

Используйте токены аутентификации fxTunnel, чтобы только авторизованные клиенты могли создавать туннели:

# Указываем токен при запуске
fxtunnel tcp 22 --token=your-secure-token

# Или через переменную окружения (рекомендуется для systemd)
export FXTUNNEL_TOKEN=your-secure-token
fxtunnel tcp 22

SSH-ключи вместо паролей

Для SSH-доступа к Raspberry Pi всегда используйте ключи вместо паролей:

# На Raspberry Pi: отключаем вход по паролю
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Файрвол на устройстве

Настройте ufw, чтобы ограничить доступ к портам:

# Устанавливаем ufw
sudo apt install ufw

# Разрешаем только SSH и необходимые порты
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 1883/tcp  # MQTT
sudo ufw enable

Принцип минимального доступа

Открывайте через туннель только те порты, которые действительно нужны. Не пробрасывайте все порты устройства «на всякий случай». Закрывайте туннели к сервисам, которые не используются в данный момент.

FAQ

Как получить удалённый доступ к Raspberry Pi без статического IP?

Выполните curl -fsSL https://fxtun.dev/install.sh | bash на Pi, затем fxtunnel tcp 22. Вы получите публичный адрес вроде tunnel.fxtun.dev:41234 и сможете зайти по SSH откуда угодно. Статический IP не нужен, проброс портов на роутере – тоже.

Поддерживает ли fxTunnel ARM-архитектуру для Raspberry Pi?

Да. Go-бинарник кросс-компилируется под armv6, armv7 и arm64. Установочный скрипт сам определяет архитектуру и скачивает подходящий файл. Работает на любой модели Pi, от Zero до Pi 5.

Можно ли пробросить MQTT-брокер через туннель?

Можно – MQTT работает поверх TCP, так что fxtunnel tcp 1883 откроет ваш локальный Mosquitto наружу. После этого IoT-устройства из любой сети смогут публиковать и получать сообщения через публичный адрес туннеля.

Как сделать автозапуск туннеля при загрузке Raspberry Pi?

Создайте systemd-сервис /etc/systemd/system/fxtunnel.service с ExecStart=/usr/local/bin/fxtunnel tcp 22 и включите его через systemctl enable fxtunnel. Туннель будет подниматься при каждой загрузке и перезапускаться автоматически при сбоях.

Безопасно ли открывать IoT-устройства через туннель?

При правильной настройке – да. fxTunnel шифрует трафик через TLS, но со своей стороны стоит использовать SSH-ключи вместо паролей, настроить файрвол (ufw) и закрывать туннели к сервисам, которые сейчас не нужны. Устройства с реальными данными не стоит открывать без аутентификации.