DEV Community

hack-tramp
hack-tramp

Posted on

TCP proxy over MQTT using a cheap ESP32 microcontroller

Full info and code : https://github.com/hack-tramp/ESP-MQTTunnel/

A working PoC that turns an ESP32 into a hardware proxy that tunnels traffic over MQTT. Because it connects to a MQTT broker, you can bypass firewalls/censorship without open ports/public IP (which would be needed for direct ESP32 - laptop comms). All traffic is relayed raw — no TLS interception, no decryption.


What it does

Here's an example to show how this works (for more technical details see below). The purpose is to bypass internet restrictions.

Location A: A country with highly censored / very restrictive internet. Laptop running Win10 + Firefox + local python proxy.

Location B: Uncensored, free internet. ESP32 is connected here.

Location C: MQTT server - this must be reachable from A and B but does not have to be in a place with uncensored internet. I used HiveMQ cloud (free tier).

(definition: MQTT (Message Queuing Telemetry Transport) is a lightweight, open-standard messaging protocol designed for resource-constrained devices and unreliable networks)

  1. laptop ---> MQTT server : The laptop browser tries to access a restricted website, and sends a request to the local python proxy, which sends this to the MQTT server.

  2. MQTT server ----> ESP32 ---> www.example.com The ESP32 reads the bytes from the MQTT server and forwards them to the restricted website which is normally inaccessible from A.

  3. www.example.com ---> ESP32 ---> MQTT server : ESP32 receives response from the website, and uploads it to the MQTT server.

  4. MQTT server ---> laptop: Laptop at A reads the restricted site's response from the MQTT server and (via python) shows it in the browser.

Tested with: Windows 10 Firefox / Python 3 <--> ESP32-S3 Dev Module <--> HiveMQ Cloud (free tier)

⚠️ The MQTT broker must be reachable from whichever country the laptop is in.

Confirmed working

  • YouTube
  • Gmail
  • Twitter / X
  • News sites
  • Reddit
  • Google, Duckduckgo etc.

Known issues

  • Instagram gets stuck

How it works

  1. The ESP32 connects to your Wi-Fi and subscribes to the MQTT req topic.
  2. The Python script runs a local HTTP proxy on the laptop (e.g. 127.0.0.1:8080).
  3. Firefox is configured to use that local proxy.
  4. When Firefox requests a site, the Python proxy:
    • Parses the CONNECT host:port line.
    • Publishes an open message over MQTT.
    • Streams the raw TLS bytes as data messages.
  5. The ESP32 receives those messages, opens a real TCP socket to the target host, and forwards the bytes.
  6. Responses from the real server come back through MQTT (res topic), are received by the Python proxy, and written back to Firefox.

Limitations

The ESP32 has limited resources, so it can't handle too many simultaneous connections. Trying to load pages in multiple tabs will not work.

Images and video do work, but to save bandwidth (especially on a free MQTT account) and improve speed, consider using a content blocker such as Block Image Reloaded in Firefox.


Usage

1. MQTT broker

Create a free cluster at HiveMQ Cloud (or use any MQTT broker reachable from both devices).

Note the following:

  • Hostname
  • Port (typically 8883 for TLS)
  • Username
  • Password

2. Configure credentials

Edit both files and set your MQTT credentials:

server.py

broker = "your-cluster-id.s1.eu.hivemq.cloud"
user   = "your-username"
pw     = "your-password"
Enter fullscreen mode Exit fullscreen mode

esp.ino

const char* ssid    = "your-wifi-ssid";
const char* pass    = "your-wifi-password";
const char* broker  = "your-cluster-id.s1.eu.hivemq.cloud";
const char* user    = "your-username";
const char* mpass   = "your-password";
Enter fullscreen mode Exit fullscreen mode

3. Flash the ESP32

Open esp.ino in the Arduino IDE, select your ESP32 board, and upload. Open the Serial Monitor at 115200 baud to see activity. I prefer to use PuTTY so I can copy large amounts of output.

4. Run the python proxy server

pip install paho-mqtt
python server.py
Enter fullscreen mode Exit fullscreen mode

You should see

Listening on 127.0.0.1:8080
Set Firefox HTTPS proxy to 127.0.0.1:8080
Press Ctrl+C to stop
Enter fullscreen mode Exit fullscreen mode

5. Point Firefox at the proxy

In Firefox:

  1. Go to Settings → General → Network Settings → Settings…

  2. Select Manual proxy configuration

  3. Set HTTPS Proxy to 127.0.0.1 port 8080

  4. Make sure localhost and 127.0.0.1 are not in the "No proxy for" list

  5. Click OK

Visit any HTTPS site. Traffic will relay through MQTT to the ESP32 and out to the real server.

MQTT topics

Topic Direction Purpose
req Python → ESP32 Client-to-server bytes (open, data, close)
res ESP32 → Python Server-to-client bytes (data)

Message format

All MQTT messages are JSON:


{
  "conn_id": 5,
  "host": "www.example.com",
  "port": 443,
  "type": "data",
  "data": "FgMBB2ABAAdc..."
}
Enter fullscreen mode Exit fullscreen mode
  • type is one of open, data, close

  • data is base64-encoded raw bytes, or null for open/close

  • conn_id identifies the TCP connection (Firefox opens several in parallel)

Top comments (0)