Introduction
Welcome to Part 2 of our comprehensive SSH guide! In Part 1, we established the fundamentals: understanding SSH, installing servers, configuring networking, and making basic password-based connections.
Now we'll elevate your SSH skills to professional level by covering advanced authentication, security hardening, and powerful features that make SSH indispensable for system administrators and developers.
šÆ What You'll Learn in Part 2:
- SSH Key Authentication: Generate keypairs, deploy public keys, passwordless login
- SSH Client Configuration: Create connection shortcuts with
~/.ssh/config
- Security Hardening: Disable password auth, restrict users, configure fail2ban
- Port Forwarding: Local, remote, and dynamic forwarding (SSH tunneling)
- File Transfer: Master SCP and SFTP for secure file operations
- SSH Agent: Manage keys, agent forwarding for multi-hop connections
- Firewall Configuration: Configure firewalld and SELinux for SSH
- Automation: Using SSH in scripts and scheduled tasks
Prerequisites: Completion of Part 1, or existing SSH setup with password authentication working.
SSH Key-Based Authentication
SSH keys provide significantly better security than passwords and enable passwordless automation. Instead of typing a password, your identity is verified using cryptographic key pairs.
Understanding Public Key Cryptography
How SSH Keys Work:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Your Computer ā
ā ā
ā Private Key (SECRET - never share!) ā
ā ~/.ssh/id_rsa or ~/.ssh/id_ed25519 ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā -----BEGIN OPENSSH PRIVATE KEY----- ā ā
ā ā b3BlbnNzaC1rZXktdjEAAAAABG5vbmU... ā ā
ā ā (Used to prove your identity) ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā
ā ā Mathematically Linked ā
ā ā¼ ā
ā Public Key (Can share freely) ā
ā ~/.ssh/id_rsa.pub or ~/.ssh/id_ed25519.pub ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... ā ā
ā ā (Copied to servers you want to access) ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā Public key copied to server
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Remote Server ā
ā ā
ā ~/.ssh/authorized_keys ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... ā ā
ā ā (Matches your public key) ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Authentication Process:
- Client says: "I want to authenticate as user X with this public key"
- Server checks: Does
~user/.ssh/authorized_keys
contain this public key? - Server sends: "Prove you have the matching private key" (encrypted challenge)
- Client uses: Private key to solve the challenge
- Server verifies: Challenge solution is correct ā Access granted ā
Why This is More Secure:
- Private key never leaves your computer
- No password transmitted over network
- Brute force attacks are computationally infeasible
- Can protect private key with passphrase
Choosing Key Type
SSH supports several key types. Here's a comparison:
Key Type | Security Level | Key Size | Performance | Recommendation |
---|---|---|---|---|
ED25519 | āāāāā Excellent | 256 bits (fixed) | Very Fast | ā Best choice for modern systems |
RSA | āāāā Good (4096-bit) | 2048-4096 bits | Moderate | Use for legacy compatibility |
ECDSA | āāāā Good | 256, 384, 521 bits | Fast | Good, but ED25519 preferred |
DSA | ā Weak (deprecated) | 1024 bits | Fast | ā Do not use (insecure) |
Recommendation: Use ED25519 for new keys. It's the most secure, fastest, and generates the smallest keys. Use RSA 4096-bit only if you need compatibility with very old systems.
Generating SSH Key Pair
Let's generate an ED25519 key pair on your WSL Ubuntu system.
Generate ED25519 Key:
ssh-keygen -t ed25519 -C "your_email@example.com"
Purpose: Generate a new ED25519 public/private key pair.
Command Breakdown:
ssh-keygen
: SSH key generation utility-t ed25519
: Type of key to create (ED25519 algorithm)-C "your_email@example.com"
: Comment/label (helps identify key, usually your email)
Interactive Output:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/username/.ssh/id_ed25519):
What's happening?
- Asking where to save the key
- Default location:
~/.ssh/id_ed25519
- Recommendation: Press Enter to accept default
Press Enter (accept default location):
Created directory '/home/username/.ssh'.
Enter passphrase (empty for no passphrase):
Passphrase Decision:
Option | Pros | Cons | Use Case |
---|---|---|---|
With Passphrase | More secure, protects stolen keys | Must enter passphrase (can use ssh-agent) | Personal workstations, interactive use |
No Passphrase | Fully automated, no interaction | If key is stolen, no protection | Automated scripts, CI/CD |
For this tutorial, we'll use no passphrase (just press Enter twice):
Enter passphrase (empty for no passphrase): [Press Enter]
Enter same passphrase again: [Press Enter]
Output:
Your identification has been saved in /home/username/.ssh/id_ed25519
Your public key has been saved in /home/username/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:abc123def456ghi789jkl012mno345pqr678stu901vwx234yz your_email@example.com
The key's randomart image is:
+--[ED25519 256]--+
| .o+*B= |
| . o.=O . |
| o .o=o |
| . + .+o. |
| S o.oo |
| + +.o |
| . = oo |
| + B.E |
| =oB+. |
+----[SHA256]-----+
Output Breakdown:
- Identification saved: Private key location (
~/.ssh/id_ed25519
) - Public key saved: Public key location (
~/.ssh/id_ed25519.pub
) - Fingerprint: Unique identifier (SHA256 hash of public key)
- Randomart image: Visual representation of key (helps verify key hasn't changed)
Verify Keys Were Created:
ls -la ~/.ssh/
Output:
total 16
drwx------ 2 username username 4096 Oct 7 18:00 .
drwxr-x---+ 8 username username 4096 Oct 7 18:00 ..
-rw------- 1 username username 464 Oct 7 18:00 id_ed25519
-rw-r--r-- 1 username username 105 Oct 7 18:00 id_ed25519.pub
-rw-r--r-- 1 username username 222 Oct 7 17:30 known_hosts
File Permissions (Important!):
- id_ed25519 (private key):
600
(-rw------) - Only you can read/write - id_ed25519.pub (public key):
644
(-rw-r--r--) - Everyone can read - known_hosts: Stores server fingerprints from previous connections
Security Critical: Private key (id_ed25519
) must have 600 permissions. SSH will refuse to use keys with incorrect permissions. Never share your private key!
View Public Key:
cat ~/.ssh/id_ed25519.pub
Output:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMQ2Ux7LqxvL8BPvM9R5rK1wZCx6YzJk8dF0pQr9XyZ your_email@example.com
Public Key Format:
ssh-ed25519
: Key typeAAAAC3Nz...
: The actual public key (Base64 encoded)your_email@example.com
: Comment/identifier
This public key is what you'll copy to servers.
Generating RSA Key (Alternative)
If you need RSA for compatibility:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
Command Breakdown:
-t rsa
: RSA algorithm-b 4096
: Key length (4096 bits - more secure than default 2048)-C "comment"
: Comment field
Process is identical to ED25519, creates ~/.ssh/id_rsa
and ~/.ssh/id_rsa.pub
.
Copying Public Key to Server
Now we'll copy the public key to the CentOS VM so it knows to trust your key.
Method 1: Using ssh-copy-id (Easiest)
From WSL Ubuntu, copy key to CentOS VM:
ssh-copy-id -i ~/.ssh/id_ed25519.pub centos9@192.168.1.150
Purpose: Automatically copies your public key to the server's authorized_keys file.
Command Breakdown:
ssh-copy-id
: Utility to copy SSH keys to servers-i ~/.ssh/id_ed25519.pub
: Identity file (public key) to copycentos9@192.168.1.150
: User and server to copy to
Output:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/username/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
centos9@192.168.1.150's password:
Enter the password for centos9 on the CentOS VM (this is the last time you'll need it!)
Output:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'centos9@192.168.1.150'"
and check to make sure that only the key(s) you wanted were added.
What Happened:
- Connected to server using password authentication
- Created
~/.ssh
directory on server (if didn't exist) - Appended your public key to
~/.ssh/authorized_keys
- Set correct permissions (700 for
.ssh
, 600 forauthorized_keys
)
Test Passwordless Login:
ssh centos9@192.168.1.150
Output:
Last login: Mon Oct 7 18:00:00 2024 from 192.168.1.200
[centos9@localhost ~]$
Success! ā You logged in without entering a password!
Method 2: Manual Copy (If ssh-copy-id Not Available)
Step 1: Display your public key on WSL:
cat ~/.ssh/id_ed25519.pub
Copy the entire output (starts with ssh-ed25519
, ends with your comment).
Step 2: SSH to CentOS VM with password:
ssh centos9@192.168.1.150
Step 3: Create .ssh directory (if doesn't exist):
mkdir -p ~/.ssh
chmod 700 ~/.ssh
Purpose: Ensure .ssh directory exists with correct permissions.
Step 4: Add public key to authorized_keys:
nano ~/.ssh/authorized_keys
Paste your public key at the end of the file (one key per line).
Save and exit (Ctrl+X, Y, Enter).
Step 5: Set correct permissions:
chmod 600 ~/.ssh/authorized_keys
Step 6: Exit and test:
exit
ssh centos9@192.168.1.150
Should login without password!
Setting Up Reverse Key Authentication (VM to WSL)
Now let's set up key authentication from CentOS VM to WSL Ubuntu.
Step 1: Generate key on CentOS VM
ssh-keygen -t ed25519 -C "centos9@vm"
Press Enter for all prompts (default location, no passphrase).
Step 2: Copy key from VM to WSL
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@192.168.1.200
Enter your WSL password when prompted.
Step 3: Test from VM to WSL
ssh username@192.168.1.200
Should connect without password!
Now you have bidirectional passwordless SSH! š
Understanding authorized_keys File
View authorized_keys on server:
cat ~/.ssh/authorized_keys
Output:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMQ2Ux7LqxvL8BPvM9R5rK1wZCx6YzJk8dF0pQr9XyZ your_email@example.com
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBbX9Yx3KpQwN5L7RtM8S6qK2xZDx5YxJl9eG1rSt0aB other_user@host
Format:
- One public key per line
- Can contain multiple keys (multiple users/devices can access same account)
- Keys are checked in order until match found
Advanced Options in authorized_keys:
You can add restrictions to keys:
# Only allow from specific IP
from="192.168.1.0/24" ssh-ed25519 AAAAC3... user@example.com
# Disable port forwarding
no-port-forwarding ssh-ed25519 AAAAC3... user@example.com
# Force specific command
command="/usr/bin/backup-script.sh" ssh-ed25519 AAAAC3... backup@example.com
# Multiple restrictions
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command="rsync --server" ssh-ed25519 AAAAC3... rsync@example.com
SSH Client Configuration
Instead of typing long SSH commands with options, use ~/.ssh/config
to create connection shortcuts.
Creating SSH Config File
Create/edit config file on WSL:
nano ~/.ssh/config
Purpose: Create persistent SSH client configuration.
Add Configuration:
# CentOS VM Connection
Host centos-vm
HostName 192.168.1.150
User centos9
Port 22
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
# WSL Ubuntu (from VM)
Host wsl-ubuntu
HostName 192.168.1.200
User username
IdentityFile ~/.ssh/id_ed25519
# Production Server Example
Host prod-server
HostName production.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_rsa_prod
ForwardAgent yes
# Wildcard for all *.example.com hosts
Host *.example.com
User admin
IdentityFile ~/.ssh/id_ed25519_company
StrictHostKeyChecking yes
Save and exit (Ctrl+X, Y, Enter).
Set Correct Permissions:
chmod 600 ~/.ssh/config
Purpose: SSH requires config file to be readable only by owner.
Using SSH Config Shortcuts
Before (without config):
ssh -i ~/.ssh/id_ed25519 -p 22 centos9@192.168.1.150
After (with config):
ssh centos-vm
That's it! All connection parameters are read from config.
Other commands work with aliases too:
# SCP with alias
scp file.txt centos-vm:/home/centos9/
# SFTP with alias
sftp centos-vm
# Execute command
ssh centos-vm "hostname"
Common SSH Config Options
Option | Purpose | Example |
---|---|---|
HostName | Actual server address | HostName 192.168.1.100 |
User | Username for login | User admin |
Port | SSH port (default 22) | Port 2222 |
IdentityFile | Private key to use | IdentityFile ~/.ssh/id_ed25519 |
ServerAliveInterval | Keep connection alive (seconds) | ServerAliveInterval 60 |
ServerAliveCountMax | Max keepalive attempts | ServerAliveCountMax 3 |
ForwardAgent | Enable agent forwarding | ForwardAgent yes |
LocalForward | Port forwarding rule | LocalForward 8080 localhost:80 |
StrictHostKeyChecking | Host key verification | StrictHostKeyChecking yes |
Compression | Enable compression | Compression yes |
Security Hardening
Now let's harden SSH security on the server side by configuring /etc/ssh/sshd_config
.
SSH Server Configuration
Edit sshd_config on CentOS VM:
sudo nano /etc/ssh/sshd_config
Purpose: Configure SSH daemon behavior and security settings.
Recommended Security Settings:
# Disable root login
PermitRootLogin no
# Disable password authentication (use keys only)
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Disable challenge-response authentication
ChallengeResponseAuthentication no
# Enable public key authentication
PubkeyAuthentication yes
# Limit authentication attempts
MaxAuthTries 3
# Login grace time (disconnect if not authenticated within 60 seconds)
LoginGraceTime 60
# Limit max sessions per connection
MaxSessions 5
# Allow specific users only
AllowUsers centos9 admin deploy
# Or allow specific groups
AllowGroups sshusers wheel
# Disable X11 forwarding (if not needed)
X11Forwarding no
# Disable TCP forwarding (if not needed)
AllowTcpForwarding no
# Use specific protocol version (version 2 only)
Protocol 2
# Change default port (security through obscurity, but helps)
Port 2222
# Listen on specific IP only (if multi-homed)
ListenAddress 192.168.1.150
# Strong ciphers only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# Strong MACs only
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
# Strong key exchange algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
# Client alive interval (disconnect idle sessions)
ClientAliveInterval 300
ClientAliveCountMax 2
# Log level
LogLevel VERBOSE
# Use PAM
UsePAM yes
Critical Warning: Before disabling password authentication:
- Test that key-based authentication works
- Keep an existing SSH session open
- Test new connection in separate terminal
- Only then disable password auth
If you lock yourself out, you'll need physical/console access to recover!
Test Configuration Syntax:
sudo sshd -t
Purpose: Test sshd_config for syntax errors before restarting.
If errors:
/etc/ssh/sshd_config line 42: Bad configuration option: InvalidOption
Fix the error before proceeding.
If OK:
(no output - silence means success)
Restart SSH Service:
sudo systemctl restart sshd
Verify Service Restarted:
sudo systemctl status sshd
Should show "active (running)" with recent start time.
Test New Connection (BEFORE closing current session!):
Open new terminal and test:
ssh centos-vm
Should still work. If not, fix issues while you still have your original session open!
Understanding Security Settings Impact
Setting | Security Benefit | Potential Impact |
---|---|---|
PermitRootLogin no | Prevents direct root access attacks | Must use sudo for admin tasks |
PasswordAuthentication no | Prevents brute force password attacks | Must use SSH keys (no password recovery) |
MaxAuthTries 3 | Limits brute force attempts | Locked out after 3 failures (temporary) |
AllowUsers | Only specified users can SSH | Must explicitly add each user |
Port 2222 | Reduces automated scans on port 22 | Must remember custom port, update firewall |
Firewall Configuration for SSH
Check firewall status on CentOS:
sudo firewall-cmd --state
Output:
running
List current rules:
sudo firewall-cmd --list-all
Output:
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
If SSH not in services, add it:
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
Purpose:
--permanent
: Make rule persistent across reboots--add-service=ssh
: Allow SSH traffic (port 22)--reload
: Apply changes
If using custom port (e.g., 2222):
# Remove SSH service (port 22)
sudo firewall-cmd --permanent --remove-service=ssh
# Add custom port
sudo firewall-cmd --permanent --add-port=2222/tcp
# Reload
sudo firewall-cmd --reload
Verify:
sudo firewall-cmd --list-ports
Output:
2222/tcp
Advanced Firewall Rules (Rate Limiting)
Limit SSH connection attempts (prevent brute force):
sudo firewall-cmd --permanent --add-rich-rule='rule service name=ssh limit value=3/m accept'
sudo firewall-cmd --reload
Purpose: Allow maximum 3 SSH connection attempts per minute per IP.
Check rich rules:
sudo firewall-cmd --list-rich-rules
Output:
rule service name="ssh" limit value="3/m" accept
Port Forwarding and SSH Tunneling
SSH tunneling allows you to securely access services through encrypted SSH connections.
Local Port Forwarding
Scenario: Access a web service running on remote server's localhost:8080.
ssh -L 9000:localhost:8080 centos-vm
Purpose: Forward local port 9000 to remote port 8080.
Command Breakdown:
-L 9000:localhost:8080
: Local forward specification9000
: Local port (on your machine)localhost:8080
: Remote destination (from server's perspective)
What happens:
- Open browser to
http://localhost:9000
- Traffic encrypted through SSH to centos-vm
- centos-vm connects to its own localhost:8080
- Response tunneled back to you
Use Cases:
- Access remote web interfaces (databases, admin panels)
- Connect to services behind firewall
- Secure insecure protocols (HTTP ā HTTPS tunnel)
Example - Access Remote MySQL:
ssh -L 3307:localhost:3306 database-server
Now connect MySQL client to localhost:3307, actually connects to remote:3306.
Remote Port Forwarding
Scenario: Allow remote server to access service on your local machine.
ssh -R 8080:localhost:3000 centos-vm
Purpose: Remote port 8080 forwards to your local port 3000.
Command Breakdown:
-R 8080:localhost:3000
: Remote forward specification8080
: Port on remote serverlocalhost:3000
: Your local service
What happens:
- Service running on your machine at port 3000
- Remote server can access it via localhost:8080
- Useful for demos, testing, sharing local dev server
Use Cases:
- Show local development to remote team
- Receive webhooks on local dev server
- Bypass NAT/firewall on your side
Dynamic Port Forwarding (SOCKS Proxy)
Create SOCKS proxy through SSH:
ssh -D 1080 centos-vm
Purpose: Create SOCKS5 proxy on local port 1080.
Command Breakdown:
-D 1080
: Dynamic forwarding on port 1080
Configure browser to use proxy:
- SOCKS5 proxy:
localhost:1080
Now all browser traffic routes through centos-vm, encrypted!
Use Cases:
- Browse as if you're on remote network
- Bypass geo-restrictions
- Secure browsing on untrusted networks
Persistent Tunnels in SSH Config
Add to ~/.ssh/config:
Host db-tunnel
HostName database-server.com
User admin
LocalForward 3307 localhost:3306
Host dev-share
HostName remote-server.com
User developer
RemoteForward 8080 localhost:3000
Usage:
ssh db-tunnel # Automatically sets up MySQL tunnel
ssh dev-share # Automatically shares your local dev server
Secure File Transfer with SCP and SFTP
SCP (Secure Copy)
Copy file TO server:
scp file.txt centos-vm:/home/centos9/
Purpose: Securely copy file.txt to remote home directory.
Command Breakdown:
scp
: Secure copy commandfile.txt
: Source file (local)centos-vm:/home/centos9/
: Destination (uses SSH config alias)
Output:
file.txt 100% 1024 1.0MB/s 00:00
Shows progress, transfer speed, and time.
Copy file FROM server:
scp centos-vm:/var/log/messages ./
Copy directory recursively:
scp -r /local/directory/ centos-vm:/remote/path/
Command Breakdown:
-r
: Recursive (include subdirectories)
Copy with specific port:
scp -P 2222 file.txt user@host:/path/
Note: SCP uses -P
(uppercase) for port, SSH uses -p
(lowercase).
Preserve permissions and timestamps:
scp -p file.txt centos-vm:/home/centos9/
Command Breakdown:
-p
: Preserve modification times, access times, and modes
Copy multiple files:
scp file1.txt file2.txt file3.txt centos-vm:/destination/
SFTP (SSH File Transfer Protocol)
Start interactive SFTP session:
sftp centos-vm
Output:
Connected to centos-vm.
sftp>
Common SFTP Commands:
Command | Purpose | Example |
---|---|---|
ls | List remote files | ls -la |
lls | List local files | lls -la |
pwd | Print remote working directory | pwd |
lpwd | Print local working directory | lpwd |
get | Download file from remote | get remote-file.txt |
put | Upload file to remote | put local-file.txt |
mget | Download multiple files (wildcards) | mget *.txt |
mput | Upload multiple files (wildcards) | mput *.log |
rm | Remove remote file | rm old-file.txt |
mkdir | Create remote directory | mkdir backup |
rmdir | Remove remote directory | rmdir old-backup |
chmod | Change remote file permissions | chmod 644 file.txt |
exit | Exit SFTP session | exit or quit |
Example SFTP Session:
sftp> ls
backup.tar.gz file.txt script.sh
sftp> get backup.tar.gz
Fetching /home/centos9/backup.tar.gz to backup.tar.gz
backup.tar.gz 100% 10MB 5.0MB/s 00:02
sftp> lls
local-file.txt
sftp> put local-file.txt
Uploading local-file.txt to /home/centos9/local-file.txt
local-file.txt 100% 2048 2.0KB/s 00:00
sftp> exit
Batch SFTP (non-interactive):
sftp centos-vm << EOF
get remote-file.txt
put local-file.txt
exit
EOF
SSH Agent and Key Management
SSH agent manages your private keys so you don't have to enter passphrases repeatedly.
Starting SSH Agent
Check if agent running:
echo $SSH_AUTH_SOCK
If empty, start agent:
eval $(ssh-agent)
Output:
Agent pid 12345
Add key to agent:
ssh-add ~/.ssh/id_ed25519
If key has passphrase:
Enter passphrase for /home/username/.ssh/id_ed25519:
Enter passphrase once.
Output:
Identity added: /home/username/.ssh/id_ed25519 (your_email@example.com)
List loaded keys:
ssh-add -l
Output:
256 SHA256:abc123def456ghi789jkl012mno345pqr678stu901vwx234yz your_email@example.com (ED25519)
Now SSH without passphrase (for this session):
ssh centos-vm
No passphrase prompt! Agent automatically provides key.
Remove all keys from agent:
ssh-add -D
Auto-start SSH agent (add to ~/.bashrc
or ~/.zshrc
):
# Auto-start SSH agent
if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
Agent Forwarding
Scenario: SSH from your machine ā Server A ā Server B, using keys from your machine.
Enable in ~/.ssh/config:
Host server-a
HostName server-a.example.com
User admin
ForwardAgent yes
Or use -A flag:
ssh -A centos-vm
Now from centos-vm:
ssh another-server
Uses YOUR keys (forwarded through agent) to authenticate to another-server!
Security Warning: Only use agent forwarding on trusted servers. Malicious root user on intermediate server could use your forwarded keys!
SSH in Scripts and Automation
Non-Interactive SSH
Execute command without opening shell:
ssh centos-vm "hostname && uptime"
Output:
localhost.localdomain
18:30:15 up 2:45, 1 user, load average: 0.15, 0.20, 0.18
Run multiple commands:
ssh centos-vm "cd /var/log && du -sh * | sort -h | tail -5"
Execute local script on remote server:
ssh centos-vm 'bash -s' < local-script.sh
Automation Script Example
Backup Script:
#!/bin/bash
# Automated backup to remote server
REMOTE="centos-vm"
REMOTE_PATH="/backups"
BACKUP_FILE="backup-$(date +%Y%m%d-%H%M%S).tar.gz"
# Create backup
echo "Creating backup..."
tar czf "$BACKUP_FILE" /important/data/
# Copy to remote
echo "Copying to remote server..."
scp "$BACKUP_FILE" "${REMOTE}:${REMOTE_PATH}/"
# Verify
echo "Verifying remote file..."
REMOTE_SIZE=$(ssh "$REMOTE" "du -b ${REMOTE_PATH}/${BACKUP_FILE} | cut -f1")
LOCAL_SIZE=$(du -b "$BACKUP_FILE" | cut -f1)
if [ "$REMOTE_SIZE" -eq "$LOCAL_SIZE" ]; then
echo "Backup successful!"
rm "$BACKUP_FILE"
else
echo "ERROR: Size mismatch!"
exit 1
fi
Make executable:
chmod +x backup.sh
Run:
./backup.sh
Cron Job with SSH
Edit crontab:
crontab -e
Add daily backup at 2 AM:
0 2 * * * /home/username/backup.sh >> /var/log/backup.log 2>&1
Ensure SSH key has no passphrase or use ssh-agent in script:
#!/bin/bash
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519_nopass
# ... rest of script
Best Practices Summary
Security Best Practices
-
Always Use Key Authentication
- Disable password authentication in production
- Use Ed25519 or RSA 4096-bit keys
- Protect private keys with passphrases
-
Harden SSH Configuration
- Disable root login
- Restrict users with AllowUsers/AllowGroups
- Use non-standard port
- Implement rate limiting
-
Regular Maintenance
- Update OpenSSH regularly
- Rotate keys periodically
- Audit authorized_keys files
- Review SSH logs
-
Network Security
- Use firewall rules
- Consider fail2ban for brute force protection
- Use VPN for extra security layer
- Restrict SSH access by IP when possible
Operational Best Practices
-
Use SSH Config
- Create aliases for frequent connections
- Standardize settings across hosts
- Document custom configurations
-
Key Management
- Use different keys for different purposes
- Label keys with descriptive comments
- Store keys securely (encrypted backups)
- Never commit private keys to git!
-
Monitoring and Logging
- Enable verbose logging
- Monitor auth.log for suspicious activity
- Set up alerts for repeated failures
- Keep audit trail
-
Automation
- Use ssh-agent for convenience
- Create scripts for repetitive tasks
- Document automation workflows
- Test scripts in safe environment first
Command Cheat Sheet
Key Generation and Management
Command | Purpose |
---|---|
ssh-keygen -t ed25519 -C "comment" | Generate ED25519 key pair |
ssh-keygen -t rsa -b 4096 | Generate 4096-bit RSA key |
ssh-copy-id user@host | Copy public key to server |
ssh-add ~/.ssh/id_ed25519 | Add key to SSH agent |
ssh-add -l | List keys in agent |
ssh-keygen -R hostname | Remove host from known_hosts |
SSH Connection Commands
Command | Purpose |
---|---|
ssh user@host | Basic SSH connection |
ssh -p 2222 user@host | Connect to custom port |
ssh -i ~/.ssh/key user@host | Use specific key |
ssh -v user@host | Verbose output (debugging) |
ssh -A user@host | Enable agent forwarding |
ssh user@host "command" | Execute command remotely |
Port Forwarding
Command | Purpose |
---|---|
ssh -L 8080:localhost:80 host | Local port forwarding |
ssh -R 8080:localhost:80 host | Remote port forwarding |
ssh -D 1080 host | Dynamic forwarding (SOCKS proxy) |
ssh -fNT -L 8080:localhost:80 host | Background tunnel (no shell) |
File Transfer
Command | Purpose |
---|---|
scp file user@host:/path/ | Copy file to remote |
scp user@host:/path/file ./ | Copy file from remote |
scp -r dir user@host:/path/ | Copy directory recursively |
scp -P 2222 file user@host:/path/ | Copy using custom port |
sftp user@host | Interactive SFTP session |
rsync -avz -e ssh src/ user@host:dest/ | Sync files over SSH |
Server Configuration
Command | Purpose |
---|---|
sudo sshd -t | Test sshd_config syntax |
sudo systemctl restart sshd | Restart SSH daemon |
sudo systemctl status sshd | Check SSH daemon status |
sudo journalctl -u sshd -f | Follow SSH logs in real-time |
sudo firewall-cmd --add-service=ssh | Allow SSH through firewall |
Summary
Congratulations! You've mastered advanced SSH techniques:
ā Key Authentication: Generated keypairs, deployed keys, passwordless login ā SSH Config: Created connection shortcuts and persistent configurations ā Security Hardening: Disabled password auth, restricted access, configured firewalls ā Port Forwarding: Local, remote, and dynamic tunneling for secure access ā File Transfer: Mastered SCP and SFTP for secure file operations ā SSH Agent: Managed keys efficiently, enabled agent forwarding ā Automation: Integrated SSH into scripts and scheduled tasks ā Best Practices: Implemented security and operational standards
Final Recommendations
For Personal Use
- Use ED25519 keys with passphrases
- Configure SSH aliases for frequent connections
- Enable ssh-agent for convenience
- Keep SSH client and server updated
For Production Servers
- Mandatory: Disable password authentication
- Mandatory: Disable root login
- Implement fail2ban or similar
- Use AllowUsers to restrict access
- Consider custom port (defense in depth)
- Monitor logs regularly
- Implement key rotation policy
For Automation
- Use separate keys for automation (no passphrase)
- Restrict automated keys with forced commands
- Monitor automated access carefully
- Rotate automation keys periodically
You now have professional-level SSH skills! Use them wisely and securely. š