服务器安全指南
最近飞牛 OS 跟群晖 NAS 都爆出了很严重的安全漏洞,虽然我的服务器没用这两个系统并且配置了防火墙只开启必要的端口访问没有受到波及,但有台处于公网下的 VPS 服务器的安全问题还是需要重视,整理了一下服务器的一些安全配置。
SSH 登录防护
鉴于当下网络扫描及其严重的情况,”仅密钥验证登录 + 改用非默认端口“是必须的,我之前在腾讯云、阿里云服务器上做过测试,开放默认 22 端口,开放当天就有差不多 2 万次 ssh 用户名 + 密码暴力登录!
生成密钥对方法很简单,服务器一般都带了这个工具:
ssh-keygen -t ed25519 -C "备注" -f xxx/key_ed25519_xxx
# 会在 xxx目录下生成两个文件:key_ed25519_xxx(私钥)和key_ed25519_xxx.pub(公钥)生成后将公钥内容复制粘贴到需要 ssh 密钥登录用户的 ~/.ssh/authorized_keys 文件中,一行一个公钥。将私钥复制内容粘贴到本地用户目录 ~/.ssh/key_ed25519_xxx 文件中。然后编辑或新建本地用户 ~/.ssh/config 文件,写入如下内容:
Host server-xxx # host别名自己编一个别重复,登录时使用 ssh server-xxx 即可登录对应服务器
HostName xxx # 服务器ip/域名
User xxx # 登录用户名
Port 2222 # 登录端口
IdentityFile xxx # 私钥认证文件路径,比如 C:\Users\xxx\.ssh\key_ed25519_xxx
ServerAliveInterval 30 # 保活周期 30s
ServerAliveCountMax 30 # 心跳包发送30次服务端没回应则断开连接ssh 服务器配置文件路径是 /etc/ssh/ssd_config 文件和 /etc/ssh/sshd_config.d/ 目录下 .conf 后缀文件。修改配置推荐在 /etc/ssh/sshd_config.d/ 目录下新建配置文件进行覆写,这样在 ssh 服务器升级后不用担心配置文件在更新时被强制覆盖。
修改默认端口 + 禁止密码登录 + 启用密钥登录的配置:
## /etc/ssh/sshd_config.d/99-custom.conf
# 修改默认端口
Port 2222
# 启用公钥验证
PubkeyAuthentication yes
# 禁止密码登录
PasswordAuthentication no
#(可选)禁止空密码
# PermitEmptyPasswords no
# 禁止root用户密码登录,如果不希望root用户直接登录ssh,推荐直接设置为 no
PermitRootLogin prohibit-password要想更安全,可以启用“白名单用户或者组”,只有特定用户或组可以登录 ssh。先创建一个普通用户,启用该用户的 sudo 权限(EDITOR=nvim visudo)或者 su root --login 登录 root 后使用。启用 ssh 的白名单用户或者组可以在配置文件中添加下面字段:
# 使用白名单用户,指定的用户可以使用ssh登录
AllowUsers youruser1 youruser2
# 使用白名单组,在这个组的用户可以使用ssh登录
# AllowGroup sshlogin修改后使用 systemctl restart ssh 重启 ssh 服务。如果配置了防火墙记得开启新端口防火墙并关闭默认 ssh 端口。
修改 ssh 配置并重启服务后不要急着关闭当前 ssh 连接,应该新开一个终端使用新配置验证 ssh 能正常登录后才能安全关闭当前 ssh 连接,否则可能没法登录服务器。
修改默认端口和禁止密码登录之后已经可以缓解很大一部分脚本暴力撞库了,如果还想要进一步防御可以考虑安装 fail2ban ,不过这种基于日志的防护具有滞后性,我不用这个,我更倾向于使用 endlessh 、fwknop 这些工具。
endlessh 防护思路不是传统的封堵,而是拖延。首先将 ssh 端口修改到到非标准端口比如某个高位端口,然后在标准 ssh 端口上运行 endlessh,攻击者连接 22 端口时,endlessh 会极度缓慢的发送随机 SSH 协议头信息,这样会卡住攻击者连接,无法完成握手也无法报错退出,浪费攻击者带宽和连接池。
fwknop 则是让 ssh 端口隐身,服务器默认丢弃所有 ssh 端口包,当需要连接时,客户端先发送一个经过加密和签名的 SPA 包,fwknop 解密验证后临时修改 iptables 或者其他系统防火墙仅对来源 ip 开放。优势是实现了 0 攻击面,缺点是客户端也需要安装对应工具使用。这个我没有使用,防护级别过于高了使用太麻烦。
系统级防火墙
这一层防火墙主要控制整个服务器的端口开放、端口转发规则,比如只开放 ssh 登录端口、http/https 端口。如果服务器厂商有完善的防火墙配置,优先使用厂商自带的防火墙,这级防火墙优先级最高,默认禁止全部入站,允许出站,然后针对性开放特定端口。如果不使用厂商自带的,就必须使用系统内的 iptables (控制 ipv4)和 ip6tables(控制 ipv6),也可以用 ufw 这种更方便的前端,不过我一般使用 iptables ,更加灵活。
iptables 最核心的就是 四表五链 ,iptables 是位于用户空间的 Linux 防火墙管理工具,真正实现防火墙的是 Linux 内核中实现包过滤的 netfilter ,iptables 工作流程用下面这个图可以很清楚的展示:

日常使用防火墙主要配置 filter 表中的 INPUT 链内规则,链中规则从上往下匹配,匹配到某条规则后之后的规则就不会生效了。如果使用 docker 部署服务并进行 8080:80 这种端口映射,你就会发现配置 filter 表中 INPUT 链没有任何效果!那是因为 docker 容器都是运行在虚拟机中,拥有自己的 ip,docker 为了保证端口映射后流量能正确到达容器,利用 iptables 自定义链功能,在 nat 表中的 PREROUTING 链阶段便进行了劫持,发往 8080 端口流量被修改到了 docker 内部 ip 的特定端口,iptables 判断不是本机流量,直接走了 FORWARD 不经过 INPUT 链,INPUT 链内规则也就不会生效了。也不用担心,docker 官方预留了一个 DOCKER-USER 链,这个链位于 docker 自定义链最前面,确保这个链的规则最先执行,docker 默认不会修改这个链,用户可以操作这个链来对 docker 服务流量进行控制。
使用 iptables 时如果规则较多也推荐使用自定义链,保持 iptables 默认链整洁,便于维护。自定义链类似于定义了一个函数,这个函数包含多个规则,在需要用到的地方进行调用,比如创建一个白名单链(WHITELIST),针对某些 ip 可以进入就可以这样操作:
- 创建自定义链:
iptables -t filter -N WHITELIST,这会在 filter 表中创建新的链。 - 添加规则到自定义链:
iptables -A WHITELIST -s 110.110.110.110 -j ACCEPT,添加特定 ip 直接丢弃的规则。 - 在特定地方调用自定义链,比如在 INPUT 链阶段调用:
iptables -I INPUT -j WHITELIST,-I 指定插入到 INPUT 链最前面,-j 指定跳转到 WHITELIST 链(类似函数调用)。此时数据包到达 INPUT 链就会跳转到 WHITELIST 链进行规则匹配。
针对黑名单这种 IP 量级较大情况下直接使用 iptables 会验证影响性能(iptables 匹配规则从上到下一条一条的,性能是 O(n) ),使用 ipset 扩展配合 iptables 更好,ipset 内部使用哈希表匹配,占用空间相对会大一点,但是现在硬件水平几千万量级还是很轻松的。使用方式如下:
# 创建一个ip集合,最大数量100万,hash:net指定可以存储单个ip 1.1.1.1 或者一个网段 1.1.1.0/24
ipset create blacklist hash:net maxelem 1000000
# 添加单个ip
ipset add blacklist 1.2.3.4
# 添加网段
ipset add blacklist 1.2.3.0/24
# 在iptables中引用集合
iptables -I BLACKLIST -m set --match-set blacklist src -j DROPiptables 修改后立即生效,但是默认不会持久化,重启后规则就重置了,需要使用 iptables-persistent 来持久化:
# 安装
apt install iptables-persistent
# 启用服务
systemctl enable --now iptables-persistent
# 持久化当前配置
netfilter-persistent saveWeb 防火墙
Web 防火墙主要针对 http/https 服务器进行防护,通常配合 Nginx 这类反代工具使用,这类防火墙我之前没有用过,了解了下感觉 ModSecurity、雷池社区版、coraza 比较流行,其中雷池使用起来比较方便,规则引擎基于语义分析而不是正则,有 Web 后台可以管理,也支持在线订阅,缺点是社区版功能较少,https 证书只支持 http01 形式自动申请,不支持 dns 形式,但个人使用对 web 服务进行简单防御是够的。
雷池安装方法可以直接看官方文档,写得很详细,通过 docker 进行部署。需要注意的是不要将管理后台端口开放到公网,使用 ssh 转发进行访问,开放到公网则必须配置 2FA。