ADR-0034: Bouncer IP Intelligence Provider¶
Bouncer 도메인의 IP 인텔리전스는 MaxMind GeoLite2 (국가 판정) + ipquery.io (VPN/Proxy/Tor 플래그) + Tor Project 공식 exit-node 목록 (Tor 확정) 의 무료 조합으로 시작한다. 세 소스는 단일 outbound port
IPIntelligence뒤로 통합되며, 향후 유료 MaxMind GeoIP2 Anonymous IP 로 adapter 만 교체 가능하다.
Status¶
Proposed
- Decided at — 2026-04-18
- Decided by — Pablo
Context¶
ADR-0033 에서 신설한 engine/bouncer/ 는 IP 주소로부터 다음 두 종류의 정보가 필요하다.
- 국가 코드(ISO 3166-1 alpha-2) — 국가 블랙리스트 판정
- 익명화 플래그 — VPN / Public Proxy / Tor exit-node 여부
업계 표준인 MaxMind 제품군은 두 DB 로 나뉘어 있다.
| 제품 | 가격 | 국가/도시 | VPN/Proxy/Tor |
|---|---|---|---|
| GeoLite2 | 무료 (계정 등록만) | ✅ Country, City, ASN | ❌ 없음 |
| GeoIP2 Anonymous IP | 유료 ($100+/mo 또는 CSV 라이선스) | — | ✅ VPN, Public Proxy, Tor, Hosting, Residential Proxy |
즉, GeoLite2 무료만으로는 VPN/Proxy/Tor 감지가 불가능 하다. 이 간극을 메울 무료 옵션은 제한적이다.
- ipquery.io — 무료 REST API.
is_vpn,is_proxy,is_tor플래그 제공. 정확도는 MaxMind 대비 낮지만 충분히 실용적. - Tor Project 공식 exit-node list —
https://check.torproject.org/torbulkexitlist또는https://www.dan.me.uk/torlist/?exit. Tor 는 100% 판정 가능한 유일한 카테고리. - ASN 블랙리스트 (자체 유지) — AWS / DigitalOcean / OVH / Hetzner 등 Hosting provider ASN. 대부분의 VPN 이 여기서 나오지만 오탐 위험(정당한 회사 네트워크도 클라우드일 수 있음).
공급자를 하나만 고르는 대신 역할별로 최적 소스를 조합 하는 것이 무료 범위에서 최고 정확도를 얻는 방법이다.
초기 비용 감내 한계는 "무료 범위 안" 이다. 운영 지표(오탐률, 미감지율) 를 관찰 후 유료 전환 여부를 재평가한다.
Decision¶
다음 세 소스의 무료 조합 을 MVP 의 IP 인텔리전스로 채택한다.
Source 1 — MaxMind GeoLite2 Country¶
- 역할 — 국가 판정
- 배포 — 임베디드 binary DB (
.mmdb).apps/api와apps/worker컨테이너 이미지에 포함 - 갱신 — asynq cron 으로 주 1회 자동 다운로드 (MaxMind 라이선스 키 필요)
- 지연 — 0 (로컬 lookup)
- 라이선스 — GeoLite2 EULA, 계정 등록 후 무료. Attribution 요구 (
docs/에 반영)
Source 2 — ipquery.io¶
- 역할 — VPN / Public Proxy / Tor 플래그 획득
- 배포 — REST API 호출. 공식 client 없어
platform/httpclient기반 자체 어댑터 - 갱신 — 실시간 lookup (API 호출)
- 지연 — 200~500ms (외부 API)
- fallback — API 장애 시 fail-open (정책 평가를 통과시키되
decisions.provider_error=true로 기록) - rate limit — 공개 문서상 무료 한도 존재. Redis 캐시 (IP 당 24h) 로 호출량 억제
Source 3 — Tor Project exit-node list¶
- 역할 — Tor 확정 판정 (ipquery.io 누락분 보강)
- 배포 — 공식 목록을 시간당 fetch → Redis Set 으로 저장
- 갱신 — asynq 시간별 cron
- 지연 — 0 (Redis 조회)
- 근거 — Tor 는 사실상 "공식 목록이 곧 진실" 이라 자체 판정이 외부 API 보다 신뢰성 높음
통합 port¶
세 소스는 하나의 outbound port 뒤에 숨긴다.
type IPIntelligence interface {
Lookup(ctx context.Context, ip netip.Addr) (*IPProfile, error)
}
type IPProfile struct {
CountryCode string // ISO 3166-1 alpha-2
IsVPN bool
IsProxy bool
IsTor bool
Source string // 'geolite2+ipquery+tor-list'
LookupAt time.Time
}
Adapter 구현 (engine/bouncer/adapter/ipintel/) 은 세 소스를 내부에서 조합하여 단일 IPProfile 을 반환. Bouncer 도메인 코어는 세 소스의 존재를 모름.
Caching¶
- Key —
bouncer:ip:{ip} - TTL — 24h
- 목적 — ipquery.io 호출 억제, Tor list 조회 스킵, 판정 일관성 (같은 방문자가 분 단위로 재시도 시 동일 결과)
Fail-open vs fail-close¶
공급자(특히 ipquery.io) 장애 시 정책:
- MVP — fail-open — 판정을 allow 로 통과시키되
decisions.provider_status='degraded'기록. 이유: 정상 사용자가 공급자 장애로 가입 불가해지는 UX 가 오남용 차단보다 더 큰 리스크 - 옵션 — 길드별
strict_mode토글로 fail-close (공급자 장애 시 deny). Phase 2 고려
Consequences¶
Positive¶
- 무료 범위에서 최대 정확도 — Tor 는 공식 목록으로 확정, VPN/Proxy 는 ipquery, 국가는 MaxMind 임베디드
- 국가 판정 무지연 — 임베디드 mmdb 는 RTT 0. 대다수 판정이 네트워크 없이 완결
- Provider 교체 용이 — port 경계 하에 adapter 교체만으로 유료 GeoIP2 Anonymous IP 로 전환 가능
- 비용 0 (MVP) — MaxMind 무료, ipquery 무료, Tor list 무료
- 감사 용이 —
decisions.source필드로 어떤 소스 조합이 판정에 쓰였는지 추적
Negative¶
- 정확도 한계 — ipquery 는 MaxMind 유료 대비 오탐·미감지 존재 (구체 수치는 운영 후 관찰)
- 외부 API 의존 — ipquery.io 장애 영향. fail-open 으로 완화하지만 "장애 중엔 VPN 이 뚫린다" 는 리스크
- 세 소스 동기화 부담 — MaxMind mmdb 주 1회 갱신, Tor list 시간별 갱신, ipquery 실시간. 각각의 cron 과 실패 복구 로직 필요
- 라이선스 제약 — MaxMind GeoLite2 는 Attribution 의무가 있다
Neutral¶
- ipquery.io 무료 한도 초과 시 유료 전환 필요 — 월간 호출량 모니터링
- Residential proxy 감지는 모든 무료 소스에서 불가능 (유료 GeoIP2 Anonymous IP 만 커버). MVP 수용 범위 밖
Alternatives considered¶
Alternative 1: MaxMind GeoIP2 Anonymous IP (유료 전면 채택)¶
Pros
- 업계 최고 정확도 (VPN, Public Proxy, Tor, Hosting, Residential Proxy 전 카테고리)
- 단일 공급자로 통합 간단
- SLA 명시된 안정성
Cons
- $100+/mo 고정 비용 — pre-MVP 단계에 과도
- 무료로 충분히 가능한 범위(국가 판정·Tor 확정) 까지 유료로 커버
- Residential Proxy 는 공개 웹 조인 가입자 중 드물어 ROI 낮음
Why rejected — 무료 조합으로 MVP 정확도 요구를 충족 가능. 오탐률이 실제 운영에서 문제되면 Revisit trigger 발동.
Alternative 2: ipquery.io 단일 공급자¶
Pros
- 가장 단순 — 국가와 VPN/Proxy/Tor 모두 하나의 API
- 어댑터 하나만 유지
Cons
- 국가 판정마다 외부 API 호출 — 지연 누적
- Rate limit 빠르게 소진 (국가 판정은 모든 방문자에 대해 필요)
- 공급자 장애 시 모든 판정 기능 중단
Why rejected — 국가 판정은 무료 mmdb 로 완결 가능한데 굳이 외부 의존을 만들 이유 없음. 지연·rate limit 모두 이득이 없다.
Alternative 3: 자체 VPN/Proxy 판정 구현¶
Pros
- 외부 의존 0
- 커스터마이즈 자유
Cons
- Hosting ASN 리스트, Tor list, VPN 서비스 IP range 를 직접 수집·갱신 필요
- 데이터 품질은 필연적으로 전문 업체에 뒤짐
- 유지보수 비용 높음 (Umbra 의 차별화 영역도 아님)
Why rejected — 핵심 도메인이 아닌 영역의 자체 구현은 ROI 없음. 품질 좋은 무료 소스를 조합하는 것이 합리적.
Alternative 4: Cloudflare Workers 에서 CF-IPCountry 헤더만 활용¶
Pros
- CF 가 이미 앞에 있으므로 zero-effort 국가 판정
- 비용 0
Cons
- VPN/Proxy/Tor 플래그 없음 (CF 유료 Bot Management 플랜 필요)
- per-guild 정책 평가 로직이 Worker 에 들어가면 코드 경계 흐려짐
- 차단 이력 저장·감사 어려움
Why rejected — 국가만 커버하고 VPN 은 못 막는다. 도메인 경계를 CF Worker 로 흘리는 건 ADR-0017 프로세스 분리 원칙에도 역행.
Compliance¶
- IP 인텔리전스 호출은 반드시
engine/bouncer/adapter/ipintel/를 경유 - 다른 도메인·프로세스에서
maxmind,ipquery,tor-list라이브러리를 직접 import 금지 (grep 로 CI 검증) - MaxMind GeoLite2 Attribution 은
apps/webFooter 또는docs/에 명시 - API 키(
MAXMIND_LICENSE_KEY,IPQUERY_API_KEY가 유료 전환 시) 는 Fly secrets 에만 저장 - Redis 캐시 key 는 prefix
bouncer:ip:로 네임스페이스 격리
Revisit triggers¶
- 운영 중 VPN/Proxy 오탐 또는 미감지 빈도가
decisions분석에서 유의미하게 높게 관찰되면 MaxMind GeoIP2 Anonymous IP 유료 전환 재평가 - ipquery.io 무료 한도 초과 또는 SLA 문제 발생 시 대체 공급자(IPinfo, proxycheck.io) 검토
- Residential proxy 를 이용한 조직적 Raid 가 반복되면 유료 공급자 필요성 증대
- 국가 판정 외에 "도시 단위" 정책 요구가 생기면 GeoLite2 → GeoLite2 City 또는 GeoIP2 City 로 확장