Exposing virtual machines to the internet it's not an easy task. Often managed using an SSH connection we must secure our machines as much as we can against hacker attacks.
Let's go through some often and ease configurations that will make it an attacker hard if trying to access your machine.
💬 In this issue
- Creating a non-root user and use eventually elevated privileged
- Enabling UFW Uncomplicated Firewall
- Unattended Upgrades
- Hardening the SSH Access
👉 Information used in our lab
User: root
Password: generated with a password generator
Description: Our Root user
User: secondary
Password: generated with a password generator
Description: Non Root user for daily basis access
Operating System: Ubuntu Server 20.04 - but it also working for different flavors of Linux.
Creating a non-root user
First thing first: Never use the root user for non-privilege tasks.
To accomplish that let's create a non-root user and use the sudo command to elevate our privileges only when necessary.
Logged as the root user let's create a non-root user called secondary. Execute the following command in our bash shell
.
# create a regular user with name secondary
$ adduser secondary
Creating the Secondary User
We should answer some questions (starting with the password) and we shall have the user.
Now we have a user with fewer privileges. Next step is to grant the use sudo command when necessary.
Add to Sudo Group
Run usermod
command to include secondary into sudo group.
# includes the secondary into sudo group
$ usermod -aG sudo secondary
Now we're able to use sudo in front of a command to run as administrative rights.
Enabling UFW Uncomplicated Firewall
UFW is the default firewall system (under the hood is the great IPTables) in Ubuntu Server.
With the use of ufw it's very simple to allow or block a port.
# allow port 22
$ sudo ufw allow 22
Don't forget to use sudo we should be using the secondary user instead of the root
# Deny all incoming by default, allow outgoing, and finally enable the ufw service
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
$ sudo ufw enable
Deny all incoming by default, allow outgoing, and finally enable the ufw service
# display status of ufw
$ sudo ufw status
Displaying Status of ufw Service
Great. Now we have ufw up and running.
Adding Unattended Upgrades
Unattended-Upgrade is a service that regularly checks and updates the OS packages.
The purpose of unattended-upgrades is to keep the computer current with the latest security (and other) updates automatically.
https://wiki.debian.org/UnattendedUpgrades
# Install unattended upgrades and enable it
$ sudo apt install unattended-upgrades
$ sudo systemctl status unattended-upgrades.service
After installing just enable the service and we should be good.
The default configuration will automatically retrieve new package updates for most of the packages included in the Ubuntu repositories.
Hardening the SSH Configuration
Most Linux servers are administered remotely using SSH using OpenSSH technology. Which is the default SSH server software used within Ubuntu.
This is one of the major vectors of attack by hackers on Linux exposed on the internet. Let's work on improving the configurations of our SSH server.
Using SSH keys instead of passwords
SSH keys are the recommended way to log into any Linux server environment remotely.
Back in our client machine (not on the server) let's create a pair of keys - private and public keys. This also works on Windows 10/11.
$ ssh-keygen
You can specify a different location to save the keys if you like.
Generating the Public and Private Keys
The next step is to copy the SSH key to the Ubuntu server.
$ cat /home/{username}/.ssh/id_rsa.pub
Copy the content output to the transfer area.
Back into our server
# create .ssh if not created
$ sudo mkdir -p ~/.ssh
# copy the public key to authorized_keys file
$ sudo echo public_key_string >> ~/.ssh/authorized_keys
# remove all permissions on .ssh folder
$ sudo chmod -R go= ~/.ssh
# adding read permission to our user - our scenario is secondary - on .ssh folder
$ chown -R secondary:secondary ~/.ssh
Now we have the public key generated by our client machine in the server in the property location and with permissions in place.
This is it. Now we're able to log in using the recently created SSH Key. Let's try.
# ssh -i {key path}
$ ssh -i $HOME/.ssh/id_rsa secondary@{SERVER-IP}
Using SSH Key to log into the Server
Changing default Port for SSH
Open the SSH configuration file sshd_config with a text editor.
$ sudo nano /etc/ssh/sshd_config
Look for the entry Port 22
- it could be commented on. Replace it with the desired value - port between 1024 and 65536
. . .
Port 2222
. . .
Port Configuration
Save and Restart the OpenSSH service.
$ sudo systemctl restart ssh
Restarting the SSH service
Disable Password Authentication over SSH
Change property to PasswordAuthentication no
. . .
PasswordAuthentication no
. . .
Password Authentication Configuration
Disable Root User Login
Change property to PermitRootLogin no
. . .
PermitRootLogin no
. . .
Password Authentication Configuration
Tweaking some other sshd_config Configurations
Open the config file sshd_config one more time
$ sudo nano /etc/ssh/sshd_config
. . .
MaxAuthTries 3
. . .
. . .
PermitEmptyPasswords no
. . .
Test and Restart the SSH Service to reflect the new configuration
# test the configuration changes
$ sudo sshd -t
# restart the service and load the new configuration
$ sudo systemctl restart ssh
Using ssh config file on SSH Client
Adding a config file in the .ssh
folder on the ssh client allows us to define the key, host, user, and other configurations by default per connection.
In our Client's machine
Create the config file in ~/.ssh/config location
# THIS IS OUR CLIENT - NOT OUR UBUNTU SERVER
$ nano ~/.ssh/config
Past the following content.
# connection name
Host ssh-server
# host address (ip or hostname)
Hostname 192.168.0.100
# port of ssh server
Port 2222
# user
User secondary
# private key to use
IdentityFile ~/.ssh/id_rsa_locaweb_secondary
config file
Then we can connect using only the connection name
$ ssh ssh-server
Final thoughts
Wow, finally we made it. That is enough!
Of course, we can keep going hardening it even more adding more protection - it never stops.
Share in the comments section what you would do to make it even better or different.
Top comments (8)
Thanks for this excellent start in hardening an Internet-exposed system!
In case folks don't know, the Center for Internet Security provide well-known, trusted benchmark guides for hardening many systems connected to the Internet, including Linux, eg:
cisecurity.org/benchmark/distribut...
(I'm not affiliated with CIS, these benchmarks are widely used, including by myself across multiple of my previous roles in information security)
There are also automated tools to check systems against CIS benchmarks (and NIST ones!), particularly for Unix-like systems. I've previously used Lynis, others are available, eg:
That is very nice. Thanks for sharing
Good but an rsa key is no longer recommended edt25519 is preferred
ssh-keygen -t ed25519
Also the recommended method to copy the key on server is ssh-copy-id command
I have just read that in GitHub article about generating ssh keys. Use ed25
That is great. I’ll give a try.
Thanks for that
Really helpful article gives lots of Knowledge .
Hi, when you changed the default SSH port from 22 to 2222, you need to allow also inbound connections from port 2222 in UFW right?
You’re correct.