G
GuideDevOps
Lesson 14 of 17

SSH & Remote Access

Part of the Linux Fundamentals tutorial series.

Secure Shell (SSH)

SSH is a cryptographic network protocol for operating network services securely over an unsecured network. As a DevOps engineer, SSH is your primary method for accessing cloud VMs, physical servers, and containers.

# Connect using username and IP address
$ ssh admin@192.168.1.100
admin@192.168.1.100's password: ********
 
# Connect using a specific port (non-default)
$ ssh -p 2222 admin@example.com
 
# Connect with a specific SSH key
$ ssh -i ~/.ssh/custom_key.pem admin@192.168.1.100

SSH Config File — ~/.ssh/config

Save time by configuring hosts in your SSH config:

$ cat ~/.ssh/config
# Production server
Host prod-server
    HostName 54.123.45.67
    User admin
    Port 22
    IdentityFile ~/.ssh/prod_key
    ForwardAgent yes
 
# Staging server
Host staging
    HostName staging.example.com
    User deploy
    Port 2222
    IdentityFile ~/.ssh/staging_key
 
# Jump host / bastion
Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/bastion_key
 
# Use bastion to reach internal servers
Host webserver
    HostName 10.0.1.50
    User ubuntu
    ProxyJump bastion
 
# Default settings for all hosts
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
# Now connect simply by nickname
$ ssh prod-server
$ ssh staging

Public Key Authentication

Passwords can be guessed. SSH keys are cryptographically strong and are the production standard.

Step 1: Generate a Keypair

$ ssh-keygen -t ed25519 -C "admin@example.com"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/admin/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/admin/.ssh/id_ed25519.
Your public key has been saved in /home/admin/.ssh/id_ed25519.pub.
 
$ ls -la ~/.ssh/
total 48
drwxr-xr-x  3 root root  4096 Apr 10 08:00 .
drwxr-xr-x  2 root root  4096 Apr 10 08:00 ..
-rw-------  1 admin admin  464 Apr 10 08:00 id_ed25519 Private key (keep secret!)
-rw-r--r--  1 admin admin  103 Apr 10 08:00 id_ed25519.pub Public key (can share)
-rw-r--r--  1 admin admin  205 Apr 10 08:00 config
-rw-r--r--  1 admin admin 3867 Apr 10 08:00 known_hosts

Key types:

TypeBitsRecommendation
ed25519256Recommended — modern, fast, secure
rsa4096Legacy — widely supported
ecdsa256/384/521Older elliptic curve

Step 2: Copy Public Key to Server

# The easy way (copies to server's authorized_keys)
$ ssh-copy-id admin@192.168.1.100
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: /home/admin/.ssh/id_ed25519.pub
/usr/bin/ssh-copy-id: INFO: attempting to log into server with new key(s)
admin@192.168.1.100's password: ********
 
Number of key(s) added: 1
 
# Now you can log in without a password
$ ssh admin@192.168.1.100

Manual method:

# On server, add your public key to authorized_keys
$ cat ~/.ssh/id_ed25519.pub | ssh admin@192.168.1.100 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
 
# Or directly edit on the server
$ ssh admin@192.168.1.100
$ mkdir -p ~/.ssh
$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5..." >> ~/.ssh/authorized_keys
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys

How Key Authentication Works

Client (has private key)          Server (has public key in authorized_keys)
      |                                        |
      |  1. Client: "I'd like to connect"     |
      |───────────────────────────────────────>
      |                                        |
      |  2. Server: "Prove yourself - sign this challenge"
      |<───────────────────────────────────────|
      |                                        |
      |  3. Client: Signs with private key    |
      |───────────────────────────────────────|
      |                                        |
      |  4. Server: Verifies signature using   |
      |     stored public key → Access granted |
      |<───────────────────────────────────────|

SSH Tunneling (Port Forwarding)

Local Port Forwarding

Access a remote service through an SSH tunnel — useful for accessing internal databases or services.

# Forward local port 5432 to remote PostgreSQL's port 5432
# Access remote DB through localhost:5432
$ ssh -L 5432:localhost:5432 admin@prod-server
 
# Forward local port 8080 to internal app at 10.0.1.50:8080
$ ssh -L 8080:10.0.1.50:8080 admin@bastion-server
 
# In another terminal, connect as if the DB is local
$ psql -h localhost -p 5432 -U myapp mydb

Dynamic Port Forwarding (SOCKS Proxy)

# Create a SOCKS proxy on local port 1080
$ ssh -D 1080 admin@bastion-server
 
# Configure browser to use localhost:1080 as SOCKS proxy
# All traffic routes through the bastion

Executing Remote Commands

Run commands on remote servers without entering an interactive shell:

# Run a single command
$ ssh admin@192.168.1.100 "systemctl status nginx"
 nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled)
     Active: active (running)
 
# Run with sudo (requires passwordless sudo on server)
$ ssh admin@192.168.1.100 "sudo systemctl restart nginx"
 
# Run multiple commands
$ ssh admin@192.168.1.100 "df -h && free -h"
 
# Copy files using SSH (remote to local)
$ scp admin@192.168.1.100:/var/log/app.log ./app.log
 
# Copy files (local to remote)
$ scp ./config.yaml admin@192.168.1.100:/tmp/config.yaml
 
# Copy entire directory
$ scp -r ./myapp admin@192.168.1.100:/opt/myapp

SSH Hardening

Server Configuration — /etc/ssh/sshd_config

# Disable root login
PermitRootLogin no
 
# Use key-based authentication only
PasswordAuthentication no
PubkeyAuthentication yes
 
# Change default port (security through obscurity)
Port 2222
 
# Disable empty passwords
PermitEmptyPasswords no
 
# Limit users who can SSH in
AllowUsers admin deploy
 
# Disable agent forwarding unless needed
AllowAgentForwarding no
 
# Client alive check (detect dead connections)
ClientAliveInterval 300
ClientAliveCountMax 2
 
# After changes, reload SSH
$ sudo systemctl reload ssh

SSH Jump Host / Bastion

Use a bastion/jump host to reach internal network servers:

# Direct (requires VPN or direct network access)
$ ssh admin@10.0.1.50
 
# Via bastion (standard cloud architecture)
$ ssh -J admin@bastion.example.com admin@10.0.1.50
 
# Or via SSH config (preferred)
# In ~/.ssh/config:
# Host webserver
#     HostName 10.0.1.50
#     ProxyJump admin@bastion.example.com

Quick Reference

TaskCommand
Connect to serverssh user@host
Connect with keyssh -i key.pem user@host
Connect on portssh -p 2222 user@host
Copy remote filescp user@host:/path local
Copy local filescp local user@host:/path
Run remote commandssh user@host "command"
Local port forwardssh -L local:remote user@host
Jump/bastionssh -J jumpuser@jumphost user@host
Generate keyssh-keygen -t ed25519 -C "email"
Copy public keyssh-copy-id user@host

Practice Challenge

  1. Generate an SSH keypair: ssh-keygen -t ed25519 -C "your_email"
  2. View your public key: cat ~/.ssh/id_ed25519.pub
  3. Create a ~/.ssh/config file with a server alias
  4. Use ssh-copy-id to install your public key on a test server
  5. Try connecting with ssh server-alias (using your config nickname)
  6. Use ssh user@host "df -h" to run a remote command