SSH and Remote Administration¶
Overview¶
SSH (Secure Shell) is the standard transport for remote Linux administration, file transfer, Git operations, and tunneling application ports. Password authentication is brittle and brute-forceable; key-based authentication with a hardened sshd_config is the production baseline.
This tutorial covers generating Ed25519 keys, organizing multi-host workflows with ~/.ssh/config, transferring files with scp and sftp, hardening the SSH server, and understanding why agent forwarding is powerful but dangerous.
This is Module 5, Tutorial 11 in the REBASH Academy Linux series.
Prerequisites¶
- Shell Scripting Fundamentals — environment variables and file permissions
- User and Group Management — users, home directories,
sudo - Two Linux systems (or one system connecting to
localhost) for lab exercises - OpenSSH client and server (
openssh-client,openssh-server)
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Generate and deploy Ed25519 SSH key pairs with correct file permissions
- Configure
~/.ssh/configfor host aliases, identity files, and jump hosts - Transfer files securely with
scpand interactivesftp - Harden
sshd_config(disable root login, disable passwords, limit users) - Diagnose connection failures with
ssh -vand log files - Explain the security trade-offs of SSH agent forwarding
Architecture Diagram¶
flowchart LR
subgraph Client
A[ssh client]
B["~/.ssh/config"]
C[Private Key]
D[ssh-agent]
end
subgraph Network
E["Port 22 / TCP"]
end
subgraph Server
F[sshd]
G[authorized_keys]
H["Shell / sftp-server"]
end
A --> B
A --> C
C -.-> D
A -->|encrypted session| E
E --> F
F --> G
F --> H```
## Theory
### How SSH authentication works
1. Client initiates TCP connection to port 22 (or custom port).
2. Server presents host key; client verifies against `~/.ssh/known_hosts`.
3. Client and server negotiate encryption (modern defaults: chacha20-poly1305, AES-GCM).
4. Client authenticates via:
- **Public key** — server checks `~/.ssh/authorized_keys` on the server
- **Password** — discouraged in production
- **Keyboard-interactive / MFA** — often layered on top of keys
The private key never leaves the client. Only the public key is copied to the server.
### Key types and generation
| Algorithm | Key size | Recommendation |
|-----------|----------|----------------|
| Ed25519 | 256-bit | **Preferred** — fast, secure, short keys |
| RSA | 4096-bit | Legacy compatibility |
| ECDSA | 256-bit | Acceptable; prefer Ed25519 for new keys |
Never reuse personal keys for production servers. Use separate keys per environment (lab, staging, prod) with descriptive comments.
### File permissions — non-negotiable
SSH refuses keys with loose permissions:
| Path | Permission |
|------|------------|
| `~/.ssh/` | `700` |
| `~/.ssh/id_ed25519` (private) | `600` |
| `~/.ssh/id_ed25519.pub` (public) | `644` |
| `~/.ssh/config` | `600` |
| `~/.ssh/authorized_keys` | `600` |
### Client configuration (`~/.ssh/config`)
Host blocks simplify daily workflows:
Host prod-web HostName 10.0.1.50 User deploy IdentityFile ~/.ssh/prod_ed25519 Port 2222 Supports **ProxyJump** for bastion access: `ProxyJump bastion.example.com`.
### scp vs sftp
Both run over SSH:
- **scp** — fast one-off copies; syntax mirrors `cp`. Being deprecated in favor of `sftp` in some OpenSSH versions; use `scp -O` for legacy protocol if needed.
- **sftp** — interactive session, resumable transfers, scripting with `sftp -b batchfile`.
For automation, consider `rsync -avz -e ssh` for incremental sync.
### Server hardening essentials
Production `sshd_config` baseline:
After changes: `sudo sshd -t && sudo systemctl reload sshd` (or `sshd` on RHEL).
### SSH agent and agent forwarding
**ssh-agent** holds decrypted private keys in memory so you enter the passphrase once.
**Agent forwarding** (`ssh -A` or `ForwardAgent yes`) lets a remote server use your local agent to authenticate further SSH hops (e.g., laptop → bastion → internal server).
!!! danger "Agent forwarding warning"
If an attacker gains code execution on the intermediate server, they can use your forwarded agent to authenticate as **you** to any server that trusts your key — for the lifetime of the connection. Only enable forwarding to hosts you fully trust, prefer `ProxyJump` over `-A`, and use `AllowAgentForwarding no` on bastions when possible. Consider `ProxyCommand` with `ssh -W` instead.
## Hands-on Lab
### Step 1 – Generate an Ed25519 key pair
```bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -C "rebash-lab-$(whoami)@$(hostname -s)" \
-f ~/.ssh/rebash_ed25519 -N ""
ls -la ~/.ssh/rebash_ed25519*
Expected output: -rw------- private key and -rw-r--r-- public key.
View the public key:
Expected output: single line starting with ssh-ed25519 AAAA....
Step 2 – Install public key for local login¶
Test key-based login to localhost:
Expected output: SSH key auth OK
Step 3 – Configure SSH client¶
cat >> ~/.ssh/config << 'EOF'
Host lab-local
HostName localhost
User YOUR_USER
IdentityFile ~/.ssh/rebash_ed25519
StrictHostKeyChecking accept-new
EOF
chmod 600 ~/.ssh/config
Replace YOUR_USER with your username, then:
Expected output: localhost hostname and your username.
Step 4 – Verbose debugging¶
Simulate a failed connection to see diagnostics:
Expected output: debug lines showing key load failure and authentication methods tried.
Successful verbose tail:
Expected output: lines mentioning Offering public key and Authenticated.
Step 5 – scp file transfer¶
echo "deploy artifact v1" > /tmp/rebash_deploy.txt
scp -i ~/.ssh/rebash_ed25519 /tmp/rebash_deploy.txt lab-local:/tmp/
ssh lab-local 'cat /tmp/rebash_deploy.txt'
Expected output: deploy artifact v1 printed from remote /tmp/.
Copy directory recursively:
mkdir -p /tmp/rebash_dir && echo "nested" > /tmp/rebash_dir/nested.txt
scp -r -i ~/.ssh/rebash_ed25519 /tmp/rebash_dir lab-local:/tmp/
ssh lab-local 'find /tmp/rebash_dir -type f'
Expected output: /tmp/rebash_dir/nested.txt
Step 6 – sftp interactive session¶
sftp -i ~/.ssh/rebash_ed25519 lab-local << 'EOF'
pwd
ls /tmp/rebash_deploy.txt
get /tmp/rebash_deploy.txt /tmp/downloaded_deploy.txt
bye
EOF
cat /tmp/downloaded_deploy.txt
Expected output: remote working directory, file listing, then deploy artifact v1 from downloaded copy.
Step 7 – Inspect and harden sshd (requires sudo)¶
Review current effective settings:
Expected output: current server values (varies by distro).
Recommended hardening snippet (review before applying):
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
sudo sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl reload ssh 2>/dev/null || sudo systemctl reload sshd
Verify syntax test passes (no output = success).
Keep an active session open
When hardening SSH, keep your current terminal connected. Test a new session before closing the old one to avoid lockout.
Step 8 – Agent forwarding demonstration and risk¶
Start agent and add key:
Expected output: key fingerprint listed.
Connect with forwarding and check remote socket:
Expected output: non-empty AGENT= path and your key fingerprint visible on the remote side — demonstrating why a compromised remote host can abuse your agent.
Disable forwarding in config for untrusted hosts:
Commands¶
| Command | Description |
|---|---|
ssh-keygen -t ed25519 | Generate Ed25519 key pair |
ssh-keygen -lf FILE.pub | Show fingerprint of public key |
ssh-copy-id -i KEY.pub user@host | Install public key on remote server |
ssh user@host 'command' | Run remote command non-interactively |
ssh -i KEY user@host | Specify identity (private key) file |
ssh -p PORT user@host | Connect to non-default port |
ssh -v / -vv / -vvv | Verbose debug output (more v = more detail) |
ssh -A user@host | Enable agent forwarding (use cautiously) |
ssh -J bastion user@host | Jump through bastion (ProxyJump) |
scp FILE user@host:/path | Copy file to remote |
scp -r DIR user@host:/path | Copy directory recursively |
sftp user@host | Interactive SFTP session |
ssh-add KEY | Add private key to ssh-agent |
ssh-add -l | List keys loaded in agent |
sshd -T | Dump effective server configuration |
Code¶
SSH config template for multi-environment ops¶
# ~/.ssh/config
Host *
AddKeysToAgent yes
IdentitiesOnly yes
ServerAliveInterval 60
Host bastion
HostName bastion.example.com
User jump
IdentityFile ~/.ssh/corp_ed25519
Host prod-*
User deploy
IdentityFile ~/.ssh/prod_ed25519
ProxyJump bastion
ForwardAgent no
Host staging-*
User deploy
IdentityFile ~/.ssh/staging_ed25519
ForwardAgent no
Batch sftp upload script¶
#!/bin/bash
set -euo pipefail
HOST="${1:?usage: sftp_upload.sh HOST LOCAL_FILE REMOTE_PATH}"
LOCAL="${2:?}"
REMOTE="${3:?}"
sftp -b - "$HOST" <<EOF
put $LOCAL $REMOTE
ls -l $REMOTE
EOF
Common Mistakes¶
Private key permissions too open
SSH ignores keys with group/world read. Fix with chmod 600 ~/.ssh/id_ed25519.
Enabling agent forwarding globally
ForwardAgent yes in Host * exposes your agent on every server you touch. Enable per trusted host only.
Editing sshd_config without syntax test
A typo locks everyone out. Always run sudo sshd -t before reload.
Copying the private key to the server
Only the public key goes in authorized_keys. Never upload, email, or store private keys on shared drives unencrypted.
Best Practices¶
Use Ed25519 keys with per-environment separation
Comment keys clearly: ssh-keygen -C "shaik@laptop-prod-2026".
Prefer ProxyJump over agent forwarding
ssh -J bastion internal authenticates hop-by-hop without exposing your agent socket on the bastion.
Audit authorized_keys regularly
Remove keys for departed team members. Each line is one trusted key — no comments needed for identification if you use key comments.
Use fail2ban or cloud firewall rules
Rate-limit port 22 at the network edge; combine with key-only auth for defense in depth.
Troubleshooting¶
| Issue | Cause | Solution |
|---|---|---|
Permission denied (publickey) | Key not in authorized_keys, wrong permissions, wrong user | Verify pubkey on server; check ~/.ssh perms; use -i correct key |
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED | Server rebuilt or MITM | Verify out-of-band; remove old entry from known_hosts |
Connection refused | sshd not running or firewall | sudo systemctl status ssh; check security groups / ufw |
Too many authentication failures | Client offers too many keys | Use IdentitiesOnly yes and -i specific key |
ssh: connect to host port 22: Connection timed out | Network ACL, wrong IP, sshd on different port | Verify IP, route, and -p port |
| Agent forwarding not working | AllowAgentForwarding no on server | Enable only if justified; prefer ProxyJump |
| scp fails on special files | scp protocol limitations | Use rsync -e ssh or tar over ssh |
Summary¶
- SSH encrypts remote administration and file transfer; Ed25519 key pairs replace password auth in production.
- Correct permissions on
~/.sshand keys are enforced by OpenSSH — not optional. ~/.ssh/configscales multi-host workflows with aliases, identity files, and ProxyJump for bastions.scpandsftpmove files over the same encrypted channel; choose based on interactive vs scripted needs.- Harden
sshd: no root login, no passwords, limitAllowUsers, test withsshd -tbefore reload. - Agent forwarding (
-A) is convenient but dangerous on untrusted hosts — an attacker can pivot using your credentials.
Interview Questions¶
- Why is Ed25519 preferred over RSA 2048 for new SSH keys?
- What permissions must
~/.ssh/authorized_keysand the private key file have? - Explain the difference between
scpandsftp. When would you use each? - What does
PermitRootLogin noaccomplish, and what is the safer alternative for admin tasks? - What is
ProxyJump, and how does it differ from agent forwarding? - Why is agent forwarding considered a security risk? Describe an attack scenario.
- How would you debug a
Permission denied (publickey)error? - What happens when a server's host key changes and you reconnect?
- What is the purpose of
IdentitiesOnly yesin SSH config? - How do you validate
sshd_configchanges before applying them to avoid lockout?
Related Tutorials¶
- Linux – Category Overview
- Shell Scripting Fundamentals (previous)
- Remote systemd Service Control (next)
- Linux Security Hardening Basics
- Learning Paths – DevOps Engineer