Web服务器-Nginx的访问控制(二)

70 阅读3分钟

作者介绍:简历上没有一个精通的运维工程师。请点击上方的蓝色《运维小路》关注我,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。

我们上一大章介绍了Kubernetes的知识,本章节我们进入中间件的讲解,这里会包含很多不同的类型组件,中间件的第一个大类我这里定义的是Web服务器。由于目前使用最广泛的Web服务器是Nginx,所以我们这里的讲解主要以Nginx服务器为主。

我们上个小节介绍了通过设置账号密码来保护的我们的后端服务,但是Nginx作为作为一个Web服务器,其主要功能还是对外提供服务,所以我们Nginx服务只要暴露到公网,就会被人扫描和攻击。这个和前面说的SSH实际上一样的情况。但是SSH可以通过修改端口,使用密钥,禁止root登录等方式来保护我们的服务器。甚至可以只开放我们自己IP,但是Nginx作为Web服务器是在如何对外提供服务的同时保证我们不受恶意攻击和扫描呢?

基于 IP 的访问控制

使用 ngx_http_access_module 模块,通过 allowdeny 指令控制客户端 IP 访问。

白名单模式,允许特定 IP,拒绝其他所有。

server {
    listen 80;
    server_name example.com;

    location /admin {
        # 允许指定 IP 或网段
        allow 192.168.1.100;
        allow 10.0.0.0/24;
        deny all;  # 拒绝其他所有 IP

        # 反向代理或静态文件配置
        proxy_pass http://backend;
    }
}

黑名单模式,拒绝特定IP,允许钞所有。

location /api {
    deny 203.0.113.5;   # 拒绝单个 IP
    deny 45.33.0.0/16;  # 拒绝网段
    allow all;           # 允许其他 IP

    proxy_pass http://api_backend;
}

虽然可以通过以上两个方式实现控制,但是这个是需要手工配置,在实际的环境中是没办法保护自己的Web服务器的,如果要实现则需要把这个操作实现自动化。

#!/bin/bash
# 配置参数
LOG_PATH="/var/log/nginx/access.log"   # Nginx 访问日志路径
DENY_CONF="/etc/nginx/conf.d/deny.conf" # 黑名单配置文件
WHITE_LIST=("192.168.1.100" "8.8.8.8")  # 白名单 IP 列表
THRESHOLD=100                          # 请求阈值(1分钟内请求超过该值封禁)
LOCK_FILE="/tmp/deny_ip.lock"          # 防止重复执行锁文件
# 异常检测函数:提取1分钟内请求超过阈值的 IP
find_abnormal_ips() {
    local start_time=$(date -d "1 minute ago" +"%d/%b/%Y:%H:%M:%S")
    awk -v start="$start_time" -v threshold="$THRESHOLD" '
    $4 > "["start {
        ip_count[$1]++
    }
    END {
        for (ip in ip_count) {
            if (ip_count[ip] > threshold) {
                print ip
            }
        }
    }' "$LOG_PATH"
}
# 主流程
main() {
    # 避免重复执行
    if [ -f "$LOCK_FILE" ]; then
        exit 0
    fi
    touch "$LOCK_FILE"
    # 获取异常 IP 列表(过滤白名单)
    mapfile -t BAD_IPS < <(find_abnormal_ips | grep -vF "${WHITE_LIST[*]}")
    # 更新黑名单配置
    for ip in "${BAD_IPS[@]}"; do
        if ! grep -q "deny ${ip};" "$DENY_CONF"; then
            echo "deny ${ip};" >> "$DENY_CONF"
            echo "$(date) - Deny IP: $ip" >> /var/log/deny_ip.log
        fi
    done
    # 重载 Nginx 配置(如果黑名单有更新)
    if [ ${#BAD_IPS[@]} -gt 0 ]; then
        nginx -t && nginx -s reload
    fi
    rm -f "$LOCK_FILE"
}
main

当然这个阈值是很不好控制的,真实的环境开始的时候是一般不会启用该控制功能,而是随着暴露出去服务被攻击以后才会逐步开始配置,然后根据实际情况调整阈值。

而且我们还可以衍生高级的功能,比如封禁的ip自动解封,基于geoip模块进行封禁,防止sql注入等封禁。然后还可以进行封禁告警等,这些都是要根据实际情况来实现的。

下图就是模拟测试,然后执行脚本,测试请求的IP被拒绝。

#日志也从200变成403
127.0.0.1 - - [08/Apr/2025:00:12:41 +0800] "GET / HTTP/1.1" 200 703 "-" "curl/7.29.0" "-"
127.0.0.1 - - [08/Apr/2025:00:12:42 +0800] "GET / HTTP/1.1" 200 703 "-" "curl/7.29.0" "-"
127.0.0.1 - - [08/Apr/2025:00:12:43 +0800] "GET / HTTP/1.1" 403 153 "-" "curl/7.29.0" "-"
127.0.0.1 - - [08/Apr/2025:00:12:44 +0800] "GET / HTTP/1.1" 403 153 "-" "curl/7.29.0" "-"

运维小路

一个不会开发的运维!一个要学开发的运维!一个学不会开发的运维!欢迎大家骚扰的运维!

关注微信公众号《运维小路》获取更多内容。