SSH into your private machines from anywhere, for free, using Cloudflare Tunnel
Outcome
By the end of this post, you’ll be able to run: ssh $machine_name
from anywhere in the internet-connected planet, using SSH keys. It is free and requires no future maintainance. This guide uses Cloudflare Tunnel, a service by Cloudflare with a free-tier. It will filter traffic to your machines through Cloudflare’s network, including authenticating you. Because of this, your machines won’t directly be exposed to threat actors and “1337 haxors”.
This was discussed on Hacker News.
Motivation
You might have a machine running in your local network and want to access from anywhere in the world. Alternative solutions include:
- configuring your router to port forward to your specific machine or setting your machine as a demilitarized zone (DMZ). Disadvantages: your home router’s IP address might change, so you’ll need to use the new IP address. Each port is also limited to a single machine, so you’d have to choose a different port for a different machine. Your DNS record also publishes the IP address of your router, which can be used to locate you - somewhat unreliably, depending on who you are.
- Many alternatives are listed in anderspitman/awesome-tunnelling, including Cloudflare Tunnel. Many of these require configuration, and the last time I tried ngrok, I couldn’t choose the domain name on the free tier.
I already use Cloudflare for this website (Cloudflare Pages), web analytics, configuring DNS records, and registering my domains (with no additional fee on top of the Administrator fee, e.g. Verisign for .com
and Nominet for .uk
). Hey UK domain owners, Cloudflare Registrar supports uk
and co.uk
TLDs now 😉.
So I used cloudflared
to do this, but realised there was no guide written that takes me step by step, to success. There was a Cloudflare official guide, but some of the steps are not necessary, confusing (e.g. limiting the “application” to 1 month) and missing the config.yml
schema.
Pre-requisites 🦑
- A free Cloudflare account
- Understand Cloudflare Tunnel, by following Set up your first tunnel. You’ll end up with a website added to Cloudflare,
cloudflared
installed and logged in on your machine, and a high level understanding of Cloudflare Tunnel. You don’t have to do step 4 (create configuration file) or later.
Setup SSH server on your target machine 🎯
- Skip if you’ve done this already, for example, you can already SSH into your machine.
- For Ubuntu/Debian,
- Install service: run
sudo apt install openssh-server
- Start service: run
sudo systemctl start ssh
- Schedule service (to start on machine start-up):
sudo systemctl enable ssh
- Install service: run
- For other operating systems, try using search engines.
Quick check: setup a tunnel and SSH 🧪
- This step is just a quick check that you can SSH into the machine, before you waste time configuring stuff.
- Start a cloudflare tunnel: run
cloudflared tunnel --hostname machine.example.com --url ssh://localhost:22
- Reminder: edit
example.com
to one you have added on Cloudflare, and updatemachine
to whatever you prefer.cloudflared
will create a CNAME record calledmachine.example.com
.
- Reminder: edit
- SSH into your machine using password based authentication: run
ssh [email protected]
- Reminder: Update username and host.
Configure the cloudflared
service 🧑💻
- We want this service to be running all the time (daemon), not just when I run the command. Also, some of the steps in Cloudflare: Run as a service are broken.
- Create a tunnel:
cloudflared tunnel create $tunnel_name
, where$tunnel_name
is a name you can use to reference it. - Update a DNS record:
cloudflared tunnel route dns $tunnel_id phanteks.orth.uk
- Create a config file:
~/.cloudflare/config.yml
, containing:
tunnel: $tunnel_id
credentials-file: /home/$user/.cloudflared/$tunnel_id.json
url: ssh://localhost:22
- Run the tunnel:
cloudflared tunnel run --url=ssh://localhost:22 $tunnel_name
- Install service:
sudo cloudflared --config /home/ben/.cloudflared/config.yml service install
- Start service: run
sudo systemctl start cloudflared
- Schedule service (to start on machine start-up):
sudo systemctl enable cloudflared
Configure SSH client 💻
- Create a keypair. The private key is for your client and the public key should be given to anyone who wants to authenticate you (the machine you want to SSH to, Phanteks). My keypair is called
phanteks
(private key) andphanteks.pub
(public key):- Run
ssh-keygen
. Before that, preferablycd ~/.ssh
, so that your generated keys are stored there.
- Run
- Copy over the public SSH key (
$KEYNAME.pub
): Runssh-copy-id -i ~/.ssh/KEYNAME.pub $machine_domain
- Add the following to
~/.ssh/config
. Create the file if it doesn’t exist.
# This only works when you are on the same local network
Host phanteksLocal
HostName Phanteks.local
IdentityFile ~/.ssh/phanteks
User ben
Port 22
# Cloudflare Tunnel
Host phanteks
HostName phanteks.orth.uk
IdentityFile ~/.ssh/phanteks
ProxyCommand cloudflared access ssh --hostname %h
User ben
Secure SSH server 🚔
- Optional: Change the port used by SSH, since the default port is well known (22). You’ll also have to update
~/.ssh/config
and~/.cloudflared/config.yml
with this new port.- Warning ‼️:
cloudflared
actually duplicates this file into/etc/cloudflared/config.yml
first when you install the service. If you change~/.cloudflared/config.yml
, be sure to:- manually update the file, or
- delete the file (run
sudo rm /etc/cloudflared/config.yml
) and reinstall the service (runsudo cloudflared --config /home/$USER/.cloudflared/config.yml service install
).
- Warning ‼️:
- Optional: Disable password authentication
- This is usually done to improve security: mitigate random password attacks, but this is already mitigated with Cloudflare Tunnels.
- But in the spirit of “defense in depth”, we should still disable it. We should assume attackers might still come from within your local network, including successfully authenticating with Cloudflare Tunnel
- Restart the services: run
sudo systemctl restart ssh cloudflared
Usage: connect to machine 🤞
- Run
ssh phanteks
Conclusion 💌
- Maybe there is a way to automate this so all machines I use in the future will be set up automatically. Thankfully, I am not building a server farm at home or moving homes very often, so this is a very low priority. I was just taking a quick look at Ansible yesterday.
- Question: do you use a different tool which require no maintenance or cost to run?