SSH、FTP 与 SFTP
远程服务器管理和文件传输是现代开发和运维工作的核心环节。SSH(Secure Shell)作为安全远程访问的标准协议,已经成为系统管理的必备工具。理解 SSH 的工作原理、密钥管理和安全配置,对于构建安全的运维体系至关重要。
SSH 协议概述
SSH(Secure Shell)是一种加密的网络传输协议,用于在不安全的网络中安全地传输数据。它取代了不安全的 Telnet、rlogin、rsh 等协议,成为远程登录和命令执行的事实标准。
SSH 协议架构
SSH 协议由三层组成,定义在多个 RFC 文档中:
传输层协议(RFC 4253):负责服务器认证、密钥交换、加密和完整性保护。这是 SSH 安全性的基础。
认证协议(RFC 4252):定义了多种认证方式,包括公钥认证、密码认证、主机认证等。
连接协议(RFC 4254):在加密通道上复用多个逻辑通道,支持远程命令执行、Shell 访问、端口转发等功能。
SSH 与 Telnet 的对比
| 特性 | SSH | Telnet |
|---|---|---|
| 数据加密 | 是(AES、ChaCha20 等) | 否(明文传输) |
| 身份认证 | 公钥、密码、证书 | 仅密码 |
| 完整性保护 | 是(HMAC) | 否 |
| 端口转发 | 支持 | 不支持 |
| 文件传输 | 内置(SCP/SFTP) | 需要 FTP |
| 安全性 | 高 | 极低 |
SSH 连接建立过程
SSH 连接的建立分为三个阶段:
密钥交换详解
SSH 使用 Diffie-Hellman 或 ECDH 进行密钥交换,确保即使有人截获了通信,也无法推导出会话密钥:
- 算法协商:双方交换支持的算法列表,选择双方都支持的最强算法
- 密钥交换:使用 DH/ECDH 算法生成共享密钥
- 服务器认证:服务器用私钥签名,客户端用已知的公钥验证
- 密钥派生:从共享密钥派生出加密密钥、MAC 密钥、IV 等
SSH 密钥管理
公钥认证是 SSH 最安全的认证方式,也是自动化运维的基础。
密钥对生成
# 生成 RSA 密钥对(传统方式,兼容性好)
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# 生成 Ed25519 密钥对(推荐,更安全更高效)
ssh-keygen -t ed25519 -C "[email protected]"
# 生成 ECDSA 密钥对
ssh-keygen -t ecdsa -b 521 -C "[email protected]"
密钥类型对比:
| 类型 | 密钥长度 | 安全性 | 性能 | 兼容性 |
|---|---|---|---|---|
| RSA | 2048-4096 位 | 高 | 中等 | 最好 |
| ECDSA | 256-521 位 | 高 | 快 | 好 |
| Ed25519 | 固定 256 位 | 很高 | 最快 | 较好 |
密钥生成交互过程
$ ssh-keygen -t ed25519 -C "[email protected]"
Generating public/private ed25519 key pair.
# 选择保存位置(默认 ~/.ssh/id_ed25519)
Enter file in which to save the key (/home/user/.ssh/id_ed25519):
# 设置密码短语(增加安全性)
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_ed25519
Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 [email protected]
The key's randomart image is:
+--[ED25519 256]--+
| ooo. |
| o.ooo |
| + .+ o |
| o = +o . |
| . + * S. |
| . = Bo . |
| . *.*o |
| + =+. |
| .E+. |
+----[SHA256]-----+
密钥文件结构
私钥文件(id_ed25519):
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDGxMTE0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTA4
-----END OPENSSH PRIVATE KEY-----
公钥文件(id_ed25519.pub):
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGXAMPLE... [email protected]
公钥文件格式为:算法名 公钥数据 注释
密钥部署
将公钥部署到远程服务器:
# 方法 1:使用 ssh-copy-id(推荐)
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
# 方法 2:手动复制
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# 方法 3:手动编辑
# 将公钥内容添加到服务器的 ~/.ssh/authorized_keys 文件中
密钥权限设置
正确的文件权限对于 SSH 安全至关重要:
# 本地机器
chmod 700 ~/.ssh # .ssh 目录
chmod 600 ~/.ssh/id_ed25519 # 私钥
chmod 644 ~/.ssh/id_ed25519.pub # 公钥
chmod 644 ~/.ssh/known_hosts # 已知主机
# 远程服务器
chmod 700 ~/.ssh # .ssh 目录
chmod 600 ~/.ssh/authorized_keys # 授权密钥
权限设置错误会导致 SSH 拒绝使用密钥:
Permissions 0644 for '/home/user/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
SSH Agent 管理
使用 SSH Agent 可以避免每次连接都输入密码短语:
# 启动 SSH Agent
eval "$(ssh-agent -s)"
# 添加密钥到 Agent
ssh-add ~/.ssh/id_ed25519
# 列出已添加的密钥
ssh-add -l
# 删除所有密钥
ssh-add -D
# 锁定 Agent(需要密码解锁)
ssh-add -x
# 解锁 Agent
ssh-add -X
SSH 配置详解
客户端配置文件
SSH 客户端配置文件位于 ~/.ssh/config,可以简化连接命令:
# 全局默认配置
Host *
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
# 服务器别名配置
Host web-server
HostName 192.168.1.100
User admin
Port 2222
IdentityFile ~/.ssh/web_server_key
# 通过跳板机连接
Host internal-server
HostName 10.0.0.50
User deploy
ProxyJump jump-server
# 跳板机配置
Host jump-server
HostName jump.example.com
User jumper
IdentityFile ~/.ssh/jump_key
# 多重跳板
Host final-server
HostName 10.10.10.10
User admin
ProxyJump jump1,jump2
# 会话复用
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
配置后,可以直接使用别名连接:
# 不需要记住 IP、端口、用户名
ssh web-server
# 自动通过跳板机连接
ssh internal-server
服务器配置文件
SSH 服务器配置文件位于 /etc/ssh/sshd_config:
# 监听端口
Port 22
# 监听地址(0.0.0.0 表示所有接口)
ListenAddress 0.0.0.0
# 协议版本(SSH-2)
Protocol 2
# 主机密钥
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
# 认证配置
PermitRootLogin no # 禁止 root 登录
PubkeyAuthentication yes # 启用公钥认证
PasswordAuthentication no # 禁用密码认证
PermitEmptyPasswords no # 禁止空密码
ChallengeResponseAuthentication no # 禁用挑战响应认证
# 授权密钥文件
AuthorizedKeysFile .ssh/authorized_keys
# 连接限制
MaxAuthTries 3 # 最大认证尝试次数
MaxSessions 10 # 最大会话数
LoginGraceTime 60 # 登录超时时间
# 安全配置
X11Forwarding no # 禁用 X11 转发
AllowTcpForwarding yes # 允许 TCP 转发
AllowAgentForwarding yes # 允许 Agent 转发
# 加密算法(推荐配置)
KexAlgorithms [email protected],diffie-hellman-group-exchange-sha256
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]
# 允许的用户/组
AllowUsers admin deploy
# AllowGroups ssh-users
# 日志
SyslogFacility AUTH
LogLevel INFO
修改配置后需要重启服务:
# 检查配置语法
sudo sshd -t
# 重启服务
sudo systemctl restart sshd
# 查看状态
sudo systemctl status sshd
SSH 隧道与端口转发
SSH 隧道可以安全地转发网络流量,是解决内网穿透的经典方案。
本地端口转发
将远程服务的端口映射到本地:
# 语法:ssh -L 本地端口:目标主机:目标端口 跳板机
ssh -L 8080:internal-server:80 jump-server
# 访问本地 8080 端口等于访问 internal-server:80
curl http://localhost:8080
应用场景:通过跳板机访问内网服务
远程端口转发
将本地服务的端口暴露到远程:
# 语法:ssh -R 远程端口:本地主机:本地端口 远程服务器
ssh -R 3000:localhost:3000 server.example.com
# 远程服务器上访问 localhost:3000 等于访问本地的 3000 端口
应用场景:让外部访问本地开发服务器
# 在本地运行开发服务器
npm run dev # 监听 localhost:3000
# 通过 SSH 暴露到公网服务器
ssh -R 8080:localhost:3000 [email protected]
# 外部访问 public-server.com:8080 即可访问本地服务
动态端口转发(SOCKS 代理)
创建一个 SOCKS 代理服务器:
# 语法:ssh -D 本地端口 远程服务器
ssh -D 1080 server.example.com
# 配置浏览器使用 SOCKS5 代理:localhost:1080
# 所有流量都通过 SSH 隧道转发
应用场景:安全浏览、绕过网络限制
配置文件中的端口转发
在 ~/.ssh/config 中配置:
Host tunnel-web
HostName jump-server.example.com
User admin
LocalForward 8080 internal-server:80
LocalForward 3306 db-server:3306
Host expose-local
HostName public-server.example.com
User deploy
RemoteForward 8080 localhost:3000
SCP 与 SFTP 文件传输
SCP(Secure Copy)
SCP 是基于 SSH 的简单文件复制命令:
# 上传文件
scp local-file.txt user@server:/remote/path/
# 下载文件
scp user@server:/remote/file.txt ./local/path/
# 上传目录
scp -r local-directory/ user@server:/remote/path/
# 从跳板机下载
scp -J jump-server user@internal-server:/file.txt ./
# 使用特定端口
scp -P 2222 file.txt user@server:/path/
# 显示进度
scp -v large-file.iso user@server:/path/
SFTP(SSH File Transfer Protocol)
SFTP 提供交互式文件传输,功能更丰富:
# 连接服务器
sftp [email protected]
# SFTP 交互命令
sftp> ls # 列出远程目录
sftp> lcd ~/Downloads # 切换本地目录
sftp> get remote-file.txt # 下载文件
sftp> put local-file.txt # 上传文件
sftp> mget *.txt # 下载多个文件
sftp> mput *.jpg # 上传多个文件
sftp> mkdir new-dir # 创建远程目录
sftp> rm file.txt # 删除远程文件
sftp> exit # 退出
SFTP 与 FTP 的对比:
| 特性 | SFTP | FTP |
|---|---|---|
| 传输协议 | SSH | 独立协议 |
| 端口 | 22 | 20, 21 |
| 数据加密 | 是 | 否 |
| 通道数量 | 单一通道 | 控制连接 + 数据连接 |
| 防火墙友好 | 是 | 否(需要被动模式) |
| 断点续传 | 支持 | 支持 |
SSH 安全最佳实践
服务器端安全加固
# 1. 禁用密码认证,只允许密钥
# /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
# 2. 禁止 root 登录
PermitRootLogin no
# 3. 使用非标准端口
Port 2222
# 4. 限制登录用户
AllowUsers admin deploy
# 5. 配置失败锁定
# 安装 fail2ban
sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
# 6. 使用强加密算法
KexAlgorithms [email protected],diffie-hellman-group-exchange-sha256
Ciphers [email protected],[email protected]
MACs [email protected],[email protected]
客户端安全建议
# 1. 使用密码短语保护私钥
ssh-keygen -t ed25519 -C "[email protected]"
# 设置一个强密码短语
# 2. 定期轮换密钥
# 建议每年更换一次密钥对
# 3. 使用 SSH Agent 转发时要谨慎
# 只在信任的网络中使用
ForwardAgent yes # 仅在需要时启用
# 4. 验证服务器指纹
# 首次连接时验证指纹是否正确
ssh-keygen -l -f ~/.ssh/known_hosts
# 5. 使用 known_hosts 进行主机验证
StrictHostKeyChecking ask
UserKnownHostsFile ~/.ssh/known_hosts
密钥管理最佳实践
# 1. 为不同用途使用不同密钥
~/.ssh/
├── id_ed25519 # 日常使用
├── id_ed25519_github # GitHub 专用
├── id_ed25519_aws # AWS 专用
└── id_ed25519_deploy # 部署专用
# 2. 备份私钥(加密存储)
# 将私钥备份到安全位置,如密码管理器
# 3. 定期审计授权密钥
# 检查服务器上的 authorized_keys
cat ~/.ssh/authorized_keys
# 4. 使用证书认证(大规模环境)
# SSH CA 可以集中管理密钥信任关系
SSH 故障排查
常见问题诊断
# 1. 详细输出模式
ssh -vvv user@server
# 2. 检查配置文件语法
sudo sshd -t
# 3. 测试特定端口
ssh -p 2222 user@server
# 4. 检查服务器是否运行
sudo systemctl status sshd
sudo netstat -tlnp | grep 22
# 5. 检查防火墙
sudo ufw status
sudo iptables -L -n | grep 22
# 6. 查看日志
sudo tail -f /var/log/auth.log
sudo journalctl -u sshd -f
常见错误及解决
错误 1:Permission denied (publickey)
原因:密钥认证失败 解决:
# 检查密钥是否正确
ssh-add -l
# 检查服务器上的 authorized_keys
cat ~/.ssh/authorized_keys
# 检查权限
ls -la ~/.ssh/
错误 2:Host key verification failed
原因:服务器指纹变化(可能是重新安装或中间人攻击) 解决:
# 删除旧指纹
ssh-keygen -R server.example.com
# 重新连接验证新指纹
ssh [email protected]
错误 3:Connection refused
原因:SSH 服务未运行或端口错误 解决:
# 检查服务状态
sudo systemctl status sshd
# 检查端口
sudo netstat -tlnp | grep sshd
# 尝试其他端口
ssh -p 2222 user@server
FTP 协议简介
FTP(File Transfer Protocol)是历史悠久的文件传输协议,但由于安全性问题,现在已被 SFTP 替代。
FTP 工作模式
FTP 使用两个连接:
控制连接(端口 21):传输命令和响应 数据连接(端口 20 或随机端口):传输文件内容
主动模式与被动模式
主动模式:服务器主动连接客户端的数据端口
- 客户端告诉服务器自己的数据端口
- 服务器从端口 20 连接客户端
- 问题:客户端防火墙通常阻止入站连接
被动模式:客户端连接服务器的数据端口
- 服务器告诉客户端自己的数据端口
- 客户端主动连接服务器
- 现代 FTP 的默认模式
FTP 的安全问题
| 问题 | 说明 |
|---|---|
| 明文传输 | 用户名、密码、数据都是明文 |
| 多端口 | 防火墙配置复杂 |
| 被动模式端口预测 | 可能被利用进行端口扫描 |
结论:生产环境应使用 SFTP 替代 FTP。
总结
SSH 是现代系统管理的基石,掌握 SSH 的核心技能对于运维和开发工作至关重要:
- 协议架构:传输层、认证层、连接层三层结构确保安全性
- 密钥管理:Ed25519 密钥是当前最佳选择,正确设置权限是安全的前提
- 配置优化:通过配置文件简化连接,服务器端加固提升安全性
- 隧道技术:端口转发实现内网穿透,解决复杂网络环境下的访问问题
- 文件传输:SFTP 比 FTP 更安全,SCP 适合简单快速的文件复制
在安全威胁日益严峻的今天,正确使用 SSH 并遵循最佳实践,是保护服务器安全的基本要求。