如何保障个人网站的安全
前言
我的服务器2次被攻击,一次是我端口映射到windows的远程桌面,但是居然被人在不停的爆破。第二次是我的后台管理页面,经历了 ACK Flood,让我甚至不能正常的ssh连接上服务器。于是我学习了linux怎么去配置防火墙,如何自动去拉黑IP。但是这个工作太过于繁琐,防火墙的配置非常复杂,线性的匹配各个规则,互相耦合。而且针对每个端口不同的用途,需要不同的防火墙设置。fail2ban的配置也比较复杂,尽管我之前熟悉linux的日志管理,但是每个应用的日志在不同的地方,还是带来了很多的麻烦。
我这才意识到,原来nginx是非常伟大的项目,只通过80和443端口,反向代理其他的本地端口,极大的减少了攻击面。而且速率控制等高级功能,可以在nginx上配置,不用繁琐的去配置各个端口的防火墙,我只要简单配置这两个端口的防火墙即可。Nginx 提供了一种单入口、多服务分发的方案,解决了下面的核心问题:
- 安全风险:暴露的端口越多,越容易成为攻击目标(如 DDoS、Flood)。
- 配置复杂:每个端口都需要单独配置防火墙规则、限速、日志记录等。
- 管理成本高:难以对所有端口进行统一监控和审计。
另外原生的 iptables 配置起来也很麻烦,建议使用 UFW(Uncomplicated Firewall)或者其他有图形界面的防火墙。
核心思想:减小攻击面是最重要的做法。一台云服务器,开放的端口会包括基于HTTP的web(包括内容分发和后台管理),SSH 控制端口,以及一些通信的TCP和UDP端口。
原则1:如果不提供公网服务,只是个人使用,那么只考虑SSH隧道和组网。
所有的管理后台,都不应该暴露在公网,而应该用隧道访问,或者组虚拟局域网网。但是SSH隧道的性能不如wireguard,而且需要在terminal中配置端口转发,我还是建议虚拟组网的方式。
原则2:使用nginx代理web,并且设置cloudflare WAF。TCP 和UDP服务,可以通过 Nginx Stream 模块处理。
简单的说安全策略应该做好:
- 能用私钥文件就不要用口令,比如ssh。口令用随机口令,不要自己手动设置常用密码。
- 非公网服务就不应该允许外部访问,比如管理端口等。
- 登陆要加入双重验证以及字典爆破的保护。
组网+Nginx代理+端口敲门(可选)。
端口和流量管理
防火墙原理
iptables 是基于 Linux 内核的防火墙工具,其工作原理是通过内核中的 Netfilter 框架来处理和管理网络流量。iptables 的规则按照 匹配优先级 和 操作链 的逻辑工作。
包匹配的基本流程
- 数据包进入 Netfilter:
- 数据包从网卡或其他网络接口接收后,会进入 Netfilter 进行处理。
- 数据包按 网络栈的不同阶段 进入不同的 iptables 链。
- 匹配规则的顺序:
- 链内规则按顺序执行,从上到下逐条匹配,直到命中一条规则。
- 如果没有匹配到规则,则执行链的默认策略(如 ACCEPT 或 DROP)。
- 动作类型:
- 如果数据包匹配规则,iptables 可执行以下操作:
- ACCEPT: 允许数据包通过。
- DROP: 丢弃数据包,不做任何回应。
- REJECT: 丢弃数据包,并向源发送一个 ICMP 错误消息。
- LOG: 记录日志,但继续匹配后续规则。
- 如果数据包匹配规则,iptables 可执行以下操作:
设置防火墙
安装和配置 iptables
1 | sudo apt update |
iptables-persistent: 保存和恢复防火墙规则。
xtables-addons-common: 提供扩展模块支持,如 PSD(Port Scan Detection)。
查看和清空现有规则
1 | sudo iptables -L -n -v |
iptables -L -n -v: 查看当前防火墙规则。
iptables -F: 清空所有规则,确保配置从干净状态开始
防火墙默认策略
设置默认策略为丢弃所有流量,只允许规则显式放行的流量。
1 | sudo iptables -P INPUT DROP |
拒绝无效连接
1 | sudo iptables -A INPUT -m conntrack --ctstate INVALID -j LOG --log-prefix "INVALID-CONN: " --log-level 4 |
检测无效连接并记录日志,同时丢弃数据包。防止 ACK Flood 和异常数据包。
限制 Ping(ICMP 类型 8)
1 | sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP |
禁止 ping 请求,防止 Ping Flood 攻击。
限制单 IP 的并发连接数
1 | sudo iptables -A INPUT -p tcp -m connlimit --connlimit-above 100 -j LOG --log-prefix "CONNLIMIT-EXCEEDED: " |
限制单个 IP 的并发连接数,记录超限日志。 防止单 IP 恶意占用服务器资源。
限制SSH端口请求数
1 | sudo iptables -A INPUT -p tcp --dport SSH端口 --syn -m conntrack --ctstate NEW -m limit --limit 10/s --limit-burst 20 -j ACCEPT |
限制端口 SSH端口 每秒新连接请求的数量。超限时记录日志并丢弃。
限制特定端口速率
1 | sudo iptables -A INPUT -p tcp --dport 网站端口 --syn -m limit --limit 10/s --limit-burst 20 -j ACCEPT |
限制端口 网站端口 的连接速率,防止暴力破解。
检测和防御端口扫描
1 | sudo modprobe xt_psd |
日志记录所有被丢弃的数据包
1 | sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "my-iptables-dropped: " --log-level 4 |
限制日志频率,记录被丢弃的异常流量。
保存防火墙配置
1 | sudo netfilter-persistent save |
save 将规则保存到 /etc/iptables/rules.v4 文件中。
验证防火墙规则
1 | sudo iptables -L -n -v --line-numbers |
拉黑IP
安装和配置
Fail2Ban 是一个入侵防御软件,通过监控系统日志来阻止可疑的活动。它的工作原理如下:
- 监控系统日志:持续扫描指定的日志文件(如 SSH、Apache 等服务的日志)
- 识别攻击模式:根据预设的规则识别可疑行为,比如多次登录失败
- 自动封禁:当某个 IP 地址在指定时间内触发规则达到阈值时,自动将其加入防火墙黑名单
- 自动解封:被封禁的 IP 在设定的封禁时间后会自动解除限制
主要配置参数说明:
- enabled:是否启用该规则(true/false)
- mode:检测模式,可选 normal(默认)、ddos、extra 或 aggressive
- maxretry:允许的最大尝试次数,超过后触发封禁
- findtime:检测时间窗口(秒),在此时间内达到 maxretry 次数则封禁
- bantime:封禁时长(秒)
Fail2Ban 通过这种方式可以有效防止暴力破解和 DOS 攻击等恶意行为。它不仅可以保护 SSH 服务,还可以配置保护其他服务,如 Web 服务器、邮件服务器等。
Fail2Ban 的默认行为是基于 action 配置的规则,如果设置了端口,那么只是不允许某个IP访问特定的端口,但是可以访问其他端口。
安装 Fail2Ban
1 | sudo apt install fail2ban -y |
确保已经运行之后,可以查看已经开启的服务。
1 | ➜ ~ sudo fail2ban-client status |
详细查看每一类的服务的情况
1 | ➜ ~ sudo fail2ban-client status sshd |
配置文件的地址所在 /etc/fail2ban/jail.conf
。复制默认配置到 jail.local,,Fail2Ban 会优先读取该文件中的配置。
1 | sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local |
新的配置文件中找到这一部份
1 | [sshd] |
然后修改成这样,注意端口请修改成自己的。
1 | [sshd] |
随后重启服务:
1 | sudo systemctl restart fail2ban |
测试
找一台机器,多次测试
1 | ssh -p PORT fakeuser@IP |
然后查看是否已经拉黑这个IP了 sudo fail2ban-client status sshd
,
自定义
既然 Fail2Ban 是根据日志的,那么就可以根据日志输出,如果出现多条某种日志,就拉黑对应的IP,我们开始来解决利用TLS耗尽资源的案例。下面是我遇到的 TLS 扫描,还有暴力攻击。
- EOF:客户端与服务器建立连接后,直接关闭了连接。可能是恶意工具测试你的服务是否运行 TLS。
- tls: first record does not look like a TLS handshake:客户端发送的数据不符合 TLS 协议。通常是恶意流量(例如 HTTP 请求尝试连接到 HTTPS 端口,或发送随机数据包)。
这里说明攻击者在进行TLS 握手探测,并且还在发送二进制垃圾数据。由于我们过滤掉了无效的数据包,这里就少了很多了。
1 | var/log/syslog:Dec 1 11:28:29 服务器主机名 x-ui[86955]: 2024/12/01 11:28:29 http: TLS handshake error from 206.168.34.116:43752: EOF |
在 /etc/fail2ban/filter.d/ 目录下创建名为 x-ui.conf 的文件:
1 | [Definition] |
这样会过滤得到攻击者的IP。由于服务器只有我自己用,所以错误率其实非常低的。
编辑 /etc/fail2ban/jail.local ,出现5次同样的IP后拉黑。这里用上了设置好的过滤器。
1 | [x-ui] |
最后 fail2ban-client reload
,测试是否成功:
1 | ➜ ~ echo "Dec 3 09:44:56 服务器主机名 x-ui[287307]: 2024/12/03 09:44:56 http: TLS handshake error from 165.154.43.179:40674: local error: tls: unexpected message" >> /var/log/syslog |
手动操作
下面是一些常用的 fail2ban 命令:
手动封禁 IP:
1 | sudo fail2ban-client set sshd banip 192.168.1.100 |
手动解封 IP:
1 | sudo fail2ban-client set sshd unbanip 192.168.1.100 |
查看 fail2ban 日志:
1 | sudo tail -f /var/log/fail2ban.log |
解封所有被封禁的 IP:
1 | sudo fail2ban-client unban --all |
使用Nginx代理
从安全的角度,直接将服务端口暴露在公网,就增加了攻击面。攻击者可以轻松扫描端口和指纹信息,尝试漏洞攻击。而且一些开发测试的端口,本身缺乏高级访问控制,无法限制来源 IP、速率或特定的访问策略。特别是这样可以隐藏端口了。
从性能的角度,直接服务需要每次处理完整请求,无法缓存静态内容,增加资源开销。高并发请求可能导致服务线程或进程资源耗尽。
主配置文件 (/etc/nginx/nginx.conf)
1 | http { |
自行配置站点配置文件 /etc/nginx/sites-available/proxy-services.conf
,需要链接到 sudo ln -s /etc/nginx/sites-available/proxy-services.conf /etc/nginx/sites-enabled/
确保 Nginx 配置无语法错误:
1 | ➜ ~ sudo nginx -t |
重启服务
1 | sudo systemctl restart nginx |
检查端口是否开启
1 | sudo netstat -tuln | grep 443 |
查看https是否设置成功。200表示请求成功,服务支持 HTTP/2 协议。
1 | ➜ ~ curl -I DOMAIN |
端口敲门
实现端口敲门技术的主要目标是隐藏服务器上的关键端口(如 SSH),仅允许合法用户通过特定的访问顺序来临时打开该端口。这通常通过一个守护程序(如 knockd)来完成。以下是完整的实现步骤:
sudo apt install knockd -y
安装之后,配置文件通常位于 /etc/knockd.conf
编辑文件 /etc/knockd.conf
1 | [options] |
sequence 是敲门的端口顺序。例如,用户必须按顺序访问 7000 -> 8000 -> 9000,才能打开 SSH 端口。seq_timeout 指的是完成整个敲门顺序的最大允许时间。
编辑 /etc/default/knockd 文件,将 START_KNOCKD 设置为 1,实现自启动。
1 | sudo systemctl start knockd |
用knock工具,就能够实现啦。注意敲门的端口都是要打开的。
1 | knock <服务器IP> 7000 8000 9000 |
虚拟局域网
方案比较
虚拟局域网(VPN)是一种安全的网络解决方案,可以让远程用户安全地访问服务器资源。以下是几种常用的VPN方案:
VPN方案 | 特点 | 优势 | 缺点 |
---|---|---|---|
WireGuard | 现代VPN协议 | 配置简单,代码量少。性能优秀,延迟低。内置Linux内核 | 多设备管理复杂,每个节点都必须配置所有其他节点的公钥,才可能完成点对点通信。. |
Tailscale | 基于WireGuard的商业方案 | 零配置,自动NAT穿透。 个人用户免费 | 自定义中继服务器复杂 |
ZeroTier | 虚拟网络方案 | 支持点对点连接。 可创建多个虚拟网络。跨平台支持良好。提供免费套餐 | 初始连接较慢。网络延迟相对较高。免费版节点数量有限 |
wireguard 缺少公钥分发,导致每个节点要配置其他节点的公钥,很复杂。
对于个人用户来说,Tailscale 是最简单的选择,几乎不需要配置就能使用。但是中国大陆地区缺少中继服务器,导致路由延迟太高了。而且搭建derper中继很麻烦,所以不建议使用。
Tailscale 基础教程:部署私有 DERP 中继服务器
虽然还有其他的便利化的工具,用于生成wireguard的配置,比如netmaker。但是zoretier还是非常方便的。
ZeriTier
任何运行 ZeroTier 客户端的设备都可以成为一个 节点。节点可以是个人电脑、服务器、手机或物联网设备。每个节点都通过其唯一的 40 位地址(Node ID)进行标识。这些地址通过加密算法生成,并且是 ZeroTier 网络中的唯一标识符。
基础使用看这里:https://makefile.so/2022/05/27/install-zerotier/
Planet 节点是 ZeroTier 的根目录服务,全球只有一个,项目方建议使用它维护的planet节点。它用于管理网络的分布式地址映射(ID到IP和端口的解析)。它的功能类似于 DNS 系统,但作用于 ZeroTier 网络。并且还允许传递控制器配置给客户端节点。
Moon Peer 是一种特殊类型的节点,用于优化节点之间的连接。类似于DHT(分布式哈希表),节点通过Moon来定位网络中的其他节点。通过Moon建立起初始的通信路径,之后节点会尝试直接建立点对点连接。
这两类节点统称为root server。moon 节点无法代币planet节点,网络的初始配置依赖Planet 节点分发,不过如果已经配置完成,就不依赖 planet了。一般来说没必要自己部署planet节点,但是部署moon节点能提高路由的网速。但如果卫星根节点看起来更快或者没有其他可用选项时,它们会使用月球根节点。
ZeroTier的路径选择遵循以下逻辑:
- **优先点对点连接(P2P):**如果两个设备可以通过NAT打洞建立直接连接,ZeroTier会优先使用点对点通信。
- 次选MOON**作为中继:**如果点对点连接不可用,ZeroTier会选择MOON节点作为中继。
- 最终使用PLANET**作为中继:**如果MOON也不可用,流量会通过ZeroTier官方的PLANET节点中继。
1 | curl -s https://install.zerotier.com | sudo bash # 安装 |
Private Root Servers | ZeroTier Documentation
按照这里的配置例子,修改moon.json文件。一台服务器也可以,如果你有多台服务器,记得roots里多台服务器的信息都写进去。
确保端口开放:每台Moon服务器的stableEndpoints中指定的端口(如9993)需要在防火墙中开放,确保外部可以访问。
IP地址必须是固定公网IP:stableEndpoints中的IP需要是Moon服务器的固定公网IP,否则客户端无法连接。
1 | sudo zerotier-idtool genmoon moon.json # 签名 |
客户端的配置,把签名后的文件,放在对应的文件夹即可。配置文件位置见:https://docs.zerotier.com/config/
MAC的重启命令比较特别,参考:https://docs.zerotier.com/faq-macos/
最后检查是否成功连接上了:
1 | zerotier-cli listmoons |
windows的配置
1 | choco install zerotier-one --force |
找到正确的服务名称之后 Restart-Service -Name "ZeroTierOneService"
一些优化
可以自定义DNS,然后就用特殊域名来代替虚拟IP,不过配置稍微麻烦,而且每个节点要单独配置,并且并非全局有效。所以不打算继续。
1 | ➜ ~ zerotier-cli listpeers |
是可以看到延迟的,不过缺少显示信息的,是无法P2P连接,需要moon或者planet节点。