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 -tsshd -T/etc/ssh/sshd_config.dPermitRootLoginPasswordAuthenticationAllowGroupssemanage portsystemctl reload sshd
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
| Keyword | Typical secure value | Effect |
|---|---|---|
PermitRootLogin | no (RHEL default: prohibit-password) | Whether root may log in over SSH |
PasswordAuthentication | no | Disable passwords, keys only |
PubkeyAuthentication | yes | Allow key login |
AllowUsers / AllowGroups | alex dana / sshusers | Only these may log in |
MaxAuthTries | 3 | Attempts per connection |
Port | 22 or other | TCP port to listen on (repeat to listen on several) |
ClientAliveInterval | 300 | Drop idle connections |
The safe procedure
- Change the setting in your drop-in file.
sshd -tchecks syntax; no output means OK. A bad file would stop sshd from starting.systemctl reload sshdapplies it without dropping existing sessions. A port change needsrestart.sshd -Tshows the effective value.- 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:
| Layer | What to do | If forgotten |
|---|---|---|
| sshd | Port 2222 | Nothing listens |
| SELinux | semanage port -a -t ssh_port_t -p tcp 2222 | sshd cannot bind: Permission denied, the service fails to start |
| firewalld | firewall-cmd --permanent --add-port=2222/tcp + --reload | Connection 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.
- Warm-up
What is sshd really doing?
Show the effective values of
permitrootlogin,passwordauthentication,pubkeyauthenticationandport. 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.dshows files such as50-redhat.conf.Hint
sshd -Tprints 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 - 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 fromnode2thatssh root@node1is refused whilessh alex@node1still works.Check your result
sudo sshd -T | grep permitrootloginprintspermitrootlogin no.ssh root@node1fails withPermission denied.ssh alex@node1works.Hint
sshd uses the first value it reads for each keyword, and reads files in
sshd_config.din alphabetical order, so name yours01-local.conf. Always runsshd -tbeforesystemctl 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 - Practice
Keys only, no passwords
First confirm that key login works for
alexfrom 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@node1ends withPermission denied (publickey,...).ssh alex@node1 truestill works with the key.Hint
PasswordAuthentication noin 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 - Practice
Only members of one group may connect
Allow SSH logins only for members of the group
sshusers. Create the group and addalex, but notdana. 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 trueworks;ssh dana@node1fails.journalctl -u sshdshowsUser dana from ... not allowed because none of user's groups are listed in AllowGroups.Hint
AllowGroups sshusersin 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 - 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 sshdshows both 22 and 2222.ssh -p 2222 alex@node1 trueworks from node2. Without the SELinux label sshd fails to bind (Permission denied); without the firewall rule the connection times out.Hint
(1)
Port 2222in sshd config (keepPort 22too while testing), (2)semanage port -a -t ssh_port_t -p tcp 2222, (3)firewall-cmd --permanent --add-port=2222/tcpand--reload.semanageis inpolicycoreutils-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