问题背景
SSH 是 Linux 服务器最常用的远程管理方式,也是攻击者最常攻击的入口。弱密码、暴力破解、未授权访问等安全问题频发,需要对 SSH 进行全面加固。
本文适用于以下角色:
- 负责服务器安全的运维工程师
- 需要加固 SSH 的系统管理员
- 负责安全合规的安全工程师
- 需要配置跳板机的 DevOps 工程师
SSH 安全的特点:
- 默认配置不够安全
- 是攻击者的首要目标
- 需要多层防护
- 需要平衡安全和易用性
适用场景
本文适用于以下场景:
- 新服务器初始化时的 SSH 加固
- 生产环境服务器的安全加固
- 合规要求的 SSH 配置
- 跳板机的 SSH 配置
- 多用户环境的 SSH 权限管理
- 防止暴力破解的 SSH 配置
- 审计和日志管理
不适用于以下场景:
- 桌面 Linux 的 SSH 配置(安全要求较低)
- 开发测试环境(可以适当放松限制)
- 容器内的 SSH(不推荐在容器内运行 SSH)
核心知识点
SSH 认证方式
密码认证
- 优点:简单易用
- 缺点:容易被暴力破解、容易泄露、难以管理
密钥认证
- 优点:安全性高、难以破解、支持自动化
- 缺点:密钥管理复杂、初次配置较繁琐
双因素认证(2FA)
- 优点:安全性最高
- 缺点:配置复杂、依赖第三方工具
SSH 密钥类型
RSA
- 密钥长度:2048、4096 位
- 安全性:高(4096 位)
- 性能:一般
- 兼容性:最好
ECDSA
- 密钥长度:256、384、521 位
- 安全性:高
- 性能:好
- 兼容性:好
Ed25519
- 密钥长度:固定 256 位
- 安全性:最高
- 性能:最好
- 兼容性:需要 OpenSSH 6.5+
推荐使用 Ed25519 或 RSA 4096 位。
SSH 配置文件
服务端配置
/etc/ssh/sshd_config:SSH 服务器配置/etc/ssh/ssh_host_*_key:服务器密钥
客户端配置
~/.ssh/config:用户级配置/etc/ssh/ssh_config:系统级配置~/.ssh/authorized_keys:授权的公钥~/.ssh/known_hosts:已知主机指纹
SSH 攻击方式
暴力破解
- 使用字典或常见密码尝试登录
- 防御:禁用密码登录、使用 fail2ban、限制登录次数
中间人攻击
- 伪装成服务器,窃取凭据
- 防御:验证主机指纹、使用证书认证
密钥泄露
- 密钥文件被窃取
- 防御:密钥加密、权限控制、定期轮换
端口扫描
- 扫描开放的 22 端口
- 防御:修改默认端口、防火墙限制
整体加固思路
SSH 安全加固遵循纵深防御原则:
网络层防护
↓
认证加固
↓
授权控制
↓
审计日志
↓
持续监控
加固步骤:
- 修改默认配置(端口、协议版本)
- 禁用密码登录,启用密钥认证
- 限制登录用户和来源 IP
- 配置密钥权限和管理
- 启用双因素认证(可选)
- 配置防火墙和 fail2ban
- 配置日志和审计
- 定期检查和更新
实战步骤
第一步:备份现有配置
目的:确保可以快速回滚。
bash# 备份 SSH 服务器配置
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup_$(date +%Y%m%d_%H%M%S)
# 备份 SSH 客户端配置
cp /etc/ssh/ssh_config /etc/ssh/ssh_config.backup_$(date +%Y%m%d_%H%M%S)
# 备份用户 SSH 配置
cp ~/.ssh/config ~/.ssh/config.backup_$(date +%Y%m%d_%H%M%S)
# 备份 authorized_keys
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.backup_$(date +%Y%m%d_%H%M%S)
风险提醒:
- 修改 SSH 配置前必须备份
- 保留至少一个可用的 SSH 连接(不要关闭当前会话)
- 使用新会话测试配置,确认无误后再关闭旧会话
第二步:生成 SSH 密钥对
目的:为密钥登录准备密钥对。
2.1 生成 Ed25519 密钥(推荐)
bash# 在客户端(本地机器)生成密钥
ssh-keygen -t ed25519 -C "your_email@example.com"
# 指定密钥文件名
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_server1 -C "server1_access"
# 为密钥设置密码(推荐)
# 生成过程中会提示输入密码,直接回车则不设置密码
预期输出:
Generating public/private ed25519 key pair.
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:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx your_email@example.com
2.2 生成 RSA 密钥(兼容性好)
bash# 生成 4096 位 RSA 密钥
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 指定密钥文件名
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_server1 -C "server1_access"
2.3 查看生成的密钥
bash# 查看私钥(不要泄露)
cat ~/.ssh/id_ed25519
# 查看公钥(可以公开)
cat ~/.ssh/id_ed25519.pub
# 查看密钥指纹
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
# 查看所有密钥
ls -la ~/.ssh/
2.4 设置密钥权限
bash# 私钥权限必须为 600(仅所有者可读写)
chmod 600 ~/.ssh/id_ed25519
# 公钥权限可以为 644
chmod 644 ~/.ssh/id_ed25519.pub
# .ssh 目录权限必须为 700
chmod 700 ~/.ssh/
判断逻辑:
- 如果私钥权限不正确,SSH 会拒绝使用该密钥
- 如果 .ssh 目录权限不正确,SSH 会忽略该目录下的配置
第三步:上传公钥到服务器
目的:将公钥添加到服务器的授权密钥列表。
3.1 使用 ssh-copy-id(推荐)
bash# 上传公钥到服务器
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip
# 指定端口
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@server-ip
# 如果服务器禁用了密码登录,需要先临时启用
# 或使用其他方式上传公钥
预期输出:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
user@server-ip's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'user@server-ip'"
and check to make sure that only the key(s) you wanted were added.
3.2 手动上传公钥
如果 ssh-copy-id 不可用,可以手动上传。
bash# 方法 1:使用 scp 上传
scp ~/.ssh/id_ed25519.pub user@server-ip:/tmp/
# 在服务器上执行
ssh user@server-ip
mkdir -p ~/.ssh
chmod 700 ~/.ssh
cat /tmp/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
rm /tmp/id_ed25519.pub
# 方法 2:直接复制粘贴
# 在客户端查看公钥
cat ~/.ssh/id_ed25519.pub
# 在服务器上执行
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your_email@example.com" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
3.3 验证公钥是否上传成功
bash# 在服务器上查看 authorized_keys
cat ~/.ssh/authorized_keys
# 尝试使用密钥登录
ssh -i ~/.ssh/id_ed25519 user@server-ip
# 如果成功登录且未提示输入密码,说明公钥配置成功
第四步:配置 SSH 服务器
目的:加固 SSH 服务器配置。
4.1 修改 SSH 配置文件
编辑 /etc/ssh/sshd_config:
bash# 备份配置
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# 编辑配置
vim /etc/ssh/sshd_config
推荐配置:
sshd_config# 基础配置
Port 22222 # 修改默认端口(可选,但建议修改)
AddressFamily inet # 仅使用 IPv4(如果不需要 IPv6)
ListenAddress 0.0.0.0 # 监听所有 IP(或指定特定 IP)
# 协议和算法
Protocol 2 # 仅使用 SSH 协议版本 2
HostKey /etc/ssh/ssh_host_ed25519_key # 使用 Ed25519 主机密钥
HostKey /etc/ssh/ssh_host_rsa_key # 使用 RSA 主机密钥
# 加密算法(仅使用强加密)
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
# 认证配置
PubkeyAuthentication yes # 启用公钥认证
AuthorizedKeysFile .ssh/authorized_keys # 公钥文件位置
PasswordAuthentication no # 禁用密码认证
PermitEmptyPasswords no # 禁止空密码
ChallengeResponseAuthentication no # 禁用质询响应认证
UsePAM yes # 使用 PAM 认证(用于日志和会话管理)
# 登录控制
PermitRootLogin no # 禁止 root 直接登录
MaxAuthTries 3 # 最大认证尝试次数
MaxSessions 10 # 最大会话数
MaxStartups 10:30:60 # 并发连接限制
# 访问控制
AllowUsers deploy ops # 仅允许指定用户登录
# AllowGroups sshusers # 或仅允许指定组登录
# DenyUsers baduser # 拒绝指定用户登录
# DenyGroups badgroup # 拒绝指定组登录
# 会话配置
ClientAliveInterval 300 # 客户端存活检测间隔(5 分钟)
ClientAliveCountMax 2 # 最大存活检测次数
LoginGraceTime 60 # 登录宽限时间(60 秒)
TCPKeepAlive yes # 启用 TCP keepalive
# 转发和隧道
X11Forwarding no # 禁用 X11 转发
AllowTcpForwarding no # 禁用 TCP 转发(如需端口转发则设为 yes)
AllowStreamLocalForwarding no # 禁用 Unix 域套接字转发
GatewayPorts no # 禁用网关端口
PermitTunnel no # 禁用隧道
# 其他安全配置
PermitUserEnvironment no # 禁止用户设置环境变量
StrictModes yes # 严格检查文件权限
IgnoreRhosts yes # 忽略 .rhosts 文件
HostbasedAuthentication no # 禁用基于主机的认证
PrintMotd yes # 显示登录消息
PrintLastLog yes # 显示上次登录信息
Banner /etc/ssh/banner # 登录前显示警告信息(可选)
# 日志配置
SyslogFacility AUTH # 日志设施
LogLevel VERBOSE # 日志级别(推荐 VERBOSE 或 INFO)
# Subsystem
Subsystem sftp /usr/libexec/openssh/sftp-server # SFTP 子系统
关键配置说明:
Port
- 默认 22 端口容易被扫描
- 修改为非标准端口(如 22222)可以减少扫描和攻击
- 修改端口后防火墙也需要同步修改
PermitRootLogin
- 禁止 root 直接登录,强制使用普通用户 + sudo
- 如果必须允许 root 登录,设置为
prohibit-password(仅允许密钥)
PasswordAuthentication
- 禁用密码认证,仅使用密钥认证
- 配置密钥登录后再禁用
- 禁用后无法使用密码登录,确保密钥已配置
AllowUsers / AllowGroups
- 白名单方式,仅允许指定用户或组登录
- 优先使用 AllowUsers
- 如果用户数量多,使用 AllowGroups
ClientAliveInterval
- 定期向客户端发送存活检测包
- 防止长时间空闲连接被防火墙关闭
- 也可以及时发现断开的连接
LogLevel
- VERBOSE:记录详细日志,包括密钥指纹
- INFO:记录常规日志
- DEBUG:记录调试日志(不推荐生产环境)
4.2 创建登录警告信息(可选)
bash# 创建 banner 文件
cat > /etc/ssh/banner << 'EOF'
###############################################################################
# WARNING #
###############################################################################
# Unauthorized access to this system is forbidden and will be prosecuted by #
# law. By accessing this system, you agree that your actions may be monitored#
# if unauthorized usage is suspected. #
###############################################################################
EOF
# 在 sshd_config 中启用
# Banner /etc/ssh/banner
4.3 检查配置语法
bash# 检查配置语法
sshd -t
# 或
/usr/sbin/sshd -t
预期输出:
# 无输出表示配置正确
异常输出:
/etc/ssh/sshd_config line 25: Bad configuration option: InvalidOption
/etc/ssh/sshd_config: terminating, 1 bad configuration options
判断逻辑:
- 如果配置有误,sshd -t 会报错并指出错误行
- 修复错误后再次检查,直到无错误
4.4 重新加载 SSH 服务
风险提醒:
- 重新加载 SSH 配置可能导致当前连接断开
- 必须保留至少一个已登录的会话(不要关闭)
- 使用新会话测试配置
bash# 方法 1:重新加载配置(不中断现有连接,推荐)
systemctl reload sshd
# 方法 2:重启服务(会中断现有连接,不推荐)
systemctl restart sshd
# 检查服务状态
systemctl status sshd
# 查看日志
journalctl -u sshd -f
4.5 测试 SSH 连接
保留当前会话,打开新终端测试:
bash# 测试密钥登录
ssh -i ~/.ssh/id_ed25519 user@server-ip -p 22222
# 测试密码登录(应该被拒绝)
ssh user@server-ip -p 22222
# 测试 root 登录(应该被拒绝)
ssh root@server-ip -p 22222
预期结果:
- 密钥登录成功
- 密码登录被拒绝
- root 登录被拒绝
如果测试失败:
- 不要关闭当前会话
- 检查配置文件
- 查看日志:
journalctl -u sshd -f - 修复问题后再次测试
- 确认无误后再关闭旧会话
第五步:配置客户端 SSH
目的:简化 SSH 连接,配置客户端选项。
5.1 配置 SSH 客户端配置文件
编辑 ~/.ssh/config:
bash# 创建或编辑配置文件
vim ~/.ssh/config
# 设置权限
chmod 600 ~/.ssh/config
配置示例:
sshconfig# 默认配置
Host *
# 使用强加密算法
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
# 连接保持
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
# 连接复用
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 600
# 其他选项
Compression yes
ForwardAgent no
ForwardX11 no
StrictHostKeyChecking ask
HashKnownHosts yes
# 生产服务器
Host prod-web1
HostName 192.168.1.101
Port 22222
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
ForwardAgent no
Host prod-web2
HostName 192.168.1.102
Port 22222
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
# 跳板机配置
Host jump
HostName 1.2.3.4
Port 22222
User jump_user
IdentityFile ~/.ssh/id_ed25519_jump
# 通过跳板机访问内网服务器
Host internal-*
ProxyJump jump
User deploy
IdentityFile ~/.ssh/id_ed25519_internal
Host internal-db1
HostName 10.0.1.101
Port 22
Host internal-db2
HostName 10.0.1.102
Port 22
# 开发环境
Host dev-*
User developer
IdentityFile ~/.ssh/id_ed25519_dev
ForwardAgent yes
Host dev-web1
HostName 192.168.2.101
配置说明:
ControlMaster / ControlPath / ControlPersist
- 连接复用,多个 SSH 会话共享一个连接
- 提高连接速度,减少认证次数
- ControlPersist 600 表示主连接空闲 600 秒后关闭
ServerAliveInterval / ServerAliveCountMax
- 定期向服务器发送存活检测包
- 防止连接被防火墙关闭
- ServerAliveInterval 60 表示每 60 秒发送一次
- ServerAliveCountMax 3 表示最多发送 3 次
ProxyJump
- 通过跳板机访问内网服务器
- 相当于
ssh -J jump internal-db1 - 比 ProxyCommand 更简洁
ForwardAgent
- SSH 代理转发,将本地 SSH 密钥转发到远程服务器
- 风险:远程服务器可以使用你的密钥访问其他服务器
- 仅在需要时启用,默认禁用
5.2 使用 SSH 配置连接
bash# 直接使用别名连接
ssh prod-web1
# 相当于
ssh -i ~/.ssh/id_ed25519_prod -p 22222 deploy@192.168.1.101
# 通过跳板机连接内网服务器
ssh internal-db1
# 相当于
ssh -J jump_user@1.2.3.4:22222 deploy@10.0.1.101
5.3 配置连接复用目录
bash# 创建连接复用目录
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets
第六步:配置 sudo 权限
目的:禁止 root 直接登录后,使用 sudo 提权。
6.1 创建普通用户
bash# 创建用户
useradd -m -s /bin/bash deploy
# 设置密码(可选,仅用于本地登录或紧急恢复)
passwd deploy
# 或创建用户时指定 UID 和 GID
useradd -m -s /bin/bash -u 1001 -g 1001 deploy
6.2 配置 sudo 权限
bash# 编辑 sudoers 文件
visudo
# 或编辑单独的配置文件
vim /etc/sudoers.d/deploy
配置示例:
sudoers# 允许 deploy 用户无密码执行所有命令
deploy ALL=(ALL) NOPASSWD: ALL
# 允许 deploy 用户执行指定命令
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/docker
# 允许 ops 组无密码执行所有命令
%ops ALL=(ALL) NOPASSWD: ALL
# 要求输入密码
deploy ALL=(ALL) ALL
配置说明:
NOPASSWD:无需输入密码即可 sudoALL=(ALL) ALL:可以以任何用户身份执行任何命令/usr/bin/systemctl:仅允许执行指定命令%ops:% 表示组
风险提醒:
NOPASSWD: ALL风险较大,仅在自动化场景使用- 生产环境建议要求输入密码:
ALL=(ALL) ALL - 或仅授权特定命令
6.3 测试 sudo 权限
bash# 切换到 deploy 用户
su - deploy
# 测试 sudo
sudo whoami
# 应该输出: root
# 查看 sudo 权限
sudo -l
# 执行需要 root 权限的命令
sudo systemctl status sshd
第七步:配置防火墙
目的:限制 SSH 访问来源。
7.1 使用 iptables 限制访问
bash# 清空现有规则(谨慎操作)
iptables -F
iptables -X
# 设置默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 允许本地回环
iptables -A INPUT -i lo -j ACCEPT
# 允许已建立的连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许特定 IP 访问 SSH(修改为实际端口)
iptables -A INPUT -p tcp -s 1.2.3.4 --dport 22222 -j ACCEPT
iptables -A INPUT -p tcp -s 5.6.7.0/24 --dport 22222 -j ACCEPT
# 拒绝其他 SSH 连接
iptables -A INPUT -p tcp --dport 22222 -j DROP
# 允许其他必要服务(根据实际情况)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 保存规则
iptables-save > /etc/iptables/rules.v4
# 或(CentOS/RHEL)
service iptables save
风险提醒:
- 修改防火墙规则前必须保留当前 SSH 连接
- 错误的规则可能导致无法连接
- 建议先在测试环境验证
7.2 使用 firewalld 限制访问
bash# 查看当前规则
firewall-cmd --list-all
# 移除默认的 SSH 服务(端口 22)
firewall-cmd --permanent --remove-service=ssh
# 添加自定义端口(修改为实际端口)
firewall-cmd --permanent --add-port=22222/tcp
# 限制来源 IP
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="1.2.3.4" port port="22222" protocol="tcp" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="5.6.7.0/24" port port="22222" protocol="tcp" accept'
# 重新加载规则
firewall-cmd --reload
# 查看规则
firewall-cmd --list-all
7.3 使用云服务商安全组
如果是云服务器,优先使用安全组:
- 阿里云:安全组规则
- 腾讯云:安全组
- AWS:Security Group
- Azure:Network Security Group
安全组配置示例:
入方向规则:
协议 端口 来源 动作
TCP 22222 1.2.3.4/32 允许
TCP 22222 5.6.7.0/24 允许
TCP 80 0.0.0.0/0 允许
TCP 443 0.0.0.0/0 允许
ALL ALL 其他 拒绝
第八步:配置 fail2ban
目的:防止暴力破解。
8.1 安装 fail2ban
bash# CentOS/RHEL
yum install -y epel-release
yum install -y fail2ban
# Ubuntu/Debian
apt install -y fail2ban
# 启动服务
systemctl start fail2ban
systemctl enable fail2ban
8.2 配置 fail2ban
bash# 创建自定义配置(不要直接修改 jail.conf)
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# 编辑配置
vim /etc/fail2ban/jail.local
配置示例:
ini[DEFAULT]
# 忽略的 IP(白名单)
ignoreip = 127.0.0.1/8 ::1 1.2.3.4
# 封禁时长(秒)
bantime = 3600
# 检测时间窗口(秒)
findtime = 600
# 最大失败次数
maxretry = 3
# 邮件通知(可选)
destemail = admin@example.com
sendername = Fail2Ban
mta = sendmail
action = %(action_mwl)s
[sshd]
enabled = true
port = 22222
filter = sshd
logpath = /var/log/secure
# CentOS/RHEL 使用 /var/log/secure
# Ubuntu/Debian 使用 /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
配置说明:
bantime:封禁时长,3600 秒 = 1 小时findtime:检测时间窗口,600 秒内maxretry:最大失败次数,超过 3 次封禁logpath:日志文件路径
8.3 启动 fail2ban
bash# 重启服务
systemctl restart fail2ban
# 检查状态
systemctl status fail2ban
# 查看 SSH 保护状态
fail2ban-client status sshd
预期输出:
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/secure
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
8.4 测试 fail2ban
bash# 故意输错密码 3 次
ssh user@server-ip -p 22222
# 查看封禁列表
fail2ban-client status sshd
# 手动解封 IP
fail2ban-client set sshd unbanip 1.2.3.4
# 手动封禁 IP
fail2ban-client set sshd banip 1.2.3.4
第九步:配置日志和审计
目的:记录 SSH 登录和操作日志,方便审计。
9.1 配置 SSH 日志
SSH 日志默认记录在:
- CentOS/RHEL:
/var/log/secure - Ubuntu/Debian:
/var/log/auth.log
查看 SSH 日志:
bash# 查看所有 SSH 日志
grep sshd /var/log/secure
# 查看登录成功日志
grep "Accepted" /var/log/secure
# 查看登录失败日志
grep "Failed" /var/log/secure
# 查看特定用户的登录日志
grep "user deploy" /var/log/secure
# 查看特定 IP 的登录日志
grep "1.2.3.4" /var/log/secure
# 实时监控日志
tail -f /var/log/secure | grep sshd
9.2 配置命令审计(记录所有执行的命令)
使用 script 命令记录会话:
bash# 在用户的 .bashrc 或 .bash_profile 中添加
cat >> ~/.bashrc << 'EOF'
# 记录所有命令到日志文件
if [ -z "$SCRIPT_LOG" ]; then
export SCRIPT_LOG=1
logdir=/var/log/user_commands
mkdir -p $logdir
logfile=$logdir/$(whoami)_$(date +%Y%m%d_%H%M%S).log
script -q -a -f $logfile
exit
fi
EOF
风险提醒:
- 日志文件会占用磁盘空间,需要定期清理
- 日志文件包含敏感信息,需要严格控制权限
- 日志目录需要 root 权限,普通用户无法删除
9.3 使用 auditd 审计
bash# 安装 auditd
yum install -y audit # CentOS/RHEL
apt install -y auditd # Ubuntu/Debian
# 启动服务
systemctl start auditd
systemctl enable auditd
# 添加审计规则(审计 SSH 相关文件)
auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
auditctl -w /etc/ssh/ssh_host_rsa_key -p wa -k ssh_host_key
auditctl -w /home/ -p wa -k home_directory
# 查看审计规则
auditctl -l
# 查看审计日志
ausearch -k sshd_config
ausearch -k ssh_host_key
# 生成审计报告
aureport --summary
第十步:密钥管理和轮换
目的:定期更新密钥,防止密钥泄露。
10.1 定期更新密钥
bash# 生成新密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -C "new_key"
# 上传新密钥到服务器
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub user@server-ip
# 测试新密钥是否可用
ssh -i ~/.ssh/id_ed25519_new user@server-ip
# 确认无误后,删除旧密钥
# 在服务器上编辑 authorized_keys,删除旧公钥
vim ~/.ssh/authorized_keys
# 备份并删除旧密钥
mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.old
mv ~/.ssh/id_ed25519_new ~/.ssh/id_ed25519
10.2 撤销泄露的密钥
bash# 在服务器上删除泄露的公钥
vim ~/.ssh/authorized_keys
# 删除对应的公钥行
# 或使用命令删除特定公钥
ssh-keygen -R hostname # 删除 known_hosts 中的主机指纹
10.3 密钥备份
bash# 备份私钥(加密存储)
tar -czf ssh_keys_backup.tar.gz ~/.ssh/
gpg -c ssh_keys_backup.tar.gz
rm ssh_keys_backup.tar.gz
# 存储到安全位置(离线存储、密码管理器等)
第十一步:启用双因素认证(可选)
目的:增加额外的安全层。
11.1 安装 Google Authenticator
bash# CentOS/RHEL
yum install -y google-authenticator
# Ubuntu/Debian
apt install -y libpam-google-authenticator
11.2 配置 Google Authenticator
bash# 以需要配置的用户身份运行
google-authenticator
# 按提示操作:
# - Do you want authentication tokens to be time-based? (y/n) y
# - 扫描二维码或手动输入密钥到手机 APP
# - Do you want me to update your "/home/user/.google_authenticator" file? (y/n) y
# - Do you want to disallow multiple uses of the same authentication token? (y/n) y
# - By default, tokens are good for 30 seconds. Do you want to increase? (y/n) n
# - Do you want to enable rate-limiting? (y/n) y
11.3 配置 PAM
编辑 /etc/pam.d/sshd:
bash# 在文件开头添加
auth required pam_google_authenticator.so
11.4 配置 SSH
编辑 /etc/ssh/sshd_config:
sshd_config# 启用双因素认证
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
11.5 重启 SSH 服务
bashsystemctl restart sshd
11.6 测试双因素认证
bash# 连接服务器
ssh user@server-ip
# 应该提示输入验证码
Verification code:
# 输入手机 APP 显示的 6 位数字
第十二步:配置跳板机(堡垒机)
目的:集中管理 SSH 访问,增强审计能力。
12.1 跳板机架构
用户 → 跳板机 → 内网服务器
跳板机配置要点:
- 仅允许特定用户登录跳板机
- 跳板机到内网服务器使用密钥认证
- 记录所有通过跳板机的操作
- 限制跳板机的权限(仅允许 SSH 转发)
12.2 配置跳板机 SSH
跳板机 /etc/ssh/sshd_config:
sshd_config# 仅允许特定用户登录
AllowUsers jump_user1 jump_user2
# 禁止 shell 登录(仅允许端口转发)
ForceCommand /bin/false
# 或允许登录但限制命令
ForceCommand /usr/local/bin/jump_menu.sh
# 启用 TCP 转发
AllowTcpForwarding yes
GatewayPorts no
# 记录详细日志
LogLevel VERBOSE
12.3 配置跳板机脚本
创建 /usr/local/bin/jump_menu.sh:
bash#!/bin/bash
# 记录登录信息
logger -t JUMP "User $USER logged in from $SSH_CLIENT"
# 显示菜单
cat << EOF
=== Jump Server Menu ===
1) Connect to prod-web1
2) Connect to prod-web2
3) Connect to prod-db1
4) Exit
========================
EOF
read -p "Select option: " option
case $option in
1)
ssh deploy@10.0.1.101
;;
2)
ssh deploy@10.0.1.102
;;
3)
ssh deploy@10.0.2.101
;;
4)
exit 0
;;
*)
echo "Invalid option"
exit 1
;;
esac
12.4 使用 ProxyJump 连接
客户端配置 ~/.ssh/config:
sshconfig# 跳板机
Host jump
HostName jump.example.com
Port 22222
User jump_user
IdentityFile ~/.ssh/id_ed25519_jump
# 通过跳板机访问内网服务器
Host prod-web1
HostName 10.0.1.101
User deploy
Port 22
ProxyJump jump
IdentityFile ~/.ssh/id_ed25519_prod
连接:
bash# 一条命令连接到内网服务器
ssh prod-web1
# 相当于
ssh -J jump_user@jump.example.com:22222 deploy@10.0.1.101
常用命令
SSH 密钥管理
bash# 生成密钥
ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 上传公钥
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip
# 查看密钥指纹
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
# 修改密钥密码
ssh-keygen -p -f ~/.ssh/id_ed25519
# 测试连接
ssh -v user@server-ip
SSH 服务管理
bash# 检查配置
sshd -t
# 重新加载配置
systemctl reload sshd
# 重启服务
systemctl restart sshd
# 查看状态
systemctl status sshd
# 查看日志
journalctl -u sshd -f
tail -f /var/log/secure
fail2ban 管理
bash# 查看状态
fail2ban-client status
fail2ban-client status sshd
# 解封 IP
fail2ban-client set sshd unbanip 1.2.3.4
# 封禁 IP
fail2ban-client set sshd banip 1.2.3.4
# 重新加载配置
fail2ban-client reload
日志分析
bash# 查看登录成功日志
grep "Accepted" /var/log/secure
# 查看登录失败日志
grep "Failed" /var/log/secure
# 统计失败登录次数
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn
# 查看当前登录用户
w
who
# 查看登录历史
last
lastlog
配置示例
完整的配置示例已在实战步骤中给出。
风险提醒
高风险操作
禁用密码认证
bash# 风险:如果密钥未配置或丢失,将无法登录
# 操作前:确保密钥已配置并测试成功
# 操作前:保留至少一个已登录的会话
# 操作:修改 sshd_config,设置 PasswordAuthentication no
# 回滚:恢复配置,重新加载服务
修改 SSH 端口
bash# 风险:防火墙未同步修改,导致无法连接
# 操作前:确保防火墙规则正确
# 操作前:保留至少一个已登录的会话
# 操作:修改 sshd_config,设置 Port 22222
# 回滚:恢复配置,重新加载服务
禁止 root 登录
bash# 风险:如果普通用户未配置 sudo,将无法管理服务器
# 操作前:确保普通用户已配置 sudo 权限
# 操作前:测试普通用户可以 sudo 执行命令
# 操作:修改 sshd_config,设置 PermitRootLogin no
# 回滚:恢复配置,重新加载服务
配置防火墙
bash# 风险:错误的防火墙规则可能导致无法连接
# 操作前:保留至少一个已登录的会话
# 操作前:在测试环境验证规则
# 操作:添加防火墙规则,允许特定 IP 访问 SSH
# 回滚:清除规则,恢复默认策略
常见误区
误区 1:禁用密码后立即关闭当前会话
应该保留当前会话,使用新会话测试密钥登录,确认无误后再关闭。
误区 2:私钥权限设置为 644
私钥权限必须为 600,否则 SSH 会拒绝使用该密钥。
误区 3:在容器内运行 SSH
容器内运行 SSH 不符合容器设计理念,且安全性差。应该使用 docker exec 或 kubectl exec 进入容器。
误区 4:所有用户都配置 NOPASSWD sudo
NOPASSWD: ALL 风险较大,应仅在自动化场景使用。生产环境建议要求输入密码。
误区 5:修改 SSH 配置后直接重启服务
应该使用 systemctl reload sshd 重新加载配置,而非 restart,避免中断现有连接。
验证方式
验证密钥登录
bash# 测试密钥登录
ssh -i ~/.ssh/id_ed25519 user@server-ip
# 应该不提示输入密码(或仅提示密钥密码)
验证密码登录已禁用
bash# 尝试密码登录
ssh user@server-ip
# 应该提示 Permission denied (publickey)
验证 root 登录已禁止
bash# 尝试 root 登录
ssh root@server-ip
# 应该提示 Permission denied (publickey)
验证防火墙规则
bash# 从允许的 IP 测试
ssh user@server-ip
# 应该成功
# 从不允许的 IP 测试
ssh user@server-ip
# 应该超时或被拒绝
验证 fail2ban 工作
bash# 故意输错密码 3 次
ssh user@server-ip
# 查看是否被封禁
fail2ban-client status sshd
回滚方案
SSH 配置回滚
bash# 恢复备份
cp /etc/ssh/sshd_config.backup /etc/ssh/sshd_config
# 检查配置
sshd -t
# 重新加载
systemctl reload sshd
防火墙规则回滚
bash# iptables 回滚
iptables-restore < /etc/iptables/rules.v4.backup
# firewalld 回滚
firewall-cmd --reload
fail2ban 回滚
bash# 停止服务
systemctl stop fail2ban
# 清空封禁列表
rm -f /var/lib/fail2ban/*
# 恢复配置
cp /etc/fail2ban/jail.local.backup /etc/fail2ban/jail.local
# 重启服务
systemctl start fail2ban
生产环境注意事项
操作前检查
- 备份当前配置
- 保留至少一个已登录的会话
- 准备回滚方案
- 在测试环境验证
- 通知相关团队
操作中注意
- 逐项配置,每项配置后测试
- 使用
systemctl reload而非restart - 随时准备回滚
- 记录所有操作
操作后验证
- 测试密钥登录
- 测试密码登录(应被拒绝)
- 测试防火墙规则
- 测试 fail2ban
- 查看日志
持续维护
- 定期更新密钥
- 定期检查日志
- 定期更新 SSH 版本
- 定期审计用户权限
- 定期测试备份恢复
总结
SSH 安全加固是服务器安全的基础,需要从多个层面进行防护:
- 认证加固:禁用密码登录,启用密钥认证
- 访问控制:限制登录用户和来源 IP,禁止 root 直接登录
- 网络防护:修改默认端口,配置防火墙,使用 fail2ban
- 权限管理:配置 sudo 权限,使用跳板机
- 审计日志:记录所有 SSH 操作,定期审计
- 持续维护:定期更新密钥,定期检查配置
关键配置:
PasswordAuthentication no:禁用密码认证PermitRootLogin no:禁止 root 登录PubkeyAuthentication yes:启用密钥认证AllowUsers:限制登录用户Port:修改默认端口(可选)
安全是一个持续的过程,需要不断监控、审计和改进。
SSH 安全加固不是一劳永逸的,需要根据实际情况调整配置,平衡安全和易用性,确保服务器安全可控。
文末福利

本文链接:https://www.yunweipai.com/archives/49399




网友评论comments