Self-host-cloudflare-tunnels
Self-host-cloudflare-tunnels
Section titled “Self-host-cloudflare-tunnels”Self-host with Cloudflare
Section titled “Self-host with Cloudflare”How to use Cloudflare
Section titled “How to use Cloudflare”Why use Cloudflare
Section titled “Why use Cloudflare”Securing Cloudflare Tunnels
Section titled “Securing Cloudflare Tunnels”Many clearnet websites related to DNM, such as link rotators and directories, host on Cloudflare. However, they often fail to take measures to prevent bots from scanning their HTTP services (e.g., Shodan, Censys), which can expose their backend IP addresses.
To verify if you are affected, you can use:
telnet YOUR-BACKEND-IP 80or
nmap YOUR-BACKEND-IPIf you can connect via telnet or Nmap returns your HTTP port, follow the steps below to enhance your security. A simple way to protect yourself is to block any traffic from outside Cloudflare on HTTP port 80 (or whatever port you have proxied with Cloudflare).
This script does:
-
Allows SSH traffic (customizable based on your setup)
-
Fetches Cloudflare’s current IPs (both IPv4 and IPv6)
-
Adds iptables rules to allow connections from Cloudflare’s IPs
-
Adds iptables rules to block all connections on HTTP port 80
-
Allows localhost and other essential traffic (these rules can be modified or deleted based on your requirements)
#!/bin/bash
CLOUDFLARE_IPV4_URL="https://www.cloudflare.com/ips-v4"CLOUDFLARE_IPV6_URL="https://www.cloudflare.com/ips-v6"
CLOUDFLARE_IPV4=$(curl -s $CLOUDFLARE_IPV4_URL)CLOUDFLARE_IPV6=$(curl -s $CLOUDFLARE_IPV6_URL)
iptables -F INPUTip6tables -F INPUT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPTip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
for ip in $CLOUDFLARE_IPV4; do iptables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPTdone
for ip in $CLOUDFLARE_IPV6; do ip6tables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPTdone
iptables -A INPUT -p tcp --dport 80 -j DROPip6tables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -i lo -j ACCEPTip6tables -A INPUT -i lo -j ACCEPTiptables -A INPUT -p icmp --icmp-type 8 -j ACCEPTip6tables -A INPUT -p ipv6-icmp -j ACCEPTNote: If your SSL mode is set to Full or Full Strict, you will also need to apply similar rules to SSL port 443.
Additionally, if you run a hidden service with Tor, you can further enhance security by allowing traffic only from Tor exit IPs. A separate post will be made for Hidden Services.
SSH Access Through a Tunnel
Section titled “SSH Access Through a Tunnel”Once cloudflared is running on your server you can also reach SSH through it — closing port 22 on the public internet entirely. There are two architecturally different paths; pick based on whether you want Cloudflare to manage authentication too.
Path 1 — Access for Infrastructure (recommended, GA October 2024)
Section titled “Path 1 — Access for Infrastructure (recommended, GA October 2024)”Cloudflare issues short-lived (~3 minute) SSH certificates tied to your SSO identity. The user’s machine runs the system ssh (with WARP installed); sshd on the server trusts Cloudflare’s SSH CA via TrustedUserCAKeys. No long-lived ~/.ssh/id_ed25519 on user laptops.
Server-side setup:
# 1. cloudflared is already running on the server (per the rest of this page).
# 2. In the Cloudflare Zero Trust dashboard, create an Infrastructure Target:# Networks → Targets → Add target (hostname + private IP + protocol SSH/22)# Then an Infrastructure Application + an Access policy listing which# Unix usernames each identity may assume.
# 3. Save the Cloudflare SSH CA public key to the server:sudo tee /etc/ssh/ca.pub <<'EOF'ecdsa-sha2-nistp256 AAAA...your-ca-pubkey-from-dashboard... open-ssh-ca@cloudflareaccess.orgEOFsudo chmod 600 /etc/ssh/ca.pub
# 4. /etc/ssh/sshd_config — trust the Cloudflare CA:# PubkeyAuthentication yes# TrustedUserCAKeys /etc/ssh/ca.pubsudo systemctl reload ssh # or sshd on RHEL
# 5. Close port 22 to the public internet in your firewall.# The cloudflared tunnel is now the only inbound path.The local Unix user account must already exist — Cloudflare authenticates and issues the cert, but it doesn’t provision users.
Client-side setup:
- Install Cloudflare WARP in “Gateway with WARP” mode.
- In the dashboard, route the server’s private CIDR through WARP’s Gateway proxy.
- SSH normally:
ssh alice@prod-server # WARP intercepts, Cloudflare issues cert, system ssh usedThat’s it — no ProxyCommand, no custom config. The first connection of the day prompts an SSO sign-in via your browser; subsequent connections re-use the auth session.
Path 2 — Legacy cloudflared access ssh (still works)
Section titled “Path 2 — Legacy cloudflared access ssh (still works)”Useful when you can’t deploy WARP to user machines (BYOD environments, contractors). Uses a ProxyCommand in ~/.ssh/config.
Client-side setup:
# One-time auth:cloudflared access login https://ssh.example.com
# Generate the SSH config block (writes to ~/.ssh/config):cloudflared access ssh-config --hostname ssh.example.com --short-lived-cert >> ~/.ssh/config
# Now plain ssh works:ssh user@ssh.example.comThe generated ~/.ssh/config stanza sets:
Host ssh.example.com ProxyCommand cloudflared access ssh --hostname %h IdentityFile ~/.cloudflared/ssh.example.com-cf_key CertificateFile ~/.cloudflared/ssh.example.com-cf_key-cert.pubMarked as legacy in current Cloudflare docs — works fine, just not the recommended path for new deployments. Migrate to Access for Infrastructure when you can.
Tradeoffs
Section titled “Tradeoffs”| Pro | Con |
|---|---|
| Port 22 closed to the public internet — no scanner exposure, no 0-day risk | Hard Cloudflare dependency (account, WARP client, cloudflared on server) |
| No long-lived SSH keys on developer laptops | Latency: extra hop through Cloudflare edge (typically 10–40 ms) |
| ZTNA gating: device posture, MFA, geo enforcement per session | Free for ≤50 users; larger orgs need a Zero Trust / Access subscription |
| Identity-keyed audit trail (email → cert → session); Logpush integration | Access for Infrastructure currently has no SSH agent forwarding, no X11, no port forwarding; 10h max session |
| Same Cloudflare account already protects your web services | Lock-in: rolling back means re-issuing keys + reopening port 22 |
Gotchas
Section titled “Gotchas”--protocol http2, not QUIC. The cloudflared QUIC transport has known stream-cancellation bugs (cloudflared issues #1105, #1300, #1519) — for any SSH or video traffic, force HTTP/2 on the tunnel.- WARP split-tunnel: add the server’s CIDR to “Include IPs.” RFC1918 ranges aren’t routed through WARP by default on macOS; without the include,
ssh alice@prod-serverwon’t hit the proxy. - cloudflared version drift. Keep the server-side
cloudflaredreasonably current — older versions miss target-routing features Access for Infrastructure relies on. - Gateway network rules are evaluated before Access policies. A blanket “block private IPs” rule with per-target allows is the recommended layering.
- Email-to-Unix-username mapping is explicit in Access for Infrastructure. Unlike the legacy flow which auto-maps
jdoe@example.com→jdoe, you list allowed Unix usernames per policy.
Cross-references
Section titled “Cross-references”- SSH & GPG Keys → SSH Through a Tunnel or Mesh VPN — the decision matrix comparing this against Tailscale-based options
- Tailscale → SSH Access — the other “hide port 22” path