SSH is the standard OpenSSH workflow for encrypted remote login, command execution, file transfer, port forwarding, and X11 forwarding.

Basic Use

ssh user@host
ssh -p 2222 user@host
ssh -i ~/.ssh/id_ed25519 user@host
ssh -J bastion user@internal-host
ssh user@host 'uname -a'

Useful client checks:

ssh -V
ssh -G host | less
ssh -vvv user@host

ssh -G prints the final client configuration after Host and Match blocks are applied. ssh -vvv is the fastest way to see which keys, algorithms, and authentication methods are being attempted.

Key Authentication

Generate a modern user key:

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519 -C "user@host"

Install the public key on the server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

Manual install on the server:

install -d -m 700 ~/.ssh
cat /tmp/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Load a key into the local agent:

ssh-add ~/.ssh/id_ed25519
ssh-add -l

Client-side permissions:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
[ -f ~/.ssh/config ] && chmod 600 ~/.ssh/config

Server-side permissions:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R "$USER:$USER" ~/.ssh

Client Config

Put repeatable settings in ~/.ssh/config:

Host lab
    HostName 192.0.2.10
    User user
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

Jump host:

Host internal
    HostName 10.10.10.25
    User user
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump bastion

Connection reuse:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/cm-%r@%h:%p
    ControlPersist 10m

Server Config

Edit /etc/ssh/sshd_config and test before reloading:

sudo sshd -t
sudo systemctl reload ssh || sudo systemctl reload sshd

Common key-only server settings:

PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password

Restrict login users when the host should only accept named accounts:

AllowUsers user deploy

Keep UsePAM yes on normal Linux distributions unless there is a specific reason to disable PAM-backed account and session handling.

Public-Key Failures

Permission denied (publickey) usually means the client did not offer the expected key, the server rejected the key, or the target account cannot use the configured authorized_keys file.

Debug from the client:

ssh -vvv -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes user@host

Check the server logs:

sudo journalctl -u ssh -u sshd -n 100 --no-pager

Check effective server options:

sudo sshd -T | grep -Ei 'pubkey|password|authorizedkeys|permitroot|strictmodes|allowusers'

Common fixes:

chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R "$USER:$USER" ~/.ssh

Also verify:

  • The login user is correct.
  • The public key is in that user’s ~/.ssh/authorized_keys.
  • AuthorizedKeysFile points to the expected path.
  • AllowUsers, DenyUsers, AllowGroups, or DenyGroups are not blocking the account.
  • The client is not offering too many keys before the right one. Use IdentitiesOnly yes.
  • The server is not too old for the key or signature algorithm being offered.

Tunneling

Use -N when the SSH session only exists for forwarding. Add -f only when you want the client to go into the background after authentication.

Local Forward

Forward a local port to a service reachable from the remote side:

ssh -N -L 127.0.0.1:8080:127.0.0.1:80 user@server

Then open http://127.0.0.1:8080 locally. From the server’s point of view, the target is 127.0.0.1:80.

Config form:

Host web-tunnel
    HostName server
    User user
    LocalForward 127.0.0.1:8080 127.0.0.1:80
    ExitOnForwardFailure yes

Remote Forward

Expose a local service through the remote server:

ssh -N -R 127.0.0.1:9000:127.0.0.1:3000 user@server

On the server, 127.0.0.1:9000 forwards back to the local machine’s 127.0.0.1:3000.

To bind the remote forwarded port beyond localhost, request an explicit bind address and allow it on the server:

ssh -N -R 0.0.0.0:9000:127.0.0.1:3000 user@server
GatewayPorts clientspecified

Dynamic Forward

Create a local SOCKS proxy:

ssh -N -D 127.0.0.1:1080 user@server
curl --socks5-hostname 127.0.0.1:1080 https://example.com

Config form:

Host socks
    HostName server
    User user
    DynamicForward 127.0.0.1:1080
    ExitOnForwardFailure yes

X11 Forwarding

X11 forwarding runs a graphical application on the remote host and displays it through the local X server over SSH.

Client side:

  • Linux desktop sessions usually already provide an X server or Xwayland bridge.
  • macOS needs an X server such as XQuartz.
  • Windows needs an X server such as VcXsrv, Xming, or a terminal package that includes one.

Server side, install xauth and the graphical program you want to run:

sudo apt update
sudo apt install xauth x11-apps

Enable forwarding in /etc/ssh/sshd_config:

X11Forwarding yes
X11UseLocalhost yes

Reload SSH after testing the config:

sudo sshd -t
sudo systemctl reload ssh || sudo systemctl reload sshd

Connect with untrusted X11 forwarding:

ssh -X user@host
xeyes

Use trusted forwarding only for applications that require it:

ssh -Y user@host

Troubleshoot with:

echo "$DISPLAY"
xauth list
ssh -vvv -X user@host

Legacy Algorithms

Modern OpenSSH disables weak legacy algorithms by default. Prefer fixing or upgrading the old server. If a temporary exception is required, scope it to one host:

Host old-host
    HostName old.example
    User user
    HostKeyAlgorithms +ssh-rsa
    PubkeyAcceptedAlgorithms +ssh-rsa

Remove the exception after the server supports modern host keys and signatures.

References