You spin up a Linux machine, and the next thing you want is SSH access from anywhere.
Then the connection dies after about two minutes of silence.
If you were like me, your first hypothesis would always be “the campus/network firewall is blocking SSH.” Well, more often that’s the case if you are working in a public/corporate network.
However, on the other side, it isn’t the firewall that is blocking the protocol, but it is complacency: evicting idle connections.
What’s the difference? A blocked protocol needs a different transport. An idle timeout just needs you to never go silent.
In this post, I will guide you on a quick way to keep the SSH connection alive.
I. The setup
The solution presented below uses no public ports and no outbound SSH filtering.
The solution is this: relay SSH over HTTPS using Cloudflare’s free Quick Tunnel.
This setup does not require you to have an account.
1. Install cloudflared on the server
Pick the method that matches your distro:
Debian / Ubuntu
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install cloudflared
Fedora / RHEL / CentOS
curl -fsSL https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
sudo dnf install cloudflared # or yum
Arch Linux
sudo pacman -Syu cloudflared
Any other distro
Download the binary from the official downloads page.
2. Start the tunnel on the server
cloudflared tunnel --url ssh://localhost:22 --protocol http2
It prints a temporary *.trycloudflare.com URL. Keep that terminal open.
3. Connect from another Linux machine
Install cloudflared on the client the same way (or just grab the binary), then:
ssh -o ProxyCommand="cloudflared access ssh --hostname %h" $USER@YOUR-LINK.trycloudflare.com
And if everything goes well, you notice that it works… until you stop typing for about a minute. Then it vanishes.
II. But why does it drop?
Stateful packet filters (and Cloudflare’s edge) treat silent connections as stale and reap them.
To overcome this, there are two fixes that have worked for me:
Fix 1: Client side
We want the client to never be idle.
Add this helper to ~/.bashrc (or ~/.zshrc) on the client:
cf-ssh() {
local host="${1%.trycloudflare.com}"
host="${host//[^[:alnum:]-]/}"
ssh -o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-o ProxyCommand="cloudflared access ssh --hostname %h" \
"$USER@${host}.trycloudflare.com"
}
Usage:
cf-ssh some-random-words.trycloudflare.com
# or just
cf-ssh some-random-words
-
ServerAliveInterval 15sends a small encrypted packet every 15 seconds of silence. -
ServerAliveCountMax 3gives up only after three missed replies.
To the filter, the session now looks busy.
Fix 2: Server side
If the server is not at fault, let’s make the client busy. The other end can time out too.
Edit /etc/ssh/sshd_config on the server:
ClientAliveInterval 15
ClientAliveCountMax 3
Then reload:
sudo systemctl restart sshd
# or on systems without systemd:
sudo service ssh restart
Both sides now communicate with each other. Both of them are now busy. The network does not filter, nor does the tunnel ever see a dead link.
And there you go, the problem is solved.
NOTE:
Caution: trycloudflare.com URLs are semi-public and change every time you restart the tunnel. Use key-based authentication only, never passwords, and shut the tunnel down when you’re finished.




Top comments (0)