限流防护 #
一、限流概述 #
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);
}
十二、总结 #
本章我们学习了:
- 限流概述:原因、策略
- vsthrottle模块:安装、基本用法
- IP限流:基本限流、分级限流、白名单
- URL限流:API、登录、搜索限流
- Token限流:API Token、用户ID限流
- DDoS防护:连接限制、慢速攻击防护
- 访问控制:IP黑白名单、User-Agent过滤
- 请求验证:Token验证、签名验证
- 错误响应:自定义错误页面
- 监控告警:统计、日志、监控脚本
掌握限流防护后,让我们进入下一章,学习性能调优!
最后更新:2026-03-28