Top Tags

Cloudflared SSH Tunnel

Guide to setting up an SSH tunnel using Cloudflared for secure remote access.

Overview

Cloudflare Tunnel (formerly Argo Tunnel) creates secure, outbound-only connections from your infrastructure to Cloudflare's edge network without opening inbound ports in your firewall. This guide demonstrates how to expose SSH access through Cloudflare Tunnel, eliminating the need for VPNs or port forwarding while maintaining security through Cloudflare's zero-trust network architecture.

Key Benefits:

  • No inbound firewall rules required - connections are established outbound from your server
  • Protection against DDoS attacks through Cloudflare's edge network
  • Automatic certificate management and encryption via TLS
  • Access control and audit logging capabilities
  • No public IP exposure of your SSH service

Prerequisites:

  • A Cloudflare account with an active domain
  • Root or sudo access on the target server
  • A domain or subdomain managed through Cloudflare DNS

Install Cloudflared

The cloudflared daemon is Cloudflare's lightweight connector that maintains persistent connections to Cloudflare's edge network. It runs on your server and proxies traffic between your local services and Cloudflare.

bash
1curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
2sudo dpkg -i cloudflared.deb

Note: For other operating systems and architectures, visit the Cloudflared releases page. Available packages include RPM for Red Hat/Fedora, binaries for macOS (ARM/Intel), Windows, and various Linux architectures.

Authenticate Cloudflared

Authentication links your server's cloudflared instance to your Cloudflare account, granting it permission to create and manage tunnels under your domain. This step opens a browser window where you'll authorize the connection.

bash
1cloudflared tunnel login

This command generates a certificate file (cert.pem) stored in ~/.cloudflared/ that authenticates your tunnel operations.

Create a Tunnel

A tunnel is a secure pathway between your server and Cloudflare's network. Each tunnel is identified by a unique UUID and name. The creation process generates credentials that authenticate your tunnel's connection to Cloudflare's edge.

bash
1cloudflared tunnel create <TUNNEL_NAME>

Replace <TUNNEL_NAME> with a descriptive name (e.g., ssh-tunnel, home-server-ssh). This command creates a tunnel credentials JSON file in ~/.cloudflared/ containing your tunnel's UUID and authentication secret.

Example output:

Tunnel credentials written to /home/user/.cloudflared/abc12345-def6-7890-gh12-ijklm3456789.json Created tunnel ssh-tunnel with ID abc12345-def6-7890-gh12-ijklm3456789

Configure the Tunnel

The configuration file defines how traffic is routed through your tunnel. It specifies which hostnames map to which local services using an ingress rules system. Rules are evaluated top-to-bottom, with the first matching rule handling the request.

bash
1nano ~/.cloudflared/config.yml

Add the following configuration

yaml
1tunnel: ssh-tunnel
2credentials-file: /home/youruser/.cloudflared/<tunnel-uuid>.json
3
4ingress:
5 - hostname: ssh.example.com
6 service: ssh://localhost:22
7 - service: http_status:404

Configuration Explanation:

  • tunnel: The name of your tunnel (must match the name used during creation)
  • credentials-file: Absolute path to the tunnel credentials JSON file created in the previous step
  • ingress: Routing rules that define traffic handling
    • First rule: Routes traffic from ssh.example.com to the local SSH daemon on port 22
    • Last rule: Catch-all rule (required) that returns 404 for any unmatched traffic - this prevents unauthorized service exposure

Important: Replace <tunnel-uuid> with your actual tunnel UUID from the credentials filename, and update youruser with your actual username. The service ssh://localhost:22 points to your local SSH server.

Copy the configuration to the system directory for the cloudflared service:

bash
1sudo cp ~/.cloudflared/config.yml /etc/cloudflared/config.yml

Also copy the credentials file to the system directory:

bash
1sudo mkdir -p /etc/cloudflared
2sudo cp ~/.cloudflared/<tunnel-uuid>.json /etc/cloudflared/

Then update the config.yml credentials path to point to the system location:

bash
1sudo nano /etc/cloudflared/config.yml

Change the credentials-file path to: /etc/cloudflared/<tunnel-uuid>.json

Route DNS

DNS routing creates a CNAME record in your Cloudflare DNS that points your subdomain to the tunnel. This tells Cloudflare's edge network to route traffic for this hostname through your specific tunnel.

bash
1cloudflared tunnel route dns ssh-tunnel ssh.example.com

This command automatically creates a CNAME record in your Cloudflare DNS dashboard pointing ssh.example.com to <tunnel-uuid>.cfargotunnel.com. You can verify this in your Cloudflare DNS settings.

Behind the scenes: Cloudflare's edge servers receive requests for ssh.example.com, identify the associated tunnel via the CNAME, and forward the traffic through the secure tunnel to your server.

Install as a Service

Running cloudflared as a systemd service ensures automatic startup on boot and recovery from failures. The service runs with system privileges and maintains a persistent connection to Cloudflare's edge network.

bash
1sudo cloudflared service install
2sudo systemctl start cloudflared
3sudo systemctl enable cloudflared

Service management commands:

  • Check status: sudo systemctl status cloudflared
  • View logs: sudo journalctl -u cloudflared -f
  • Restart: sudo systemctl restart cloudflared

The service automatically establishes four redundant connections to different Cloudflare edge locations for high availability and load balancing.

Client Connection Methods

Connect via SSH

Method 1: Using Cloudflared Access Proxy

This method uses cloudflared on the client side to proxy your SSH connection through Cloudflare's network. No SSH configuration changes are required.

bash
1cloudflared access ssh --hostname ssh.example.com

Note: This command requires cloudflared to be installed on your client machine as well.

For seamless integration with standard SSH workflows, configure SSH to automatically use cloudflared as a proxy. This allows you to use normal SSH commands like ssh ssh.example.com while the connection is transparently routed through Cloudflare.

First, ensure cloudflared is installed on your client machine, then configure SSH:

bash
1nano ~/.ssh/config

Add the following configuration

bash
1Host ssh.example.com
2 ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
3 User yourlinuxuser

Configuration Parameters:

  • Host: The hostname pattern this configuration applies to
  • ProxyCommand: Command that SSH executes to establish the connection (cloudflared handles the proxying)
  • %h: SSH variable that expands to the hostname you're connecting to
  • User: Your username on the remote server (optional, can be specified at connection time)

Usage: After configuration, simply run: ssh ssh.example.com

SSH will automatically invoke cloudflared to establish the secure tunnel connection.

Security Considerations

Encryption: All traffic through Cloudflare Tunnel is encrypted end-to-end with TLS. SSH provides an additional layer of encryption, creating double encryption for your connection.

Authentication: You can enhance security by enabling Cloudflare Access policies, requiring additional authentication (email OTP, SSO, etc.) before allowing SSH connections.

Firewall: Since the tunnel establishes outbound connections only, your SSH service never needs to be exposed to the internet. Your firewall can block all inbound SSH connections while maintaining remote access capability.

Audit Logging: Cloudflare Tunnel logs connection attempts and traffic patterns, providing visibility into access patterns and potential security events.

Troubleshooting

Connection Issues:

  • Verify the cloudflared service is running: sudo systemctl status cloudflared
  • Check DNS propagation: nslookup ssh.example.com
  • Review logs for errors: sudo journalctl -u cloudflared -n 50

Common Problems:

  • 404 errors: DNS may not be propagated yet (wait 5-10 minutes), or the hostname doesn't match the ingress rule
  • Connection refused: SSH service may not be running on the target server
  • Certificate errors: Ensure the credentials file path in config.yml is correct and accessible

Additional Resources