我没有特别的 SSH 安全配置技巧
1. 前情提要
今天又在盛传SSH被暴力破解了,其实大部分都是脚本小子24小时不停扫描公网暴露的SSH端口,只要你开着常见的SSH端口就会被扫描
我很早之前摸索的一套方法,应该算是对这种情况有所缓解,主要是高位端口+仅密钥登陆+22端口ssh蜜罐+fail2ban监听蜜罐和sshd全局封禁,可以预防绝大部分自动扫描了,以下是实践步骤
2. 实践步骤
2.1 编译更新 SSH 新版本
部分系统的默认 SSH 版本过低,存在安全漏洞,如果小鸡性能不足,建议安装 Alpine 3.22 版本,自带的 openssh 已经更新到了 10以上
如果是 Debian13 这种新系统,可以不需要更新版本
一键更新命令(修改自科技Lion大佬的脚本,感谢)
wget -O upgrade_openssh.sh https://gist.github.com/Seameee/2061e673132b05e5ed8dd6eb125f1fd1/raw/upgrade_openssh.sh && sudo chmod +x ./upgrade_openssh.sh && sudo ./upgrade_openssh.sh
2.2 设置 SSH 高位端口
[!Tip]
感谢 LuckyZ 佬的提醒
由于 Ubuntu24.04 版本以后默认使用ssh.socket管理,你应该直接修改/etc/ssh/sshd_config文件中的端口字段来修改,后续步骤中需要编辑ssh配置的也都需要手动修改这个文件中的内容,当然也可以禁用ssh.socket还原默认的ssh.service具体请参考下文
https://dev.to/saishanmukkha/understanding-ssh-socket-based-activation-in-ubuntu-2404-28m
⚠️警告⚠️:此时不要关闭当前终端窗口
在目录 /etc/ssh/sshd_config.d/下,新建任意名称配置文件,文件名后缀为.conf,写入如下内容即可,端口号建议填写 10000 - 65535 以内没有被占用的端口
Port 33445
检查端口是否被占用:
lsof -i:33445
一键命令:
echo -e "Port 33445" | sudo tee /etc/ssh/sshd_config.d/00-custom_port.conf
设置完配置文件后,使用如下命令重启 SSH 服务
sudo systemctl restart ssh.service # Debian 等使用 systemd 的使用这个命令
sudo rc-service sshd restart # Alpine 等无法使用 systemd 的系统可以使用 openrc 管理
⚠️警告⚠️:此时不要关闭当前终端窗口
如果有防火墙,请放行新的端口,并禁止旧的SSH端口访问
sudo ufw allow 33445 # 放行新的SSH端口
sudo ufw deny 22 # 禁用原有SSH端口
修改完 ufw 规则后,使用命令启用防火墙
sudo ufw enable
打开一个新的终端窗口,使用旧的端口和新的端口测试是否修改成功,再关闭当前的终端窗口,否则一旦你修改出错就只能去VNC里重新开启了
2.3 设置 SSH 仅密钥登陆
一键命令:
echo -e "PasswordAuthentication no\nPubkeyAuthentication yes" | sudo tee /etc/ssh/sshd_config.d/01-custom_auth.conf
与上面的重启SSH和检查端口方式相同,我就不再赘述
2.4 配置 Fail2ban 将 SSH 访问失败的 IP 封禁
安装 fail2ban 和 ufw 防火墙
sudo apt update && sudo apt upgrade && sudo apt install fail2ban ufw -y
其他系统安装方式可以参考 How to install fail2ban packages · fail2ban/fail2ban Wiki · GitHub
配置 fail2ban 的 jail.local
nano /etc/fail2ban/jail.local
写入以下配置,注意修改里面的 port为你的 SSH 端口
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
bantime = 86400
findtime = 1800
action = %(action_)s
banaction = ufw
[sshd]
enabled = true
port = 33445
filter = sshd
action = ufw
logpath = /var/log/auth.log
maxretry = 3
findtime = 604800s
bantime = 604800s
usedns = no
保存退出后,使用以下命令重启 fail2ban 服务,以应用配置
sudo systemctl restart fail2ban.service # Debian & Ubuntu
sudo rc-service fail2ban restart # Alpine
ufw 防火墙注意放行你使用的端口,比如上面修改的 SSH端口、Nginx使用的80和443端口等,相关参数见
Usage: ufw COMMAND
Commands:
enable enables the firewall
disable disables the firewall
default ARG set default policy
logging LEVEL set logging to LEVEL
allow ARGS add allow rule
deny ARGS add deny rule
reject ARGS add reject rule
limit ARGS add limit rule
delete RULE|NUM delete RULE
insert NUM RULE insert RULE at NUM
prepend RULE prepend RULE
route RULE add route RULE
route delete RULE|NUM delete route RULE
route insert NUM RULE insert route RULE at NUM
reload reload firewall
reset reset firewall
status show firewall status
status numbered show firewall status as numbered list of RULES
status verbose show verbose firewall status
show ARG show firewall report
version display version information
Application profile commands:
app list list application profiles
app info PROFILE show information on PROFILE
app update PROFILE update PROFILE
app default ARG set default application policy
修改完 ufw 规则后,使用命令启用防火墙
sudu ufw enable
查看 fail2ban 实时封禁日志(刚安装可能不会立刻有内容)
tail -f -n 50 /var/log/fail2ban.log
2.5 配置 SSH 蜜罐和 fail2ban 监听蜜罐实现全局封禁
⚠️警告⚠️
高级操作,请衡量你的Linux能力再研究,不确保适合每个系统
长期监听可能会影响系统性能,建议使用性能不太差的服务器
2.5.1 安装相关依赖
sudo apt update && sudo apt upgrade && sudo apt install ipset iptables -y
2.5.2 安装简单 SSH 蜜罐fakessh
以下附上 docker-compose.yml
name: fakessh
services:
fakessh:
restart: always
ports:
- 22:22
container_name: fakessh
image: fffaraz/fakessh
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
使用命令 docker compose up -d运行容器
然后运行以下命令查看 docker 容器直接保存的日志路径(之前尝试将容器日志使用外挂 volume 的方式导出失败了,有知道的大佬可以楼下补充)
docker inspect fakessh | grep LogPath
需要保存的日志路径形如:/var/lib/docker/containers/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b-json.log
2.5.3 配置 fail2ban
新建/etc/fail2ban/filter.d/fakessh.conf文件,并写入以下内容
[Definition]
failregex = ^{"log":".* .* <HOST>:\d+\\n","stream":"stderr","time":".*"}$
ignoreregex =
datepattern = {^LN-BEG} ExYear (?P<_sep>[/]) Month (?P=_sep) Day 24hour:Minute:Second (?:[.,] Microseconds)?
在 /etc/fail2ban/jail.local 底部添加 fakessh 规则如下,注意替换 logpath为你自己的容器日志路径
[fakessh]
enabled = true
filter = fakessh
action = iptables-ipset-proto6-allports[name=fakessh, bantime=1209600, chain=INPUT]
logpath = /var/lib/docker/containers/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b-json.log
maxretry = 1
bantime = 1209600
findtime = 604800
usedns = no
backend = polling
ignoreip = 127.0.0.1/8 ::1
在保证 fakessh 已经被写入外部扫描IP日志的情况下,使用下面命令测试规则是否生效
fail2ban-regex /var/lib/docker/containers/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b-json.log /etc/fail2ban/filter.d/fakessh.conf
结果只要 Lines 里 matched 里有数量说明正常,因为只匹配 TCP 传入流量
# root @ LeasewebSG in ~ [12:18:19] C:130
$ fail2ban-regex /var/lib/docker/containers/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b-json.log /etc/fail2ban/filter.d/fakessh.conf
Running tests
=============
Use failregex filter file : fakessh, basedir: /etc/fail2ban
Use datepattern : {^LN-BEG} ExYear (?P<_sep>[/]) Month (?P=_sep) Day 24hour:Minute:Second (?:[.,] Microseconds)? : {^LN-BEG} ExYear (?P<_sep>[/]) Month (?P=_sep) Day 24hour:Minute:Second (?:[.,] Microseconds)?
Use log file : /var/lib/docker/containers/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b/1586b2735886350e0e6cd285c76bf1737ea597a5c7c425550e086a453c73ae1b-json.log
Use encoding : UTF-8
Results
=======
Failregex: 36987 total
|- #) [# of hits] regular expression
| 1) [36987] ^{"log":".* .* <HOST>:\d+\\n","stream":"stderr","time":".*"}$
`-
Ignoreregex: 0 total
Date template hits:
Lines: 73028 lines, 0 ignored, 36987 matched, 36041 missed
[processed in 1.78 sec]
Missed line(s): too many to print. Use --print-all-missed to print all 36041 lines
重启 fail2ban 即可
如果你还需要让 docker 容器暴露的端口也服从上面的封禁规则,包括 fakessh 本体,可以运行添加下面的 iptables 规则
iptables -I DOCKER-USER 1 -m set --match-set f2b-fakessh src -j REJECT --reject-with icmp-port-unreachable
ip6tables -I DOCKER-USER 1 -m set --match-set f2b-fakessh src -j REJECT --reject-with icmp-port-unreachable
3. 效果展示



@seamee #23 直接把reject改成drop就行了,fail2ban严厉的规则,用nftables,几千个ip性能都不影响。
仅密钥登陆就够了
22蜜罐意义不大,直接ssh 22端口+密钥+fail2ban+drop action即可
私钥带口令保护。
@11904 #46 https://github.com/TermoraDev/termora
欢迎大佬们在下面交流
病情经验关闭密码登录。只允许密钥登录就行了。。
假的22蜜罐。fail2ban。开了也是浪费资源。。
我到手先dd,dd时候顺便设置一下公钥。然后别的就不管了...
然后开启ufw,只开放ssh,https,http端口
直接用科技烂的一键脚本
@新来的 #2 其实我实测,也没那么特别浪费资源,占用还是挺低的
@a徐静雨 #4 弄清原理也是实践的一部分
可以了,这样下来固若金汤
@最大-abc #3 我也不是所有服务器都这么设置,太麻烦了,只有主服务器搞了
那假如使用24位以上的密码跟密钥登陆安全性上有什么区别么,感觉两者都无法爆破啊