SSH

Basic Connection

ssh user@host                      # connect to remote
ssh -p 2222 user@host              # custom port
ssh user@host "ls -la /tmp"        # run command and exit
ssh -l user host                   # with -l flag
ssh -v user@host                   # verbose (debug)
ssh -vvv user@host                 # maximum verbosity
ssh -J jumpuser@jumphost user@target   # via jump host
ssh -o StrictHostKeyChecking=no user@host
ssh -o ConnectTimeout=10 user@host

Key Generation (ssh-keygen)

ssh-keygen -t ed25519              # Ed25519 (recommended)
ssh-keygen -t ed25519 -C "user@host"
ssh-keygen -t rsa -b 4096 -C "user@host"  # RSA 4096-bit
ssh-keygen -t ecdsa -b 521         # ECDSA P-521

ssh-keygen -f ~/.ssh/mykey         # custom filename
ssh-keygen -p -f ~/.ssh/id_ed25519 # change passphrase
ssh-keygen -y -f ~/.ssh/id_ed25519 # print public key from private
ssh-keygen -l -f ~/.ssh/id_ed25519 # fingerprint
ssh-keygen -R host                 # remove host from known_hosts
ssh-keygen -r host                 # export DNS SSHFP records

Key-based Auth

ssh-copy-id user@host              # copy key to remote
ssh-copy-id -i ~/.ssh/mykey.pub user@host
ssh-copy-id -p 2222 user@host

# manual copy
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# remote side
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

ssh-agent

eval "$(ssh-agent -s)"             # start agent
ssh-add ~/.ssh/id_ed25519          # add key to agent
ssh-add -l                         # list loaded keys
ssh-add -L                         # list public keys
ssh-add -d ~/.ssh/id_ed25519       # remove key
ssh-add -D                         # remove all keys
ssh-add -t 3600 ~/.ssh/id_ed25519  # add with 1hr lifetime
ssh-add -x                         # lock agent
ssh-add -X                         # unlock agent

Config File (~/.ssh/config)

Host dev
    HostName dev.example.com
    User admin
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes

Host prod-*
    User deploy
    IdentityFile ~/.ssh/prod_key
    StrictHostKeyChecking no

Host jump
    HostName bastion.example.com
    User jumpuser
    IdentityFile ~/.ssh/jump_key

Host *.internal
    ProxyJump jump
    User ubuntu

Host github.com
    User git
    IdentityFile ~/.ssh/github_key
    IdentitiesOnly yes

Host tunnel
    HostName db.example.com
    LocalForward 5432 localhost:5432
    User admin
ssh dev                            # uses config
ssh prod-web1                      # wildcard match
ssh -G dev                         # show effective config

SCP & SFTP

scp file.txt user@host:/tmp/       # local → remote
scp user@host:/tmp/file.txt ./     # remote → local
scp -r dir/ user@host:/tmp/        # recursive copy
scp -P 2222 file.txt user@host:/tmp/

sftp user@host
sftp> ls
sftp> cd /var/log
sftp> get syslog ./local_syslog
sftp> put local_file /tmp/
sftp> lls                          # local ls
sftp> lcd ~/Downloads              # local cd
sftp> mkdir remote_dir
sftp> quit

SSH Tunneling (Local)

ssh -L 8080:internal:80 user@bastion
# localhost:8080 → bastion → internal:80

ssh -L 5432:db.example.com:5432 user@bastion
# localhost:5432 → bastion → db.example.com:5432

ssh -L 8888:localhost:80 user@host
# localhost:8888 → host → localhost:80 (on host)

ssh -NL 3306:db:3306 user@bastion  # -N: no remote command
ssh -fNL 8080:internal:80 user@bastion  # -f: background

SSH Tunneling (Remote)

ssh -R 9090:localhost:80 user@remote
# remote:9090 → tunnel → localhost:80 (your machine)

ssh -R 2222:localhost:22 user@remote
# remote:2222 → tunnel → your SSH server

ssh -NR 9090:localhost:8080 user@remote  # background remote forward
ssh -R 0.0.0.0:9090:localhost:80 user@remote  # bind all interfaces
# Remote server: GatewayPorts yes in sshd_config
# to allow connections from any host to remote:9090

ProxyJump / Bastion

ssh -J jumpuser@bastion user@target
ssh -J j1@host1,j2@host2 user@target   # multi-hop

# in ~/.ssh/config
Host target
    HostName 10.0.0.50
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User jumpuser

ssh target                          # auto-through bastion
ssh -o ProxyCommand="ssh -W %h:%p bastion" user@target
# equivalent to ProxyJump (legacy syntax)

Port Forwarding

ssh -L local_port:dest_host:dest_port user@ssh_server   # local
ssh -R remote_port:local_host:local_port user@ssh_server # remote
ssh -D 1080 user@ssh_server                              # SOCKS proxy

curl --socks5 localhost:1080 http://internal-site.local
ssh -ND 1080 user@bastion             # dynamic (SOCKS) in background

ssh -L 8888:localhost:8888 -L 3000:localhost:3000 user@host  # multiple forwards

X11 Forwarding

ssh -X user@host                   # X11 forwarding
ssh -Y user@host                   # trusted X11 (less secure)
ssh -X user@host firefox           # run remote GUI app

ssh -XC user@host                  # with compression
# Remote: X11Forwarding yes in /etc/ssh/sshd_config
# Local: X server must be running
# Linux: native X11
# macOS: XQuartz
# Windows: Xming, VcXsrv, WSLg

SSH Hardening

# /etc/ssh/sshd_config
Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers admin deploy
AllowGroups ssh-users
X11Forwarding no
PermitEmptyPasswords no
HostKey /etc/ssh/ssh_host_ed25519_key
KexAlgorithms curve25519-sha256
Ciphers chacha20-poly1305@openssh.com
MACs hmac-sha2-512-etm@openssh.com
sshd -t                            # validate config
systemctl reload sshd              # apply changes
ssh-audit host                     # audit SSH config

SSHFP DNS Records

ssh-keygen -r host.example.com     # generate SSHFP records
ssh-keygen -r host.example.com -D sha256  # SHA-256 fingerprints
; Add to DNS zone
host.example.com. IN SSHFP 1 1 4A3C...
host.example.com. IN SSHFP 1 2 B2D8...
host.example.com. IN SSHFP 4 1 7F2A...
host.example.com. IN SSHFP 4 2 C4E1...
; Algorithm: 1=RSA, 2=DSA, 3=ECDSA, 4=Ed25519
; Fingerprint: 1=SHA-1, 2=SHA-256
ssh -o VerifyHostKeyDNS=yes user@host

Troubleshooting

ssh -vvv user@host                 # verbose debug output
ssh -T user@host                   # test connection (no shell)
ssh -o BatchMode=yes user@host     # fail if key auth not possible

# check SSH service
systemctl status sshd
ss -tlnp | grep :22

# connection issues
nc -zv host 22                     # test port
telnet host 22                     # check SSH banner

# key issues
ssh-add -l                         # list agent keys
ssh -i ~/.ssh/key user@host        # specify identity
ssh-keygen -l -f ~/.ssh/known_hosts
ssh-keygen -R host                 # remove stale host key

# agent forwarding
ssh -A user@host                   # enable agent forwarding
ssh user@host "SSH_AUTH_SOCK=$SSH_AUTH_SOCK ssh thirdhost"

基本连接

ssh user@host                      # 连接远程主机
ssh -p 2222 user@host              # 自定义端口
ssh user@host "ls -la /tmp"        # 执行命令后退出
ssh -l user host                   # 使用 -l 参数
ssh -v user@host                   # 详细输出(调试)
ssh -vvv user@host                 # 最大详细度
ssh -J jumpuser@jumphost user@target   # 通过跳板机
ssh -o StrictHostKeyChecking=no user@host
ssh -o ConnectTimeout=10 user@host

密钥生成 (ssh-keygen)

ssh-keygen -t ed25519              # Ed25519(推荐)
ssh-keygen -t ed25519 -C "user@host"
ssh-keygen -t rsa -b 4096 -C "user@host"  # RSA 4096 位
ssh-keygen -t ecdsa -b 521         # ECDSA P-521

ssh-keygen -f ~/.ssh/mykey         # 自定义文件名
ssh-keygen -p -f ~/.ssh/id_ed25519 # 修改密码短语
ssh-keygen -y -f ~/.ssh/id_ed25519 # 从私钥输出公钥
ssh-keygen -l -f ~/.ssh/id_ed25519 # 指纹
ssh-keygen -R host                 # 从 known_hosts 移除
ssh-keygen -r host                 # 导出 DNS SSHFP 记录

密钥认证

ssh-copy-id user@host              # 复制公钥到远程
ssh-copy-id -i ~/.ssh/mykey.pub user@host
ssh-copy-id -p 2222 user@host

# 手动复制
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# 远程端设置权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

ssh-agent

eval "$(ssh-agent -s)"             # 启动 agent
ssh-add ~/.ssh/id_ed25519          # 添加密钥
ssh-add -l                         # 列出已加载密钥
ssh-add -L                         # 列出公钥
ssh-add -d ~/.ssh/id_ed25519       # 移除密钥
ssh-add -D                         # 移除所有密钥
ssh-add -t 3600 ~/.ssh/id_ed25519  # 添加(有效期 1 小时)
ssh-add -x                         # 锁定 agent
ssh-add -X                         # 解锁 agent

配置文件 (~/.ssh/config)

Host dev
    HostName dev.example.com
    User admin
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes

Host prod-*
    User deploy
    IdentityFile ~/.ssh/prod_key
    StrictHostKeyChecking no

Host jump
    HostName bastion.example.com
    User jumpuser
    IdentityFile ~/.ssh/jump_key

Host *.internal
    ProxyJump jump
    User ubuntu

Host github.com
    User git
    IdentityFile ~/.ssh/github_key
    IdentitiesOnly yes

Host tunnel
    HostName db.example.com
    LocalForward 5432 localhost:5432
    User admin
ssh dev                            # 使用配置
ssh prod-web1                      # 通配符匹配
ssh -G dev                         # 显示生效配置

SCP 与 SFTP

scp file.txt user@host:/tmp/       # 本地 → 远程
scp user@host:/tmp/file.txt ./     # 远程 → 本地
scp -r dir/ user@host:/tmp/        # 递归复制
scp -P 2222 file.txt user@host:/tmp/

sftp user@host
sftp> ls
sftp> cd /var/log
sftp> get syslog ./local_syslog
sftp> put local_file /tmp/
sftp> lls                          # 本地 ls
sftp> lcd ~/Downloads              # 本地 cd
sftp> mkdir remote_dir
sftp> quit

本地隧道

ssh -L 8080:internal:80 user@bastion
# localhost:8080 → 堡垒机 → internal:80

ssh -L 5432:db.example.com:5432 user@bastion
# localhost:5432 → 堡垒机 → db.example.com:5432

ssh -L 8888:localhost:80 user@host
# localhost:8888 → host → localhost:80(host 上的)

ssh -NL 3306:db:3306 user@bastion  # -N:不执行远程命令
ssh -fNL 8080:internal:80 user@bastion  # -f:后台运行

远程隧道

ssh -R 9090:localhost:80 user@remote
# remote:9090 → 隧道 → localhost:80(你的机器)

ssh -R 2222:localhost:22 user@remote
# remote:2222 → 隧道 → 你的 SSH 服务

ssh -NR 9090:localhost:8080 user@remote  # 后台远程转发
ssh -R 0.0.0.0:9090:localhost:80 user@remote  # 绑定所有接口
# 远程服务器需在 sshd_config 中设置 GatewayPorts yes
# 以允许任何主机连接到 remote:9090

代理跳转 / 堡垒机

ssh -J jumpuser@bastion user@target
ssh -J j1@host1,j2@host2 user@target   # 多跳

# 在 ~/.ssh/config 中配置
Host target
    HostName 10.0.0.50
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User jumpuser

ssh target                          # 自动通过堡垒机
ssh -o ProxyCommand="ssh -W %h:%p bastion" user@target
# 等效于 ProxyJump(旧语法)

端口转发

ssh -L local_port:dest_host:dest_port user@ssh_server   # 本地
ssh -R remote_port:local_host:local_port user@ssh_server # 远程
ssh -D 1080 user@ssh_server                              # SOCKS 代理

curl --socks5 localhost:1080 http://internal-site.local
ssh -ND 1080 user@bastion             # 动态(SOCKS)后台运行

ssh -L 8888:localhost:8888 -L 3000:localhost:3000 user@host  # 多个转发

X11 转发

ssh -X user@host                   # X11 转发
ssh -Y user@host                   # 信任 X11(安全性较低)
ssh -X user@host firefox           # 运行远程 GUI 程序

ssh -XC user@host                  # 启用压缩
# 远程端:/etc/ssh/sshd_config 中 X11Forwarding yes
# 本地端:X 服务器必须运行
# Linux:原生 X11
# macOS:XQuartz
# Windows:Xming, VcXsrv, WSLg

SSH 安全加固

# /etc/ssh/sshd_config
Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers admin deploy
AllowGroups ssh-users
X11Forwarding no
PermitEmptyPasswords no
HostKey /etc/ssh/ssh_host_ed25519_key
KexAlgorithms curve25519-sha256
Ciphers chacha20-poly1305@openssh.com
MACs hmac-sha2-512-etm@openssh.com
sshd -t                            # 验证配置
systemctl reload sshd              # 应用更改
ssh-audit host                     # 审计 SSH 配置

SSHFP DNS 记录

ssh-keygen -r host.example.com     # 生成 SSHFP 记录
ssh-keygen -r host.example.com -D sha256  # SHA-256 指纹
; 添加到 DNS 区域
host.example.com. IN SSHFP 1 1 4A3C...
host.example.com. IN SSHFP 1 2 B2D8...
host.example.com. IN SSHFP 4 1 7F2A...
host.example.com. IN SSHFP 4 2 C4E1...
; 算法:1=RSA, 2=DSA, 3=ECDSA, 4=Ed25519
; 指纹:1=SHA-1, 2=SHA-256
ssh -o VerifyHostKeyDNS=yes user@host

故障排查

ssh -vvv user@host                 # 详细调试输出
ssh -T user@host                   # 测试连接(无 shell)
ssh -o BatchMode=yes user@host     # 密钥认证失败则报错

# 检查 SSH 服务
systemctl status sshd
ss -tlnp | grep :22

# 连接问题
nc -zv host 22                     # 测试端口
telnet host 22                     # 检查 SSH 横幅

# 密钥问题
ssh-add -l                         # 列出 agent 密钥
ssh -i ~/.ssh/key user@host        # 指定密钥
ssh-keygen -l -f ~/.ssh/known_hosts
ssh-keygen -R host                 # 移除过期的主机密钥

# Agent 转发
ssh -A user@host                   # 启用 agent 转发
ssh user@host "SSH_AUTH_SOCK=$SSH_AUTH_SOCK ssh thirdhost"