Skip to content

Self-host-cloudflare-tunnels

DarkWeb

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 80

or

nmap YOUR-BACKEND-IP

If 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 INPUT
ip6tables -F INPUT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
for ip in $CLOUDFLARE_IPV4; do
iptables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPT
done
for ip in $CLOUDFLARE_IPV6; do
ip6tables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPT
done
iptables -A INPUT -p tcp --dport 80 -j DROP
ip6tables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

Note: 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.


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.

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:

Terminal window
# 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.org
EOF
sudo chmod 600 /etc/ssh/ca.pub
# 4. /etc/ssh/sshd_config — trust the Cloudflare CA:
# PubkeyAuthentication yes
# TrustedUserCAKeys /etc/ssh/ca.pub
sudo 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:

  1. Install Cloudflare WARP in “Gateway with WARP” mode.
  2. In the dashboard, route the server’s private CIDR through WARP’s Gateway proxy.
  3. SSH normally:
Terminal window
ssh alice@prod-server # WARP intercepts, Cloudflare issues cert, system ssh used

That’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:

Terminal window
# 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.com

The 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.pub

Marked as legacy in current Cloudflare docs — works fine, just not the recommended path for new deployments. Migrate to Access for Infrastructure when you can.

ProCon
Port 22 closed to the public internet — no scanner exposure, no 0-day riskHard Cloudflare dependency (account, WARP client, cloudflared on server)
No long-lived SSH keys on developer laptopsLatency: extra hop through Cloudflare edge (typically 10–40 ms)
ZTNA gating: device posture, MFA, geo enforcement per sessionFree for ≤50 users; larger orgs need a Zero Trust / Access subscription
Identity-keyed audit trail (email → cert → session); Logpush integrationAccess for Infrastructure currently has no SSH agent forwarding, no X11, no port forwarding; 10h max session
Same Cloudflare account already protects your web servicesLock-in: rolling back means re-issuing keys + reopening port 22
  • --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-server won’t hit the proxy.
  • cloudflared version drift. Keep the server-side cloudflared reasonably 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.comjdoe, you list allowed Unix usernames per policy.