# Cloudflare Tunnel — Dev Container SSH Access ## Overview Cloudflare Tunnel provides SSH access to dev containers for developers using local VS Code or terminal. No VPN required. Uses the same hostname as the browser IDE. --- ## How It Works — Two Sides This requires `cloudflared` on **both** the server (bastion) and the client (developer machine). They do completely different things: **Bastion (server side):** `cloudflared` runs as a persistent service maintaining an outbound tunnel to Cloudflare's edge. This is already set up. **Developer machine (client side):** `cloudflared` is a lightweight binary used as an SSH ProxyCommand. It intercepts the SSH connection and routes it through Cloudflare instead of going directly to the IP. Without this, SSH bypasses Cloudflare entirely, hits the raw IP on port 22, and gets rejected. The two sides meet at Cloudflare — no inbound ports needed on either: ``` Developer machine cloudflared (ProxyCommand) → Cloudflare edge ← cloudflared (tunnel) ↓ Bastion VM ↓ Dev container ``` **Critical:** If SSH connects directly to the IP instead of through cloudflared, you will see: ``` kex_exchange_identification: Connection closed by remote host ``` This means the ProxyCommand is not being invoked — SSH is bypassing Cloudflare entirely. --- ## Architecture ``` Developer laptop ↓ ssh dev-6070.zerolaghub.dev (via cloudflared ProxyCommand) Cloudflare edge ↓ CF Tunnel (persistent, runs on bastion) Bastion VM (private IP, no public exposure) ↓ SSH proxy jump Dev container (10.100.x.x) ``` HTTPS and SSH share the same hostname. Cloudflare routes them separately: - HTTPS → Traefik → API → container (browser IDE) - SSH → CF Tunnel → bastion → container --- ## Current State - ✅ CF Tunnel created and connected to bastion VM - ✅ Cloudflare Zero Trust free plan active (covers up to 50 users) - ⏳ Tunnel SSH hostname mapping not yet configured in Zero Trust dashboard - ⏳ Bastion SSH proxy jump config not yet done - ⏳ Dev container SSH server not yet verified - ⏳ Portal SSH config snippet not yet built --- ## Remaining Steps ### 1. Install cloudflared on developer machines This is required on every developer's machine — not optional. ```bash # macOS brew install cloudflare/cloudflare/cloudflared # Linux curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \ -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared # Windows # Download from https://github.com/cloudflare/cloudflared/releases # Termux (Android) pkg install cloudflared ``` ### 2. Developer SSH config (one-time) Developer adds this to `~/.ssh/config`: ``` Host *.zerolaghub.dev ProxyCommand cloudflared access ssh --hostname %h User dev ``` This is what makes `ssh dev-6070.zerolaghub.dev` route through Cloudflare instead of going directly to the IP. Without this config, SSH will fail. To confirm it's working, run with `-v` and look for: ``` Executing proxy command: cloudflared access ssh --hostname dev-6070.zerolaghub.dev ``` ### 3. Configure tunnel ingress in Cloudflare Zero Trust dashboard In Zero Trust → Networks → Tunnels → your tunnel → Configure: Add a public hostname: - Subdomain: `*` (wildcard) or specific `dev-ssh` - Domain: `zerolaghub.dev` - Service type: `SSH` - URL: `localhost:22` ### 4. Configure bastion SSH for proxy jump On the bastion, edit `/etc/ssh/sshd_config`: ``` AllowTcpForwarding yes PermitOpen 10.100.0.0/24:22 ``` Restart SSH: `systemctl restart sshd` ### 5. Ensure dev containers have SSH running Each dev container needs openssh-server installed. Add to agent dev provisioning pipeline: ```bash apt-get install -y openssh-server systemctl enable ssh systemctl start ssh ``` ### 6. VS Code Remote SSH After SSH config is in place, developer opens VS Code → Remote Explorer → Add new host → `ssh dev-6070.zerolaghub.dev`. VS Code handles the rest. --- ## Security Notes - Bastion has no public IP — all SSH access goes through CF Tunnel only - CF Tunnel is outbound-only from bastion to Cloudflare — no inbound ports open - Zero Trust free tier: up to 50 users, core access control included - `cloudflared` on the client is a lightweight binary, not a service - code-server runs `--auth none` — SSH is enforced separately at OS level --- ## Portal Integration (Future) Portal dev container page should show: ``` SSH Access ────────── 1. Install cloudflared: https://developers.cloudflare.com/cloudflared/install 2. Add to ~/.ssh/config: Host *.zerolaghub.dev ProxyCommand cloudflared access ssh --hostname %h User dev 3. Connect: ssh dev-6070.zerolaghub.dev ``` One-time setup. Works for all dev containers after that. --- ## Troubleshooting | Issue | Likely Cause | |-------|--------------| | `kex_exchange_identification: Connection closed` | ProxyCommand not in SSH config — SSH going direct to IP | | `cloudflared: command not found` | cloudflared not installed on client machine | | `Connection refused` | SSH not running in container or bastion jump not configured | | `Permission denied` | SSH key not added to container's `dev` user | | Tunnel not connecting | `cloudflared` service not running on bastion — `systemctl status cloudflared` |