Initial Discord bot in Docker behind a per-container Cloudflare WARP tunnel
- discord.js 14 bot with /ping and /warp, periodic WARP egress-IP logging - Dockerfile: node:22-bookworm-slim + cloudflare-warp 2026.7.1377.0 (.deb) - entrypoint boots dbus/warp-svc, registers, connects, verifies warp=on - docker-compose with NET_ADMIN, /dev/net/tun, sysctls, healthcheck - deploy script, README, CHANGELOG, lockfile generated inside Docker Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
# DiscordBot-CloudflareWarp
|
||||
|
||||
A small [discord.js](https://discord.js.org/) bot that runs **entirely inside Docker** and sends all of its
|
||||
traffic through a **Cloudflare WARP** (Cloudflare One / Zero Trust client) tunnel. Every container runs its
|
||||
own WARP client, so every bot gets its own VPN tunnel and its own Cloudflare egress IP. The bot proves it is
|
||||
still on Cloudflare by logging the egress IP and `warp=on` flag on startup, on a timer and on demand.
|
||||
|
||||
Nothing runs on the host except Docker: `npm install` happens in the image build, and the lockfile was
|
||||
generated inside a `node:22-bookworm-slim` container.
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
+-------------------------------------------------------------+
|
||||
| container: discordbot-warp |
|
||||
| |
|
||||
| dbus -> warp-svc (Cloudflare WARP daemon, TUN device) |
|
||||
| ^ |
|
||||
| entrypoint.sh: register -> mode warp -> connect -> verify |
|
||||
| | |
|
||||
| node src/index.js ---- all traffic ----> WARP tunnel ---> Cloudflare ---> Discord
|
||||
+-------------------------------------------------------------+
|
||||
```
|
||||
|
||||
1. `docker/entrypoint.sh` starts `dbus` and `warp-svc`, creates (or reuses) a WARP registration, sets
|
||||
`mode warp`, connects and waits until `warp-cli status` says `Connected`.
|
||||
2. It then calls `https://www.cloudflare.com/cdn-cgi/trace` and **refuses to start the bot** unless
|
||||
Cloudflare reports `warp=on` (or `warp=plus`). The egress IP and colo are logged.
|
||||
3. `bot/src/index.js` logs into Discord and repeats the same trace check every `WARP_CHECK_INTERVAL`
|
||||
seconds (default 60), logging a line like:
|
||||
|
||||
```
|
||||
[2026-09-08T00:20:15.123Z] [WARP] CONNECTED via Cloudflare (periodic) ip=104.28.x.x warp=on colo=AMS loc=NL http=http/2 tls=TLSv1.3 | warp-cli: Status update: Connected
|
||||
```
|
||||
|
||||
If the tunnel drops it logs `NOT CONNECTED` and runs `warp-cli connect` to recover.
|
||||
4. A Docker `HEALTHCHECK` performs the same `warp=on` check, so `docker ps` shows `unhealthy` if the
|
||||
container ever falls off Cloudflare.
|
||||
|
||||
### Slash commands
|
||||
|
||||
| Command | What it does |
|
||||
|---------|-------------------------------------------------------------------------------|
|
||||
| `/ping` | Replies with round-trip and gateway latency. |
|
||||
| `/warp` | Runs a live tunnel check and replies with the Cloudflare IP, colo and status. |
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
bot/
|
||||
package.json, package-lock.json Node project (discord.js 14)
|
||||
src/index.js the bot
|
||||
docker/
|
||||
Dockerfile node:22-bookworm-slim + cloudflare-warp .deb
|
||||
entrypoint.sh boots WARP, verifies, then execs the bot
|
||||
docker-compose.yml one service per bot, each with its own WARP tunnel
|
||||
scripts/deploy.sh ships the committed tree to the Docker host and restarts the stack
|
||||
.env.example configuration template
|
||||
CHANGELOG.md every change, newest first
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
* A Docker host (tested on Debian 12 with Docker 20.10 and Compose v5) with `/dev/net/tun` available.
|
||||
* Containers need `NET_ADMIN` and the TUN device. Both are set in `docker-compose.yml`.
|
||||
* A Discord bot token from the [Developer Portal](https://discord.com/developers/applications).
|
||||
|
||||
The Cloudflare WARP client is downloaded during the image build from
|
||||
`https://downloads.cloudflareclient.com/v1/download/bookworm-intel/version/<WARP_VERSION>`
|
||||
(`WARP_VERSION` defaults to `2026.7.1377.0`, the Debian 12 build).
|
||||
|
||||
## Quick start (on the Docker host)
|
||||
|
||||
```bash
|
||||
git clone https://gitea.ikbengino.nl/ginoblij/DiscordBot-CloudflareWarp.git
|
||||
cd DiscordBot-CloudflareWarp
|
||||
cp .env.example .env # put your DISCORD_TOKEN in .env
|
||||
docker compose up -d --build
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
You should see the entrypoint report the tunnel, then the bot log in:
|
||||
|
||||
```
|
||||
[entrypoint] warp-svc is up
|
||||
[entrypoint] no registration found, creating a new one
|
||||
[entrypoint] mode set to warp
|
||||
[entrypoint] warp-cli status: Status update: Connected
|
||||
[entrypoint] WARP tunnel verified: ip=104.28.x.x warp=on colo=AMS
|
||||
[entrypoint] starting bot: node src/index.js
|
||||
[bot] logged in as ... in 1 guild(s)
|
||||
[WARP] CONNECTED via Cloudflare (startup) ip=104.28.x.x warp=on colo=AMS ...
|
||||
```
|
||||
|
||||
## Configuration (`.env`)
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|-----------------------|---------|----------------------------------------------------------------------|
|
||||
| `DISCORD_TOKEN` | | Bot token. Required. |
|
||||
| `WARP_CHECK_INTERVAL` | `60` | Seconds between tunnel checks logged by the bot. |
|
||||
| `GUILD_ID` | | Register slash commands in one guild (instant) instead of globally. |
|
||||
| `WARP_MODE` | `warp` | `warp-cli mode` to use (`warp`, `doh`, `warp+doh`, ...). |
|
||||
| `WARP_CONNECT_TIMEOUT`| `60` | Seconds the entrypoint waits for `Connected` before giving up. |
|
||||
|
||||
## Running more than one bot (one VPN per container)
|
||||
|
||||
Each service in `docker-compose.yml` is an independent container with its own `warp-svc`, its own
|
||||
registration volume and therefore its own tunnel and egress IP. To add a bot:
|
||||
|
||||
1. Create `.env.bot2` with the second token.
|
||||
2. Uncomment the `bot2` service and the `warp-data-bot2` volume in `docker-compose.yml`.
|
||||
3. `docker compose up -d --build`.
|
||||
|
||||
## Deploying from this repo
|
||||
|
||||
`scripts/deploy.sh` sends the **committed** tree (`git archive HEAD`) to the host over SSH, rebuilds the
|
||||
image there and restarts the stack. The `.env` on the host is never touched or overwritten.
|
||||
|
||||
```bash
|
||||
scripts/deploy.sh # defaults to root@192.168.5.67:/opt/discordbot-cloudflarewarp
|
||||
scripts/deploy.sh user@host /some/dir
|
||||
```
|
||||
|
||||
## Checking the tunnel by hand
|
||||
|
||||
```bash
|
||||
docker compose exec bot warp-cli --accept-tos status
|
||||
docker compose exec bot curl -s https://www.cloudflare.com/cdn-cgi/trace
|
||||
docker compose logs -f | grep WARP
|
||||
```
|
||||
|
||||
## Change history
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md). Every change is committed and pushed with a changelog entry.
|
||||
Reference in New Issue
Block a user