Sandbox99 Chronicles

SSH Server Configuration Guide: From Defaults to Defense

SSH server

Written by Jose Mendez

Hi, I’m Jose Mendez, the creator of sandbox99.cc. with a passion for technology and a hands-on approach to learning, I’ve spent more than fifteen years navigating the ever-evolving world of IT.

Published May 22, 2025 | Last updated on May 22, 2025 at 12:05PM

Reading Time: 7 minutes

Introduction

If you’ve felt overwhelmed by the Ansible playbooks, Bash scripts, or Python snippets in my previous blogs—don’t worry. This time, we’re focusing solely on the sshd_config file for SSH hardening and configuration. No complex automation, just straightforward settings to help you secure your SSH server with confidence.

The configuration shared here follows modern security best practices, aiming for strong authentication, minimized attack surfaces, and better session management—without the intimidation.

You can implement this configuration directly by copying the content below and pasting it into your sshd_config file, typically located at /etc/ssh/sshd_config. However, before proceeding, it is crucial to create a backup of your existing sshd_config file. This will allow you to easily revert to your previous setup if any issues arise or if you decide this configuration doesn’t perfectly suit your needs. A simple way to back up your file is:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# Go back here if you want to restore or revert your config
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config

Once backed up, you can proceed with updating your sshd_config file. After saving the changes, remember to restart your SSH service (e.g., sudo systemctl restart sshd) for the new settings to take effect.

SSH Key Generation

Since this configuration strongly emphasizes key-based authentication by disabling passwords, it’s essential to know how to generate and deploy SSH keys.

Step 1. Generate an SSH Key Pair (on your local machine):

Open a terminal on your local machine (your computer from which you’ll connect to the server) and run the following command to generate a new RSA key pair with 4096 bits:

ssh-keygen -t rsa -b 4096

When prompted:

  • “Enter a file in which to save the key (/home/your_user/.ssh/id_rsa):”: You can press Enter to accept the default location. If you have existing keys, you might want to specify a different filename (e.g., id_rsa_myserver) to avoid overwriting them.
  • “Enter passphrase (empty for no passphrase):”: It’s highly recommended to enter a strong passphrase. This passphrase will protect your private key, adding an extra layer of security. You will need to enter this passphrase whenever you use the key.

This command will create two files in your ~/.ssh/ directory (or the location you specified):

  • id_rsa (or your chosen filename): This is your private key. Keep this file secure and never share it.
  • id_rsa.pub (or your chosen filename with .pub): This is your public key. This is the key you will copy to your server.

Step 2. Copy Your Public Key to the SSH Server:

There are a few ways to copy your public key to the server. The easiest and most recommended method is using ssh-copy-id:

ssh-copy-id -p 22 your_username@your_server_ip_or_hostname

Replace 22 with your SSH port if different, your_username with the user you log in as on the server, and your_server_ip_or_hostname with the server’s address.

ssh-copy-id will prompt you for the user’s password on the remote server (for this one-time copy operation). It will then append your public key to the ~/.ssh/authorized_keys file for that user on the server.

If ssh-copy-id is not available or you prefer to do it manually:

Step 3.1 Copy the content of your public key:

cat ~/.ssh/id_rsa.pub

Copy the entire output (it’s a single long line starting with ssh-rsa and ending with your username@hostname).

Step 3.2 Log in to your server using a password (temporarily):

ssh your_username@your_server_ip_or_hostname

(You might need to use the default port 22 for this temporary login if you’ve not yet changed it in sshd_config).

Step 3.3 Create the .ssh directory and authorized_keys file on the server (if they don’t exist) and set proper permissions:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Step 3.4 Paste your public key into ~/.ssh/authorized_keys:

nano ~/.ssh/authorized_keys

Paste the public key content you copied earlier onto a new line in this file. Save and exit the editor.

Step 4: Verify if you’re key-based authentication is working or not.

# If default port is different feel free to adjust the port number
ssh your_username@your_server_ip_or_hostname -p 22

After these steps, your public key should be securely placed on the server, allowing you to authenticate using your private key.

Consolidated sshd_config with Comments

# This is the sshd server system-wide configuration file. See sshd_config(5) for more information.

# Include other configuration files.
Include /etc/ssh/sshd_config.d/*.conf

# Listen on a non-standard port for increased security by obscurity.
# This helps reduce automated scanning and brute-force attempts on the default port 22.
# Customized your ssh port 1024-65535
Port 44444

# Specify the host key for the server. Using Ed25519 is generally recommended for its security and performance.
HostKey /etc/ssh/ssh_host_ed25519_key

# Logging level.
# VERBOSE is useful for troubleshooting, but can generate a lot of log data in production.
# INFO is generally sufficient for routine monitoring.
LogLevel INFO
# LogLevel VERBOSE # Uncomment this line temporarily for detailed debugging

# Authentication timeout for unauthenticated sessions.
LoginGraceTime 30

# DO NOT permit root login directly via SSH.
# Users should log in with a regular user account and use `sudo` for administrative tasks.
PermitRootLogin no

# Enforce strict permissions on user home directories and authentication files.
# If permissions are too permissive, sshd will refuse to authenticate.
# This helps prevent unauthorized access due to misconfigured file permissions.
StrictModes yes

# Limit the number of authentication attempts per connection.
# This helps mitigate brute-force attacks.
MaxAuthTries 3

# Limit the maximum number of open sessions per network connection.
MaxSessions 4

# Disable password authentication entirely.
# This is a critical security measure. All users must use key-based authentication.
PasswordAuthentication no

# Disable challenge-response authentication (e.g., keyboard-interactive).
# This is often tied to PAM, but explicitly disabling it helps enforce key-only authentication.
ChallengeResponseAuthentication no

# Enable PAM (Pluggable Authentication Modules) for authentication and session management.
# This allows for integration with system-wide authentication policies (e.g., 2FA, password policies).
# Ensure your /etc/pam.d/sshd configuration is properly secured and reviewed.
UsePAM yes

# Disable SSH agent forwarding.
# If enabled and the SSH server is compromised, an attacker could potentially use the forwarded agent
# to authenticate to other systems where the user has access.
AllowAgentForwarding no

# Disable TCP port forwarding (tunneling).
# If enabled, users could bypass firewalls or create tunnels to internal networks, posing a security risk.
AllowTcpForwarding no

# Disable X11 forwarding (graphical display forwarding).
# If enabled, it could expose the X server to vulnerabilities or allow capturing graphical output.
X11Forwarding no

# Do not permit tun device forwarding.
# This is a more advanced form of tunneling and should be disabled unless specifically required and secured.
PermitTunnel no

# Display the message of the day upon login.
PrintMotd yes

# Send TCP keep-alive messages to the client. This helps detect dead connections.
TCPKeepAlive yes

# Disable compression. Compression can sometimes introduce vulnerabilities (e.g., CRIME/BREACH attacks)
# and may not offer significant performance benefits over modern fast networks.
Compression no

# Number of client alive messages (keepalives) that may be sent without sshd receiving any messages back from the client.
# If this threshold is reached, sshd will disconnect the client.
ClientAliveCountMax 3

# Server sends a null packet to the client if no data has been received from the client for this interval.
# The effective idle timeout is calculated as: ClientAliveInterval + (ClientAliveInterval * ClientAliveCountMax)
# For these settings (300 seconds + (300 seconds * 3)) = 1200 seconds, or 20 minutes.
ClientAliveInterval 300

# Limit the number of concurrent unauthenticated connections to the SSH daemon.
# This helps mitigate Denial-of-Service (DoS) attacks.
# Format: start:rate:full (e.g., 10:30:100 means allow 10 unauthenticated connections,
# then drop 30% of new connections until 100 connections are reached, after which all new connections are dropped).
MaxStartups 10:30:100

# Path to a file that contains a banner to be displayed before authentication.
# Ensure the content includes appropriate legal warnings and disclaimers.
Banner /etc/ssh/banner

# Allow clients to pass locale environment variables.
AcceptEnv LANG LC_*

# Configure the SFTP subsystem.
Subsystem sftp /usr/lib/openssh/sftp-server

# Preferred ciphers (encryption algorithms).
# Ordered from most to least preferred. Prioritize modern, strong ciphers.
# NOTE: Corrected typo 'opensssh.com' to 'openssh.com' for aes256-gcm
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

# Preferred Message Authentication Codes (MACs) for integrity checking.
# Ordered from most to least preferred. Prioritize SHA-2 based MACs.
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256

# Preferred Key Exchange (Kex) Algorithms.
# Ordered from most to least preferred. Prioritize modern, secure algorithms like Curve25519.
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256

How to copy this content to your sshd_config file:

Step 1: Open your sshd_config file for editing:

    sudo nano /etc/ssh/sshd_config 

    (You can replace nano with your preferred text editor like vim).

    Step 2: Delete existing content (optional, but recommended for clean implementation): If you want a clean start with only the recommended settings, you can delete all existing content in the editor.

    • In nano, you can usually press Ctrl+K repeatedly to cut lines, or Alt+A to select all, then Ctrl+K to cut.
    • In vim, type :%d and press Enter to delete all lines.

    Step 3: Copy the entire content from the “Consolidated sshd_config with Comments” section above. Make sure to select everything from # This is the sshd server... down to KexAlgorithms ....

    Step 4: Paste the copied content into your editor:

    • In nano, press Ctrl+Shift+V.
    • In vim, press i for insert mode, then Shift+Insert or right-click and choose “Paste”.

    Step 5: Save the changes:

    • In nano, press Ctrl+X, then Y (for Yes) when prompted to save, and Enter to confirm the filename.
    • In vim, press Esc, then type :wq and press Enter.

    Execution and Verification

    After you have updated your /etc/ssh/sshd_config file with the new content and saved it, you must restart the SSH service for the changes to take effect.

    01. Restart the SSH Service: Open your terminal and run the following command:

      sudo systemctl restart sshd

      This command will gracefully restart the SSH daemon, applying your new configuration.

      02. Verify SSH Service Status: To confirm that the SSH service restarted successfully and is running without errors, check its status with:

      sudo systemctl status sshd 

      You should see output indicating that the service is active (running). If there are any errors, review the output carefully for clues (e.g., permission issues, syntax errors in the config file). You can also check the system logs for more detailed error messages (e.g., sudo journalctl -u sshd).

      03. Test Your SSH Access: From a separate terminal window or another machine, attempt to connect to your server using SSH with your key-based authentication (and the new port if you changed it).

      # Adjust the port number according to your sshd_config
      ssh -p 44444 your_username@your_server_ip_or_hostname

      Verify that you can log in successfully. Do NOT close your current SSH session until you have confirmed you can log in with the new configuration from a new session. This precaution is vital in case of a configuration error.

        Final Thoughts

        The provided sshd_config represents a robust and highly recommended security posture for your SSH server. By adopting this configuration, you are implementing several key security best practices that significantly reduce your system’s attack surface and improve its resilience against common threats.

        In summary, this configuration ensures:

        • No Root Login: Preventing direct root logins eliminates a major target for attackers.
        • No Password Authentication: By exclusively relying on SSH key-based authentication, you remove the vulnerability to password brute-forcing and dictionary attacks, which are common initial compromise vectors. This means your SSH access is solely dependent on the secure exchange of cryptographic keys, eliminating traditional usernames and passwords for remote login.
        • Modern Cryptography: All SSH communication is secured with state-of-the-art encryption, integrity, and key exchange algorithms, protecting your data in transit.
        • Controlled Access: Features like agent, TCP, X11, and TUN forwarding are explicitly disabled, preventing unauthorized tunneling and potential network bypasses.
        • DoS Protection: Mechanisms like MaxStartups are in place to limit the impact of denial-of-service attempts.
        • Session Management: Idle sessions are automatically terminated, and limits are placed on authentication attempts and concurrent sessions, contributing to a more secure and stable environment.

        This configuration shifts your SSH security model from being susceptible to weak passwords and common exploits to a more resilient, key-centric approach. Remember, while this configuration provides excellent baseline security, ongoing system updates and adherence to general security best practices remain essential for maintaining a strong defensive posture.

        Related Post

        Automating SSH Hardening with Ansible Playbooks

        Automating SSH Hardening with Ansible Playbooks

        Introduction Getting started with Ansible Building a baseline In a previous blog post, we walked through the essential steps of initial server setup using Ansible, laying the groundwork for secure and automated infrastructure management. Continuing that journey, this...

        read more

        0 Comments

        Submit a Comment

        Your email address will not be published. Required fields are marked *

        This site uses Akismet to reduce spam. Learn how your comment data is processed.