perf(security): reduce GeoIP lookups and deduplicate trusted proxy check
- Check(): single GeoIP LookupCountry call, result reused for both deny and allow checks. Removed goto label for structured flow. - getClientIP(): single trusted proxy CIDR scan gates both X-Forwarded-For and X-Real-IP processing.
This commit is contained in:
parent
e535b9062c
commit
197d0d2344
@ -200,51 +200,41 @@ func (ac *AccessControl) Check(ip net.IP) bool {
|
|||||||
ac.mu.RLock()
|
ac.mu.RLock()
|
||||||
defer ac.mu.RUnlock()
|
defer ac.mu.RUnlock()
|
||||||
|
|
||||||
// 1. 先检查 CIDR 拒绝列表(显式拒绝优先)
|
|
||||||
for _, network := range ac.denyList {
|
for _, network := range ac.denyList {
|
||||||
if network.Contains(ip) {
|
if network.Contains(ip) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查 GeoIP 国家拒绝(如果启用)
|
var country string
|
||||||
|
var hasCountry bool
|
||||||
if ac.geoip != nil && ac.geoipConfig.Enabled {
|
if ac.geoip != nil && ac.geoipConfig.Enabled {
|
||||||
country, err := ac.geoip.LookupCountry(ip)
|
if c, err := ac.geoip.LookupCountry(ip); err == nil {
|
||||||
if err == nil {
|
if c == geoPrivateDeny {
|
||||||
// 处理私有 IP 特殊标记
|
|
||||||
if country == geoPrivateAllow {
|
|
||||||
// 私有 IP 自动允许,跳过国家检查
|
|
||||||
goto checkAllow
|
|
||||||
}
|
|
||||||
if country == geoPrivateDeny {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if c != geoPrivateAllow {
|
||||||
if slices.Contains(ac.geoipConfig.DenyCountries, country) {
|
country = c
|
||||||
return false
|
hasCountry = true
|
||||||
|
if slices.Contains(ac.geoipConfig.DenyCountries, country) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAllow:
|
|
||||||
// 3. 检查 CIDR 允许列表
|
|
||||||
for _, network := range ac.allowList {
|
for _, network := range ac.allowList {
|
||||||
if network.Contains(ip) {
|
if network.Contains(ip) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 检查 GeoIP 国家允许(如果启用)
|
if hasCountry {
|
||||||
if ac.geoip != nil && ac.geoipConfig.Enabled {
|
if slices.Contains(ac.geoipConfig.AllowCountries, country) {
|
||||||
country, err := ac.geoip.LookupCountry(ip)
|
return true
|
||||||
if err == nil && country != geoPrivateDeny {
|
|
||||||
if slices.Contains(ac.geoipConfig.AllowCountries, country) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 返回默认操作
|
|
||||||
return ac.defaultAction == ActionAllow
|
return ac.defaultAction == ActionAllow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,56 +350,43 @@ func (ac *AccessControl) SetDefault(action string) error {
|
|||||||
func (ac *AccessControl) getClientIP(ctx *fasthttp.RequestCtx) net.IP {
|
func (ac *AccessControl) getClientIP(ctx *fasthttp.RequestCtx) net.IP {
|
||||||
remoteIP := netutil.GetRemoteAddrIP(ctx)
|
remoteIP := netutil.GetRemoteAddrIP(ctx)
|
||||||
|
|
||||||
// 仅当配置了可信代理且请求来自可信代理时,才解析 X-Forwarded-For
|
if len(ac.trustedProxies) == 0 || remoteIP == nil {
|
||||||
if len(ac.trustedProxies) > 0 && remoteIP != nil {
|
return remoteIP
|
||||||
isTrusted := false
|
}
|
||||||
for _, network := range ac.trustedProxies {
|
|
||||||
if network.Contains(remoteIP) {
|
|
||||||
isTrusted = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTrusted {
|
isTrusted := false
|
||||||
// 使用右侧(最接近客户端)的非可信 IP
|
for _, network := range ac.trustedProxies {
|
||||||
if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 {
|
if network.Contains(remoteIP) {
|
||||||
ips := strings.Split(string(xff), ",")
|
isTrusted = true
|
||||||
for _, v := range slices.Backward(ips) {
|
break
|
||||||
ipStr := strings.TrimSpace(v)
|
}
|
||||||
if ip := net.ParseIP(ipStr); ip != nil {
|
}
|
||||||
// 检查该 IP 是否在可信代理列表中
|
if !isTrusted {
|
||||||
trusted := false
|
return remoteIP
|
||||||
for _, network := range ac.trustedProxies {
|
}
|
||||||
if network.Contains(ip) {
|
|
||||||
trusted = true
|
if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 {
|
||||||
break
|
ips := strings.Split(string(xff), ",")
|
||||||
}
|
for _, v := range slices.Backward(ips) {
|
||||||
}
|
ipStr := strings.TrimSpace(v)
|
||||||
if !trusted {
|
if ip := net.ParseIP(ipStr); ip != nil {
|
||||||
return ip
|
trusted := false
|
||||||
}
|
for _, network := range ac.trustedProxies {
|
||||||
|
if network.Contains(ip) {
|
||||||
|
trusted = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !trusted {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查 X-Real-IP 头部(仅来自可信代理时)
|
if xri := ctx.Request.Header.Peek("X-Real-IP"); len(xri) > 0 {
|
||||||
if len(ac.trustedProxies) > 0 && remoteIP != nil {
|
if ip := net.ParseIP(string(xri)); ip != nil {
|
||||||
isTrusted := false
|
return ip
|
||||||
for _, network := range ac.trustedProxies {
|
|
||||||
if network.Contains(remoteIP) {
|
|
||||||
isTrusted = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTrusted {
|
|
||||||
if xri := ctx.Request.Header.Peek("X-Real-IP"); len(xri) > 0 {
|
|
||||||
if ip := net.ParseIP(string(xri)); ip != nil {
|
|
||||||
return ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user