Secure Shell provides encrypted remote login and file transfer. This lesson covers key generation, agent setup, per host configuration, safe permissions, copying public keys to servers, verifying host keys, and moving files with scp
and rsync
. It also shows useful port forwarding patterns and reliability settings.
Create modern SSH keys, log in without passwords, store per host options in ~/.ssh/config
, transfer files, and keep sessions stable on flaky networks.
Prerequisites
Generate a key pair
Use an Ed25519 key for most cases. Add a comment to identify the key.
ssh-keygen -t ed25519 -C "majd@laptop" -f ~/.ssh/id_ed25519
Prompts:
- passphrase: protects the private key at rest
- files created:
~/.ssh/id_ed25519
and~/.ssh/id_ed25519.pub
Set correct permissions.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
The file ~/.ssh/id_ed25519
is private. Share only the .pub
file.
Load the key into ssh-agent
The agent holds decrypted keys in memory so repeated logins do not ask for the passphrase.
# start an agent in the current shell if not already running
eval "$(ssh-agent -s)"
# add the key
ssh-add ~/.ssh/id_ed25519
# list keys currently loaded
ssh-add -l
Desktop environments often start an agent automatically. Keys can be loaded on first use by entering the passphrase.
Use the desktop keyring on Linux or the built in keychain on macOS to remember the passphrase. On servers, consider entering the passphrase per login instead of storing it.
Copy the public key to a server
Use ssh-copy-id
when available. The target must allow password login once to install the public key.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Manual method if ssh-copy-id
is not present:
cat ~/.ssh/id_ed25519.pub | ssh user@server 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
Test login.
ssh user@server
Edit /etc/ssh/sshd_config
to limit authentication methods. Example lines:
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 30
Restart the service after a syntax check:
sudo sshd -t && sudo systemctl restart sshd 2>/dev/null || sudo systemctl restart ssh
Verify host keys and known hosts
On first connect, SSH records the server fingerprint in ~/.ssh/known_hosts
. Verify the fingerprint out of band when possible.
ssh -o StrictHostKeyChecking=ask user@server
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub 2>/dev/null | awk '{print $2, $3}'
If a host key changes unexpectedly, SSH warns about a potential attack. Investigate before accepting the new key.
Avoid StrictHostKeyChecking no
except in throwaway test environments. Host key checks prevent man in the middle attacks.
Per host settings in ~/.ssh/config
Create a configuration file to avoid long command lines and to set safe defaults.
mkdir -p ~/.ssh
chmod 700 ~/.ssh
cat > ~/.ssh/config <<'EOF'
Host server
HostName 203.0.113.10
User user
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
ServerAliveCountMax 4
AddKeysToAgent yes
IdentitiesOnly yes
# jump host example
Host bastion
HostName 198.51.100.5
User admin
Host app-*
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
Compression yes
ForwardAgent no
EOF
chmod 600 ~/.ssh/config
Now connect with a short name.
ssh server
ssh app-01
Connection sharing reduces repeated handshakes. Add these lines:
Host *
ControlMaster auto
ControlPath ~/.ssh/ctl-%C
ControlPersist 10m
Copy files with scp
scp
copies files over SSH. Basic patterns:
# upload
scp local.txt user@server:/home/user/
# download
scp user@server:/var/log/syslog ./
# copy a directory recursively
scp -r site/ user@server:/var/www/site/
Flags:
-P
port number when the server does not use 22-i
identity file if not in config-C
compression for text heavy transfers
When copying to system locations such as /var/www
, prefer uploading to a home directory and then using sudo
on the server to move files into place with correct ownership.
Sync directories with rsync over SSH
rsync
is efficient for repeated transfers. It sends only changed blocks.
# archive mode, verbose, compress, show progress
rsync -avzP -e ssh ./site/ user@server:/srv/site/
# delete files on the destination that no longer exist locally (use with care)
rsync -avzP --delete -e ssh ./site/ user@server:/srv/site/
# use a non default port
rsync -avzP -e 'ssh -p 2222' ./data/ user@server:/srv/data/
Trailing slashes matter:
./site/
copies the contents into/srv/site/
./site
creates a subdirectory/srv/site/site
Add --dry-run
to preview changes before using --delete
.
Port forwarding and jump hosts
Local forwarding exposes a remote service on a local port.
# open localhost:8080 to reach remote 127.0.0.1:80
ssh -L 8080:127.0.0.1:80 user@server
Remote forwarding exposes a local service to the remote host.
# on the client, expose local 3000 as remote 9000
ssh -R 9000:127.0.0.1:3000 user@server
Dynamic forwarding creates a SOCKS proxy on localhost.
ssh -D 1080 user@server
Jump through a bastion host with -J
or ProxyJump
.
ssh -J bastion user@app-01
ForwardAgent yes
exposes keys to the remote host while the session is active. Enable only on trusted hosts, or prefer signed SSH certificates when available.
Keep sessions healthy
Add keepalives to reduce disconnects on idle links.
# client side in ~/.ssh/config
ServerAliveInterval 30
ServerAliveCountMax 4
# server side in /etc/ssh/sshd_config
ClientAliveInterval 60
ClientAliveCountMax 3
Servers may have both IPv4 and IPv6. Use ssh -4
or ssh -6
to force a family if connectivity issues appear.
Practical lab
- Generate a key and install it on the target server.
ssh-keygen -t ed25519 -C "lab@client"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
ssh user@server 'echo ok'
- Create a per host entry and test a short
ssh
command.
cat >> ~/.ssh/config <<'EOF'
Host lab
HostName server
User user
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
IdentitiesOnly yes
EOF
ssh lab 'uname -a'
- Transfer files with
scp
andrsync
.
scp /etc/hosts lab:/tmp/hosts.copy
rsync -avzP -e ssh /etc/ lab:/tmp/etc-copy/ --dry-run
- Try a local port forward to view a remote web app.
ssh -L 8080:127.0.0.1:80 lab
# then open http://127.0.0.1:8080 in a browser from the client
Troubleshooting
Permission denied (publickey)
The public key is missing fromauthorized_keys
, permissions are too open, or the wrong identity is used. Check-i
,IdentitiesOnly yes
, and directory modes:~/.ssh
700,authorized_keys
600.- Host key mismatch after a server rebuild. Remove the old line for that host from
~/.ssh/known_hosts
only after confirming the new fingerprint out of band. - Slow logins. Check DNS reverse lookups on the server. Disable with
UseDNS no
insshd_config
if appropriate, or fix name resolution. scp
permission errors to system paths. Upload to a home directory, then usesudo
on the server to move files with the correct ownership.rsync
transfers no files. Confirm the trailing slash usage and that the remote path exists and is writable.
Next steps
Day 11 introduces disks and filesystems. It covers lsblk
, partitions versus filesystems, mounting with mount
and /etc/fstab
, checking space with df
and du
, and a safe workflow to add a second disk in a VM.