SSH over VPN
Managing servers on the public internet brings a lot of security threats with it. RDP is so bad it isn't recommended to even expose it to the public internet. With SSH, things are a bit more safe, but only after you perform a set of SSH hardening tasks, e.g.:
- Change default SSH port (security through obscurity, only helps reduce number of bots attempting to connect)
- Disable root login
- Disable password login, only use PubKey authentication
- Setup 2FA for SSH
- etc.
In order to add another layer of protection, you can setup VPN to protect your SSH connections (while leaving your website available to the public internet).
Having SSH over VPN shouldn't make you forget about the aforementioned SSH hardening steps. VPN is just one of them.
Wireguard Server setup
Install Wireguard on the server
Debian offers fairly outdated version of Wireguard in the official stable
repository. At the time of writing, stable
has 20210223
, while testing
has 20210424
. You can check available version with apt
.
$ apt-cache policy wireguard
Output:
wireguard:
Installed: (none)
Candidate: 1.0.20210223-1
Version table:
1.0.20210223-1 500
500 https://ftp.sh.cvut.cz/debian bullseye/main amd64 Packages
Install from stable
You can decide if you want to use the stable
or testing
version. To install stable
, simply type:
$ sudo apt install wireguard
Install from testing
To install from the testing
repo, you have to perform the steps shown HERE. TLDR, add testing to /etc/apt/sources.list
, adjust apt
preferences in /etc/apt/preferences
and update apt
. Once you have enabled the testing repository with appropriate priorities, you can install Wireguard. To see if the preferences are set properly, try to see what apt would install. By default, everything should be installed from stable, unless specified otherwise.
$ apt-cache policy wireguard
Candidate line shows what would be installed.
wireguard:
Installed: (none)
Candidate: 1.0.20210223-1
Version table:
1.0.20210424-1 -10
-10 http://deb.debian.org/debian testing/main amd64 Packages
1.0.20210223-1 900
900 https://ftp.sh.cvut.cz/debian stable/main amd64 Packages
Now specify that you want to install from testing:
$ apt-cache policy -t testing wireguard
Candidate should now point to the newer version:
wireguard:
Installed: (none)
Candidate: 1.0.20210424-1
Version table:
1.0.20210424-1 990
990 http://deb.debian.org/debian testing/main amd64 Packages
1.0.20210223-1 900
900 https://ftp.sh.cvut.cz/debian stable/main amd64 Packages
Finally, to install from testing (-t
to specify repository to install from):
$ sudo apt install -t testing wireguard
Generate keys
Regardless of the version you have installed, it's time to create the configuration, directories and keys. We will be working in a restricted directory (only readable by root), so elevate shell:
$ sudo su
(root)$
Create directories
We need some place to store our public, private and preshared keys. All of these should be located in /etc/wireguard
directory and only readable by root
. The directory structure itself is up to you, I prefer having directory keys
in /etc/wireguard
for server public and private key and psk
directory for preshared keys.
Set umask
to 077
to create files and directories only readable by root:
(root)$ umask 077
Create the aforementioned directories in /etc/wireguard
:
(root)$ mkdir keys psk
Generate server keys
(root)$ cd keys
(root)$ wg genkey | tee wg0_private.key | wg pubkey > wg0_public.key
The wg genkey
command generates a random private key in base64 and prints it to standard output (terminal). The output is instead redirected to tee
, which both prints it to stdout (terminal), but also saves it into a file wg0_private.key
. The private key printed to stdout is then piped (|
symbol) to wg pubkey
, which calculates the public key and prints it in base64 to stdout from a corresponding private key (the one we redirected to it with the pipe), lastly redirect the public key from stdout to a file wg0_public.key
You will now have two files in /etc/wireguard
directory. One containing public, the other private key.
wg0_private.key wg0_public.key
Create server configuration
Still under umask 077
and in the root
shell, create a config file in /etc/wireguard
directory. The name of the file will be used as the name of the interface.
(root)$ touch wg0.conf
Replace server_private_key
with the private key of your server (content of wg0_private.key
in /etc/wireguard/keys
). ListenPort
is the port you want Wireguard to listen on and Address
specifies the subnet for the Wireguard tunnel. I chose /29
due to small subnet size, because more hosts aren't necessary.
[Interface]
Address = 10.20.20.1/29
ListenPort = 51895
PrivateKey = server_private_key
Client Wireguard setup
Download and install Wireguard
Download the Windows Installer from the official Wireguard website.
Run the wireguard-installer.exe
and after a few moments, this window should appear:
Click the arrow next to Add Tunnel
and select Add empty tunnel...
Wireguard will automatically generate a private and public key for this client.
*note: all keys shown will be destroyed afterwards and are only used for demonstration purposes
Configure the client
Under the PrivateKey
line, add Address
, which will be the address of the Wireguard client within the Wireguard tunnel.
[Interface]
PrivateKey = private_key
Address = 10.20.20.2/29
Even though I am refering to the server as Wireguard server and my management station as the client, the truth is Wireguard doesn't care about that. In it's eyes, both are on the same level and there's no difference between them, they are both peers.
To configure the server, add [Peer]
block to the client config.
[Peer]
PublicKey = server_public_key
AllowedIPs = 10.20.20.1/32
Endpoint = publicIP:port
PublicKey
– Copy the content of/etc/wireguard/wg0_public.key
to this lineAllowedIPs
– IP of the server within the tunnel, use/32
to allow only the server and not the whole subnetEndpoint
– Public IP of the server and port where Wireguard is running (same asListenPort
in the serverwg0.conf
)
The entire client config should now look like this:
[Interface]
PrivateKey = private_key
Address = 10.20.20.2/29
[Peer]
PublicKey = server_public_key
AllowedIPs = 10.20.20.1/32
Endpoint = publicIP:port
Add client to the server configuration
To finish setting up the tunnel, add the client's public key and IP address to the wg0.conf
in /etc/wireguard
.
[Peer]
PublicKey = public_key_from_client
AllowedIPs = 10.20.20.2/32
You have to copy the public key from the client to the server config. Make sure you don't switch up the public and private keys. Always copy the public key only! The private key should never leave the device it was created on.
wg0.conf
will look something like this right now:
[Interface]
Address = 10.20.20.1/29
ListenPort = 51895
PrivateKey = server_private_key
[Peer]
PublicKey = public_key_from_client
AllowedIPs = 10.20.20.2/32
Restart interface to refresh config
After editing wg0.conf
, always run wg-quick
to restart the interface, otherwise the changes to the configuration won't propagate.
$ sudo wg-quick down wg0
[#] ip link delete dev wg0
$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.20.20.1/29 dev wg0
[#] ip link set mtu 1420 up dev wg0
The tunnel should be now set up. Try pinging the server from the client. If it isn't working, make sure firewall like iptables
isn't blocking the communication and you have ICMP enabled.
Secure with Preshared keys
To add additional level of security, you can generate prehared keys for the client, which act as a shared secret and provide another layers of cryptographical security.
Generate Preshared keys
(root)$ cd /etc/wireguard/psk
(root)$ umask 077
(root)$ wg genpsk > windows10_client.psk
Add to server config
Open wg0.conf
and add PresharedKey
to the [Peer]
block for the client:
[Peer]
PublicKey = public_key_from_client
PresharedKey = preshared_key
AllowedIPs = 10.20.20.2/32
Add to client config
[Interface]
PrivateKey = private_key
Address = 10.20.20.2/29
[Peer]
PublicKey = server_public_key
PresharedKey = preshared_key
AllowedIPs = 10.20.20.1/32
Endpoint = publicIP:port