Harden your public webserver (Part 3: shutdown SSH)

This is the third part of a three-part tutorial.

Closing this port will make your server unmaintainable. Allowing access only to your private IP address will not work if you use dynamic IPs. Unfortunately, this is common practice in Germany, for example.‌‌Regardless of that, you won't be able to access your beloved server while on vacation.

The solution I am showing here allows you to access your server from anywhere.

For this we use Wireguard. Wireguard is a free, modern and very secure VPN solution that is also very resource-efficient (unlike OpenVPN, for example).

Install and configure wireguard

For simplicity's sake, I'm showing this for Linux. The configuration on Windows, Mac, iOS and Android differs only slightly.

Install Wireguard on your server:

apt install wireguard

Install wireguard on all of your clients.

On your server:

Generate wireguard keys:

#/etc/wireguard/ 
wg genkey | tee privatekey | wg pubkey > publickey
generate wireguard keys

Your private key is in

/etc/wireguard/privatekey

Your public key is in:‌

/etc/wireguard/publickey

Create a preshared key:

#/etc/wireguard/ 
cd /etc/wireguard/
mkdir psk
cd psk
wg genpsk > presharedkey

Your preshared key is in /etc/wireguard/psk/presharedkey

Create a server configuration:

#/etc/wireguard/wg0.conf 
[Interface] 
Address = 172.80.11.1/28 
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT;\
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT;\
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820 
PrivateKey = <copy and paste from \
# /etc/wireguard/privatekey on your server> 

# Client configuration 
[Peer] PublicKey = <publickey from client config> 
PresharedKey = <copy and paste from \
/etc/wireguard/psk/presharedkey>
AllowedIPs = 172.80.11.2/32
PersistentKeepalive = 25
/etc/wireguard/wg0.conf 

Enable portforwarding:

# /etc/sysctl.conf
# comment out the following line
net.ipv4.ip_forward=1
/etc/sysctl.conf

And activate your changes:

sysctl -p

Start and enable your config:

systemctl enable --now wg-quick@wg0.service

On your client:

If not already done. Install wireguard on the client as well.‌‌Repeat the step from above to generate the keys.

Create a client configuration:

#/etc/wireguard/wg0.conf 
[Interface]
PrivateKey = <copy and paste from /etc/wireguard/privatekey\
# on your clinet> 
Address = 172.80.11.2/28 

# Server is your clients peer
[Peer] 
PublicKey = <copy and paste from /etc/wireguard/publickey\
#from your server> 
PresharedKey = <copy and paste from \
#/etc/wireguard/psk/presharedkey from your server> 
AllowedIPs = 172.80.11.1/32 
Endpoint = <YourServerIP>:51820
PersistentKeepalive = 25
/etc/wireguard/wg0.conf

You have now created the missing client key and can add it to the configuration on the server side:

PublicKey = <publickey from client config>
/etc/wireguard/wg0.conf on your server‌

Start and enable your config:

systemctl enable --now wg-quick@wg0.service

Test connection:

You should see a connection on both with the following command:

wg show

If not, check if the firewall blocks port 51820/UPD or if the Wireguard service is running:

# should show show active
systemctl status wg-quick@wg0

Often there is a typo in the configuration.

Configure SSH on your server

This part is a little tricky.

Please be sure to check beforehand whether wireguard can establish a stable connection. Otherwise you will be locked out.

It's best to make a backup/snapshot beforehand.

The SSH process must be started after the Wireguard process.

We can set this via SystemD. ‌‌Add wg-quick@wg0.service as required to sshd.service.

#/etc/systemd/system/sshd.service 
[Unit] 
Description=OpenBSD Secure Shell server 
Documentation=man:sshd(8) man:sshd_config(5) 
After=network.target auditd.service 
# make SSH start after Wireguard 
Requires=wg-quick@wg0.service 
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run 

[Service] 
EnvironmentFile=-/etc/default/ssh 
ExecStartPre=/usr/sbin/sshd -t 
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS 
ExecReload=/usr/sbin/sshd -t 
ExecReload=/bin/kill -HUP $MAINPID 
KillMode=process Restart=on-failure 
RestartPreventExitStatus=255 
Type=notify 
RuntimeDirectory=sshd 
RuntimeDirectoryMode=0755 

[Install] 
WantedBy=multi-user.target Alias=sshd.service
/etc/systemd/system/sshd.service‌

This setting is not to be executed on the client!

Now change your SSH listener:

# /etc/ssh/sshd_config 
ListenAddress 172.80.11.1
/etc/ssh/sshd_config

Restart SSH:

systemctl restart ssh.service

Your server is now only accessible from outside via Wireguard over SSH.

Since SSH only listens to this IP, you don't need to do any firewall settings. You can leave port 22 open for SSH worldwide.