A server is a potential target for a wide range of attacks. Hackers may try to get access with brute force attacks. Or they try to take the server down with DDoS attacks, to name some possibilities. Many server distros nowadays come with preinstalled tools like SSH or a firewall to reduce such attack vectors.

This post will show how to mitigate the risk of attacks. We will change the SSH default config to limit access to the server. Besides, we will set up a firewall to limit exposure.

Scenario

This scenario assumes the following:

  • You have a public/private SSH key-pair
  • You own a server
  • The server runs on a clean CentOS7 install
  • The server has a root user and a standard user

(You can use any distro you like. I am just recommending CentOS for the baked-in firewall and SELinux. Just make sure you have OpenSSH and a firewall installed.)

Boot the host and login as root.

Preparing the host

Before we can start, CentOS7 requires us to enable ethernet connectivity on the host machine.

nmcli c up eth0

This will only last for this session. To make it permanent, we need to set ONBOOT in /etc/sysconfig/network-scripts/ifcfg-eth0 to YES. Or use a CLI command:

nmcli c mod eth0 connection.autoconnect yes

Update the SELinux policy

With an established internet connection it is time to take care of security. We want to prevent root logins and only permit logins via SSH. As an extra measure, we change the SSH port from 22 to 1234.

Before we can do this, SELinux needs to know the new port. Otherwise, it enforces port 22 as SSH port, and the daemon can not restart with another one.

Changing SELinux settings requires the policycoreutils-python package:

yum install -y policycoreutils-python

Now we can update the default SSH port:

semanage port -a -t ssh_port_t -p tcp 1234

Enable the new port

For us to use SSH the firewall needs to allow communication via port 1234. Therefore, we create a new custom ruleset based on the original SSH ruleset.

cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh-custom.xml

Then we change the port value in ssh-custom.xml to 1234:

<port protocol="tcp" port="1234"/>

With that in place, remove the old ruleset, and add the custom one to the firewall service. Use --permanent to keep the setting after reboots.

firewall-cmd --permanent --remove-service='ssh'
firewall-cmd --permanent --add-service='ssh-custom'
firewall-cmd --reload

Use the new port

The next step is to tell the SSH daemon to use port 1234. We can change the port in /etc/ssh/sshd_config.

# Run ssh on a non-standard port:
Port 1234  #Uncomment and change port

Restart the daemon to apply the change:

service sshd restart

Before we can start testing, we need to add our public key to .ssh/authorized_keys in the user’s home dir.

echo <PUB_KEY> >> /home/users/<USER>/.ssh/authorized_keys
chown -R <USER>:<USER> /home/users/<USER>/.ssh/authorized_keys
chmod 700 /home/users/<USER>/.ssh
chmod 600 /home/users/<USER>/.ssh/authorized_keys

Create a ~/.ssh/config on the client machine to shorten calls to the SSH command:

Host <HOSTNAME>
	HostName 123.456.789.0 # or domain, if available
	User <USER>
	Port 1234

Then set the permissions:

chmod 600 ~/.ssh/config

Now we can establish an SSH connection:

ssh <HOSTNAME>

# Use 'Crtl + D' to close the connection in the terminal.

Limit login to SSH

After a successful test, we apply the last changes in /etc/ssh/sshd_config to get some additional security:

### uncomment, change, or add if not present

# disable root and password logins
PermitRootLogin no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
PasswordAuthentication no

# enable public key authentication
PubkeyAuthentication yes

# only allow defined users access
AllowUsers <USER> # multiple users are separated by space

# use the latest protocol
Protocol 2

# Complete login within X seconds, here 2min
LoginGraceTime 120

# logout after idle timeout in X and Y missed packages, here 2min
ClientAliveInterval 1m
ClientAliveCountMax 2

# disable graphic shell
X11Forwarding no

# enforcing correct files and permissions
StrictMode yes

This will leave SSH as the only way to log in and only permit <USER> to log in.

The SSH daemon requires a last restart to apply the settings.

Reduce open ports

With a secure login, it’s time to see what else is open for communication:

netstat -tulnp

The output should look similar to this:

tcp    0   0   0.0.0.0:1234   0.0.0.0:*   LISTEN   -
tcp6  0   0   :::443              :::*             LISTEN   -
tcp6  0   0   :::1234            :::*             LISTEN   -

Port 1234 is our SSH port, and since this is a server, we also allow secure traffic on port 443.

If you see more ports than this, use the firewall to close them:

firewall-cmd --zone=<ZONE> --remove-port=<PORT>/tcp --permanent

Be careful: Your hoster may use some ports for a web shell. Double-check before closing them.

Congratulations, you did it. You hopefully got a better understanding of SSH and the firewall features along the way.

Going one step further

You could improve on the topic by:

Let me end this excursion with a quote for your meditation

Security is always excessive until it’s not enough. — Robbie Sinclair