限流防护 #

一、限流概述 #

1.1 为什么需要限流 #

  • 防止DDoS攻击
  • 保护后端服务器
  • 公平分配资源
  • 避免服务过载

1.2 限流策略 #

策略 说明 适用场景
IP限流 基于客户端IP限制 防止单IP攻击
URL限流 基于URL路径限制 保护特定接口
Token限流 基于API Token限制 API调用限制
全局限流 全局请求限制 系统容量保护

二、vsthrottle模块 #

2.1 安装模块 #

bash
# 安装varnish-modules
sudo apt install varnish-modules

# 或编译安装
git clone https://github.com/varnish/varnish-modules.git
cd varnish-modules
./configure
make
sudo make install

2.2 导入模块 #

vcl
import vsthrottle;

2.3 基本用法 #

vcl
sub vcl_recv {
    # 每秒最多100请求
    if (vsthrottle.is_denied("client_" + client.ip, 100, 1s)) {
        return (synth(429, "Too Many Requests"));
    }
}

三、IP限流 #

3.1 基本IP限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 每秒最多100请求
    if (vsthrottle.is_denied("ip_" + client.ip, 100, 1s)) {
        return (synth(429, "Too Many Requests"));
    }
}

3.2 分级限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 每秒限制
    if (vsthrottle.is_denied("ip_sec_" + client.ip, 100, 1s)) {
        return (synth(429, "Too Many Requests"));
    }
    
    # 每分钟限制
    if (vsthrottle.is_denied("ip_min_" + client.ip, 3000, 60s)) {
        return (synth(429, "Too Many Requests"));
    }
    
    # 每小时限制
    if (vsthrottle.is_denied("ip_hour_" + client.ip, 100000, 3600s)) {
        return (synth(429, "Too Many Requests"));
    }
}

3.3 白名单 #

vcl
import vsthrottle;

acl whitelist {
    "192.168.1.0"/24;
    "10.0.0.0"/8;
}

sub vcl_recv {
    # 白名单不限流
    if (client.ip !~ whitelist) {
        if (vsthrottle.is_denied("ip_" + client.ip, 100, 1s)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

四、URL限流 #

4.1 API限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # API接口限流
    if (req.url ~ "^/api/") {
        if (vsthrottle.is_denied("api_" + client.ip, 50, 1s)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

4.2 登录限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 登录接口严格限流
    if (req.url ~ "^/login" && req.method == "POST") {
        if (vsthrottle.is_denied("login_" + client.ip, 5, 1m)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

4.3 搜索限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 搜索接口限流
    if (req.url ~ "^/search") {
        if (vsthrottle.is_denied("search_" + client.ip, 10, 1m)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

五、Token限流 #

5.1 API Token限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 基于API Token限流
    if (req.http.X-API-Token) {
        if (vsthrottle.is_denied("token_" + req.http.X-API-Token, 1000, 1h)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

5.2 用户ID限流 #

vcl
import vsthrottle;

sub vcl_recv {
    # 基于用户ID限流
    if (req.http.Cookie ~ "user_id") {
        set req.http.X-User-ID = regsub(req.http.Cookie, ".*user_id=([^;]+).*", "\1");
        
        if (vsthrottle.is_denied("user_" + req.http.X-User-ID, 500, 1h)) {
            return (synth(429, "Too Many Requests"));
        }
    }
}

六、DDoS防护 #

6.1 连接限制 #

vcl
import vsthrottle;

sub vcl_recv {
    # 全局连接限制
    if (vsthrottle.is_denied("global", 10000, 1s)) {
        return (synth(503, "Service Unavailable"));
    }
}

6.2 慢速攻击防护 #

vcl
# 设置超时参数
# varnishadm param.set timeout_idle 5
# varnishadm param.set timeout_linger 1

6.3 请求大小限制 #

vcl
sub vcl_recv {
    # 限制请求体大小
    if (req.http.Content-Length > 10485760) {
        return (synth(413, "Payload Too Large"));
    }
}

6.4 URL长度限制 #

vcl
sub vcl_recv {
    # 限制URL长度
    if (req.url.len > 2048) {
        return (synth(414, "URI Too Long"));
    }
}

七、访问控制 #

7.1 IP黑名单 #

vcl
acl blacklist {
    "192.168.100.0"/24;
    "10.0.100.10";
}

sub vcl_recv {
    if (client.ip ~ blacklist) {
        return (synth(403, "Forbidden"));
    }
}

7.2 IP白名单 #

vcl
acl whitelist {
    "192.168.1.0"/24;
    "10.0.0.0"/8;
}

sub vcl_recv {
    # 管理后台仅允许白名单访问
    if (req.url ~ "^/admin/" && client.ip !~ whitelist) {
        return (synth(403, "Forbidden"));
    }
}

7.3 User-Agent过滤 #

vcl
sub vcl_recv {
    # 阻止恶意爬虫
    if (req.http.User-Agent ~ "(?i)(bot|crawler|spider|scraper|harvest)") {
        return (synth(403, "Forbidden"));
    }
    
    # 阻止空User-Agent
    if (!req.http.User-Agent) {
        return (synth(403, "Forbidden"));
    }
}

7.4 地理限制 #

vcl
import geoip;

sub vcl_recv {
    # 阻止特定国家
    if (geoip.country_code(client.ip) ~ "(CN|RU)") {
        return (synth(403, "Forbidden"));
    }
}

八、请求验证 #

8.1 Token验证 #

vcl
sub vcl_recv {
    # API Token验证
    if (req.url ~ "^/api/") {
        if (!req.http.X-API-Token) {
            return (synth(401, "Unauthorized"));
        }
        
        # 验证Token格式
        if (req.http.X-API-Token !~ "^[a-zA-Z0-9]{32}$") {
            return (synth(403, "Invalid Token"));
        }
    }
}

8.2 签名验证 #

vcl
import digest;

sub vcl_recv {
    # 验证请求签名
    if (req.url ~ "^/api/") {
        set req.http.X-Expected-Sig = digest.hmac_sha256(
            "secret-key",
            req.method + req.url + req.http.X-Timestamp
        );
        
        if (req.http.X-Signature != req.http.X-Expected-Sig) {
            return (synth(403, "Invalid Signature"));
        }
        
        # 验证时间戳(防重放)
        if (std.time(req.http.X-Timestamp, 0) < now - 300s) {
            return (synth(403, "Request Expired"));
        }
    }
}

九、错误响应 #

9.1 自定义错误页面 #

vcl
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    
    if (resp.status == 429) {
        synthetic({"<!DOCTYPE html>
<html>
<head>
    <title>429 Too Many Requests</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
        h1 { color: #f39c12; }
        .retry { margin-top: 20px; color: #666; }
    </style>
</head>
<body>
    <h1>429 Too Many Requests</h1>
    <p>You have exceeded the rate limit.</p>
    <p class="retry">Please wait a moment and try again.</p>
</body>
</html>"});
    } elseif (resp.status == 403) {
        synthetic({"<!DOCTYPE html>
<html>
<head>
    <title>403 Forbidden</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
        h1 { color: #e74c3c; }
    </style>
</head>
<body>
    <h1>403 Forbidden</h1>
    <p>Access to this resource is forbidden.</p>
</body>
</html>"});
    }
    
    return (deliver);
}

9.2 JSON错误响应 #

vcl
sub vcl_synth {
    if (req.http.Accept ~ "application/json") {
        set resp.http.Content-Type = "application/json";
        synthetic({"{"error":{"code":} + resp.status + {","message":"} + resp.reason + {"}}"});
    } else {
        set resp.http.Content-Type = "text/html; charset=utf-8";
        synthetic({"<!DOCTYPE html>
<html>
<head><title>"} + resp.status + {"</title></head>
<body><h1>"} + resp.status + " " + resp.reason + {"</h1></body>
</html>"});
    }
    return (deliver);
}

十、监控与告警 #

10.1 限流统计 #

bash
# 查看限流统计
varnishstat -1 -f VSC.vsthrottle.*

# 输出示例
VSC.vsthrottle.bucket_dropped    1234    Dropped requests
VSC.vsthrottle.bucket_overflow   567     Bucket overflow

10.2 日志记录 #

vcl
import std;

sub vcl_synth {
    # 记录限流事件
    if (resp.status == 429) {
        std.syslog(6, "Rate limit exceeded: " + client.ip + " " + req.url);
    }
}

10.3 监控脚本 #

bash
#!/bin/bash
# rate_limit_monitor.sh

echo "=== Rate Limiting Statistics ==="
varnishstat -1 -f VSC.vsthrottle.*

echo ""
echo "=== Recent Rate Limited Requests ==="
varnishlog -d -q "RespStatus == 429" | head -20

echo ""
echo "=== Top Rate Limited IPs ==="
varnishlog -d -q "RespStatus == 429" | grep "ReqHeader:X-Forwarded-For" | awk '{print $3}' | sort | uniq -c | sort -rn | head -10

十一、完整配置示例 #

vcl
vcl 4.1;

import std;
import vsthrottle;

# ACL定义
acl whitelist {
    "192.168.1.0"/24;
    "10.0.0.0"/8;
}

acl blacklist {
    "192.168.100.0"/24;
}

# 后端定义
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

# 请求处理
sub vcl_recv {
    # 黑名单
    if (client.ip ~ blacklist) {
        return (synth(403, "Forbidden"));
    }
    
    # User-Agent过滤
    if (req.http.User-Agent ~ "(?i)(bot|crawler|spider)") {
        return (synth(403, "Forbidden"));
    }
    
    if (!req.http.User-Agent) {
        return (synth(403, "Forbidden"));
    }
    
    # 白名单跳过限流
    if (client.ip ~ whitelist) {
        return (hash);
    }
    
    # 全局限流
    if (vsthrottle.is_denied("global", 10000, 1s)) {
        return (synth(503, "Service Unavailable"));
    }
    
    # IP限流
    if (vsthrottle.is_denied("ip_" + client.ip, 100, 1s)) {
        std.syslog(6, "Rate limit: " + client.ip);
        return (synth(429, "Too Many Requests"));
    }
    
    # API限流
    if (req.url ~ "^/api/") {
        if (vsthrottle.is_denied("api_" + client.ip, 50, 1s)) {
            return (synth(429, "Too Many Requests"));
        }
    }
    
    # 登录限流
    if (req.url ~ "^/login" && req.method == "POST") {
        if (vsthrottle.is_denied("login_" + client.ip, 5, 1m)) {
            return (synth(429, "Too Many Requests"));
        }
    }
    
    # 请求大小限制
    if (req.http.Content-Length > 10485760) {
        return (synth(413, "Payload Too Large"));
    }
    
    # URL长度限制
    if (req.url.len > 2048) {
        return (synth(414, "URI Too Long"));
    }
    
    # 管理后台访问控制
    if (req.url ~ "^/admin/" && client.ip !~ whitelist) {
        return (synth(403, "Forbidden"));
    }
    
    return (hash);
}

# 错误响应
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    
    if (resp.status == 429) {
        set resp.http.Retry-After = "60";
        synthetic({"<!DOCTYPE html>
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<h1>429 Too Many Requests</h1>
<p>Please slow down your requests.</p>
</body>
</html>"});
    } elseif (resp.status == 403) {
        synthetic({"<!DOCTYPE html>
<html>
<head><title>403 Forbidden</title></head>
<body>
<h1>403 Forbidden</h1>
<p>Access denied.</p>
</body>
</html>"});
    } else {
        synthetic({"<!DOCTYPE html>
<html>
<head><title>"} + resp.status + {"</title></head>
<body>
<h1>"} + resp.status + " " + resp.reason + {"</h1>
</body>
</html>"});
    }
    
    return (deliver);
}

十二、总结 #

本章我们学习了:

  1. 限流概述:原因、策略
  2. vsthrottle模块:安装、基本用法
  3. IP限流:基本限流、分级限流、白名单
  4. URL限流:API、登录、搜索限流
  5. Token限流:API Token、用户ID限流
  6. DDoS防护:连接限制、慢速攻击防护
  7. 访问控制:IP黑白名单、User-Agent过滤
  8. 请求验证:Token验证、签名验证
  9. 错误响应:自定义错误页面
  10. 监控告警:统计、日志、监控脚本

掌握限流防护后,让我们进入下一章,学习性能调优!

最后更新:2026-03-28