Adds enforced Proxy Protocol config and docs: a patch-set (config/patches/patch-set.json) that enables proxy protocol, whitelists TCPShield, and sets proxy/direct IPs; exposes PATCH_DEFINITIONS and mounts ./config/patches in docker-compose.yml; adds a featured "Proxy Protocol Support" entry to the dashboard mods page; and adds docs/tcpshield.md describing setup, verification, security model, and first-boot behavior.
3.8 KiB
TCPShield (DDoS protection + real player IPs)
TCPShield sits in front of the server: players connect to your TCPShield domain, and your real server IP stays hidden and DDoS-protected. The catch: without extra setup, every connection appears to come from TCPShield's proxies, so the Security page, IP bans, and connection logs are useless.
TCPShield's own RealIP plugin is Bukkit/Bungee/Velocity-only — it does not exist for Fabric. The working path for this stack is PROXY protocol v2 plus the Proxy Protocol Support Fabric mod.
Setup
-
TCPShield panel → your backend → enable Proxy Protocol.
-
Mods page → Recommended → add Proxy Protocol Support → Apply (or append
proxy-protocol-supporttoMODRINTH_PROJECTSin.envanddocker compose up -d mc). -
Nothing else — the mod's configuration is enforced automatically on every boot by
config/patches/patch-set.json(see below). Restart once after installing so the patch applies:docker compose restart mc
Verifying it works
Watch the startup log (docker compose logs -f mc). You must see all three:
Proxy Protocol Support is enabled!
TCPShield integration enabled! Fetching official IPs...
Successfully added 2 TCPShield IPs to the trusted proxy list.
Then join through your TCPShield domain — the console shows your real IP, and
docker compose ps shows mc healthy.
How the config is managed (don't hand-edit!)
The mod rewrites config/proxy_protocol_support.json at every startup, which
silently clobbers hand edits. Instead, the server image applies
config/patches/patch-set.json from this repo before every server start,
enforcing:
| Setting | Value | Why |
|---|---|---|
enableProxyProtocol |
true |
decode PROXY protocol headers |
whitelistTCPShieldServers |
true |
auto-fetch TCPShield's proxy IP ranges — no hardcoding, survives their infra changes |
proxyServerIPs |
127.0.0.1 |
(TCPShield's ranges are added automatically at runtime) |
directAccessIPs |
localhost (127.0.0.1, ::1) + private ranges (10/8, 172.16/12, 192.168/16) |
container healthcheck + LAN/panel access |
To change a value, edit the patch file in the repo and docker compose up -d mc.
First-boot note: on a brand-new install the mod's config file doesn't exist
yet when patches run, so the very first boot uses mod defaults; every boot after
that is patched. One docker compose restart mc after first install settles it.
Security model
PROXY protocol headers carry no signature, so anyone who finds your raw server IP could spoof client IPs — the mod prevents this with default-deny:
- PROXY headers are only accepted from TCPShield's published ranges (auto-fetched)
- Direct, header-less connections are only accepted from localhost + LAN
- Everything else →
REJECTED unauthorized direct connectionin the log
So occasional REJECTED lines from random public IPs are the wall working —
that's scanner bots hitting your raw IP and bouncing. A REJECTED ... /0:0:0:0:0:0:0:1
(IPv6 localhost) every few seconds means the container healthcheck is being
blocked — that's exactly what the ::1 entry in the patch prevents; if you see
it, the patch didn't apply (check docker compose logs mc | head -50).
For belt-and-braces, also enable TCPShield's Advanced Firewall (panel → Network →
Firewall), and consider host-firewalling port 25565 to TCPShield's ranges
(curl tcpshield.com/v4) once everything works.
Gotchas
- Friends must connect via the TCPShield domain — direct-IP connections from outside your LAN are rejected by design once this is active.
- If real players start bouncing with
REJECTED, TCPShield may have changed their IP ranges and the auto-fetch failed at boot — restart the server so it re-fetches.