A well defined firewall configuration that only permits necessary traffic is essential to securing any server. In this guide we will go over the basic thought process that goes into defining a firewall configuration and apply this logic to secure our Cardano nodes.

What you will need?

For our purposes, we will be using the UFW package.

sudo apt update
sudo apt install ufw

Once installed, you have everything needed to get started.

What is UFW?

UFW is a package that abstracts away a lot of the complexity that normally goes into configuring a firewall using iptables by providing an interface through which simple language can be used to describe a desired set of firewall rules to be applied. It’s all in the name really as UFW is an acronym for Uncomplicated Firewall.

Understanding Your Requirements

The first thing that needs to happen before you start configuring your firewall is to form a solid understanding of how you need your server to be able to communicate. Once this is established, you can work your way backwards from these requirements to lock everything down except the very specific channels you need to allow.

As the scope of this guide focuses on setting up Cardano nodes, we will work with the assumption that your server topology will need to accommodate relay nodes, which are advertised to the network when setting up and registering your pool, and a core node, which handles block production and only needs to communicate directly with the relays. Beyond this, we will also assume that we will need to be able to connect remotely to our various servers via SSH, so we will need to keep that port open as well. There are likely other ports you will want to open up when setting up other features, such as active monitoring through Prometheus/Graphana, but we’ll keep things simple for now as you can always apply additional rules later as they become necessary. Configuring the ports on which nodes need to communicate with the Cardano network and access open via SSH is a good place to start.

Now that we understand what communication we need to allow, the last thing we need before we can proceed is an understanding of the IP & port combinations that we will be using in our topology. For the purposes of this guide we will use up the topology below.

We will also assume that SSH has been configured on port 2001 (the default is 22, but this can be changed, as outlined in our earlier guide).

Getting Started 

Let’s begin by setting up UFW’s default policies.

sudo ufw default deny incoming
sudo ufw default allow outgoing

This is a good place to start as now, by default, all traffic leaving the server is allowed, however, and perhaps more importantly, nothing is able to get in. It’s also worth noting that our firewall policy won’t take effect until we turn on UFW, so nothing has gone into effect yet.

Next, let’s allow communication on the port we’ve configured for SSH. We can also specify the TCP protocol in this case which SSH uses to communicate.

sudo ufw allow 2001/tcp

That’s all that’s needed. You will now be able to access your server via the configured SSH port. These rules can be applied to the relays and the core. Next we’ll look at our relay and core configurations in isolation as they differ slightly.

Configuring Relays

For this next step, we’ll assume we are working with “Relay 1” from the earlier diagram. Given that we will need other nodes on the network (our own, as well those outside our control), it will suffice to simply open up the port on which we are running our node. So let’s open up port 3001 for the TCP protocol.

sudo ufw allow 3001/tcp

For “Relay 2” we just do the same thing, but with port 3002, as per our earlier topology diagram.

Configuring Core

Since our core is listening for network traffic on port 3000, we will open it up. However, unlike our relay nodes, we know specifically which IPs will be communicating with our core. As a result, we can further lock down our permissions to only allow our specific relay nodes to communicate with our core.

sudo ufw allow from 10.138.0.4 proto tcp to any port 3000
sudo ufw allow from 10.148.0.4 proto tcp to any port 3000 

The above specifies the IP (from xx.xx.xx.xx), protocol (proto xxx) and the port (to any port xxxx) since we are able to be very specific with our relay connections.

Turning It All On

Now that everything is configured, it’s just a matter of turning it all on.

sudo ufw enable

And that’s it. You now have a firewall configured on your server that only allows the incoming traffic you need to be handling. Everything else stays out.

If you want to review your firewall settings, just do the following after it’s been enabled.

sudo ufw status

Doing so will list out the current firewall configuration as long as UFW is enabled. If UFW is disabled, the command will not display the configured rules and will only report that UFW is not active.

Editing Firewall Rules

Removing rules can easily be done in one of two ways, by either specifying which numbered rule in the list you want to delete, or by writing up the rule you want to remove with a delete prefix ahead of it. For example, let’s consider the following rules table output from when using the sudo ufw status command while UFW is enabled.

2001/tcp          ALLOW      Anywhere     
3000/tcp          ALLOW      10.138.0.4   
3000/tcp          ALLOW      10.148.0.4   
2001/tcp (v6)     ALLOW      Anywhere (v6)

Let’s say we no longer wanted to keep port 2001 open, then all we need to do to remove the rule is the following.

sudo ufw delete allow 2001/tcp

Doing so would remove both rules which currently open up port 2001.

We can also remove rules by their numeric position in the list, which would be easier than writing up a delete command matching a longer rule we would like to see removed. Assuming that the initial list of rules listed above is unchanged, if we wanted to remove port 3000 access for 10.148.0.4, the following command would do the trick.

sudo ufw delete 3

Inputing a numeric deletion command will generate a prompt detailing the rule that is about to be deleted to confirm your intentions as an added safety.

Additional Considerations

The above only looks at the basics of using UFW to configure a firewall on your server. It’s also likely that the network on which your machines reside has a firewall managing traffic moving in and out the local network as well. While configuration of that particular firewall is outside the scope of this guide, it’s good to apply the same thought process to those firewall settings as well, by evaluating what network traffic is leaving from and coming into your local network. It’s simply a matter of identifying how permissive you need to be with your firewall settings so that you can do exactly what you need to be doing, and nothing more.

Conclusion

You now have a properly configured firewall on your server that will block any incoming traffic except for Cardano network traffic on the ports your nodes are configured to listen to, as well as the port you’ve configured for SSH. You also have the tools to you need to make additional changes to your UFW configuration as your needs change and grow. Remember, it’s always best to keep everything you don’t need locked down, and to make adjustments later as you need them.

The most important parts of a setting up a secure server are often the simplest. However, to the uninitiated or less experienced, these details can often be overlooked. These gaps in knowledge are easily corrected when it becomes clear that there is a better way to do things. Such is the purpose of this guide in which we discuss SSH security.

What is SSH?

SSH is a simple protocol by which communication between two computers can be securely verified and the traffic exchanged is secured cryptographically. Use of encryption in online communications is particularly useful when dealing with situations where the material being exchanged is sensitive in nature. Given the need to ensure that your operation remains secure from outside threats, having a good understanding of this simple yet effective tool is a necessity. This guide walks you through setting up SSH effectively as part of setting up and securing your operation for communication through SSH.

What you will need

We will be using the OpenSSH implementation of SSH. On the machine that will be initiating the connection you will need to ensure the you have the openssh-client installed. On the machine to which you will be connecting, it is required that the openssh-server be installed.

sudo apt update
# For the client
sudo apt install openssh-client
# For the server
sudo apt install openssh-server

Keep in mind these packages may already be installed, specifically with virtual machines which must provide you with an option to connect to them remotely out of the box. Once you have these packages installed in the right places, you have everything you need to get started.

Windows client users will need to supplement information in this guide with another to ensure they have everything they need to get started on the client side. This guide appears to be a good place to start as SSH was recently built into Windows 10.

Initial Connections

Once the correct packages are installed the command format to reach any server is as follows.

# Basic command format: ssh <user>@<host>
ssh myusername@93.74.21.78

A successful connection will often result in a password request (assuming one has been set) and a successful password entry then grants access to the CLI of the server to which you have connected. Boom! You are in and ready to work.

If you are unable to connect, this could point to a number of issues. The most common of which can be remedied by confirming you have entered a correct user@host location and ensuring that your firewall has not been set to block connections on port 22 which is the port on which SSH is configured to listen by default.

This is a good start, but we need to do a bit more to improve security further.

Changing The Default Port

Defaults are a great feature for any software. They provide a means to easily onboard new users, but at the same time these defaults can make it easier for a potential attacker to find and probe your system. In the initial setup above, we worked with the assumption that the default connection via SSH would take place over port 22. This same assumption is made every day by bad actors operating automated systems designed to probe at commonly exposed openings. SSH on port 22 being one such opening.

SSH probing by an unknown user (as taken from /etc/log/auth.log)

The above type of probing happens all the time, but it’s generally done blindly, as it’s far easier for an attacker to test the default than it is to guess which of the other 65534 ports on which SSH might currently be listening.

The configuration file which determines the port on which SSH listens by default is located in the /etc/ssh folder.

cd /etc/ssh/
sudo nano sshd_config

This file contains a number of useful configuration parameters which can be toggled to harden your SSH security policy. For now we’ll focus on adjusting the port location, which should be visible commented out with the default value.

#Port 22

To change the default, simply uncomment the line and change the port number. Then save an exit to commit the change to the file.

Port 2001

It is generally advisable to select a port on which no other services are known to be assigned to listen. You can refer to this Wikipedia page for a full list of port values, as well as the services on which certain ports are known to listen to by default. You’ll notice SSH at 22, as well as other common Internet protocols such as protocols such as HTTP at 80 and DNS at 53.

Once any change is made to the sshd_config file, for them to take effect, your SSH server must be restarted.

sudo systemctl restart ssh
sudo systemctl status ssh

The first command executes the restart. The second is entirely optional, but allows you to confirm the functional status of the SSH service so you can confirm it was restarted correctly.

Once you’ve made this change, make sure you remember the new port which you set for SSH to listen, as you will now need to specify this information explicitly to the command line when performing an SSH connection from your client machine.

ssh myusername@93.74.21.78 -p 2001

The default argument set for the port number is (you guessed it) 22, so now that we are on port 2001, as per our example, then we must specify this new piece of information in order for our connection to succeed. This is done by specifying the port with -p.

SSH Keys

An SSH key is an additional layer of security tied the authentication process of a connection between a client and a server. It consists of generating a private-public key pair, which, once installed, is then used to securely establish connections between both systems, without the need for password prompting on the part of the server. Once in place the only systems that will be able to connect to the remote server are systems with valid SSH keys installed on them. As an additional layer of protection, passwords are set on these keys which the client requests from the user prior to being able to establish the connection with the server.

Security is improved as only someone in possession of a valid private key matching a public key installed on the server can gain access to it via SSH. This also means it is important to keep these keys safe and securely backed up, in case there is ever a need to use them with a different machine as the client.

The first step is to generate a private-public key pair with the below command.

ssh-keygen

Executing this command begins a series of prompts through which the key pair is generated.

First you are asked to name your key. By default it will be named id_rsa and will be placed in the ~/.ssh/ default location. It is advisable to pick a different name to avoid accidentally overwriting an existing pair of keys in the future. Then select a password you will remember, which you will be prompted to enter each time you want to use the key to connect to the remote server (unlocking your ability to use it).

The result should be a pair of keys with the name you specified located in the default folder where ssh keys are generally stored, located at ~/.ssh. Notice that one key has .pub at the end of it. This is your public key. The other file is your private key.

The next step is to now install your newly generated public key onto the server to which you will be connecting. To do this we will use the ssh-copy-id functionality built into OpenSSH.

ssh-copy-id -i ~/.ssh/serverxyz_rsa.pub -p 2001 myusername@93.74.21.78

The above command specifies the location of the keyfile via the -i argument. By default it is id_rsa. We also have to specify the port we want to use, as we’ve changed it from the default 22. You will be prompted for the remote server password prior to being able to establish the connection and transfer the public key file. Once the public key has been transferred, you will now be able to log in using your SSH key as follows.

ssh -p 2001 -i ~/ssh/serverxyz_rsa myusername@93.74.21.78

You will then be prompted for the password specified when creating the key. This step authenticates on the client side, that you have the right to use the private key to establish a connection with the remote server, on which you’ve just installed the public key. This step can be circumvented for convenience by adding your private key to the ssh-agent as follows.

ssh-add ~/ssh/serverxyz_rsa

If the command is accepted, you will be prompted for your key password again, but now, so long as the agent remains active, you will no longer need to provide your password to establish future SSH connections with the key. If the above command failed, it’s possible that an SSH agent is not currently available. If this is the case, you can spin one up using the ssh-agent command and try the above command again.

Setting Up A Config File

This step isn’t necessary, but it wraps everything we’ve done so far together really nicely by adding a lot of convenience when it comes to managing many sets of credentials for connecting to many different servers. You can collect and store all your information in a config file that SSH then uses to allow you to connect via SSH using a shortened custom name for the connection you wish to establish. To do this, you will need to create a file named config under the ~/.ssh/ folder, if one doesn’t exist there already. 

cd ~/.ssh/
nano config

Here’s how the connection we’ve setup in the previous examples would look like as information stored in the config file.

Host serverxyz
        HostName 93.74.21.78
        IdentityFile ~/.ssh/serverxyz_rsa
        User myusername
        Port 2001

You can have as many of these entries as you like inside your config file. Once setup you can establish the SSH connection using the custom name you provided in the config file under Host. In our case that would be serverxyz.

ssh serverxyz

And voila! You are now connected to your server using all of the customized settings we’ve setup in this guide, using a single custom keyword. It doesn’t get simpler than that.

It’s a beautiful thing when you can increase the security of your server setup while also making it easier to establish the connections when you need them.

Additional SSH Configuration Settings

Last, but certainly not least is the matter of additional security settings which you can configure in the sshd_config file which, as you’ll recall, is the same file we updated earlier when changing our default SSH port.

cd /etc/ssh/
sudo nano sshd_config

It’s recommended to disable password based logins as we do not require or use these anymore now that we are setup to authenticate with SSH keys.

AuthenticationMethods publickey
PubkeyAuthentication yes
PermitEmptyPasswords no
PasswordAuthentication no
PermitRootLogin without-password

You can find most of the above settings commented out throughout the file, you can uncomment these entries and set the appropriate values listed above. Remember that you will need to restart your ssh service for these changes to take effect.

sudo systemctl restart ssh
sudo systemctl status ssh

Keep in mind that this is by no means an exhaustive list of configuration settings which you can modify, but for the purposes of this guide, it’s a good place to start when setting up SSH for the first time. Additional research is recommended.

In Conclusion

You’ve now setup your server to operate securely using generated SSH keys, disabling password based access, and changed the port on which the SSH service is listening for traffic. You’ve also setup a config file that captures all these changes and allows you to use a custom name with the SSH command to establish your connections. By doing so you’ve improved the usability and security of your server setup. Well done!