fix: Telegram+Discord TLS/SNI bypass via Cloudflare Worker proxy
Browse files- Confirmed HF blocks Telegram via TLS/SNI inspection (60s timeout, ECONNRESET)
- Moved NODE_OPTIONS to Dockerfile ENV (after COPY) so dns-fix.js loads in n8n
- Added keep-alive=false patch to prevent stale socket ECONNRESET on Node 22
- Removed diagnostic code from dns-fix.js
- Updated README and guide to cover both Discord and Telegram proxy workaround
- Guide now includes Telegram Base URL field instructions for n8n credential
- DISCORD_PROXY_GUIDE.md +45 -23
- README.md +3 -3
- dns-fix.js +1 -24
DISCORD_PROXY_GUIDE.md
CHANGED
|
@@ -1,45 +1,67 @@
|
|
| 1 |
-
# π οΈ
|
| 2 |
|
| 3 |
-
Hugging Face
|
| 4 |
|
| 5 |
-
|
| 6 |
|
| 7 |
-
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
### Step 1: Create a Cloudflare Worker
|
| 12 |
-
1. Log into [Cloudflare](https://dash.cloudflare.com/) (create
|
| 13 |
-
2.
|
| 14 |
-
3. Click **Create Worker**
|
| 15 |
-
4.
|
| 16 |
|
| 17 |
### Step 2: Add the Proxy Code
|
| 18 |
-
1.
|
| 19 |
-
2.
|
|
|
|
| 20 |
|
| 21 |
```javascript
|
| 22 |
export default {
|
| 23 |
async fetch(request) {
|
| 24 |
const url = new URL(request.url);
|
| 25 |
-
url.hostname = '
|
| 26 |
return fetch(new Request(url, request));
|
| 27 |
}
|
| 28 |
}
|
| 29 |
```
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
##
|
| 34 |
-
Because the native Discord Node hardcodes connections to `discord.com` when using Bot tokens, you must use the **Webhook** method.
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
---
|
| 45 |
-
|
|
|
|
|
|
| 1 |
+
# π οΈ Bypassing Hugging Face Network Restrictions
|
| 2 |
|
| 3 |
+
Hugging Face Free Tier Spaces block outgoing connections to certain messaging platforms (**Discord** and **Telegram**) via **TLS/SNI inspection**. This means the TCP connection is allowed, but the TLS handshake is silently dropped as soon as HF detects the target hostname (e.g., `discord.com`, `api.telegram.org`) in the SNI field.
|
| 4 |
|
| 5 |
+
The fix is to route your traffic through a **Cloudflare Worker proxy** with a neutral hostname. Since the SNI in the TLS handshake will show `your-proxy.workers.dev` instead of the blocked hostname, HF's filter won't block it. Cloudflare Workers are **100% free** (100,000 requests/day).
|
| 6 |
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## Setting Up a Cloudflare Worker Proxy
|
| 10 |
|
| 11 |
+
This process takes about 2 minutes and works for both Discord and Telegram.
|
| 12 |
|
| 13 |
### Step 1: Create a Cloudflare Worker
|
| 14 |
+
1. Log into [Cloudflare](https://dash.cloudflare.com/) (create a free account if needed).
|
| 15 |
+
2. Go to **Workers & Pages** in the left sidebar.
|
| 16 |
+
3. Click **Create Worker** β **Start with Hello World!**.
|
| 17 |
+
4. Give it a name (e.g., `discord-proxy` or `telegram-proxy`) and click **Deploy**.
|
| 18 |
|
| 19 |
### Step 2: Add the Proxy Code
|
| 20 |
+
1. Click **Edit Code**.
|
| 21 |
+
2. Delete all existing code and paste the following snippet.
|
| 22 |
+
3. Replace `TARGET_HOSTNAME` with the service you want to proxy (see table below).
|
| 23 |
|
| 24 |
```javascript
|
| 25 |
export default {
|
| 26 |
async fetch(request) {
|
| 27 |
const url = new URL(request.url);
|
| 28 |
+
url.hostname = 'TARGET_HOSTNAME';
|
| 29 |
return fetch(new Request(url, request));
|
| 30 |
}
|
| 31 |
}
|
| 32 |
```
|
| 33 |
|
| 34 |
+
| Service | Replace `TARGET_HOSTNAME` with |
|
| 35 |
+
| :--- | :--- |
|
| 36 |
+
| Discord | `discord.com` |
|
| 37 |
+
| Telegram | `api.telegram.org` |
|
| 38 |
+
|
| 39 |
+
4. Click **Save and Deploy**. Cloudflare gives you a URL like `https://your-proxy.yourname.workers.dev`.
|
| 40 |
+
|
| 41 |
+
---
|
| 42 |
|
| 43 |
+
## Using the Proxy in n8n
|
|
|
|
| 44 |
|
| 45 |
+
### Discord
|
| 46 |
+
The native n8n Discord node uses Bot tokens that hardcode `discord.com`. Use the **Webhook** authentication method instead:
|
| 47 |
+
|
| 48 |
+
1. Add a **Discord** node and set the **Credential** to use **Webhook** authentication.
|
| 49 |
+
2. Create a new Discord Webhook credential.
|
| 50 |
+
3. Take your Discord Webhook URL and replace `discord.com` with your Worker domain:
|
| 51 |
+
- *Original:* `https://discord.com/api/webhooks/123456/abcdef`
|
| 52 |
+
- *Proxied:* `https://discord-proxy.yourname.workers.dev/api/webhooks/123456/abcdef`
|
| 53 |
+
4. Paste the proxied URL in the **Webhook URL** field.
|
| 54 |
+
|
| 55 |
+
### Telegram
|
| 56 |
+
The n8n Telegram credential has a **Base URL** field that can be changed:
|
| 57 |
+
|
| 58 |
+
1. Go to **Credentials** β add or edit a **Telegram** credential.
|
| 59 |
+
2. Enter your Bot Token as usual.
|
| 60 |
+
3. In the **Base URL** field, replace `https://api.telegram.org` with your Worker URL:
|
| 61 |
+
- *Original:* `https://api.telegram.org`
|
| 62 |
+
- *Proxied:* `https://telegram-proxy.yourname.workers.dev`
|
| 63 |
+
4. Click **Save** and then **Test** β it should connect instantly.
|
| 64 |
|
| 65 |
---
|
| 66 |
+
|
| 67 |
+
*Note: Upgrading your Hugging Face Space to a paid hardware tier removes these network restrictions entirely, allowing you to use all n8n nodes natively without a proxy.*
|
README.md
CHANGED
|
@@ -68,10 +68,10 @@ Hugging8n automatically creates and maintains a private dataset in your Hugging
|
|
| 68 |
|
| 69 |
## β οΈ Known Limitations & Workarounds
|
| 70 |
|
| 71 |
-
**Discord Webhooks**
|
| 72 |
-
Hugging Face
|
| 73 |
|
| 74 |
-
π **[Read the Guide: How to Create a Free
|
| 75 |
*(Upgrading to a paid Space removes this firewall restriction entirely).*
|
| 76 |
|
| 77 |
## ποΈ Architecture
|
|
|
|
| 68 |
|
| 69 |
## β οΈ Known Limitations & Workarounds
|
| 70 |
|
| 71 |
+
**Discord & Telegram Webhooks Blocked by Hugging Face**
|
| 72 |
+
Hugging Face blocks outgoing connections to Discord and Telegram on Free Tier Spaces via TLS/SNI inspection β the TCP connection opens but the TLS handshake is silently dropped when the target hostname is detected. To use these services, you must route traffic through a proxy with a different hostname.
|
| 73 |
|
| 74 |
+
π **[Read the Guide: How to Create a Free Cloudflare Proxy in 2 minutes](./DISCORD_PROXY_GUIDE.md)**
|
| 75 |
*(Upgrading to a paid Space removes this firewall restriction entirely).*
|
| 76 |
|
| 77 |
## ποΈ Architecture
|
dns-fix.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
| 11 |
*/
|
| 12 |
"use strict";
|
| 13 |
|
| 14 |
-
console.error("[DNS-FIX]
|
| 15 |
|
| 16 |
const dns = require("dns");
|
| 17 |
const http = require("http");
|
|
@@ -22,29 +22,6 @@ http.globalAgent = new http.Agent({ keepAlive: false });
|
|
| 22 |
https.globalAgent = new https.Agent({ keepAlive: false });
|
| 23 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
|
| 25 |
-
// ββ TCP Connection Diagnostics ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 26 |
-
// Temporarily patch net.createConnection to log all outgoing TCP connections
|
| 27 |
-
// and their errors so we can identify the exact IP and failure mode.
|
| 28 |
-
const net = require("net");
|
| 29 |
-
const _origConnect = net.createConnection.bind(net);
|
| 30 |
-
net.createConnection = function (options, ...args) {
|
| 31 |
-
const host = options.host || options;
|
| 32 |
-
const port = options.port;
|
| 33 |
-
// Only log external connections (skip loopback)
|
| 34 |
-
if (host && host !== "127.0.0.1" && host !== "localhost" && host !== "::1") {
|
| 35 |
-
console.error(`[DNS-FIX] TCP connect β ${host}:${port}`);
|
| 36 |
-
const sock = _origConnect(options, ...args);
|
| 37 |
-
sock.on("connect", () =>
|
| 38 |
-
console.error(`[DNS-FIX] TCP connected β ${host}:${port}`),
|
| 39 |
-
);
|
| 40 |
-
sock.on("error", (err) =>
|
| 41 |
-
console.error(`[DNS-FIX] TCP error β ${host}:${port} β ${err.code}: ${err.message}`),
|
| 42 |
-
);
|
| 43 |
-
return sock;
|
| 44 |
-
}
|
| 45 |
-
return _origConnect(options, ...args);
|
| 46 |
-
};
|
| 47 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 48 |
|
| 49 |
// In-memory cache for runtime DoH resolutions
|
| 50 |
const runtimeCache = new Map(); // hostname -> { ip, expiry }
|
|
|
|
| 11 |
*/
|
| 12 |
"use strict";
|
| 13 |
|
| 14 |
+
console.error("[DNS-FIX] Loaded β DoH-first resolver + keep-alive patch active.");
|
| 15 |
|
| 16 |
const dns = require("dns");
|
| 17 |
const http = require("http");
|
|
|
|
| 22 |
https.globalAgent = new https.Agent({ keepAlive: false });
|
| 23 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
// In-memory cache for runtime DoH resolutions
|
| 27 |
const runtimeCache = new Map(); // hostname -> { ip, expiry }
|