I’ve decided to expose ssh to the homelab server to the Internet. I had been afraid of doing so for months, but it’s about time and I think I’m ready.
As a background, my homelab server already exposes web services such as blog and Immich via Caddy. Caddy and Immich are running in docker containers on Ubuntu on Proxmox.
Before exposing ssh, I needed to fortify the server to protect it from bot and other malicious attacks.
Use a strong password
My password was weak but I was used to type it. I changed to a stronger password so that even if a hacker logged in, he/she would have hard time doing sudo.
passwd
Configure ssh settings
This step is the core of hardening ssh.
sudo vim /etc/ssh/sshd_config
Here, I changed to:
- Change ssh port from 22 to some random number between 1024 to 65535
- Disable root login
- Disable password login (ie, key login only)
- Disable keyboard-interactive auth
Port <port_number>
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no # keep as no
Configure firewall
Ubuntu doesn’t use firewall by default. Enable it. Allow only the ssh port, 443/https and 80/http.
sudo ufw allow <ssh_port>/tcp # allow ssh port
sudo ufw allow 443/tcp
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw reload
sudo ufw status verbose
You should see something like this:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
<ssh_port>/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
<ssh_port>/tcp (v6) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443/tcp (v6) ALLOW IN Anywhere (v6)
Open ssh port in the router
Then, open ssh port in the router. On my tplink router portal, go to Advanced > NAT Forwardig > Virtual Servers > Add.
Try ssh
ssh <user>@<external IP or hostname> -p <ssh_port>
If it worked, add config in ~/.ssh/config
Host <some_name>
HostName blog.achiwa.co.uk
Port <ssh_port>
User <user_name>
IdentityFile ~/.ssh/<private_keyfile>
Install and configure fail2ban
Further fortify ssh and Caddy with fail2ban. If you fail to ssh or login to Immich 5 times in 10 min, you’ll be banned for an hour (configurable).
Install fail2ban with apt.
sudo apt install fail2ban
Update a jail for ssh
ssh is covered by fail2ban by default, but create a new .local file to overwrite the setting.
sudo vim /etc/fail2ban/jail.local
[sshd]
enabled = true
port = <ssh_port>
logpath = /var/log/auth.log
maxretry = 5
findtime = 10m
bantime = 1h
Create a jail for Caddy
sudo vim /etc/fail2ban/jail.d/caddy.local
[caddy]
enabled = true
port = http,https
filter = caddy
logpath = /var/log/caddy/access.log
maxretry = 10
findtime = 10m
bantime = 1h
Create a Caddy filter
sudo vim /etc/fail2ban/filter.d/caddy.conf
[Definition]
failregex = <HOST> -.*"(GET|POST).*(wp-login|xmlrpc|admin|login).*" (401|403|404)
ignoreregex =
From this failregex setting, I understand that fail2ban doesn’t intercept incoming access to Caddy, but that it gets notified of an access attempt from the access log being updated.
Restart fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
Well…
I also looked into cloudflare tunnel which is part of their free offering. It looked more secure as it doesn’t expose my external IP. But I decided to go without cloudflare as they might start charging the feature in the future. Maybe not, but who knows?
Let’s ask Copilot (AI) if I should do more. Here’s the prompt:
You are a linux security expert. I expose ssh, a static blog site and Immich on my homelab server. For the blog site and Immich, I use Caddy in docker. The server runs Ubuntu on Proxmox. As security measures, I did:
- Open 443, 80 and ssh_port at my wifi router
- Allow 443, 80 and ssh_port only with firewall on Ubuntu
- For ssh:
- Change port to a random 5-digit number
- Disable password login
- Disable root login
- Configure fail2ban for ssh and caddy
What else should I do?
And here’s an excerpt from the response:
My honest take You’ve already done the essentials. The biggest next-level improvements are:
- Put SSH behind WireGuard or Tailscale
- Use Cloudflare as a reverse proxy
- Harden Docker (non-root, limited capabilities)
- Add monitoring + offsite backups
Maybe I’d want to go for monitoring next.