2 of 2

SSH Key AuthenticationLesson 2 of 2

Securing the SSH server

Change sshd's behaviour with drop-in files, forbid root and password logins, limit who may connect, and move the port the right way.

  • sshd -t
  • sshd -T
  • /etc/ssh/sshd_config.d
  • PermitRootLogin
  • PasswordAuthentication
  • AllowGroups
  • semanage port
  • systemctl reload sshd

45 min5 exercises

After this lesson you can

  • Read the effective sshd configuration, and make changes in a drop-in file that takes effect.
  • Forbid root login and password authentication, and restrict logins to a group, without locking yourself out.
  • Move sshd to another port with the SELinux label and firewall rule it needs.

The idea

sshd is the door to the machine, so its configuration matters more than almost any other. Principles:

  • Log in as a named user and become root with sudo, so actions are attributed to a person.
  • Prefer keys to passwords.
  • Allow only the users who need it.
  • Never test a change on your only session.

Where the settings live

The main file is /etc/ssh/sshd_config. On RHEL it begins with Include /etc/ssh/sshd_config.d/*.conf, so any file ending in .conf in that directory is read first, in alphabetical order. Files shipped with the system include 50-redhat.conf and the crypto-policy file. Keep your own changes in your own drop-in rather than editing the package-owned main file.

The settings that matter

KeywordTypical secure valueEffect
PermitRootLoginno (RHEL default: prohibit-password)Whether root may log in over SSH
PasswordAuthenticationnoDisable passwords, keys only
PubkeyAuthenticationyesAllow key login
AllowUsers / AllowGroupsalex dana / sshusersOnly these may log in
MaxAuthTries3Attempts per connection
Port22 or otherTCP port to listen on (repeat to listen on several)
ClientAliveInterval300Drop idle connections

The safe procedure

  1. Change the setting in your drop-in file.
  2. sshd -t checks syntax; no output means OK. A bad file would stop sshd from starting.
  3. systemctl reload sshd applies it without dropping existing sessions. A port change needs restart.
  4. sshd -T shows the effective value.
  5. Test with a new connection while the old one stays open. Only then close the old one.

Changing the port

Three separate layers each have an opinion:

LayerWhat to doIf forgotten
sshdPort 2222Nothing listens
SELinuxsemanage port -a -t ssh_port_t -p tcp 2222sshd cannot bind: Permission denied, the service fails to start
firewalldfirewall-cmd --permanent --add-port=2222/tcp + --reloadConnection times out

Keep Port 22 as well until the new one is proven.

Try it

sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|port) '
ls /etc/ssh/sshd_config.d/
port 22
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication yes
50-redhat.conf

(without-password is the older spelling of prohibit-password: root may use keys but not a password.)

Forbid root

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/01-local.conf
sudo sshd -t && sudo systemctl reload sshd
sudo sshd -T | grep permitrootlogin
PermitRootLogin no
permitrootlogin no

From node2:

ssh root@node1
ssh alex@node1 hostname
root@node1: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
node1.lab.example

Keys only

Prove key login works first. If you switch off passwords before your key is installed, nobody can log in.

echo 'PasswordAuthentication no' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
sudo sshd -t && sudo systemctl reload sshd
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no alex@node1
alex@node1: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

Only one group

sudo groupadd sshusers
sudo usermod -aG sshusers alex
echo 'AllowGroups sshusers' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
sudo sshd -t && sudo systemctl reload sshd
sudo journalctl -u sshd -n 2 --no-pager
Nov 03 14:51:10 node1 sshd[8812]: User dana from 192.168.122.51 not allowed because none of user's groups are listed in AllowGroups
Nov 03 14:51:10 node1 sshd[8812]: Connection closed by invalid user dana 192.168.122.51 port 51842 [preauth]

The log line states the cause precisely, which is why the first place to look when someone cannot log in is journalctl -u sshd.

A second port, all three layers

sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo firewall-cmd --permanent --add-port=2222/tcp && sudo firewall-cmd --reload
printf 'Port 22\nPort 2222\n' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
sudo sshd -t && sudo systemctl restart sshd
ss -tlnp | grep sshd
LISTEN 0 128   0.0.0.0:22   0.0.0.0:* users:(("sshd",pid=9020,fd=3))
LISTEN 0 128   0.0.0.0:2222 0.0.0.0:* users:(("sshd",pid=9020,fd=5))
LISTEN 0 128      [::]:22      [::]:* users:(("sshd",pid=9020,fd=4))
LISTEN 0 128      [::]:2222    [::]:* users:(("sshd",pid=9020,fd=6))
ssh -p 2222 alex@node1 hostname
node1.lab.example

If sshd refused to start after the restart, journalctl -xeu sshd shows error: Bind to port 2222 ... Permission denied, the SELinux label was missing. If the client waits and times out, the firewall port was forgotten.

Going further

sshd -T -C user=dana,host=node2,addr=192.168.122.51 shows what would apply for a specific connection, including Match blocks (Match User alex followed by indented overrides, or Match Address 192.168.122.0/24). Banner /etc/issue.net shows a warning before login. ssh-audit and the RHEL crypto policy (update-crypto-policies --show) control which ciphers are allowed. fail2ban-style tools, or firewalld rich rules with rate limits, reduce brute-force noise. After any change, ss -tlnp | grep sshd and systemctl status sshd confirm it is running as you intended.

Practice

0 of 5 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    What is sshd really doing?

    Show the effective values of permitrootlogin, passwordauthentication, pubkeyauthentication and port. Then list the files that contribute to the configuration.

    Check your result

    sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|port) ' prints four lines. ls /etc/ssh/sshd_config.d shows files such as 50-redhat.conf.

    Hint

    sshd -T prints the configuration after all includes and defaults. The main file is /etc/ssh/sshd_config.

    Solution
    sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|port) '
    ls -l /etc/ssh/sshd_config.d/
    grep -n '^Include' /etc/ssh/sshd_config
  2. Practice

    Forbid logging in as root

    Prevent direct root login over SSH on node1, using a drop-in file that definitely takes precedence over the files that ship with the system. Check the syntax, reload the service, and test from node2 that ssh root@node1 is refused while ssh alex@node1 still works.

    Check your result

    sudo sshd -T | grep permitrootlogin prints permitrootlogin no. ssh root@node1 fails with Permission denied. ssh alex@node1 works.

    Hint

    sshd uses the first value it reads for each keyword, and reads files in sshd_config.d in alphabetical order, so name yours 01-local.conf. Always run sshd -t before systemctl reload sshd.

    Solution
    echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/01-local.conf
    sudo sshd -t && sudo systemctl reload sshd
    sudo sshd -T | grep permitrootlogin
    # from node2
    ssh root@node1
    ssh alex@node1 true
  3. Practice

    Keys only, no passwords

    First confirm that key login works for alex from node2. Then disable password authentication, reload, and show that a client that offers only a password is refused while the key login keeps working. Explain why the order of these steps matters.

    Check your result

    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no alex@node1 ends with Permission denied (publickey,...). ssh alex@node1 true still works with the key.

    Hint

    PasswordAuthentication no in the drop-in. Keep another session open while you reload.

    Solution
    ssh -o PreferredAuthentications=publickey alex@node1 true     # prove keys work first
    echo 'PasswordAuthentication no' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
    sudo sshd -t && sudo systemctl reload sshd
    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no alex@node1
    ssh alex@node1 true
  4. Practice

    Only members of one group may connect

    Allow SSH logins only for members of the group sshusers. Create the group and add alex, but not dana. Verify that alex can log in and dana is refused, and read the log line that explains the refusal.

    Check your result

    ssh alex@node1 true works; ssh dana@node1 fails. journalctl -u sshd shows User dana from ... not allowed because none of user's groups are listed in AllowGroups.

    Hint

    AllowGroups sshusers in the drop-in. A user's membership is read at login, and the primary group counts too.

    Solution
    sudo groupadd sshusers
    sudo usermod -aG sshusers alex
    echo 'AllowGroups sshusers' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
    sudo sshd -t && sudo systemctl reload sshd
    # from node2: ssh alex@node1 true ; ssh dana@node1 true
    sudo journalctl -u sshd -n 3 --no-pager
  5. Challenge

    Run sshd on port 2222

    Make sshd listen on TCP 2222 in addition to 22. Do everything required so it works with SELinux enforcing and the firewall on, test from node2 with ssh -p 2222, and only then consider removing port 22. List the three separate things that must change, and what error you get if you forget each one.

    Check your result

    ss -tlnp | grep sshd shows both 22 and 2222. ssh -p 2222 alex@node1 true works from node2. Without the SELinux label sshd fails to bind (Permission denied); without the firewall rule the connection times out.

    Hint

    (1) Port 2222 in sshd config (keep Port 22 too while testing), (2) semanage port -a -t ssh_port_t -p tcp 2222, (3) firewall-cmd --permanent --add-port=2222/tcp and --reload. semanage is in policycoreutils-python-utils.

    Solution
    sudo dnf install -y policycoreutils-python-utils
    sudo semanage port -a -t ssh_port_t -p tcp 2222
    sudo firewall-cmd --permanent --add-port=2222/tcp
    sudo firewall-cmd --reload
    printf 'Port 22\nPort 2222\n' | sudo tee -a /etc/ssh/sshd_config.d/01-local.conf
    sudo sshd -t && sudo systemctl restart sshd
    ss -tlnp | grep sshd
    # from node2
    ssh -p 2222 alex@node1 true