Scope: Debian-based and RHEL-based Linux distributions
Topics: ed25519 key pairs, public key installation, authorized_keys permissions, sshd hardening, service validation and restart, and ssh-agent.

Part 1: Debian-Based Systems#

Examples: Debian, Ubuntu

1. Generate an ed25519 Key Pair#

Run on the client machine as the user who will log in:

ssh-keygen -t ed25519 -C "<USERNAME>@<CLIENT_HOST>" -f ~/.ssh/id_ed25519

Options:

  • -t ed25519: Uses the Ed25519 algorithm. It is fast, small, and current.
  • -C: Attaches a comment to the public key, typically the account and host.
  • -f: Sets the output path for the private key.

Enter a passphrase when prompted. The private key is stored encrypted on disk.

Expected output:

Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub

Do not overwrite an existing key unless the old key is no longer needed.

2. Install the Public Key on the Server#

Copy the public key to the target account:

ssh-copy-id -i ~/.ssh/id_ed25519.pub <USERNAME>@<SERVER>

Enter the account password one last time. If ssh-copy-id is unavailable, install the key manually:

cat ~/.ssh/id_ed25519.pub | ssh <USERNAME>@<SERVER> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

The public key is appended to ~/.ssh/authorized_keys on the server. One key per line.

3. Set the Required File Permissions#

On the server, verify the ownership and modes:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

On the client, protect the private key:

chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

The files must be owned by the login user. If ~/.ssh or authorized_keys is writable by group or other, sshd ignores the key and the login silently falls back to password authentication.

4. Test Public Key Login#

Log in with the key from the client:

ssh -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>

Verify the effective user:

whoami

Expected output:

alice

For connection diagnostics, run the client in verbose mode:

ssh -v -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>

Keep the current session open until a second successful login confirms that access works.

5. Harden the SSH Daemon#

Create a drop-in configuration on the server:

mkdir -p /etc/ssh/sshd_config.d
vim /etc/ssh/sshd_config.d/99-hardening.conf

Example content:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
AllowUsers alice bob
ClientAliveInterval 300
ClientAliveCountMax 2

Options:

  • PermitRootLogin no: Blocks direct root login.
  • PasswordAuthentication no: Accepts public key authentication only.
  • PubkeyAuthentication yes: Enables public key authentication explicitly.
  • MaxAuthTries 3: Limits authentication attempts per connection.
  • AllowUsers alice bob: Restricts SSH access to the listed accounts.
  • ClientAliveInterval and ClientAliveCountMax: Close idle sessions after the stated interval.

Verify that the file is actually included:

grep -i '^Include' /etc/ssh/sshd_config

6. Validate and Restart the SSH Service#

Validate the configuration first:

sshd -t

The command prints nothing on success. Fix any reported error before continuing. Restart the service:

systemctl restart ssh
systemctl status ssh

The service unit is named ssh on Debian-based systems. Keep the existing session open and confirm that a new login succeeds before closing it.

7. Cache the Private Key with ssh-agent#

Start the agent and load the key:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

List and manage loaded keys:

ssh-add -l
ssh-add -D

The agent holds the decrypted key in memory, so the passphrase is requested once per session instead of per connection.


Part 2: RHEL-Based Systems#

Examples: RHEL, Rocky Linux, AlmaLinux

1. Generate and Install a Key Pair#

Run on the client machine:

ssh-keygen -t ed25519 -C "<USERNAME>@<CLIENT_HOST>" -f ~/.ssh/id_ed25519
ssh-copy-id -i ~/.ssh/id_ed25519.pub <USERNAME>@<SERVER>

ssh-copy-id is provided by the openssh-clients package. To install the key manually:

cat ~/.ssh/id_ed25519.pub | ssh <USERNAME>@<SERVER> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

2. Set Permissions and Test Login#

On the server:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

On the client:

chmod 600 ~/.ssh/id_ed25519
ssh -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>

sshd ignores authorized_keys if the file or ~/.ssh is writable by group or other. Keep the current session open until a second login succeeds.

3. Harden the SSH Daemon#

Create a drop-in configuration on the server:

mkdir -p /etc/ssh/sshd_config.d
vim /etc/ssh/sshd_config.d/99-hardening.conf

Example content:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
AllowUsers alice bob
ClientAliveInterval 300
ClientAliveCountMax 2

RHEL-based systems manage algorithms and ciphers through system-wide crypto policies. Do not override Ciphers, MACs, or KexAlgorithms manually unless a policy requirement demands it:

update-crypto-policies --show

4. Validate and Restart the SSH Service#

Validate the configuration first:

sshd -t

The command prints nothing on success. Restart the service and enable it at boot:

systemctl restart sshd
systemctl enable --now sshd
systemctl status sshd

The service unit is named sshd on RHEL-based systems. If SELinux reports context errors on moved files, restore the labels:

restorecon -Rv /etc/ssh

Keep the existing session open and confirm that a new login succeeds before closing it.

5. Cache the Private Key with ssh-agent#

Start the agent and load the key:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l

The agent holds the decrypted key in memory for the duration of the session.


Part 3: Important Notes#

Required File Permissions#

PathOwnerModeNotes
~/.sshLogin user700Ignored by sshd if writable by group or other
~/.ssh/authorized_keysLogin user600One public key per line
~/.ssh/id_ed25519Login user600Private key; never share it
~/.ssh/id_ed25519.pubLogin user644Public key; safe to distribute
/etc/ssh/sshd_config.d/99-hardening.confroot600Drop-in hardening configuration

If key-based login fails, check these modes first. Verbose client output names the rejected file:

ssh -v -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>

sshd Configuration Directives#

DirectiveRecommended valueEffect
PermitRootLoginnoBlocks direct root login
PasswordAuthenticationnoDisables password login entirely
PubkeyAuthenticationyesEnables public key login
PermitEmptyPasswordsnoBlocks accounts with empty passwords
MaxAuthTries3Limits attempts per connection
AllowUsersExplicit listRestricts which accounts may log in
ClientAliveInterval300Sends keepalives every 300 seconds
ClientAliveCountMax2Drops the connection after missed keepalives

Inspect the effective configuration after changes:

sshd -T

Apply AllowUsers or AllowGroups carefully. A typo locks out every account and requires console access to fix.

Command Reference#

OperationDebian-basedRHEL-based
Generate key pairssh-keygen -t ed25519 -f ~/.ssh/id_ed25519ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
Install public keyssh-copy-id -i ~/.ssh/id_ed25519.pub <USERNAME>@<SERVER>ssh-copy-id -i ~/.ssh/id_ed25519.pub <USERNAME>@<SERVER>
Set key permissionschmod 600 ~/.ssh/id_ed25519chmod 600 ~/.ssh/id_ed25519
Set authorized_keys modechmod 600 ~/.ssh/authorized_keyschmod 600 ~/.ssh/authorized_keys
Test loginssh -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>ssh -i ~/.ssh/id_ed25519 <USERNAME>@<SERVER>
Validate sshd configsshd -tsshd -t
Show effective configsshd -Tsshd -T
Restart SSH servicesystemctl restart sshsystemctl restart sshd
Check service statussystemctl status sshsystemctl status sshd
Load key into agentssh-add ~/.ssh/id_ed25519ssh-add ~/.ssh/id_ed25519

Security Guidelines#

  • Replace placeholders such as <USERNAME>, <SERVER>, and <CLIENT_HOST> with actual values. Do not include the angle brackets in commands.
  • Use ed25519 keys. Do not generate new RSA keys for SSH unless a legacy system requires them.
  • Protect every private key with a passphrase and store it with mode 600.
  • Never disable PasswordAuthentication before a key-based login is confirmed in a second session.
  • Keep one session open while testing. A bad sshd_config or a locked account otherwise requires console access.
  • Always run sshd -t before restarting the SSH service.
  • Block root login with PermitRootLogin no and administer through a regular account with sudo.
  • Restrict SSH access with AllowUsers or AllowGroups on hosts with a stable account list.
  • Remove unused public keys from authorized_keys and delete retired key pairs from the client.
  • Avoid agent forwarding (ssh -A) to hosts you do not control. A compromised host can use a forwarded agent to authenticate as you.
  • Keep the openssh packages updated and monitor /var/log/auth.log or the journal for repeated failed authentication attempts.