Arshad Mehmood

Search the blog

    SSH connection diagram

    DevOps

    Complete Guide to SSH: From Basics to Advanced

    8 min readDevOpssshsecuritylinux+2

    SSH (Secure Shell) is the foundation of secure remote access to servers and systems. This guide covers everything from basics to advanced usage, with practical examples you can use immediately.

    SSH Basics

    SSH lets you securely connect to remote servers, execute commands, and transfer files over encrypted connections.

    Basic Connection Syntax

    ssh [options] [user@]hostname
    ssh user@192.168.1.100
    ssh root@example.com

    The default SSH port is 22, but servers often use custom ports for security.

    Full TIL: SSH Connection Basics: Password, Keys, and Parameters

    Authentication Methods

    SSH supports two primary authentication methods:

    1. Password Authentication

    The simplest method—SSH prompts for password each time:

    ssh user@hostname
    # Prompted for password

    Drawbacks:

    • Requires typing password repeatedly
    • Vulnerable to brute-force attacks
    • Poor for automation and scripts

    Uses cryptographic key pairs instead of passwords. Much more secure and convenient.

    ssh -i ~/.ssh/id_ed25519 user@hostname

    Benefits:

    • ✅ No password to remember
    • ✅ Can’t be brute-forced
    • ✅ Perfect for scripts and automation
    • ✅ Passphrase-protected keys for extra security

    Full TIL: SSH Connection Basics

    Key Management

    Generating SSH Keys

    Modern best practice: use Ed25519 keys (faster, smaller, more secure than RSA):

    ssh-keygen -t ed25519 -C "your-email@example.com"
    # Or traditional RSA:
    ssh-keygen -t rsa -b 4096

    Creates two files:

    • ~/.ssh/id_ed25519 — Private key (keep secret, chmod 600)
    • ~/.ssh/id_ed25519.pub — Public key (share with servers)

    Using Specific Keys

    By default SSH uses keys from ~/.ssh/. To use a specific key:

    ssh -i /path/to/private/key user@hostname
    ssh -i ~/.ssh/custom_key -p 2222 user@hostname

    SSH Agent: Never Type Your Passphrase Again

    SSH agent stores decrypted keys in memory, so you enter your passphrase once per session instead of per connection.

    # Start the agent (usually automatic)
    eval "$(ssh-agent -s)"
    
    # Add your key (prompts for passphrase once)
    ssh-add ~/.ssh/id_ed25519
    
    # Now use SSH without typing passphrase
    ssh user@hostname  # No passphrase needed!

    Useful agent commands:

    ssh-add -l            # List loaded keys
    ssh-add -d ~/.ssh/id  # Remove specific key
    ssh-add -D            # Remove all keys

    Full TIL: Using SSH Agent to Manage Keys

    Remote Command Execution

    Run commands on remote servers without opening an interactive shell:

    ssh user@hostname "command"
    ssh user@hostname "ls -la /tmp"
    ssh user@hostname "whoami"

    Multiple Commands

    # Using && (only runs next if previous succeeds)
    ssh user@hostname "cd /tmp && ls -la"
    
    # Using ; (runs regardless)
    ssh user@hostname "uptime; df -h"
    
    # Multi-line script
    ssh user@hostname << 'EOF'
    cd /tmp
    ls -la
    whoami
    EOF

    Capturing Output

    ssh user@hostname "ls -la /tmp" > local-file.txt
    ssh user@hostname "cat /var/log/syslog" | grep "error" | wc -l

    Useful Patterns

    # Check if service is running
    ssh user@hostname "systemctl is-active nginx"
    
    # Run with sudo (prompts for password)
    ssh user@hostname "sudo systemctl restart nginx"
    
    # Execute local script on remote server
    ssh user@hostname < local-script.sh
    
    # Background task
    ssh user@hostname "nohup long-task > output.log 2>&1 &"

    Full TIL: Executing Remote Commands with SSH

    File Transfers

    SCP: Simple File Copy

    SCP is straightforward for one-off transfers:

    # Local to remote
    scp ./file.txt user@hostname:/path/to/remote/
    
    # Remote to local
    scp user@hostname:/path/to/file.txt ./local-folder/
    
    # With custom port and key
    scp -i ~/.ssh/key -P 2222 ./file.txt user@hostname:/path/

    Best for:

    • Quick, small file transfers
    • Simple one-time operations

    Full TIL: Using SCP for File Transfers

    RSYNC: Smart Synchronization

    RSYNC transfers only changed parts and can resume interrupted transfers:

    # Local to remote
    rsync -avz ./folder/ user@hostname:/path/to/remote/
    
    # Remote to local
    rsync -avz user@hostname:/path/to/folder/ ./local-folder/
    
    # Single file
    rsync -avz ./file.txt user@hostname:/path/

    Common options:

    OptionPurpose
    -aArchive mode (preserves permissions, times)
    -vVerbose output
    -zCompress during transfer
    --deleteDelete files in destination not in source
    --dry-runPreview without copying

    Best for:

    • Large backups
    • Repeated syncs
    • Resumable transfers

    Full TIL: Using RSYNC for File Transfers

    SCP vs RSYNC

    FeatureSCPRSYNC
    Speed (first transfer)SameSame
    Incremental❌ Full copy✅ Only changes
    Resume❌ Restarts✅ Continues
    ComplexitySimpleMore options
    Best useQuick transfersBackups, automation

    Advanced Techniques

    Port Forwarding

    Local port forwarding — Access remote service through local port:

    ssh -L 8080:localhost:3000 user@hostname -N -f
    # Now: localhost:8080 → remote:3000
    curl localhost:8080  # Accesses remote service

    Remote port forwarding — Expose local service to remote network:

    ssh -R 8080:localhost:3000 user@hostname -N -f
    # Remote can access: localhost:8080 → your local:3000

    Custom SSH Config

    Create ~/.ssh/config for easier, personalized connections:

    Host production
        Hostname example.com
        User deploy
        Port 2222
        IdentityFile ~/.ssh/production_key
        ServerAliveInterval 60
        LogLevel QUIET
    
    Host github
        Hostname github.com
        User git
        IdentityFile ~/.ssh/github_key

    Now use short names:

    ssh production
    ssh github  # For git operations

    ProxyJump: Jump Through Bastion Host

    Connect through a bastion/jump host:

    ssh -J bastion@jump-host internal@internal-server
    # Or in ~/.ssh/config
    Host internal-server
        Hostname 10.0.1.5
        ProxyJump jump-host

    Quiet Mode

    Suppress banners and diagnostic messages:

    ssh -q user@hostname
    ssh -q user@hostname "command"

    Full TIL: SSH Connection Without Welcome Message

    Troubleshooting

    Host Key Changed Warning

    If you see this warning:

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    This happens after server rebuilds or IP changes. Remove the old key:

    ssh-keygen -R hostname.com
    # Then reconnect
    ssh user@hostname.com

    Full TIL: Fix REMOTE HOST IDENTIFICATION HAS CHANGED

    Debug Connection Issues

    ssh -v user@hostname          # Verbose output
    ssh -vv user@hostname         # More verbose
    ssh -vvv user@hostname        # Maximum debug info

    Common Issues

    Permission Denied:

    • Check key file permissions: chmod 600 ~/.ssh/id_ed25519
    • Verify server authorized_keys contains your public key

    Connection Timeout:

    • Check if port is correct: ssh -p port ...
    • Verify firewall allows SSH traffic
    • Test connectivity: nc -zv hostname port

    Wrong Key Being Used:

    • Explicitly specify: ssh -i /path/to/key ...
    • List available keys: ssh-add -l

    Security Best Practices

    1. Use Key-Based Authentication

    Never rely on passwords for SSH. Keys are exponentially more secure.

    2. Protect Your Private Keys

    # Ensure proper permissions
    chmod 600 ~/.ssh/id_*
    
    # Use passphrase-protected keys
    ssh-keygen -p -f ~/.ssh/id_ed25519  # Change passphrase

    3. Use Ed25519 Keys (Not RSA)

    • ✅ Ed25519: Faster, smaller, newer, more secure
    • ⚠️ RSA: Still secure, but older (use 4096-bit minimum)
    ssh-keygen -t ed25519 -C "your-email@example.com"

    4. Limit SSH Access on Servers

    Edit /etc/ssh/sshd_config on your servers:

    # Disable password auth
    PasswordAuthentication no
    
    # Disable root login
    PermitRootLogin no
    
    # Change default port (security through obscurity is weak, but helps)
    Port 2222
    
    # Limit login attempts
    MaxAuthTries 3
    MaxSessions 10

    Restart SSH: sudo systemctl restart ssh or sudo systemctl restart sshd

    5. Use SSH Agent Instead of Unencrypted Keys

    Never use SSH keys without passphrases. Let SSH agent handle the passphrase.

    6. Monitor SSH Activity

    # View recent SSH attempts
    grep "sshd" /var/log/auth.log | tail -20
    
    # See who's currently connected
    who
    w
    
    # Check SSH public keys authorized on this system
    cat ~/.ssh/authorized_keys

    7. Rotate SSH Keys Periodically

    Even with secure keys, rotate them every 1-2 years as a best practice.

    Summary

    SSH is powerful, but only when used correctly. Key takeaways:

    1. Always use key-based auth never trust passwords for SSH
    2. Use Ed25519 keys they’re faster, smaller, and more secure
    3. Protect your private keys with passphrases
    4. Use SSH agent for convenience without sacrificing security
    5. Keep your ~/.ssh directory private (chmod 700 ~/.ssh)
    6. Monitor SSH access on your servers regularly

    Learning Resources

    All the TILs referenced in this post (concise, focused guides):

    Future Topics

    Topics I plan to learn and add later:

    • Host key verification and strict host checking
    • Copying public keys with ssh-copy-id
    • Advanced SSH config patterns (IdentitiesOnly, Include, multiple keys)
    • Connection multiplexing (ControlMaster, ControlPath, ControlPersist)
    • Agent forwarding and security implications
    • Keepalive tuning (ServerAliveInterval, ServerAliveCountMax)
    • Automation-focused flags (BatchMode, robust timeout handling)
    • Server hardening extras (AllowUsers, Fail2ban, firewall rules)
    • Hardware-backed SSH keys (ed25519-sk, ecdsa-sk)

    Next Steps

    • Review your SSH setup: Check your keys and permissions
    • Configure SSH config: Create ~/.ssh/config for frequently used hosts
    • Audit server access: Review who has SSH keys on your servers
    • Run SSH agent on startup: Add to your shell profile for seamless key management

    Discussion

    Comments are not enabled on this build. Production uses Giscus (GitHub Discussions) when the repository secrets are set.