Table of Contents
Understanding the OpenSSH Server
Most Linux systems use OpenSSH (sshd) as the SSH server. This chapter focuses on configuring sshd, not on using the ssh client, key generation, or general SSH security concepts (those are covered elsewhere).
Key components:
sshdbinary: the SSH daemon (the server).- Main config:
/etc/ssh/sshd_config - Host keys: usually in
/etc/ssh/(e.g.ssh_host_ed25519_key) - Service unit: typically
sshd.serviceorssh.serviceforsystemd
You should already be comfortable with:
- Editing text files as root
- Using
systemctlto manage services - Basic firewall concepts
This chapter focuses on server-side settings and patterns.
Installing and Enabling the SSH Server
On many server distributions, the SSH server is installed by default. On desktop or minimal installs, you may need to add it:
- Debian/Ubuntu:
sudo apt install openssh-server- Fedora/RHEL/CentOS:
sudo dnf install openssh-server- Arch Linux:
sudo pacman -S opensshEnable and start the service:
sudo systemctl enable --now sshd
# or on some Debian/Ubuntu:
sudo systemctl enable --now sshVerify it’s running:
sudo systemctl status sshdOr confirm it is listening:
sudo ss -tlnp | grep sshdThe sshd_config File: Structure and Basics
The main configuration file is /etc/ssh/sshd_config.
Typical structure:
- Global options at the top (logging, port, address family, allowed auth methods, etc.)
- Match blocks at the bottom to override settings based on user, group, address, etc.
A basic default might include lines like:
Port 22
Protocol 2
PermitRootLogin prohibit-password
PasswordAuthentication yes
PubkeyAuthentication yes
UsePAM yes
X11Forwarding yes
Subsystem sftp /usr/lib/openssh/sftp-server
Comments start with #. Un-commenting and changing defaults is the primary way you configure sshd.
After editing the file, always test and reload:
sudo sshd -t # test configuration
sudo systemctl reload sshd
If sshd -t returns nothing, the syntax is OK.
Core Network-Level Settings
Changing the SSH Port
To listen on a different port (for example, 2222):
In /etc/ssh/sshd_config:
Port 2222Optional: Listen on multiple ports:
Port 22
Port 2222After changing:
sudo sshd -t
sudo systemctl reload sshdUpdate your firewall to allow the new port, then connect with:
ssh -p 2222 user@serverChanging the port does not replace proper security measures, but it can reduce noise from automated scans.
Binding to Specific Interfaces
By default, sshd listens on all addresses. To restrict:
ListenAddress 192.168.1.10
# or for IPv6:
ListenAddress [2001:db8::1234]
You can specify multiple ListenAddress lines.
Use this if SSH should only be reachable on a management network or specific interface.
Protocol and Address Family
Modern OpenSSH only supports Protocol 2. You might still see:
Protocol 2To restrict to IPv4-only:
AddressFamily inetTo restrict to IPv6-only:
AddressFamily inet6
Default (any) allows both.
Managing Host Keys
Host keys identify the server to clients. They live under /etc/ssh/, for example:
/etc/ssh/ssh_host_ed25519_key/etc/ssh/ssh_host_rsa_key
Their configuration appears in sshd_config:
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_keyNotes:
- Modern setups prefer
ed25519and sometimesecdsa;rsais maintained for compatibility. - On first install, host keys are usually generated automatically.
- To (re)generate host keys manually:
sudo rm /etc/ssh/ssh_host_*
sudo ssh-keygen -ARegenerating host keys will cause clients to get “REMOTE HOST IDENTIFICATION HAS CHANGED!” warnings; handle this carefully in production.
Authentication Configuration
This is where SSH server configuration has the most impact. Below are the common knobs.
Password vs Public Key Authentication
In sshd_config:
PasswordAuthentication yes
PubkeyAuthentication yesTypical secure pattern:
- Ensure key-based auth works.
- Disable password auth:
PasswordAuthentication no- Reload and verify you can still log in with keys.
You can also disable keyboard-interactive PAM-based password prompts with:
KbdInteractiveAuthentication no
# older syntax:
ChallengeResponseAuthentication noBe cautious: disabling all password-related methods is good for security, but make sure you don’t lock yourself out.
Controlling Root Logins
To limit or disable SSH root access:
PermitRootLogin noVariants:
no: root cannot log in via SSH.without-password/prohibit-password: only allow key-based auth for root (naming differs by OpenSSH version).yes: allow all forms of root login (generally discouraged).
Typical secure setup:
PermitRootLogin prohibit-passwordor:
PermitRootLogin no
with sudo for administrative tasks.
Restricting SSH to Specific Users or Groups
Use AllowUsers, DenyUsers, AllowGroups, DenyGroups.
Examples:
Allow only specific users:
AllowUsers alice bob deployAllow anyone in specific groups:
AllowGroups sshusers adminsDeny a particular user:
DenyUsers test tempuserRules:
- If
AllowUsersis set, only those users may log in, regardless of groups. DenyUsersandDenyGroupsoverride allows if they match.- You can combine user and host patterns, e.g.
alice@192.168.1.*.
A typical approach:
- Create a group:
sudo groupadd sshusers
sudo usermod -aG sshusers alice- Limit SSH to that group:
AllowGroups sshusers
Then reload sshd.
Key-Based Access Control
Server-side, key-based auth uses authorized keys files.
Authorized Keys Locations
For each user, authorized keys are usually here:
~user/.ssh/authorized_keys(per-user)- Permissions matter:
~user: should not be world-writable~user/.ssh:700~user/.ssh/authorized_keys:600
Server config controls this via:
AuthorizedKeysFile .ssh/authorized_keysYou can add multiple paths:
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
System-wide authorized keys (for all users) can be configured with an absolute path in AuthorizedKeysFile, for example:
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
where %u is the username.
AuthorizedKeysCommand
For centralized key management (LDAP, custom scripts, etc.), you can tell sshd to call an external command when looking up keys:
AuthorizedKeysCommand /usr/local/bin/fetch-ssh-keys
AuthorizedKeysCommandUser sshkeys
The command should output public keys in the same format as authorized_keys. This is an advanced pattern common in large infrastructures.
Connection and Login Restrictions
These options help protect the server from brute force and abuse.
Max Sessions and Connections
MaxSessions 10
MaxStartups 10:30:100MaxSessions: maximum open SSH sessions per network connection (e.g. multipleControlMasterchannels).MaxStartupssyntaxstart:rate:full:start: number of unauthenticated connections allowed.rate: percentage of additional connections to drop.full: total number at which all new unauthenticated connections are refused.
Example:
MaxStartups 10:30:60means:
- Allow up to 10 pending, unauthenticated connections.
- Between 10–60, randomly drop 30% of new ones.
- At 60, drop all new ones.
This mitigates simple connection-flood attacks.
Login Grace Time
Time allowed for a client to authenticate before the server drops the connection:
LoginGraceTime 30
Set in seconds; 0 disables the limit (not recommended).
Limiting Auth Attempts
MaxAuthTries 3Controls how many failed authentication attempts per connection before disconnecting. Lower values reduce brute-force attempts per TCP connection.
Session Behavior and Environment
These control what happens after a user logs in.
Default Shell and User Environment
sshd typically starts the user’s default shell as specified in /etc/passwd. Environment configuration (shell profiles, etc.) is handled elsewhere; from sshd’s perspective, the relevant options are:
PermitUserEnvironment no
If set to yes, users can influence environment variables via ~/.ssh/environment, which can be risky. For most setups, keep this disabled.
You can preserve or clear certain environment variables:
AcceptEnv LANG LC_*
This allows clients to send LANG and LC_* variables, often useful for locale settings.
Banner
To display a legal or information banner before login:
- Create a banner file, e.g.
/etc/ssh/ssh_banner:
Authorized access only. All activity may be monitored.- Configure:
Banner /etc/ssh/ssh_bannerThis is often used for legal notices on corporate or government systems.
Idle Session Timeout
You can disconnect idle sessions automatically using two options together:
ClientAliveInterval 300
ClientAliveCountMax 2Meaning:
- Every 300 seconds (5 minutes), the server sends a “keepalive” message.
- If there is no response twice in a row, the connection is closed.
This approximates a 10-minute idle timeout. This is distinct from bash’s TMOUT; it operates at the SSH layer.
Tunneling, Forwarding, and Subsystems
These control advanced features like port forwarding and SFTP.
Port Forwarding
Server-side control of port forwarding:
AllowTcpForwarding yes
GatewayPorts noAllowTcpForwarding:yes: allow local and remote forwardingno: disable all TCP forwardinglocal/remote: restrict to one directionGatewayPorts:no: forwarded ports bind to localhost only (default)yes: forwarded ports can bind to non-loopback interfaces, exposing them to others
For a locked-down environment:
AllowTcpForwarding noFor a bastion/jump host where forwarding is required:
AllowTcpForwarding yes
GatewayPorts noX11 Forwarding
Control GUI forwarding over SSH:
X11Forwarding no
Set to yes if you explicitly support X11 apps over SSH. For servers, disabling is common for security and simplicity.
SFTP Subsystem
SFTP is usually implemented as a subsystem in sshd:
Subsystem sftp /usr/lib/openssh/sftp-server
The exact path can vary by distribution (e.g. /usr/lib/ssh/sftp-server).
You can replace this with a more controlled sftp-server wrapper or internal-sftp:
Subsystem sftp internal-sftp
Using internal-sftp is a typical building block for chrooted SFTP-only environments (see next section).
Chrooted / Restricted SSH Environments
You can restrict what users can do after connecting, such as providing SFTP-only access or confining them to a specific directory.
SFTP-Only Chrooted Users (Pattern)
Typical pattern:
- Create a group and directory:
sudo groupadd sftpusers
sudo mkdir -p /sftp
sudo chown root:root /sftp
sudo chmod 755 /sftp- Add users to the group and create a directory under
/sftp:
sudo usermod -aG sftpusers alice
sudo mkdir -p /sftp/alice
sudo chown alice:alice /sftp/alice- In
sshd_config, ensureinternal-sftpis used:
Subsystem sftp internal-sftp- Add a
Matchblock:
Match Group sftpusers
ChrootDirectory /sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding noHere:
ChrootDirectory /sftp/%uconfines the user to/sftp/username.ForceCommand internal-sftpprevents shell access.- Forwarding and X11 are disabled for this group.
Important: the chroot directory must be owned by root and not writable by the user. Subdirectories inside can be user-owned.
Per-User or Per-Group Restrictions
Match blocks can target users, groups, addresses, or multiple conditions.
Examples:
Allow password auth only from internal network:
PasswordAuthentication no
Match Address 10.0.0.0/8
PasswordAuthentication yesRestrict a single user:
Match User backup
AllowTcpForwarding no
ForceCommand /usr/local/bin/backup-shell
Match blocks must appear at the end of sshd_config, because once a Match is hit, all following lines until the next Match (or EOF) apply only within that context.
Logging and Troubleshooting sshd
Configuring Log Level
In sshd_config:
LogLevel INFO
Common values: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3.
- For normal operation:
INFO - For detailed troubleshooting of authentication:
VERBOSEorDEBUG
Remember to revert from DEBUG in production; it can leak more info than you want.
Log Locations
Logs are handled by syslog/journald. Where they show up:
- Systemd journal:
sudo journalctl -u sshd- Traditional log files (varies by distro):
/var/log/auth.log(Debian/Ubuntu)/var/log/secure(RHEL/Fedora/CentOS)
You’ll see entries for connections, failed logins, key issues, etc.
Testing Config Without Disruption
Always validate changes:
sudo sshd -tIf you suspect a configuration issue and are worried about locking yourself out:
- Keep an existing SSH session open.
- Edit
sshd_config. - Run:
sudo sshd -t- If no error, reload:
sudo systemctl reload sshdIf you lose access, the existing session may allow you to revert.
For deep debugging, start a temporary sshd on a different port:
sudo /usr/sbin/sshd -D -p 2222 -f /etc/ssh/sshd_config -d
Then connect to port 2222 and watch the debug output in the terminal. This doesn’t disturb the main daemon.
Hardening Patterns and Example Configuration
Below is an example of a more secure but still practical sshd_config for general-purpose servers. This is not a universal template, but illustrates how different options can fit together:
Port 22
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
Protocol 2
UsePAM yes
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 10
MaxStartups 10:30:60
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no
AllowGroups sshusers admins
X11Forwarding no
AllowTcpForwarding yes
GatewayPorts no
PermitTunnel no
PermitUserEnvironment no
ClientAliveInterval 300
ClientAliveCountMax 2
Banner /etc/ssh/ssh_banner
Subsystem sftp internal-sftp
Match Group sftpusers
ChrootDirectory /sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding noWhen building your own configuration:
- Start from the distribution default.
- Make one change at a time and test.
- Keep a recovery method (console, out-of-band, or another session).
Summary of Workflow for Configuring an SSH Server
- Install and start
sshdvia your package manager andsystemctl. - Adjust core settings in
/etc/ssh/sshd_config: - Port, listen addresses
- Root login, password vs key auth
- User and group allow/deny lists
- Set up host keys and authorized keys locations.
- Tune connection limits (
MaxAuthTries,MaxStartups,LoginGraceTime). - Configure session behavior:
- Banners, idle timeouts, environment
- Restrict advanced features:
- Port/X11 forwarding, SFTP, tunnels
- Use
Matchblocks for special cases (SFTP-only users, internal-only passwords, etc.) - Validate with
sshd -t, reload, and monitor logs withjournalctlor/var/log/*.
The next chapters will build on this foundation for other network services and deeper firewalling and access control strategies.