SSH and remote access
In this series (15 parts)
- What is Linux and how it differs from other OSes
- Installing Linux and setting up your environment
- The Linux filesystem explained
- Users, groups, and permissions
- Essential command line tools
- Shell scripting fundamentals
- Processes and job control
- Standard I/O, pipes, and redirection
- The Linux networking stack
- Package management and software installation
- Disk management and filesystems
- Logs and system monitoring
- SSH and remote access
- Cron jobs and task scheduling
- Linux security basics for sysadmins
SSH (Secure Shell) is how you connect to remote Linux machines. Every server you manage, every cloud instance you deploy, every CI/CD pipeline that pushes code uses SSH. Understanding it deeply matters for both operations and security.
Prerequisites
You should understand Linux networking (ports, firewalls) and users and permissions.
How SSH works
SSH creates an encrypted tunnel between your machine (the client) and the remote machine (the server). Everything that travels through this tunnel, including passwords, commands, and file transfers, is encrypted.
The connection process:
- Client connects to server on port 22
- Server sends its public host key
- Client verifies the host key (first time: asks you to trust it)
- They negotiate encryption algorithms and establish a shared secret using key exchange (similar to Diffie-Hellman)
- Client authenticates (password or public key)
- Encrypted session begins
Host key verification
The first time you connect to a server, you see:
ssh user@192.168.1.50
Output:
The authenticity of host '192.168.1.50' can't be established.
ED25519 key fingerprint is SHA256:abc123def456ghi789jkl012mno345pqr678stu901.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.1.50' (ED25519) to the list of known hosts.
This fingerprint is stored in ~/.ssh/known_hosts. If it changes later (for example, because the server was reinstalled or someone is doing a man-in-the-middle attack), SSH will warn you.
Key-based authentication
Password authentication is convenient but weak. Key-based authentication is both more secure and more convenient (no typing passwords).
How it works
You generate a key pair:
- Private key stays on your machine. Never share it.
- Public key goes on the server.
When you connect, the server sends a challenge. Your client signs it with the private key. The server verifies the signature with the public key. If it matches, you are in.
Generating keys
ssh-keygen -t ed25519 -C "pratik@devbox"
Output:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/pratik/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/pratik/.ssh/id_ed25519
Your public key has been saved in /home/pratik/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:xyz789abc456... pratik@devbox
- Use
ed25519(modern, fast, secure). RSA is also fine with 4096 bits. - Always set a passphrase. It protects the key if someone steals the file.
- The permissions must be correct: private key needs
600,.sshdirectory needs700.
ls -la ~/.ssh/
Output:
drwx------ 2 pratik pratik 4096 May 25 10:00 .
-rw------- 1 pratik pratik 411 May 25 10:00 id_ed25519
-rw-r--r-- 1 pratik pratik 97 May 25 10:00 id_ed25519.pub
-rw-r--r-- 1 pratik pratik 444 May 25 10:00 known_hosts
Example 1: Set up passwordless SSH between two hosts
Step 1: Generate the key (already done above)
Step 2: Copy the public key to the server
ssh-copy-id pratik@192.168.1.50
Output:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/pratik/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s)
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is because we want to install the new key
pratik@192.168.1.50's password: [type password]
Number of key(s) added: 1
What this did: appended your public key to ~/.ssh/authorized_keys on the remote server.
If ssh-copy-id is not available:
cat ~/.ssh/id_ed25519.pub | ssh pratik@192.168.1.50 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Step 3: Test it
ssh pratik@192.168.1.50
Output:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.5.0-44-generic x86_64)
Last login: Thu May 25 09:00:00 2026 from 10.0.0.50
pratik@server:~$
No password prompt. The key passphrase is asked by your local SSH agent (or you can use ssh-agent to cache it).
Step 4: Set up ssh-agent for passphrase caching
# Start the agent
eval $(ssh-agent)
# Add your key (enter passphrase once)
ssh-add ~/.ssh/id_ed25519
Output:
Enter passphrase for /home/pratik/.ssh/id_ed25519:
Identity added: /home/pratik/.ssh/id_ed25519 (pratik@devbox)
Now SSH connections use the cached key without asking for the passphrase again for the duration of the session.
Hardening sshd_config
The SSH server configuration lives at /etc/ssh/sshd_config. Here are the important settings for security:
# Edit the config
sudo nano /etc/ssh/sshd_config
Recommended settings:
# Disable root login
PermitRootLogin no
# Disable password authentication (key only)
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Use only SSH protocol 2
Protocol 2
# Limit authentication attempts
MaxAuthTries 3
# Set idle timeout (5 minutes)
ClientAliveInterval 300
ClientAliveCountMax 0
# Restrict to specific users
AllowUsers pratik deploy
# Change the default port (optional, defense in depth)
Port 2222
After editing:
# Test the config for syntax errors
sudo sshd -t
# Restart SSH
sudo systemctl restart ssh
⚠ Before restarting SSH, always have a second session open. If you misconfigure SSH and log out, you could lock yourself out of the server.
SSH config file
Create ~/.ssh/config to simplify connections:
cat > ~/.ssh/config << 'EOF'
Host webserver
HostName 192.168.1.50
User pratik
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host database
HostName 10.0.0.100
User deploy
IdentityFile ~/.ssh/id_ed25519
LocalForward 5433 localhost:5432
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
EOF
chmod 600 ~/.ssh/config
Now instead of ssh -p 2222 -i ~/.ssh/id_ed25519 pratik@192.168.1.50, you just type:
ssh webserver
SSH tunnels and port forwarding
Local port forwarding
Access a remote service through an encrypted tunnel. The service appears to be running on your local machine.
# Forward local port 5433 to remote machine's port 5432 (PostgreSQL)
ssh -L 5433:localhost:5432 pratik@database-server
Now you can connect to the remote PostgreSQL:
psql -h localhost -p 5433 -U dbuser mydatabase
The connection goes: your machine:5433 -> SSH tunnel -> database-server:5432.
Example 2: Create a local port forward to access a remote service
Suppose you have a web application running on a remote server’s port 8080, but the firewall only allows SSH (port 22). You need to access the web app from your browser.
Step 1: Create the tunnel
ssh -L 8080:localhost:8080 -N -f pratik@webserver
Flags:
-L 8080:localhost:8080forward local 8080 to remote 8080-Ndo not execute a remote command (tunnel only)-fgo to background after authentication
Step 2: Access the service locally
curl http://localhost:8080
Output:
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body><h1>Welcome to the application</h1></body>
</html>
Your browser can also access http://localhost:8080 and it will show the remote app.
Step 3: Close the tunnel
# Find the SSH tunnel process
ps aux | grep "ssh -L 8080"
Output:
pratik 12345 0.0 0.0 12345 1234 ? Ss 10:00 0:00 ssh -L 8080:localhost:8080 -N -f pratik@webserver
# Terminate it
kill 12345
Remote port forwarding
Make a local service accessible from the remote server:
# Make your local port 3000 accessible on the remote server's port 8080
ssh -R 8080:localhost:3000 pratik@remote-server
Now anyone who accesses remote-server:8080 hits your local port 3000.
Dynamic port forwarding (SOCKS proxy)
Create a SOCKS proxy that tunnels all traffic through the remote server:
ssh -D 1080 -N -f pratik@proxy-server
Configure your browser to use localhost:1080 as a SOCKS5 proxy. All web traffic will go through the remote server.
Transferring files
# Copy file to remote
scp local-file.txt pratik@webserver:/tmp/
# Copy file from remote
scp pratik@webserver:/var/log/app.log ./
# Copy directory recursively
scp -r ./project pratik@webserver:/home/pratik/
# Sync directories (more efficient for repeat transfers)
rsync -avz ./project/ pratik@webserver:/home/pratik/project/
rsync is preferred over scp because it only transfers changed files on subsequent runs.
What comes next
The next article covers Cron jobs and task scheduling, where you will automate tasks to run on a schedule.
For the attacker’s perspective on SSH, see Linux privilege escalation (SSH key theft and misuse) and Network attacks (SSH brute forcing).