Caddy MaxMind GeoIP 地理访问限制:白名单与黑名单模式
目录
为什么需要 GeoIP 访问限制
如果服务主要面向特定地区的用户,其他地区来的流量里常会混进恶意请求。比如:
- 你的博客主要面向中文读者,欧洲 IP 的访问可能来自扫描器
- 管理后台只需要从你所在的国家访问;API 服务也可能要限制特定区域的滥用
GeoIP 限制不能当成唯一的防护手段,VPN 可以绕过 IP 地理位置判断。它更适合拿来减少恶意流量和日志噪音,也能顺带减轻一点服务器压力。
caddy-maxmind-geolocation 插件
我用的自定义构建里已经带上这个插件(参考 xcaddy 构建文章)。它基于 MaxMind 的 GeoLite2 数据库做 IP 地理位置查询。
数据库中每条记录包含:
- IP 段起止范围,以及所属国家代码(ISO 3166-1 alpha-2,如
CN、US、JP) - 可选的地理区域和城市信息(City 版本,本文用的是 Country 版本)
两种限制模式
我写了两个 snippet,对应不同场景:
白名单模式:只允许指定国家
(geo_only_allow_countries) {
@only_allow_countries_geofilter {
not {
maxmind_geolocation {
db_path "/config/geodb/GeoLite2-Country.mmdb"
allow_countries {args[:]}
}
}
}
respond @only_allow_countries_geofilter "You are blocked" 403
}这里先定义名为 @only_allow_countries_geofilter 的命名匹配器,专门匹配不在指定国家列表里的请求,再对这些请求返回 403。
使用方式:
example.com {
import geo_only_allow_countries CN JP
reverse_proxy 127.0.0.1:3004
}这样就只放行中国和日本的 IP。
黑名单模式:禁止指定国家
(geo_not_allow_countries) {
@not_allow_countries_geofilter {
maxmind_geolocation {
db_path "/config/geodb/GeoLite2-Country.mmdb"
allow_countries {args[:]}
}
}
respond @not_allow_countries_geofilter "You are blocked" 403
}这里直接匹配指定国家列表里的请求,然后返回 403。
使用方式:
example.com {
import geo_not_allow_countries RU
reverse_proxy 127.0.0.1:8080
}这样会拦掉来自俄罗斯的 IP。
GeoLite2 数据库自动更新
MaxMind 的 GeoLite2 数据库会跟着 IP 分配变化更新,大概每月 1-2 次。数据库太旧时,部分 IP 的地理位置判断会出错。
我的容器里通过 update_geodb.sh 自动管理,xcaddy 文章里已经写过。
局限性
- VPN/代理绕过:用户可以通过 VPN 切换出口 IP,GeoIP 挡不住这个
- 数据库时效性和 CDN 回源:IP 段会重新分配,老数据库可能导致误判;如果请求经过 Cloudflare,Caddy 收到的 IP 是 Cloudflare 的回源 IP,需要正确配置
trusted_proxies才能获取真实客户端 IP
GeoIP 更适合拿来收窄攻击面,不适合单独扛安全防护。
实际配置就是几行 Caddyfile。管理后台这类场景更适合白名单模式;要挡掉特定地区的已知恶意流量,用黑名单模式更顺手。把 GeoIP 数据库自动更新接上后,后续维护也省事。