linux ssh
SSH 详解
1. SSH 是什么
SSH(Secure Shell)是一种加密网络协议,用于在不安全的网络上安全地远程登录和执行命令。默认端口 22。
SSH 替代了早期不安全的 telnet、rlogin、rsh 等协议,所有传输数据均加密。
2. 密码登录原理
2.1 流程
Client Server
| |
| ① 发起连接请求 |
|-----------------------------> |
| |
| ② 返回服务器公钥 |
|<----------------------------- |
| |
| ③ 用服务器公钥加密密码 |
|-----------------------------> |
| |
| ④ 用服务器私钥解密,验证密码 |
|<----- 登录成功/失败 --------- |
2.2 中间人攻击风险
SSH 的服务器公钥是自签发的,没有 CA 机构背书,Client 无法自动验证服务器身份。
第一次连接时会出现:
The authenticity of host 'github.com (20.205.243.166)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
输入 yes 后,服务器公钥指纹会保存到 ~/.ssh/known_hosts,下次连接直接比对,不再提示。
如果服务器公钥变了(重装系统、中间人攻击),会报错:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
确认是服务器重装导致的,清除旧记录:
ssh-keygen -R 192.168.1.100 # 删除指定主机的记录
# 或手动编辑 ~/.ssh/known_hosts 删除对应行
3. 密钥登录原理
3.1 流程
Client Server
| |
| ① 发起连接,声明用户名 |
|---------------------------------> |
| |
| ② 查找 authorized_keys 中 |
| 对应公钥,生成随机字符串 R |
| 用公钥加密 R,发回 |
|<--------------------------------- |
| |
| ③ 用本地私钥解密得到 R |
| 计算 hash(R + session_id) |
| 发送给 Server |
|---------------------------------> |
| |
| ④ Server 验证 hash 是否正确 |
|<------- 登录成功/失败 ----------- |
私钥从不离开本机,服务器无法得到私钥,安全性高于密码登录。
3.2 生成密钥对
# Ed25519(现代首选,安全性高、速度快)
ssh-keygen -t ed25519 -C "your_email@example.com"
# RSA 4096(老系统不支持 Ed25519 时使用)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
执行后交互提示:
Enter file in which to save the key (~/.ssh/id_ed25519): # 回车使用默认路径
Enter passphrase (empty for no passphrase): # 私钥口令,可为空
Enter same passphrase again:
生成两个文件:
~/.ssh/id_ed25519 ← 私钥,权限必须是 600,绝对不能泄露
~/.ssh/id_ed25519.pub ← 公钥,可以随意分发
算法对比:
| Ed25519 | RSA 4096 | ECDSA | |
|---|---|---|---|
| 年代 | 2013 | 1977 | 2005 |
| 密钥长度 | 256 bit | 4096 bit | 256 bit |
| 安全性 | 最高 | 高 | 高 |
| 性能 | 最快 | 慢 | 快 |
| 老系统兼容 | 需要较新 OpenSSH | 最兼容 | 兼容 |
3.3 部署公钥到服务器
# 方式一:ssh-copy-id(推荐)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ip
# 方式二:手动追加
cat ~/.ssh/id_ed25519.pub | ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# 验证免密登录
ssh user@server_ip
authorized_keys 权限必须是 600,目录 .ssh 权限必须是 700,否则 SSH 会拒绝使用(安全策略):
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
4. SSH 配置文件
4.1 客户端配置 ~/.ssh/config
为不同主机设置不同参数,避免每次输入长命令:
# 格式
Host <别名>
HostName <实际地址>
User <用户名>
Port <端口>
IdentityFile <私钥路径>
<其他参数>
实际示例:
# GitHub
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
# 公司服务器
Host prod
HostName 192.168.1.100
User ubuntu
Port 22
IdentityFile ~/.ssh/id_ed25519_work
# 跳板机后的内网服务器
Host inner
HostName 10.0.0.50
User root
ProxyJump jump-server # 通过跳板机连接
Host jump-server
HostName 1.2.3.4
User ubuntu
IdentityFile ~/.ssh/id_ed25519_work
配置后直接使用别名:
ssh prod # 等价于 ssh -i ~/.ssh/id_ed25519_work ubuntu@192.168.1.100
ssh inner # 自动通过跳板机
git clone git@github.com:user/repo.git # 自动使用指定密钥
常用参数:
| 参数 | 说明 |
|---|---|
HostName |
实际主机名或 IP |
User |
登录用户名 |
Port |
端口(默认 22) |
IdentityFile |
指定私钥路径 |
ProxyJump |
跳板机(ProxyCommand 的简写) |
ServerAliveInterval |
心跳间隔(秒),防止超时断开 |
ServerAliveCountMax |
心跳失败最大次数 |
ForwardAgent |
是否转发 ssh-agent |
StrictHostKeyChecking |
是否严格检查主机指纹 |
防止长时间不操作断连:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
4.2 服务端配置 /etc/ssh/sshd_config
修改后需重启服务:systemctl restart sshd
# 修改默认端口(减少暴力破解)
Port 2222
# 禁止 root 直接登录(安全加固必做)
PermitRootLogin no
# 禁止密码登录,只允许密钥登录
PasswordAuthentication no
# 允许公钥认证
PubkeyAuthentication yes
# 公钥文件位置
AuthorizedKeysFile .ssh/authorized_keys
# 限制登录用户
AllowUsers ubuntu deploy
# 登录超时时间(秒)
LoginGraceTime 30
# 最大认证尝试次数
MaxAuthTries 3
# 空闲超时(秒)
ClientAliveInterval 300
ClientAliveCountMax 2
5. ssh-agent(密钥代理)
私钥设置了口令(passphrase)时,每次使用都要输入。ssh-agent 可以将解密后的私钥缓存在内存中,只需输入一次。
# 启动 ssh-agent
eval "$(ssh-agent -s)"
# Agent pid 12345
# 添加私钥到 agent
ssh-add ~/.ssh/id_ed25519
# Enter passphrase for ~/.ssh/id_ed25519: ******
# Identity added: ~/.ssh/id_ed25519
# 查看已缓存的密钥
ssh-add -l
# 删除所有缓存
ssh-add -D
# macOS 可以让 Keychain 记住口令,系统重启后自动加载
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
在 ~/.ssh/config 中配置 macOS 自动加载:
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
Agent Forwarding(代理转发)
通过跳板机连接内网服务器时,让内网服务器使用本机的 ssh-agent,无需把私钥复制到跳板机:
ssh -A user@jump-server # -A 开启 agent forwarding
# 或在 config 中配置
Host jump-server
ForwardAgent yes
6. 端口转发
SSH 提供三种端口转发,本质都是通过 SSH 隧道转发 TCP 流量。
6.1 本地转发 -L
将本地端口的流量,通过 SSH 隧道转发到远端可达的地址。
ssh -L [本地地址:]本地端口:目标地址:目标端口 user@跳板机
场景: 服务器 B 的 MySQL 只监听 127.0.0.1:3306,本机想直接连接。
ssh -L 3306:127.0.0.1:3306 user@server_b
# 之后本机连接 127.0.0.1:3306 即可访问 server_b 的 MySQL
mysql -h 127.0.0.1 -P 3306 -u root -p
场景: 通过跳板机访问内网服务。
ssh -L 8080:10.0.0.100:80 user@jump-server
# 本机访问 localhost:8080 → 跳板机 → 内网 10.0.0.100:80
6.2 远程转发 -R
将远端端口的流量,转发到本机可达的地址。常用于内网穿透。
ssh -R [远端地址:]远端端口:目标地址:目标端口 user@远端服务器
场景: 本机没有公网 IP,让公网服务器代理流量到本机服务。
# 在本机执行,将公网服务器的 8080 端口流量转发到本机的 3000
ssh -R 8080:localhost:3000 user@公网服务器
# 外部访问 公网服务器:8080 → SSH 隧道 → 本机:3000
服务端需开启 GatewayPorts yes(sshd_config),否则远端端口只监听 127.0.0.1。
6.3 动态转发 -D(SOCKS 代理)
在本地建立 SOCKS5 代理,所有流量通过 SSH 服务器转发,相当于简易 VPN。
ssh -D 1080 user@server
# 配置浏览器/系统代理为 SOCKS5 127.0.0.1:1080
# 之后所有流量走 server 出去
6.4 常用参数组合
# 后台运行,不执行命令,保持连接
ssh -fNL 3306:127.0.0.1:3306 user@server
# -f: 后台运行
# -N: 不执行远程命令
# -C: 压缩数据(低带宽时有用)
# -q: 安静模式,不输出警告
7. SCP 与 SFTP
7.1 SCP(基于 SSH 的文件复制)
# 上传文件到服务器
scp file.txt user@server:/path/to/dest/
# 从服务器下载文件
scp user@server:/path/to/file.txt ./
# 上传目录(-r 递归)
scp -r ./mydir user@server:/path/to/dest/
# 指定端口
scp -P 2222 file.txt user@server:/path/
# 使用 config 别名
scp file.txt prod:/tmp/
7.2 SFTP(SSH 文件传输协议)
sftp user@server
# 常用 SFTP 命令
sftp> ls # 列出远端文件
sftp> pwd # 远端当前目录
sftp> lpwd # 本地当前目录
sftp> get file.txt # 下载
sftp> put file.txt # 上传
sftp> get -r mydir/ # 递归下载目录
sftp> mkdir backup # 创建远端目录
sftp> exit
8. 多跳连接(ProxyJump)
通过跳板机连接内网服务器:
# 命令行方式
ssh -J user@jump-server user@inner-server
# 多级跳板
ssh -J user@jump1,user@jump2 user@target
# 推荐在 config 中配置
Host inner-server
HostName 10.0.0.50
User root
ProxyJump jump-server
9. 安全加固
9.1 服务端加固清单
# 1. 修改默认端口
Port 2222
# 2. 禁止 root 登录
PermitRootLogin no
# 3. 禁止密码登录
PasswordAuthentication no
ChallengeResponseAuthentication no
# 4. 只允许指定用户
AllowUsers deploy ubuntu
# 5. 限制登录来源 IP(/etc/hosts.allow)
sshd: 192.168.1.0/24
# 6. 使用 fail2ban 防暴力破解
apt install fail2ban
# 默认配置:5次失败后封禁10分钟
9.2 客户端安全建议
- 私钥权限必须是
600,否则 SSH 拒绝使用 - 为私钥设置 passphrase,配合 ssh-agent 使用
- 不同服务使用不同密钥,泄露后只影响单一服务
- 定期轮换密钥
# 检查私钥权限
ls -la ~/.ssh/
# -rw------- id_ed25519 ← 600 正确
# -rw-r--r-- id_ed25519.pub ← 644 正确
# drwx------ .ssh/ ← 700 正确
10. 常见问题
10.1 Permission denied (publickey)
# 排查步骤
ssh -v user@server # 查看详细调试日志
# 常见原因:
# 1. 服务器 authorized_keys 权限不对
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh
# 2. 公钥没有正确写入 authorized_keys
cat ~/.ssh/id_ed25519.pub # 确认本地公钥
ssh user@server "cat ~/.ssh/authorized_keys" # 确认服务器内容
# 3. sshd_config 未开启公钥认证
PubkeyAuthentication yes
# 4. SELinux 问题(CentOS/RHEL)
restorecon -Rv ~/.ssh
10.2 Host key verification failed
服务器重装或 IP 复用导致公钥变化:
ssh-keygen -R server_ip_or_hostname
ssh user@server # 重新确认并添加新指纹
10.3 连接超时断开
# 客户端配置心跳(~/.ssh/config)
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
# 或连接时指定
ssh -o ServerAliveInterval=60 user@server
10.4 使用密钥仍要输密码
可能是服务器要求输入的是私钥 passphrase,不是系统密码:
# 添加到 ssh-agent 后只需输入一次
ssh-add ~/.ssh/id_ed25519
# macOS 永久保存到 Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
10.5 端口转发连接断开
# 加 -o 参数保持连接
ssh -fNL 3306:127.0.0.1:3306 \
-o ServerAliveInterval=60 \
-o ExitOnForwardFailure=yes \
user@server
# 或使用 autossh 自动重连
brew install autossh
autossh -M 0 -fNL 3306:127.0.0.1:3306 user@server
11. 实用技巧
# 执行远程命令,不进入交互
ssh user@server "df -h && free -m"
# 将本地命令输出传给远端
tar czf - ./mydir | ssh user@server "cat > backup.tar.gz"
# 远端文件传到本地(不用 scp)
ssh user@server "cat /etc/nginx/nginx.conf" > nginx.conf
# 测试 SSH 连接(不执行命令)
ssh -T git@github.com
# 查看 SSH 登录日志
tail -f /var/log/auth.log # Ubuntu/Debian
tail -f /var/log/secure # CentOS/RHEL
journalctl -u sshd -f # systemd
xingliuhua