4.6 KiB
Cloudflare Tunnel — Dev Container SSH Access
Overview
CF Tunnel SSH provides local VS Code / terminal access to dev containers for developers who want to use their own tooling instead of the browser IDE.
This is a power-user feature, not zero-install. It requires cloudflared
installed on the developer's local machine. The browser IDE remains the
zero-install option for all developers.
Install Requirement Clarification
cloudflared is required on both sides — but they do different things:
| Side | Role |
|---|---|
| Bastion VM | cloudflared runs as a persistent service maintaining the outbound tunnel to Cloudflare |
| Developer's local machine | cloudflared acts as an SSH ProxyCommand — routes SSH through Cloudflare instead of directly to the IP |
The dev container (LXC) only needs sshd running. It does not need cloudflared.
Without the ProxyCommand on the client, SSH goes directly to the raw IP and gets rejected. You will see:
kex_exchange_identification: Connection closed by remote host
This means cloudflared is not being invoked — SSH is bypassing Cloudflare entirely.
Architecture
Developer laptop (cloudflared as ProxyCommand)
↓ ssh dev-6070.zerolaghub.dev
Cloudflare edge
↓ CF Tunnel (persistent service on bastion)
Bastion VM (private IP, no public exposure)
↓ SSH proxy jump
Dev container (10.100.x.x — just needs sshd)
Zero-Install vs Power-User Access
| Access Method | Install Required | Use Case |
|---|---|---|
Browser IDE (dev-<vmid>.zerolaghub.dev) |
Nothing | All developers — zero friction |
| CF Tunnel SSH | cloudflared on local machine |
Power users wanting local VS Code or terminal |
The browser IDE is the primary access method. CF Tunnel SSH is optional and for developers who specifically want local tool integration.
Current State
- ✅ CF Tunnel created and connected to bastion VM
- ✅ Cloudflare Zero Trust free plan active (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. Configure tunnel ingress in Cloudflare Zero Trust dashboard
Zero Trust → Networks → Tunnels → your tunnel → Configure:
Add a public hostname:
- Subdomain:
*(wildcard) - Domain:
zerolaghub.dev - Service type:
SSH - URL:
localhost:22
2. Configure bastion SSH for proxy jump
Edit /etc/ssh/sshd_config on the bastion:
AllowTcpForwarding yes
PermitOpen 10.100.0.0/24:22
Restart: systemctl restart sshd
3. Ensure dev containers have SSH running
Add to agent dev provisioning pipeline:
apt-get install -y openssh-server
systemctl enable ssh
systemctl start ssh
4. Developer one-time setup (power users only)
Install cloudflared on their local machine:
# 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 GitHub releases
# Termux — pkg install cloudflared
Add to ~/.ssh/config:
Host *.zerolaghub.dev
ProxyCommand cloudflared access ssh --hostname %h
User dev
Then: ssh dev-6070.zerolaghub.dev
Confirm it's working by looking for in -v output:
Executing proxy command: cloudflared access ssh --hostname dev-6070.zerolaghub.dev
Portal Integration (Future)
Show on the dev container page as an optional section:
Local VS Code Access (Advanced)
────────────────────────────────
Requires a one-time install of cloudflared on your machine.
1. Install: 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
Or open VS Code Remote Explorer and add this host.
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 |