Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Categories

SSH & Remote Access

Overview

SSH (Secure Shell) is the standard protocol for securely connecting to and managing remote Linux systems. Beyond interactive shells, SSH provides encrypted file transfer, port forwarding, agent forwarding, and key-based authentication — all essential for daily operations work.

SSH keys

Key types and generation

An SSH key pair consists of a private key kept on your machine and a public key installed on servers. ssh-keygen creates and manages both; Ed25519 is the recommended algorithm for new keys.

# Generate an Ed25519 key (modern, recommended)
ssh-keygen -t ed25519 -C "john@example.com"

# Generate an RSA key (4096-bit)
ssh-keygen -t rsa -b 4096 -C "john@example.com"

# Generate with specific filename
ssh-keygen -t ed25519 -f ~/.ssh/prod_key -C "production access"

# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/id_ed25519

# Inspect a public key
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

Common key types

TypeAlgorithmRecommendation
ed25519Ed25519Preferred — fast, compact, secure
rsa (3072+)RSAFallback for older servers
ecdsaECDSAP-256 curve; usable but Ed25519 is better
dsaDSADeprecated — avoid

Copying keys to a server

ssh-copy-id installs your public key into the remote account's authorized_keys, enabling password-less logins.

ssh-copy-id user@remote-host # copy default key
ssh-copy-id -i ~/.ssh/prod_key user@host # copy specific key
ssh-copy-id -p 2222 user@host # non-standard port

Manual method if ssh-copy-id is unavailable:

cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Connecting

Basic connections

The basic ssh invocation opens an interactive shell on a remote host. Common options select a non-standard port, a specific identity file, and the verbosity level for diagnosing connection problems.

ssh user@host # standard connection
ssh -p 2222 user@host # non-standard port
ssh -i ~/.ssh/prod_key user@host # specific identity file
ssh -v user@host # verbose (debugging connection issues)
ssh -vvv user@host # very verbose

Connection patterns

Beyond interactive shells, SSH can run a single remote command, pipe a local script to a remote shell, or hop through a jump host to reach hosts in a private network.

# Run a single command
ssh user@host "uptime"
ssh user@host "sudo systemctl restart nginx"

# Run a local script on a remote host
ssh user@host "bash -s" < local_script.sh

# Multi-hop (jump host)
ssh -J jump-user@bastion.internal target-user@10.0.1.50

# Escape sequences (from an active session)
~. # force disconnect
~^Z # background the connection
~? # list escape sequences

SSH client configuration

The ~/.ssh/config file allows persistent per-host connection settings.

Basic host configuration

Persistent settings for a host live in a config block: an alias, the real hostname, user, port, and identity file. Wildcard patterns apply shared defaults to groups of hosts.

# ~/.ssh/config

Host prod-web-1
HostName 10.0.1.50
User deploy
Port 22
IdentityFile ~/.ssh/prod_key

Host prod-web-2
HostName 10.0.1.51
User deploy
IdentityFile ~/.ssh/prod_key

# Wildcard patterns
Host staging-*
User ubuntu
IdentityFile ~/.ssh/staging_key
StrictHostKeyChecking no
UserKnownHostsFile /dev/null

Host *.internal
ProxyJump bastion.internal
User admin

Useful SSH config options

OptionPurposeExample
HostNameTarget hostname or IPHostName 10.0.0.5
UserUsernameUser deploy
PortSSH portPort 2222
IdentityFilePrivate key pathIdentityFile ~/.ssh/prod_key
ProxyJumpJump hostProxyJump bastion
ForwardAgentAgent forwardingForwardAgent yes
ServerAliveIntervalKeep-alive intervalServerAliveInterval 60
ServerAliveCountMaxMax keep-alive countServerAliveCountMax 5
CompressionEnable compressionCompression yes
StrictHostKeyCheckingAccept unknown hostsStrictHostKeyChecking accept-new
UserKnownHostsFileKnown hosts fileUserKnownHostsFile ~/.ssh/known_hosts
LocalForwardLocal port forwardLocalForward 5432 db.internal:5432
RemoteForwardRemote port forwardRemoteForward 8080 localhost:80

Server keep-alive settings

Keep-alive settings prevent idle connections from being dropped by NATs or firewalls. Configure them per-connection on the client side, or globally for all sessions on the server.

# Client-side keep-alive
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=5 user@host

# Server-side keep-alive (/etc/ssh/sshd_config)
ClientAliveInterval 60
ClientAliveCountMax 5

Port forwarding (tunneling)

Local port forwarding

Access a remote service through an SSH tunnel:

# Forward local port 8080 to remote:80
ssh -L 8080:localhost:80 user@web-server

# Forward local port 5432 to a database via a jump host
ssh -L 5432:db.internal:5432 user@bastion

# Access remote service on localhost:8080
curl http://localhost:8080

Remote port forwarding

Expose a local service to a remote host:

# Expose local port 3000 to remote:8080
ssh -R 8080:localhost:3000 user@public-server

# Now others on public-server can access your local service via port 8080

Dynamic (SOCKS) forwarding

Create a SOCKS proxy through the SSH connection:

ssh -D 1080 user@bastion
# Configure browser to use SOCKS5 proxy at localhost:1080

Background tunnels

Adding -fN sends the connection to the background after authentication and suppresses the remote shell — useful for keeping a tunnel open without occupying a terminal.

ssh -fN -L 5432:db.internal:5432 user@bastion
# -f: background, -N: no remote command

SSH agent

The ssh-agent holds decrypted private keys in memory, so you only type your passphrase once per session.

Starting the agent

The agent keeps decrypted keys in memory so you only enter your passphrase once per session. Start it, add your keys, then list what is loaded to confirm.

eval "$(ssh-agent)" # start a new agent
ssh-add # add default key (~/.ssh/id_rsa, etc.)
ssh-add ~/.ssh/prod_key # add a specific key
ssh-add -l # list loaded keys
ssh-add -L # list public keys
ssh-add -d ~/.ssh/prod_key # remove a key
ssh-add -D # remove all keys
ssh-add -t 3600 ~/.ssh/prod_key # add key with 1-hour lifetime

Agent forwarding

Agent forwarding lets you use your local keys on a remote host without copying them:

ssh -A user@bastion # forward agent
ssh -A -J user@bastion 10.0.1.50 # jump host with agent forwarding

Security note: Agent forwarding exposes your agent socket to the remote host. Use with caution on untrusted servers, or prefer ProxyJump (which doesn't expose the agent).

Server configuration

Key server settings (/etc/ssh/sshd_config)

These hardened sshd_config settings enforce key-only authentication: disable root login and password auth, restrict which users may log in, and set timeouts to kill idle sessions.

Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
AllowUsers deploy admin
MaxAuthTries 3
ClientAliveInterval 60
ClientAliveCountMax 5
X11Forwarding no

After editing:

sshd -t # test configuration syntax
systemctl reload sshd # apply changes without dropping connections
systemctl restart sshd # restart sshd (may drop existing sessions)

Managing authorized keys

The authorized_keys file grants login access to the matching private key. Each line holds one public key, and options at the start of a line can restrict what that key may do.

# ~/.ssh/authorized_keys (one key per line)
ssh-ed25519 AAAAC3... deploy@host

# Restrict a key to a single command
command="/opt/backup/run.sh" ssh-ed25519 AAAAC3... backup-bot

# Restrict a key by source IP
from="10.0.0.0/8" ssh-ed25519 AAAAC3... internal-user

File transfer

scp

scp copies files securely over SSH — to and from remote hosts, recursively for directories, and with attribute preservation.

# Copy to remote
scp file.txt user@host:/path/to/destination/
scp -P 2222 file.txt user@host:~/app/

# Copy from remote
scp user@host:/var/log/syslog ./logs/

# Recursive copy
scp -r configs/ user@host:/etc/myapp/

# Preserve attributes
scp -p file.txt user@host:/tmp/

rsync

rsync efficiently synchronizes files between local and remote systems with delta transfers.

# Basic sync to remote
rsync -avz ./www/ user@host:/var/www/

# Sync from remote
rsync -avz user@host:/var/log/ ./logs/

# Dry-run (preview without transferring)
rsync -avzn ./www/ user@host:/var/www/

# Delete files on destination that don't exist on source
rsync -avz --delete ./www/ user@host:/var/www/

# Common flags
rsync -avP ./dir/ user@host:/dir/ # archive mode, verbose, show progress
FlagEffect
-aArchive mode: recursive, preserve permissions, timestamps, symlinks
-vVerbose
-zCompress during transfer
-PShow progress and keep partial files
-nDry-run
--deleteRemove files on dest not present on source
--excludeExclude patterns (e.g., --exclude='*.log')
-e "ssh -p 2222"Use specific SSH port

sftp

sftp provides an interactive, FTP-style session over SSH for browsing, uploading, and downloading files.

sftp user@host # interactive FTP-like session
sftp -P 2222 user@host # non-standard port

Interactive commands: ls, cd, get, put, mget, mput, rm, mkdir, bye.

Troubleshooting connectivity

Work through connection failures systematically: increase verbosity, check DNS and port reachability, verify known_hosts and key permissions, and inspect the server's authentication logs.

# Verbose connection attempts
ssh -vvv user@host

# Check DNS
dig +short host.example.com

# Check port reachability
nc -zv host.example.com 22
telnet host.example.com 22

# Check known_hosts issues
ssh-keygen -R host.example.com # remove host from known_hosts
ssh -o StrictHostKeyChecking=no user@host # skip host key check (insecure)

# Check key permissions
ls -la ~/.ssh/
# Should be: dir=700, private keys=600, public keys=644
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/id_*.pub

# Check server logs
ssh user@host "sudo tail -50 /var/log/auth.log" # Debian
ssh user@host "sudo tail -50 /var/log/secure" # RHEL

See also