fix(v2): 限流按 socket 对端 IP(SetTrustedProxies(nil),伪造 XFF 失效)+ 桶 map 硬上限防 OOM
This commit is contained in:
@@ -17,6 +17,15 @@ type bucket struct {
|
||||
updated time.Time
|
||||
}
|
||||
|
||||
// maxBuckets 是 buckets map 的硬上限——防御性 backstop:哪怕 SetTrustedProxies(nil)
|
||||
// 已经堵死"伪造 X-Forwarded-For 绕过限流"这条路,socket 对端 IP 本身也可能被
|
||||
// 大量真实/伪装源地址(如僵尸网络)刷出海量不同 IP,惰性 sweep(见 sweepLocked)
|
||||
// 只回收超过 10min 不活跃的桶,短时间集中攻击仍可能在 sweep 生效前把 map 撑爆。
|
||||
// 100k 条 * 单桶几十字节量级,内存占用可控(几 MB),对当前单机部署(阿里云,
|
||||
// ~1-2GB 内存假设,与 pangolin 单机 Runbook 同量级)足够安全。超限时宁可误限
|
||||
// (安全侧),也不无界建桶 OOM。
|
||||
const maxBuckets = 100_000
|
||||
|
||||
// IPRateLimiter 简单内存令牌桶,按客户端 IP 分桶限速——单实例部署足够(jiu 反馈波,
|
||||
// P3 v2 限流纵深)。多实例横向扩展需换外置存储(Redis INCR+EXPIRE 等)统一计数,
|
||||
// 当前 pay 只跑单进程,内存方案够用且零额外依赖。
|
||||
@@ -47,6 +56,16 @@ func (l *IPRateLimiter) allow(ip string) bool {
|
||||
l.mu.Lock()
|
||||
b, ok := l.buckets[ip]
|
||||
if !ok {
|
||||
if len(l.buckets) >= maxBuckets {
|
||||
// map 已到硬上限:先尝试一次 sweep 腾陈旧桶的位置。
|
||||
l.sweepLocked(now)
|
||||
}
|
||||
if len(l.buckets) >= maxBuckets {
|
||||
// sweep 后仍超限(短时间内大量活跃桶):拒绝新建桶,按限流命中处理
|
||||
// (429)而不是无界增长——安全侧,宁可误限未知新 IP 也不 OOM。
|
||||
l.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
b = &bucket{tokens: l.burst, updated: now}
|
||||
l.buckets[ip] = b
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user