Skip to content

SSH — Secure Remote Access

SSH (Secure Shell) is how you log into and administer remote Linux machines over an encrypted channel. This page covers connecting, key-based authentication, the client config file, file transfer, tunnelling, and the most important sshd server options.

Tested on

OpenSSH 8.7 on AlmaLinux 9.4 (server and client). The same client commands work from macOS and Windows (OpenSSH is bundled with both).

The client and the server

OpenSSH ships as two parts:

  • Client (ssh, scp, sftp) — usually installed everywhere.
  • Server (sshd, the openssh-server package) — runs as a service on the machine you connect to.
# On the server: confirm sshd is running
sudo systemctl status sshd

Connecting

# Basic connection (defaults to port 22 and your local username)
ssh user@host

# Connect as a specific user to a hostname or IP
ssh [email protected]

# Custom port with -p
ssh -p 2222 [email protected]

The first time you connect you'll be asked to verify the host's fingerprint; once accepted it's stored in ~/.ssh/known_hosts.

Key-based authentication

Keys are more secure and convenient than passwords. You generate a key pair: a private key (kept secret on your machine) and a public key (copied to the server).

1. Generate a key

# ed25519 is the modern, recommended algorithm
ssh-keygen -t ed25519 -C "deepak@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/deepak/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Your identification has been saved in /home/deepak/.ssh/id_ed25519
Your public key has been saved in /home/deepak/.ssh/id_ed25519.pub

Tip

Always set a passphrase. Combined with ssh-agent (below) you only type it once per session, and a stolen private key file is then useless on its own.

2. Copy the public key to the server

# ssh-copy-id appends your public key to the server's authorized_keys
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]

This writes your key into ~/.ssh/authorized_keys on the server. You can now log in without a password:

Permissions matter

SSH refuses keys if permissions are too loose. On the server side:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Path Mode Meaning
~/.ssh 700 Owner only
~/.ssh/authorized_keys 600 Owner read/write only
~/.ssh/id_ed25519 600 Private key, owner only
~/.ssh/id_ed25519.pub 644 Public key, world-readable OK

The client config: ~/.ssh/config

Stop memorising long commands. Define host aliases once:

# ~/.ssh/config
Host web01
    HostName 192.168.10.25
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

# Reach a private host through a bastion/jump server
Host db01
    HostName 10.0.0.5
    User admin
    ProxyJump web01

Now ssh web01 expands to the full command, and ssh db01 automatically tunnels through web01.

ssh web01

File transfer

scp — quick copies

# Local file -> remote
scp ./report.pdf deploy@web01:/home/deploy/

# Remote file -> local
scp deploy@web01:/var/log/app.log ./

# Recursive directory copy
scp -r ./site/ deploy@web01:/var/www/

sftp — interactive sessions

sftp deploy@web01
# sftp> put localfile.txt
# sftp> get remotefile.txt
# sftp> ls
# sftp> bye

rsync over SSH — efficient sync

rsync only transfers differences and is ideal for backups and deployments. It uses SSH as its transport automatically.

# Mirror a local directory to the server (trailing slash matters)
rsync -avz --delete ./site/ deploy@web01:/var/www/site/

# Use a non-standard SSH port
rsync -avz -e "ssh -p 2222" ./site/ deploy@web01:/var/www/site/

-a archive (preserve perms/times), -v verbose, -z compress, --delete removes files on the destination that no longer exist on the source.

SSH tunnelling / port forwarding

SSH can forward TCP ports through its encrypted connection — useful for reaching services that aren't exposed publicly.

Local forward (-L) — reach a remote service locally

# Forward your local port 5432 to a database reachable from web01
ssh -L 5432:localhost:5432 deploy@web01
# Now `psql -h 127.0.0.1` on your laptop hits the remote DB

Syntax: -L <local_port>:<target_host>:<target_port>, where target_host is resolved from the server's perspective.

Remote forward (-R) — expose a local service to the remote side

# Make your laptop's port 3000 reachable as web01:8080
ssh -R 8080:localhost:3000 deploy@web01

Dynamic forward (-D) — SOCKS proxy

# Turn web01 into a SOCKS5 proxy on local port 1080
ssh -D 1080 deploy@web01
# Point a browser's SOCKS proxy at 127.0.0.1:1080 to browse via web01

Jump host (-J) — connect through a bastion

# Reach db01 (private) by jumping through the bastion web01
ssh -J deploy@web01 [email protected]

This is the command-line equivalent of the ProxyJump config directive shown earlier.

ssh-agent — type your passphrase once

The agent holds your decrypted private key in memory so you don't re-enter the passphrase for every connection.

# Start an agent (often already running under your desktop session)
eval "$(ssh-agent -s)"

# Add your key (prompts for the passphrase once)
ssh-add ~/.ssh/id_ed25519

# List loaded keys
ssh-add -l

Combine with agent forwarding (ssh -A) to use your local keys from the remote host — convenient, but only enable it toward hosts you trust.

Key sshd_config server options at a glance

The server is configured in /etc/ssh/sshd_config (and drop-ins under /etc/ssh/sshd_config.d/).

# /etc/ssh/sshd_config — common hardening-relevant options
Port 2222                      # change from default 22 to cut log noise
PermitRootLogin no             # never allow direct root login
PasswordAuthentication no      # force key-based auth only
PubkeyAuthentication yes       # ensure keys are accepted
AllowUsers deploy admin        # whitelist who may log in

After editing, validate and reload:

# Validate the syntax BEFORE applying
sudo sshd -t

# Apply the new config
sudo systemctl reload sshd

Always keep a second session open

Before you reload sshd after changing config (especially Port, PasswordAuthentication, or AllowUsers), open a second SSH session and keep it connected. Test logging in via a new connection. If you locked yourself out, the existing session lets you revert. On a cloud host, also ensure your firewall/security group permits the new port first — see Firewalls.

For the full server lockdown (fail2ban, MFA, ciphers, idle timeouts), see the Server Hardening Checklist.

Verify your work

# 1. Key-based login works without a password prompt
ssh web01 'echo connected as $(whoami)'
# connected as deploy

# 2. The agent holds your key
ssh-add -l

# 3. Server config is syntactically valid
sudo sshd -t && echo "sshd_config OK"

# 4. A local forward actually maps a port
ssh -fN -L 9000:localhost:80 web01 && ss -tlnp | grep 9000

Summary

  • ssh user@host (with -p for a custom port) is the core connect command; ~/.ssh/config lets you alias hosts and set User, Port, IdentityFile, and ProxyJump.
  • Prefer ed25519 key auth: ssh-keygenssh-copy-id → keys land in ~/.ssh/authorized_keys; correct permissions (700/600) are mandatory.
  • Transfer files with scp, sftp, or rsync -e ssh.
  • Tunnel with -L (local), -R (remote), -D (SOCKS), and -J (jump host); ssh-agent + ssh-add cache your passphrase.
  • Tighten the server via sshd_config (PermitRootLogin no, PasswordAuthentication no, AllowUsers), always validating with sshd -t and testing in a second session.

Continue to Firewalls to control which ports are reachable.

Test yourself