SSH 配置与安全加固
掌握
~/.ssh/config和sshd_config的配置精髓,以及生产环境安全加固的完整检查清单。
速查卡片
| 项目 | 内容 |
|---|---|
| 客户端配置 | ~/.ssh/config |
| 系统级配置 | /etc/ssh/ssh_config |
| 服务端配置 | /etc/ssh/sshd_config |
| 测试配置语法 | sshd -t |
| 安全密钥生成 | ssh-keygen -t ed25519 |
| 禁用密码登录 | PasswordAuthentication no |
| 禁用 root 登录 | PermitRootLogin prohibit-password |
| 修改默认端口 | Port 2222 |
| 限制用户 | AllowUsers alice bob |
| 查看登录失败 | journalctl -u sshd | grep Failed |
| 测试客户端配置 | ssh -G host |
| 开连接复用 | ControlMaster auto + ControlPersist 10m |
客户端配置 —— ~/.ssh/config
配置文件优先级
- 命令行选项(最高优先级)
~/.ssh/config(用户配置)/etc/ssh/ssh_config(系统全局配置)
第一个匹配的值生效,因此将具体主机配置放在文件开头,通配符默认值放在末尾。
示例一:完整配置示例
# 通配符:适用于所有主机
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
# 连接复用
ControlMaster auto
ControlPath ~/.ssh/controlmasters/%r@%h:%p
ControlPersist 10m
# GitHub
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
# 生产服务器
Host prod-*
HostName %h.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/prod_ed25519
LogLevel VERBOSE
# 禁止 agent 转发
ForwardAgent no
# 跳板机 + 目标服务器组合
Host internal-db
HostName 10.0.1.50
User admin
ProxyJump bastion
LocalForward 3306 127.0.0.1:3306
Host bastion
HostName bastion.example.com
User jumpuser
# 针对特定命令的配置
Match host *.dev.example.com exec "pwd | grep -q /opt/deploy"
IdentityFile ~/.ssh/deploy_ed25519示例二:按网络环境切换配置
# 公司内网(匹配本地网段)
Match localnetwork 10.0.0.0/8
ProxyCommand none
# 外部网络(用跳板机)
Host *.internal.corp.com
ProxyJump user@bastion.corp.com常用配置项速查
| 配置项 | 默认值 | 推荐值 | 说明 |
|---|---|---|---|
Hostname | 命令行值 | — | 实际主机名 |
User | 本地用户 | — | 登录用户名 |
Port | 22 | — | 远程端口 |
IdentityFile | 默认密钥 | — | 私钥路径 |
ServerAliveInterval | 0 | 60 | 保活间隔(秒) |
ServerAliveCountMax | 3 | 3 | 保活失败重试次数 |
ConnectTimeout | — | 10 | 连接超时(秒) |
ConnectionAttempts | 1 | 3 | 连接重试次数 |
StrictHostKeyChecking | ask | accept-new | 主机密钥检查策略 |
ForwardAgent | no | no | Agent 转发(危险) |
ForwardX11 | no | no | X11 转发 |
Compression | no | yes(慢网络) | 压缩传输 |
TCPKeepAlive | yes | yes | TCP 保活 |
ControlMaster | no | auto | 连接复用 |
ControlPersist | — | 10m | 后台保持主连接 |
ProxyJump | — | — | 跳板机 |
IdentitiesOnly | no | yes | 仅使用配置指定的密钥 |
LogLevel | INFO | — | 日志级别 |
RequestTTY | auto | — | TTY 分配策略 |
PasswordAuthentication | yes | no | 密码认证 |
PubkeyAuthentication | yes | yes | 公钥认证 |
VerifyHostKeyDNS | no | — | DNS SSHFP 验证 |
条件匹配(Match)
Match 块是 Host 块的增强版,支持更灵活的条件:
text
Match [canonical] [final] [exec command] [localnetwork CIDR] \
host pattern user pattern localuser pattern ...
# 仅在此条件满足时生效的配置项bash
# 仅在公司网络使用特定密钥
Match host *.corp.com localnetwork 10.0.0.0/8
IdentityFile ~/.ssh/corp_ed25519
# 仅在特定目录下工作时的配置
Match exec "test $(pwd) = /opt/deploy"
IdentityFile ~/.ssh/deploy_ed25519
# 仅对 SFTP 会话(不执行命令)
Match sessiontype subsystem
RemoteCommand internal-sftpInclude 拆分管理
text
# 主配置文件 ~/.ssh/config
Include ~/.ssh/config.d/work
Include ~/.ssh/config.d/personal
Include ~/.ssh/config.d/github服务端配置 —— sshd_config
安全加固配置(推荐基线)
text
# === 端口与监听 ===
Port 2222 # 修改默认端口
ListenAddress 192.168.1.1 # 仅监听内网 IP
# ListenAddress 0.0.0.0 # 监听所有接口
# === 登录控制 ===
PermitRootLogin prohibit-password # 禁止 root 密码登录
# PermitRootLogin no # 完全禁止 root 登录
AllowUsers alice bob # 白名单(最严格)
# DenyUsers mallory # 黑名单
# AllowGroups ssh-users # 允许特定组
# === 认证方式 ===
PubkeyAuthentication yes # 启用公钥认证
PasswordAuthentication no # 禁用密码登录
PermitEmptyPasswords no # 禁止空密码
AuthenticationMethods publickey # 强制仅使用公钥
MaxAuthTries 3 # 最大认证尝试次数
# === Agent/GSSAPI ===
AllowAgentForwarding no # 禁止 agent 转发(安全)
GSSAPIAuthentication no # 禁用 GSSAPI
# === 端口转发控制 ===
AllowTcpForwarding no # 禁用 TCP 转发(严格)
# AllowTcpForwarding local # 仅允许本地转发
AllowStreamLocalForwarding no # 禁用 Unix 套接字转发
GatewayPorts no # 远程转发仅绑 127.0.0.1
PermitTunnel no # 禁止 TUN 转发
# === X11 与 Pseudo-TTY ===
X11Forwarding no # 禁止 X11 转发
PermitTTY yes # 允许 TTY(不设 no 会无法交互)
# === 会话控制 ===
MaxSessions 10 # 单连接最大会话数
MaxStartups 10:30:100 # 限制并发未认证连接
LoginGraceTime 60 # 认证超时(秒)
ClientAliveInterval 300 # 客户端保活间隔(秒)
ClientAliveCountMax 3 # 保活最大重试
# === 日志与信息 ===
LogLevel VERBOSE # 详细日志(安全审计)
SyslogFacility AUTH
PrintMotd no # 不显示 motd
PrintLastLog yes # 显示上次登录信息
Banner /etc/ssh/banner # 登录前警告横幅
# === 加密策略 ===
# 指定允许的密钥交换算法
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
# 指定允许的加密算法
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
# 指定允许的 MAC 算法
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# 最小 RSA 密钥长度
RequiredRSASize 2048
# === 其他 ===
UseDNS no # 禁用 DNS 反向查询(加速连接)
PermitUserEnvironment no # 禁止用户设置环境变量
StrictModes yes # 检查文件权限修改配置后重启
bash
# 检查配置文件语法(必须!)
sshd -t
# 如果语法正确,重载配置
systemctl reload sshd # Linux (systemd)
service sshd reload # Linux (SysVinit)
launchctl kickstart -k system/com.openssh.sshd # macOS常见服务端配置项
| 配置项 | 默认值 | 安全推荐 | 说明 |
|---|---|---|---|
Port | 22 | 非标准端口 | 监听端口 |
PermitRootLogin | prohibit-password | no/prohibit-password | root 登录策略 |
PasswordAuthentication | yes | no | 密码认证 |
PubkeyAuthentication | yes | yes | 公钥认证 |
MaxAuthTries | 6 | 3 | 最大认证尝试 |
LoginGraceTime | 120 | 60 | 登录宽限时间(秒) |
ClientAliveInterval | 0 | 300 | 保活间隔 |
X11Forwarding | no | no | X11 转发 |
AllowTcpForwarding | yes | no/local | TCP 转发 |
UseDNS | no | no | DNS 反向查询 |
AllowUsers | — | 指定用户 | 用户白名单 |
PermitEmptyPasswords | no | no | 空密码 |
StrictModes | yes | yes | 文件权限检查 |
安全最佳实践
客户端安全
- 使用 Ed25519 密钥:推荐
ssh-keygen -t ed25519 - 为私钥设置密码(passphrase):即使私钥泄露也难以直接使用
- 使用 ssh-agent 管理密钥:避免频繁输入密码
IdentitiesOnly yes:只使用显式指定的密钥,防止 agent 中的其他密钥被尝试ForwardAgent no(默认):绝不随意开启 Agent 转发StrictHostKeyChecking accept-new:首次自动接受但主机密钥变更时拒绝VerifyHostKeyDNS yes:如果 DNS 配置了 SSHFP 记录HashKnownHosts yes:known_hosts 中的主机名哈希化- 使用 Host 别名 + HostName:避免长期记住 IP
服务端安全
- 禁用密码登录:
PasswordAuthentication no - 禁用 root 密码登录:
PermitRootLogin prohibit-password - 限制用户:
AllowUsers alice bob - 修改默认端口(简单但有效):
Port 2222 - 部署 Fail2Ban 防暴力破解
- 定期审计日志:
grep "Failed password" /var/log/auth.log - 证书认证(CA) 管理大规模 SSH 密钥
- 禁用不安全的旧算法:限制 KexAlgorithms / Ciphers / MACs
- 使用 PermitOpen / PermitListen 限制端口转发目标
- ChrootDirectory 限制 SFTP-only 用户的环境
Fail2Ban 配置示例
ini
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600常见误区
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| 私钥无密码保护 | ssh-keygen -t ed25519 时设置强密码 |
ForwardAgent yes 在所有主机 | 仅在可信主机开启,或使用 ProxyJump 替代 |
StrictHostKeyChecking no | 使用 accept-new 或首次手动确认 |
| 密码登录 + 弱密码 | 禁用密码,仅用公钥认证 |
PermitRootLogin yes | 设为 prohibit-password 或 no |
| 不限制端口转发 | AllowTcpForwarding local 或 no |
GatewayPorts yes 默认开启 | 保持默认 no,需要时再开启 |
| DSA 密钥(已废弃) | 使用 Ed25519 或 RSA(≥3072 位) |
known_hosts 明文存储主机名 | ssh-keygen -H 哈希化 |
| 使用 SSH-1 协议 | 仅使用 SSH-2 |
调试与排错
连接问题排查流程
bash
# 1. 用详细日志找出问题阶段
ssh -vvv user@host
# 2. 查看客户端最终生效的配置
ssh -G host | grep -E "hostname|port|user|identityfile"
# 3. 检查权限(私钥必须 600,.ssh 目录必须 700)
ls -la ~/.ssh/
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
# 4. 检查服务端日志
ssh user@host 'sudo tail -n 50 /var/log/auth.log'
# 5. 测试密钥是否可用
ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes user@host
# 6. 检查 known_hosts 冲突
ssh-keygen -R hostname # 删除旧条目重试常见报错与解决
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Permission denied (publickey) | 密钥未授权或权限问题 | chmod 600 ~/.ssh/id_*,检查 authorized_keys |
Host key verification failed | 主机密钥变更 | ssh-keygen -R hostname 删除旧指纹 |
Connection refused | 端口错误或 sshd 未运行 | 确认端口和 sshd 状态 |
Connection timed out | 防火墙/网络不通 | 检查网络连接和防火墙规则 |
Too many authentication failures | agent 中密钥过多 | IdentitiesOnly yes 或 ssh-add -D |
WARNING: UNPROTECTED PRIVATE KEY FILE | 私钥权限过宽 | chmod 600 ~/.ssh/id_* |
Bad owner or permissions on ~/.ssh/config | 配置文件权限过宽 | chmod 600 ~/.ssh/config |
No matching host key type found | 算法不匹配 | ssh -Q key 检查,HostKeyAlgorithms +ssh-rsa 兼容旧主机 |
版本信息
- OpenSSH 服务端和客户端版本需匹配。在服务器上使用
sshd -?或ssh -V查看版本。 - 各发行版的 OpenSSH 版本可能落后于上游,
apt/yum安装的版本通常比man.openbsd.org文档旧。
来源
| 来源 | 链接 |
|---|---|
| OpenSSH ssh_config(5) 手册 | https://man.openbsd.org/ssh_config |
| OpenSSH sshd_config(5) 手册 | https://man.openbsd.org/sshd_config |
| OpenSSH 安全指南 | https://infosec.mozilla.org/guidelines/openssh |
| Fail2Ban 项目 | https://github.com/fail2ban/fail2ban |
