SSH(Secure Shell)是系统管理员最常用的远程访问协议,也是攻击者的首要目标。本文详细介绍 SSH 安全加固的各个层面,从基础配置到高级技巧,帮助你构建坚不可摧的 SSH 防线。
一、SSH 基础安全配置#
sshd_config 核心配置#
配置文件路径:/etc/ssh/sshd_config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# 禁用 root 登录
PermitRootLogin no
# 禁用密码认证(强制密钥认证)
PasswordAuthentication no
PubkeyAuthentication yes
# 禁用空密码
PermitEmptyPasswords no
# 禁用 PAM(可选,提高安全性但可能影响某些认证方式)
UsePAM no
# 限制登录用户
AllowUsers admin deploy@192.168.1.0/24
# 或使用组
AllowGroups ssh-users
# 设置登录超时(秒)
LoginGraceTime 60
# 最大认证尝试次数
MaxAuthTries 3
# 限制会话数
MaxSessions 10
# 限制启动会话数(防止资源耗尽)
MaxStartups 10:30:100
# 格式: start:rate:full
# start: 开始拒绝前的未认证连接数
# rate: 拒绝概率增长速率
# full: 完全拒绝的连接数
|
修改默认端口#
1
2
3
4
5
6
7
8
9
10
11
12
|
# 不要使用默认的 22 端口
Port 2222
# 或使用多个端口(便于过渡)
Port 22
Port 2222
# 记得更新防火墙规则
ufw allow 2222/tcp
# 或
firewall-cmd --add-port=2222/tcp --permanent
firewall-cmd --reload
|
完整的安全配置示例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# /etc/ssh/sshd_config 安全加固版
# 网络设置
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0
# 主机密钥
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# 加密算法(只允许强加密)
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
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
# 认证设置
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 5
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
# 禁用不安全功能
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
# 连接设置
LoginGraceTime 60
TCPKeepAlive yes
ClientAliveInterval 300
ClientAliveCountMax 2
# 日志
SyslogFacility AUTH
LogLevel VERBOSE
# 访问控制
AllowUsers admin@192.168.1.0/24 deploy
# 重启服务
# systemctl restart sshd
|
验证配置#
1
2
3
4
5
6
7
8
9
|
# 测试配置语法
sshd -t
# 测试配置并显示有效配置
sshd -T
# 查看特定参数
sshd -T | grep -i "passwordauthentication"
sshd -T | grep -i "permitrootlogin"
|
二、SSH 密钥管理#
生成安全的 SSH 密钥#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 推荐使用 Ed25519(性能好、安全性高)
ssh-keygen -t ed25519 -C "your_email@example.com"
# 使用 RSA(兼容性好)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 带密码保护的密钥
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_secure
# 指定密钥注释
ssh-keygen -t ed25519 -C "server-admin-$(date +%Y%m%d)"
# 使用硬件密钥(如 YubiKey)
ssh-keygen -t ecdsa-sk -C "hardware-key"
# 显示密钥指纹
ssh-keygen -lf ~/.ssh/id_ed25519.pub
|
密钥部署#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 手动复制公钥
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# 指定端口
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@server
# 手动安装(当 ssh-copy-id 不可用时)
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# 设置正确权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
|
管理 known_hosts#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 查看已知主机
cat ~/.ssh/known_hosts
# 删除特定主机
ssh-keygen -R hostname
ssh-keygen -R 192.168.1.100
# 删除特定行的主机
ssh-keygen -R hostname -f ~/.ssh/known_hosts
# 显示主机密钥指纹
ssh-keygen -l -f ~/.ssh/known_hosts
# 严格主机密钥检查
# 在 ~/.ssh/config 中设置
Host *
StrictHostKeyChecking yes
UserKnownHostsFile ~/.ssh/known_hosts
|
SSH Agent 管理#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# 启动 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-add -t 3600 ~/.ssh/id_ed25519
# 在 ~/.ssh/config 中自动添加密钥
Host *
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
|
三、SSH 客户端配置#
~/.ssh/config 配置#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
# 全局默认设置
Host *
# 使用 Ed25519 密钥
IdentityFile ~/.ssh/id_ed25519
# 自动添加密钥到 agent
AddKeysToAgent yes
# 保持连接
ServerAliveInterval 60
ServerAliveCountMax 3
# 连接复用
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 压缩传输
Compression yes
# 生产服务器
Host prod-*
User admin
Port 2222
ForwardAgent no
ForwardX11 no
# 特定服务器配置
Host myserver
HostName 192.168.1.100
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_server
# 跳板机
ProxyJump bastion.example.com
# 通过跳板机连接
Host internal-server
HostName 10.0.0.50
User admin
ProxyJump jumpuser@jumpserver.example.com:2222
# 多跳连接
Host final-destination
HostName 192.168.2.100
ProxyJump jump1.example.com,jump2.example.com
# Git 服务器
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
Host gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519_gitlab
|
创建目录结构#
1
2
3
4
5
6
7
|
# 创建 socket 目录用于连接复用
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets
# 创建配置文件
touch ~/.ssh/config
chmod 600 ~/.ssh/config
|
四、高级安全措施#
双因素认证 (2FA/MFA)#
使用 Google Authenticator:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# 安装
apt install libpam-google-authenticator
# 为用户配置
google-authenticator
# 回答问题:
# Do you want authentication tokens to be time-based? y
# Do you want me to update your "~/.google_authenticator" file? y
# Do you want to disallow multiple uses? y
# Do you want to do so? n (默认限制到30秒)
# Do you want to enable rate-limiting? y
# 编辑 /etc/pam.d/sshd
# 添加:
auth required pam_google_authenticator.so
# 编辑 /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# 重启服务
systemctl restart sshd
|
限制 IP 访问#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# 方法1:在 sshd_config 中使用 AllowUsers
AllowUsers admin@192.168.1.0/24 deploy@10.0.0.100
# 方法2:使用 AllowGroups
AllowGroups ssh-admins ssh-developers
# 方法3:使用 /etc/hosts.allow 和 /etc/hosts.deny
# /etc/hosts.allow
sshd: 192.168.1.0/255.255.255.0
sshd: 10.0.0.100
# /etc/hosts.deny
sshd: ALL
# 方法4:使用防火墙(推荐)
# UFW
ufw allow from 192.168.1.0/24 to any port 2222 proto tcp
# iptables
iptables -A INPUT -p tcp --dport 2222 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 2222 -j DROP
# firewalld
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="2222" accept'
firewall-cmd --reload
|
Fail2Ban 防暴力破解#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# 安装
apt install fail2ban
# 创建本地配置
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600
ignoreip = 127.0.0.1/8 192.168.1.0/24
[sshd-aggressive]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 2
findtime = 86400
bantime = 604800
EOF
# 启动服务
systemctl enable fail2ban
systemctl start fail2ban
# 查看状态
fail2ban-client status sshd
# 手动解封 IP
fail2ban-client set sshd unbanip 192.168.1.100
# 查看日志
tail -f /var/log/fail2ban.log
|
端口敲门 (Port Knocking)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 安装 knockd
apt install knockd
# 编辑 /etc/knockd.conf
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 5
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
# 客户端使用
knock server.example.com 7000:tcp 8000:tcp 9000:tcp
ssh -p 2222 user@server.example.com
knock server.example.com 9000:tcp 8000:tcp 7000:tcp
|
五、SSH 日志与监控#
日志配置#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 在 sshd_config 中设置日志级别
LogLevel VERBOSE
# 查看 SSH 登录日志
# Debian/Ubuntu
tail -f /var/log/auth.log
# RHEL/CentOS
tail -f /var/log/secure
# 查看成功登录
grep "Accepted password\|Accepted publickey" /var/log/auth.log
# 查看失败登录
grep "Failed password\|Connection closed by" /var/log/auth.log
# 查看特定用户的登录
grep "user" /var/log/auth.log | grep "session opened"
# 统计失败登录 IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head
|
登录监控脚本#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#!/bin/bash
# ssh_monitor.sh - SSH 登录监控
LOG_FILE="/var/log/auth.log"
ALERT_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
# 监控实时登录
tail -fn0 $LOG_FILE | while read line; do
if echo "$line" | grep -q "Accepted"; then
user=$(echo "$line" | grep -oP '(?<=for )[^ ]+')
ip=$(echo "$line" | grep -oP '\d+\.\d+\.\d+\.\d+' | tail -1)
method=$(echo "$line" | grep -oP 'publickey|password')
message="SSH 登录告警: 用户 $user 从 $ip 通过 $method 方式登录成功"
echo "$(date): $message"
# 发送告警
# curl -X POST -H 'Content-type: application/json' \
# --data "{\"text\":\"$message\"}" $ALERT_WEBHOOK
fi
done
|
使用 last 和 w 命令#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 查看登录历史
last -n 20
# 查看 SSH 登录历史
last -f /var/log/wtmp | grep ssh
# 查看当前登录用户
w
# 查看登录失败记录
lastb -n 20
# 清除登录记录(需要 root)
echo > /var/log/wtmp
echo > /var/log/btmp
|
六、SSH 高级用法#
SSH 隧道#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 本地端口转发(访问远程内网服务)
ssh -L 8080:localhost:80 user@server
# 本地访问 localhost:8080 -> 远程 localhost:80
# 远程端口转发(让远程访问本地服务)
ssh -R 8080:localhost:3000 user@server
# 远程访问 server:8080 -> 本地 localhost:3000
# 动态端口转发(SOCKS 代理)
ssh -D 1080 user@server
# 浏览器设置 SOCKS 代理: localhost:1080
# 后台运行隧道
ssh -fNL 8080:localhost:80 user@server
# 保持隧道存活
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -fNL 8080:localhost:80 user@server
|
SSH 跳转#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 通过跳板机连接
ssh -J user@jumpserver user@targetserver
# 多跳转
ssh -J user1@jump1,user2@jump2 user@target
# 使用 ProxyCommand(兼容旧版本)
ssh -o ProxyCommand="ssh -W %h:%p user@jumpserver" user@targetserver
# 在 ~/.ssh/config 中配置
Host target
HostName 10.0.0.50
ProxyJump jumpserver
|
SFTP 和 SCP#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# SCP 上传
scp -P 2222 local_file user@server:/remote/path/
# SCP 下载
scp -P 2222 user@server:/remote/file ./local/path/
# SCP 目录
scp -P 2222 -r local_dir user@server:/remote/path/
# SFTP 命令行
sftp -P 2222 user@server
# SFTP 命令
sftp> ls
sftp> cd remote_dir
sftp> get remote_file
sftp> put local_file
sftp> mkdir new_dir
sftp> exit
# 使用 rsync(更高效)
rsync -avz -e "ssh -p 2222" local_dir/ user@server:/remote/dir/
|
SSH 证书认证#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 创建 CA 密钥
ssh-keygen -t ed25519 -f /etc/ssh/ca -N ""
# 在 sshd_config 中配置 CA
TrustedUserCAKeys /etc/ssh/ca.pub
# 签发用户证书
ssh-keygen -s /etc/ssh/ca -I user_admin -n admin,deploy -V +52w ~/.ssh/id_ed25519.pub
# 查看证书
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
# 证书内容示例:
# Type: ssh-ed25519-cert-v01@openssh.com user certificate
# Public key: ED25519-CERT SHA256:xxx
# Signing CA: ED25519 SHA256:xxx
# Key ID: "user_admin"
# Serial: 0
# Valid: from 2024-03-24T00:00:00 to 2025-03-24T00:00:00
# Principals: admin deploy
# Critical Options: (none)
# Extensions: permit-X11-forwarding permit-agent-forwarding permit-port-forwarding
|
七、SSH 故障排查#
连接问题诊断#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# 详细输出模式
ssh -vvv user@server
# 检查服务状态
systemctl status sshd
# 检查端口监听
netstat -tlnp | grep :22
ss -tlnp | grep :22
# 检查防火墙
iptables -L -n
ufw status
firewall-cmd --list-all
# 测试端口连通性
nc -zv server 22
telnet server 22
# 检查 DNS 解析
nslookup server
dig server
# 检查 SELinux(RHEL/CentOS)
getenforce
# 如果是 Enforcing,检查 SSH 相关策略
ausearch -c sshd --raw | audit2allow -M mypolicy
semodule -i mypolicy.pp
|
权限问题#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 正确的权限设置
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config
# 检查文件所有权
ls -la ~/.ssh/
# 应该都是当前用户
# 修复所有权
chown -R $USER:$USER ~/.ssh/
# 服务器端检查
ls -la /home/user/.ssh/
# 确保用户目录权限正确
chmod 755 /home/user
|
常见错误解决#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 错误: Permission denied (publickey)
# 解决: 检查密钥、权限、sshd_config
# 错误: Host key verification failed
# 解决: 删除旧的主机密钥
ssh-keygen -R server
# 错误: Connection refused
# 解决: 检查服务是否运行、端口是否正确、防火墙
# 错误: Connection timed out
# 解决: 检查网络、防火墙、IP是否正确
# 错误: Too many authentication failures
# 解决: 指定密钥文件
ssh -i ~/.ssh/specific_key user@server
# 或在 config 中设置 IdentitiesOnly yes
# 错误: no matching key exchange found
# 解决: 指定算法
ssh -o KexAlgorithms=diffie-hellman-group-exchange-sha256 user@server
|
参考来源#