系统环境
操作系统:CentOS 7.9
Nginx响应头
编辑nginx配置文件:
sudo vim /etc/nginx/nginx.conf
在http部分server上方增加如下配置:
# 全局隐藏 Server 头信息
server_tokens off;
# 基础安全头
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
# 启用浏览器的 XSS 过滤器,并阻止渲染疑似 XSS 的页面
# (注:现代浏览器已逐步废弃此头,推荐改用 CSP - Content-Security-Policy 的防护策略)
add_header X-XSS-Protection "1; mode=block";
说明:可以使用 SecurityHeaders.com 扫描你的网站查看安全头情况。
Nginx请求限速
编辑nginx配置文件:
sudo vim /etc/nginx/nginx.conf
在http部分server上方增加如下配置:
# 定义全局请求速率限制区域
# 10MB内存空间,限制每个IP每秒20个请求
limit_req_zone $binary_remote_addr zone=req_perip:10m rate=20r/s;
# 可以定义多个区域用于不同限制级别
limit_req_zone $binary_remote_addr zone=strict_req_perip:10m rate=5r/s;
# 定义全局连接数限制区域
# 10MB内存空间,跟踪每个IP的连接
limit_conn_zone $binary_remote_addr zone=conn_perip:10m;
说明:
1MB内存大约可以存储16,000个IP的状态
10MB区域可以处理约160,000个唯一IP
创建配置文件/etc/nginx/conf.d/rate_limits.conf.included
:
# 请求速率限制,突发50个请求
limit_req zone=req_perip burst=50 nodelay;
# 连接数限制,每个IP最多10个并发连接
limit_conn conn_perip 10;
# 超限制返回429(Too Many Requests)状态码
limit_req_status 429;
limit_conn_status 429;
在需要的server块中包含它:
server {
include /etc/nginx/conf.d/rate_limits.conf.included;
# 其他server配置...
}
Fail2Ban
Fail2Ban 是一款开源工具,用于监控系统日志并自动禁止表现出恶意行为的IP地址(如多次登录失败、暴力破解等)。
安装 Fail2Ban
# 安装
sudo yum install fail2ban
# 启动并设置开机自启
sudo systemctl enable --now fail2ban
基础配置
主配置文件位于 /etc/fail2ban/jail.conf
,但建议创建本地配置文件避免升级被覆盖:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vim /etc/fail2ban/jail.local
关键全局配置项:
[DEFAULT]
# 禁止IP的持续时间
bantime = 24h
# 触发禁令的失败次数
maxretry = 5
# 检测时间窗口
findtime = 10m
# 通过iptables封禁
banaction = iptables-multiport
# 白名单IP(多个IP用空格分隔)
ignoreip = 127.0.0.1/8 192.168.1.0/24
常用服务保护配置
SSH保护(默认已启用)
[sshd]
enabled = true
# 解决启动警告:WARNING [sshd] Simulate NOW in operation since found time has too large deviation...
# 解决启动警告:WARNING [sshd] Please check jail has possibly a timezone issue. Line with odd timestamp...
# 在确保服务器时区是CST的情况下(可通过timedatectl查看)
timezone = Asia/Shanghai
Nginx防护
[nginx-http-auth]
enabled = true
# 配合Nginx请求限速
[nginx-limit-req]
enabled = true
maxretry = 3
findtime = 5m
[nginx-botsearch]
enabled = true
logpath = %(nginx_access_log)s
%(nginx_error_log)s
自定义过滤器
比如自定义过Nginx的access日志格式,此时Fail2ban默认的配置就不起作用。比如有如下自定义格式:
log_format json '{'
'"time_local": "$time_local", ' # 请求服务器时间 Default
'"remote_addr": "$remote_addr", ' # 客户端IP地址 Default
'"remote_user": "$remote_user", ' # 已经经过HTTP基础认证的用户名 Default
'"request": "$request", ' # 请求行 (METHOD + URI + HTTP版本) Default
'"status": "$status", ' # HTTP状态码 Default
'"body_bytes_sent": "$body_bytes_sent", ' # 发送给客户端的响应body字节数,响应头不计算在内 Default
'"request_time": "$request_time", ' # 整个请求的总时间,请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止 Default
'"http_referer": "$http_referer", ' # 记录此次请求是从哪个链接访问过来的,可以根据 refer 进行防盗链设置 Default
'"http_user_agent": "$http_user_agent", ' # 记录客户端访问信息,例如:浏览器,手机客户端等 Default
'"http_x_forwarded_for": "$http_x_forwarded_for", ' # 客户端的真实 ip,通常 web 服务器放在反向代理的后面,这样就不能获取到客户的 IP 地址了,通过$remote_add 拿到的 IP 地址是反向代理服务器的 iP 地址。反向代理服务器在转发请求的 http 头信息中,可以增加 x_forwarded_for 信息,用以记录原有客户端的 IP 地址和原来客户端的请求的服务器地址 Default
'"scheme": "$scheme", ' # 请求使用的Web协议,”http” 或 “https”
'"host": "$host", ' # 请求主机头字段,否则为服务器名称
'"http_host": "$http_host", ' # 请求地址,即浏览器中输入的地址(IP 或域名)
'"server_name": "$server_name", ' # 服务器名称
'"upstream_addr": "$upstream_addr", ' # 当 ngnix 做负载均衡时,可以查看后台提供真实服务的设备
'"upstream_response_time": "$upstream_response_time", ' # 请求过程中,upstream 响应时间
'"upstream_status": "$upstream_status", ' # upstream 状态,比如成功是 200
'"request_id": "$final_request_id"' # 获取final_request_id
'}';
创建过滤器文件(复制原nginx-botsearch配置文件):
sudo cp /etc/fail2ban/filter.d/nginx-botsearch.conf /etc/fail2ban/filter.d/nginx-botsearch-ym.local
编辑文件/etc/fail2ban/filter.d/nginx-botsearch-ym.local
的failregex
及datepattern
部分匹配自定义的日志格式:
# Fail2Ban filter to match web requests for selected URLs that don't exist
#
[INCLUDES]
# Load regexes for filtering
before = botsearch-common-ym.local
[Definition]
failregex = ^.*\"remote_addr\": \"<HOST>\".*\"request\": \"(GET|POST|HEAD) \/<block> \S+\".*\"status\": \"404\".*$
^.*\"remote_addr\": \"<HOST>\".*\"status\": \"444\".*$
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$
ignoreregex =
datepattern = ^.*\"time_local\": \"({DATE})\"
{^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
^[^\[]*\[({DATE})
{^LN-BEG}
# DEV Notes:
# Based on apache-botsearch filter
#
# Author: Frantisek Sumsal
说明:failregex
中<HOST>
是Fail2ban的关键,即IP。datepattern
中({DATE})
是 Fail2Ban 内置的 DATE 模式(通用日期格式,示例:01/Jan/2024:12:00:00 +0800)。
在jail.local
中配置启用过滤器:
[nginx-botsearch]
enabled = true
filter = nginx-botsearch-ym
logpath = %(nginx_access_log)s
%(nginx_error_log)s
操作命令
# 测试filter正则匹配
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch-ym.conf
# 查看运行状态
sudo fail2ban-client status
# 查看具体jail状态
sudo fail2ban-client status sshd
sudo fail2ban-client status nginx-botsearch
# 手动封禁IP
sudo fail2ban-client set sshd banip 192.168.1.100
# 手动解封IP
sudo fail2ban-client set nginx-botsearch unbanip 192.168.1.100
# 重新加载配置
sudo fail2ban-client reload
# 重启fail2ban
sudo systemctl restart fail2ban
# 查看日志
sudo tail /var/log/fail2ban.log
# 查看iptables
sudo iptables -L -n --line-numbers
邮件通知
Fail2ban默认使用sendmail发送邮件,由于配置复杂程度等原因这里使用自带的mail进行邮件发送。
配置mail
以QQ邮箱为例,其他邮箱可参考自行实现。
使用465端口,使用以下命令生成并设置证书:
mkdir -p /root/.certs/
# 申请证书
echo -n | openssl s_client -connect smtp.qq.com:465 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/.certs/qq.crt
# 添加证书到证书数据库
certutil -A -n "GeoTrust SSL CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
certutil -A -n "GeoTrust Global CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
# 信任证书
cd /root/.certs/
certutil -A -n "GeoTrust SSL CA - G3" -t "Pu,Pu,Pu" -d ~/.certs/./ -i qq.crt
# 列出目录下证书
certutil -L -d /root/.certs
备份并编辑mail
配置文件:
sudo mv /etc/mail.rc /etc/mail.rc.bak
sudo vim /etc/mail.rc
编辑内容:
# 邮箱账号
set smtp-auth-user=xxx@qq.com
# 发信人邮箱
set from=xxx@xxx.com
# 邮箱密码/授权码
set smtp-auth-password=password
# 证书目录
set nss-config-dir=/root/.certs
# 认证方式
set smtp-auth=login
set smtp=smtps://smtp.qq.com:465
set ssl-verify=ignore
测试邮件发送:
echo "邮件内容" | sudo mail -v -s "邮件标题" your-email@qq.com
配置Fail2ban
编辑jail.local
,在DEFAULT
下增加邮件配置:
[DEFAULT]
# 邮件配置
sender = your-email@qq.com
destemail = dest@example.com
mta = mail
# 封禁+记录日志+发邮件
action = %(action_mwl)s
通过复制sendmail的配置文件创建对应的mail-whois-lines
配置文件并编辑:
sudo cp /etc/fail2ban/action.d/sendmail-whois-lines.conf /etc/fail2ban/action.d/mail-whois-lines.local
sudo vim /etc/fail2ban/action.d/mail-whois-lines.local
调整后内容如下:
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
#
[INCLUDES]
before = mail-whois-common.conf
helpers-common.conf
[Definition]
# bypass ban/unban for restored tickets
norestored = 1
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ( printf %%b "警告!!!
被攻击机器名:<fq-hostname>
被攻击服务:<name>
时间范围:<findtime> 内
攻击次数:<failures> 次
攻击者IP:<ip>
封禁时间:<bantime> 秒\n";
printf %%b "失败日志(最多<grepmax>):\n";
%(_grep_logs)s; ) | mail -s "[<name>]新增拦截<ip>" <dest>
[Init]
# Default name of the chain
#
name = default
# Path to the log files which contain relevant lines for the abuser IP
#
logpath = /dev/null
# Number of log lines to include in the email
#
grepmax = 3
#grepopts = -m <grepmax>
重启Fail2ban后再次封禁即可收到邮件通知。