Jayaprakash S

SSH Key Authentication and Hardening

I’m documenting my approach to eliminating passwords across my cloud infrastructure. This guide covers SSH key-based authentication, hardening SSH configuration, disabling password-based login, and locking down authentication across the system. Password-less authentication is more secure than password-based login when done properly, and reduces dependency on password strength and complexity.

Why eliminate passwords?

Prerequisites

On your Mac:

On your Linux server:

Step 1: Generate SSH Key Pair on macOS

On your local Mac, generate a new SSH key pair:

ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/id_rsa

Explanation:

When prompted for passphrase, you can leave it empty for password-less login, or add one for extra security (you’ll need to use SSH agent).

Verify the key was created:

ls -la ~/.ssh/
# Should show: id_rsa (private key) and id_rsa.pub (public key)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub

Step 2: Copy Public Key to Remote Server

Copy your public key to the server (this happens over SSH with a password, last time):

ssh-copy-id -i ~/.ssh/id_rsa.pub <REMOTE_USER_NAME>@<REMOTE_IP>
# Or if using a custom SSH port:
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 5022 <REMOTE_USER_NAME>@<REMOTE_IP>

What this does:

Manual alternative (if ssh-copy-id doesn’t work):

# On your Mac:
cat ~/.ssh/id_rsa.pub

# Copy the output, then on the remote server:
mkdir -p ~/.ssh
echo "paste-your-public-key-here" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Step 3: Test Key-Based Login

Test that key-based authentication works:

ssh -i ~/.ssh/id_rsa <REMOTE_USER_NAME>@<REMOTE_IP>
# Should connect without asking for password

If this works, you’re ready to disable password authentication.

Step 4: Configure SSH on macOS

Update your local SSH config to use the key automatically:

cat >> ~/.ssh/config << 'EOF'
Host my-server
  HostName <REMOTE_IP>
  User <REMOTE_USER_NAME>
  Port 22
  IdentityFile ~/.ssh/id_rsa
  ServerAliveInterval 60
  ServerAliveCountMax 3
  TCPKeepAlive yes
EOF

Now you can simply: ssh my-server

Step 5: Harden SSH on Linux Server

Edit SSH configuration:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo vim /etc/ssh/sshd_config

Make these changes:

# Disable root login (if you have other sudo users)
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no
PubkeyAuthentication yes

# Disable empty passwords
PermitEmptyPasswords no

# Disable challenge-response (keyboard-interactive)
ChallengeResponseAuthentication no

# Disable X11 forwarding (if not needed)
X11Forwarding no

# Reduce login grace time
LoginGraceTime 30

# Limit concurrent sessions
MaxAuthTries 3
MaxSessions 10

# Use only strong key exchange algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org

# Use only strong ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com

# Use only strong MACs
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com

Verify the configuration is valid:

sudo sshd -t
# Should return no errors

Restart SSH:

sudo systemctl restart ssh
# Or: sudo systemctl restart sshd

Test the connection from your Mac:

ssh my-server
# Should connect without password

⚠️ Keep this terminal open while testing, in case you need to revert changes.

Step 6: Disable Password-Based Authentication in Other Services

PAM (Pluggable Authentication Modules)

Disable password authentication system-wide:

sudo vim /etc/pam.d/common-password

Look for lines with pam_unix.so and ensure they have nullok removed (disallows empty passwords):

password [success=1 default=ignore]      pam_unix.so obscured use_authtok try_first_pass yescrypt shadow

Sudo (if using non-root users)

Prevent password-based sudo:

sudo vim /etc/sudoers.d/99-passwordless

Add this line (allows specific users to sudo without password):

jayaprakash ALL=(ALL) NOPASSWD:ALL

Or if you want sudo to require SSH key (already covered by SSH hardening):

sudoedit /etc/sudoers
# Add: %sudo ALL=(ALL) NOPASSWD: ALL

Lock System User Accounts

Lock accounts that should never login:

# Lock the root account password (if using non-root sudo user)
sudo passwd -l root

# Lock other system accounts
sudo passwd -l www-data
sudo passwd -l mail
sudo passwd -l news

Verify locked accounts:

sudo cat /etc/shadow | grep '!' | head -5
# Lines starting with ! indicate locked accounts

Step 7: Add SSH Key to macOS Keychain (Optional)

If you set a passphrase on your SSH key, add it to SSH agent for convenience:

# Add key to agent
ssh-add -K ~/.ssh/id_rsa

# List loaded keys
ssh-add -l

# Add to ~/.ssh/config to auto-load on login:
# Host *
#   UseKeychain yes
#   AddKeysToAgent yes

For different purposes (work, personal, CI/CD), generate separate keys:

# Work key
ssh-keygen -t ed25519 -C "work-deploy" -f ~/.ssh/id_work

# Personal key
ssh-keygen -t ed25519 -C "personal-server" -f ~/.ssh/id_personal

# CI/CD key
ssh-keygen -t ed25519 -C "ci-deploy" -f ~/.ssh/id_ci

Add to your SSH config:

Host work-server
  HostName work.example.com
  User deploy
  IdentityFile ~/.ssh/id_work

Host personal-server
  HostName personal.example.com
  User root
  IdentityFile ~/.ssh/id_personal

Best Practices

1. Key Rotation

Rotate SSH keys periodically (every 1-2 years):

# Generate new key
ssh-keygen -t ed25519 -C "new-key-$(date +%Y)" -f ~/.ssh/id_rsa_new

# Copy to server
ssh-copy-id -i ~/.ssh/id_rsa_new.pub <REMOTE_USER_NAME>@<REMOTE_IP>

# Remove old key from authorized_keys after verifying new one works

2. Monitoring Failed Login Attempts

Even with password auth disabled, monitor for attacks:

# Check failed SSH attempts
sudo tail -f /var/log/auth.log | grep "Failed password"

# Count attempts by IP
sudo grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn

3. Use SSH Config Aliases

Make connection management easier:

# ~/.ssh/config
Host home
  HostName 192.168.1.100
  User <LOCAL_USER_NAME>
  IdentityFile ~/.ssh/id_rsa

Host cloud
  HostName <REMOTE_IP>
  User <REMOTE_USER_NAME>
  Port 22
  IdentityFile ~/.ssh/id_rsa
  ServerAliveInterval 60

Host *
  ServerAliveInterval 60
  ServerAliveCountMax 3
  TCPKeepAlive yes

4. Disable Root Account Login

If you have a non-root sudo user, lock the root account:

sudo passwd -l root
sudo vim /etc/ssh/sshd_config
# Set: PermitRootLogin no
sudo systemctl restart ssh

5. IP Whitelisting (Optional)

Restrict SSH to specific IPs:

sudo vim /etc/ssh/sshd_config

Add at the end:

# Allow only specific IPs
ListenAddress 0.0.0.0
AllowUsers root@192.168.1.* root@5.78.110.*

6. Use SSH Certificates (Advanced)

For managing many keys across many servers, consider SSH certificates instead of individual authorized_keys:

# Generate a CA key (keep private)
ssh-keygen -t ed25519 -C "ssh-ca" -f ~/.ssh/ca_key

# Sign user key
ssh-keygen -s ~/.ssh/ca_key -I "user-cert-2026" -n root -V +52w ~/.ssh/id_rsa.pub
# Creates id_rsa-cert.pub valid for 52 weeks

Troubleshooting

“Permission denied (publickey)”

# Verify key is on server
ssh my-server "cat ~/.ssh/authorized_keys"

# Check permissions
ssh my-server "ls -la ~/.ssh/"
# Should be: drwx------ (700) for .ssh, -rw------- (600) for authorized_keys

# Debug SSH connection
ssh -vvv my-server
# Shows detailed handshake information

“Too many authentication failures”

# SSH tried too many keys; specify which one to use
ssh -i ~/.ssh/id_rsa trade

# Or add to SSH config:
# IdentitiesOnly yes
# IdentityFile ~/.ssh/id_rsa

Lost SSH Access to Server

If you locked yourself out, you need console/VNC access to recover:

# Via VNC or console:
sudo vim /etc/ssh/sshd_config
# Change PasswordAuthentication back to yes
sudo systemctl restart ssh
# Now login with password and fix the issue

Security Summary

This setup is secure because:

When to Use This Approach

When to Consider Alternatives

Resources