| |

LFCS 112 ๐Ÿง SSH Server โ€” Hardening

โฑ๏ธ Estimated read: 9 min | |

The previous chapter established the configuration syntax and the lockout risk. This chapter covers the directives that reduce the attack surface after the basic setup is working. Hardening is not a single change but a layered strategy: eliminate the most attractive attack vectors (root login, password authentication), reduce the window for brute force (MaxAuthTries, LoginGraceTime), and close auxiliary services that are enabled by default but rarely needed (X11 forwarding, TCP forwarding, agent forwarding). The LFCS exam tests the recommended values and the reasoning behind each directive. The critical prerequisite remains: key-based authentication must be verified before password authentication is disabled.

Key point: Hardening reduces the attack surface, not the attack likelihood. A hardened server still receives scan traffic. The directives below make the scan traffic less useful to an attacker: no root account to guess, no password to brute force, no forwarding feature to abuse as a tunnel.


Why SSH hardening requires a layered approach

The root login problem. The root account exists on every Linux system and has unlimited privileges. An attacker who compromises root has full control of the machine. PermitRootLogin no forces every administrator to log in as a normal user and escalate with sudo. The attacker must now guess a valid username and a valid key, not just the universal root account . The alternative prohibit-password allows root login with a key but blocks password login. The stricter no is the correct choice for most servers.

The password brute force problem. A password can be guessed. A 4096-bit RSA key cannot. PasswordAuthentication no eliminates the entire class of credential-stuffing and brute-force attacks against SSH. The trade-off is that the key must be installed and working before the directive is applied. The lockout risk is real: a misconfigured key means no access at all .

The authentication attempt problem. Even with keys, an attacker can open connections and try keys. MaxAuthTries 3 limits the number of authentication attempts per connection. After three failures, the connection is terminated. LoginGraceTime 30 limits how long an unauthenticated connection can remain open before the server disconnects it. Both directives reduce the resources an attacker can consume .

The forwarding problem. SSH is not just a shell. It can forward X11 sessions, TCP ports, and SSH agents. X11 forwarding exposes the client’s display server. TCP forwarding can be used to tunnel arbitrary traffic through the server, bypassing firewalls. Agent forwarding exposes the client’s SSH agent to the server, which means a compromised server can use the client’s keys to connect to other hosts. All three are disabled by default in a hardened configuration unless explicitly needed .

The trade-off. Every hardening directive closes a feature. X11 forwarding is useful for remote GUI applications. TCP forwarding is useful for database tunnels. Agent forwarding is useful for bastion hosts. The correct approach is to disable everything and enable only what the specific server requires. A web server does not need X11. A database server does not need TCP forwarding. A bastion host needs agent forwarding, but a production application server does not.


a. The baseline hardening directives

The recommended baseline is a set of directives that can be placed in a drop-in file under /etc/ssh/sshd_config.d/. This preserves the vendor’s base configuration and makes the hardening changes easy to identify and revert .

# /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PermitEmptyPasswords no
IgnoreRhosts yes
HostbasedAuthentication no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2

PermitRootLogin no prevents direct root login. PermitEmptyPasswords no blocks accounts with empty passwords. IgnoreRhosts yes and HostbasedAuthentication no disable the legacy host-based trust mechanisms. PubkeyAuthentication yes enables key authentication. PasswordAuthentication no and KbdInteractiveAuthentication no disable password and keyboard-interactive authentication. X11Forwarding no, AllowAgentForwarding no, and AllowTcpForwarding no disable the forwarding features. MaxAuthTries 3 limits authentication attempts. LoginGraceTime 30 limits the unauthenticated connection window. ClientAliveInterval 300 and ClientAliveCountMax 2 disconnect idle sessions after 10 minutes (300 seconds ร— 2) .


b. Restricting access by user and source

The AllowUsers directive limits which accounts can authenticate. The AllowGroups directive limits by group membership. Both are more restrictive than the default, which allows any valid account to attempt authentication.

# Only allow specific users
AllowUsers alice bob

# Or restrict to a group
AllowGroups ssh-users

The Match block allows conditional configuration. A common pattern is to allow password authentication for a specific emergency account while disabling it globally.

PasswordAuthentication no

Match User emergency
    PasswordAuthentication yes

The Match block must appear at the end of the configuration. Directives before the first Match are global; directives inside a Match apply only when the condition is true .

The PerSourcePenalties option, introduced in OpenSSH 9.8, automatically penalizes client addresses that repeatedly fail authentication. A client that fails three times is blocked for a duration that increases with repeated offenses. The PerSourcePenaltyExemptList option exempts trusted management addresses from the penalties .


c. Validating the hardened configuration

The validation workflow is identical to the previous chapter. sshd -t checks the syntax. sshd -T displays the effective configuration. The service is reloaded with systemctl reload, not restart, to preserve the current session.

# Validate syntax
sudo sshd -t

# Display effective values
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|maxauthtries|x11forwarding'

# Reload (preserves connections)
sudo systemctl reload sshd

The sshd -T output is the authoritative check. A directive in the drop-in file that is overridden by the base configuration or by an earlier drop-in file will not appear as expected. The grep filter shows the actual effective values .


Complete Example Session

# ============================================
# PART 1: BACK UP AND PREPARE
# ============================================

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo mkdir -p /etc/ssh/sshd_config.d
# ============================================
# PART 2: CREATE THE HARDENING OVERRIDE
# ============================================

sudo tee /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'
PermitRootLogin no
PermitEmptyPasswords no
IgnoreRhosts yes
HostbasedAuthentication no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
# ============================================
# PART 3: VALIDATE SYNTAX
# ============================================

sudo sshd -t
# No output: syntax is valid.
# ============================================
# PART 4: CHECK EFFECTIVE VALUES
# ============================================

sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|maxauthtries|x11forwarding'
# permitrootlogin no
# passwordauthentication no
# maxauthtries 3
# x11forwarding no
# ============================================
# PART 5: RELOAD THE SERVICE
# ============================================

sudo systemctl reload sshd
# ============================================
# PART 6: TEST IN A NEW SESSION
# ============================================

ssh user@server
# Key-based login succeeds.
ssh -o PubkeyAuthentication=no user@server
# Permission denied (publickey).
# ============================================
# PART 7: VERIFY ROOT LOGIN IS BLOCKED
# ============================================

ssh root@server
# Permission denied (publickey).
# ============================================
# PART 8: CHECK FORWARDING IS DISABLED
# ============================================

ssh -X user@server
# X11 forwarding request failed on channel 0.

The eight parts trace the hardening workflow: backup, drop-in creation, syntax validation, effective value check, reload, authentication test, root login test, and forwarding test.


Quick Reference

Baseline Hardening Directives

DirectiveRecommended ValuePurpose
PermitRootLoginnoBlock direct root login
PermitEmptyPasswordsnoBlock empty password accounts
PasswordAuthenticationnoDisable password login
KbdInteractiveAuthenticationnoDisable keyboard-interactive
PubkeyAuthenticationyesEnable key authentication
MaxAuthTries3Limit attempts per connection
LoginGraceTime30Limit unauthenticated window
X11ForwardingnoDisable X11 forwarding
AllowAgentForwardingnoDisable agent forwarding
AllowTcpForwardingnoDisable TCP forwarding

Access Restriction Directives

DirectivePurpose
AllowUsersLimit to specific users
AllowGroupsLimit to specific groups
Match UserConditional configuration

Validation Commands

CommandPurpose
sshd -tValidate syntax
sshd -TDisplay effective configuration
systemctl reload sshdApply without dropping connections

Best Practices

โœ… Do This:

sudo sshd -t
<!-- Validate syntax before reload -->                # โœ…
ssh user@server
<!-- Test key login in a separate session first -->   # โœ…
PermitRootLogin no
<!-- Force sudo escalation -->                        # โœ…

โŒ Don’t Do This:

# Disable password auth before testing key auth
<!-- Locks out the administrator -->                  # โŒ
# Enable X11 or TCP forwarding on a web server
<!-- Expands the attack surface unnecessarily -->     # โŒ
# Use PermitRootLogin prohibit-password on a hardened host
<!-- no is stricter and preferred -->                 # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Locked out after hardeningPassword disabled before key testedConsole recovery; re-enable password
Directive ignoredOverridden by drop-in fileCheck sshd -T
X11 forwarding still worksX11Forwarding not set to noAdd to drop-in file
MaxAuthTries not enforcedDirective placed after Match blockPlace before any Match
Root login still possiblePermitRootLogin not appliedVerify with sshd -T

Real-World Examples

1. Disable Root Login

PermitRootLogin no

2. Disable Password Auth

PasswordAuthentication no

3. Limit Auth Attempts

MaxAuthTries 3

4. Reduce Grace Time

LoginGraceTime 30

5. Disable X11

X11Forwarding no

6. Disable TCP Forwarding

AllowTcpForwarding no

7. Restrict Users

AllowUsers alice bob

8. Emergency Match Block

Match User emergency
    PasswordAuthentication yes

9. Validate

sudo sshd -t

10. Check Effective Values

sudo sshd -T | grep passwordauthentication

Visual

Attack Surface Reduction

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Default SSH                                 โ”‚
โ”‚  โ”œโ”€โ”€ Root login allowed                      โ”‚
โ”‚  โ”œโ”€โ”€ Password auth allowed                   โ”‚
โ”‚  โ”œโ”€โ”€ X11 forwarding enabled                  โ”‚
โ”‚  โ”œโ”€โ”€ TCP forwarding enabled                  โ”‚
โ”‚  โ””โ”€โ”€ Unlimited auth attempts                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚ Hardening
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Hardened SSH                                โ”‚
โ”‚  โ”œโ”€โ”€ Root login: no                          โ”‚
โ”‚  โ”œโ”€โ”€ Password auth: no                       โ”‚
โ”‚  โ”œโ”€โ”€ X11 forwarding: no                      โ”‚
โ”‚  โ”œโ”€โ”€ TCP forwarding: no                      โ”‚
โ”‚  โ””โ”€โ”€ Max auth tries: 3                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Validation Workflow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Edit sshd_config.d/99-hardening.conf        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  sshd -t (syntax check)                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  sshd -T | grep (effective values)           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  systemctl reload sshd                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Test key login in new session               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Drop-in location/etc/ssh/sshd_config.d/
Root loginPermitRootLogin no
Password authPasswordAuthentication no
Key authPubkeyAuthentication yes
Max attemptsMaxAuthTries 3
Grace timeLoginGraceTime 30
X11 forwardingX11Forwarding no
TCP forwardingAllowTcpForwarding no
Agent forwardingAllowAgentForwarding no
Validationsshd -t then sshd -T

Key takeaways:

  • Hardening is layered. Disable root login, disable password authentication, limit authentication attempts, and close forwarding features. Each layer removes an attack vector .
  • The drop-in file is the correct location. /etc/ssh/sshd_config.d/99-hardening.conf preserves the vendor’s base configuration and makes the hardening changes identifiable and revertible .
  • Key authentication must be verified before password authentication is disabled. The lockout risk is the primary failure mode. Test in a separate session before applying the change .
  • sshd -T is the authoritative check. Drop-in files can override each other. The effective configuration is visible only through sshd -T .
  • Forwarding features should be disabled unless required. X11 forwarding, TCP forwarding, and agent forwarding expand the attack surface. A web server does not need them .

Remember: Hardening reduces the attack surface, not the attack volume. The scan traffic continues. The difference is that the scan finds no root account to guess, no password to brute force, and no forwarding feature to abuse. The PermitRootLogin no directive removes the highest-value target. The PasswordAuthentication no directive removes the entire class of credential attacks. The MaxAuthTries 3 directive limits the attempts. The drop-in file contains the changes. The sshd -t validates the syntax. The sshd -T confirms the effective values.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!