From 102da9c2fe9d59ffe875a5f840ac4c6e1d5e8a5f Mon Sep 17 00:00:00 2001 From: kaffa Date: Fri, 24 Apr 2026 08:11:44 +0900 Subject: [PATCH] =?UTF-8?q?cloudflare:=20Pseudo=20IPv4=20(Class=20E=20240/?= =?UTF-8?q?4)=20=EC=A0=95=EB=A6=AC=20=E2=80=94=20Netbis=20=EA=B4=80?= =?UTF-8?q?=EC=B0=B0=20=EA=B8=B0=EB=B0=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - infra/security/cloudflare.md: Pseudo IPv4 섹션 신규 (동작 원리·모드·함의·오해 주의) - services/netbis.md: client_ip 의미에서 부정확한 '254.x 범위' → '240.0.0.0/4' 정정, CF docs 링크 - history: 2026-04-24 CF Pseudo IPv4 정체 규명 (CGNAT 오진 교훈 포함) --- .../2026-04-24-cf-pseudo-ipv4-discovery.md | 109 ++++++++++++++++++ infra/security/cloudflare.md | 40 +++++++ services/netbis.md | 2 +- 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 history/2026-04-24-cf-pseudo-ipv4-discovery.md diff --git a/history/2026-04-24-cf-pseudo-ipv4-discovery.md b/history/2026-04-24-cf-pseudo-ipv4-discovery.md new file mode 100644 index 0000000..35d9122 --- /dev/null +++ b/history/2026-04-24-cf-pseudo-ipv4-discovery.md @@ -0,0 +1,109 @@ +--- +title: CF Pseudo IPv4 (Class E 240/4) 정체 규명 +updated: 2026-04-24 +tags: [cloudflare, netbis, log-analysis, incident] +--- + +## 배경 + +Netbis NPM → zlambda → VictoriaLogs 로그 파이프라인이 2026-04-23 구축 + 실 client IP 추출 VRL transform 정비 완료 직후, VL top client_ip 집계에서 **240.0.0.0/4 (클래스 E, "Reserved for Future Use") 대역이 전체 트래픽의 45%**를 차지하는 현상 관찰. + +## 초기 오진 + +- 처음에는 "한국 모바일 통신사의 CGNAT 240/4 활용" 으로 추정하고 답변 +- 근거로 UA 95%+ 모바일 + KR 앱 브랜드(KakaoTalk/Daum/NAVER) +- **이 추정은 틀렸음.** 공식 문서·RFC·APNIC/IETF 검색 결과 KR 통신사가 240/4를 CGNAT에 사용한다는 기록 없음 + +## 실제 원인 — CF Pseudo IPv4 + +Cloudflare 공식 [Pseudo IPv4 문서](https://developers.cloudflare.com/network/pseudo-ipv4/) 및 [Support 문서](https://support.cloudflare.com/hc/en-us/articles/229666767) 확인: + +> "Cloudflare uses Class E IP space (240.0.0.0 - 255.255.255.255) for these addresses. The Pseudo IPv4 service uses a hashing algorithm applied to top 64 bits of the connecting IPv6 address" +> "overwrite the existing Cf-Connecting-IP and X-Forwarded-For headers with a Pseudo IPv4 address" + +즉: + +``` +IPv6 클라이언트 → CF 엣지 + → IPv6 상위 64비트 MD5 해시 → 240/4 매핑 + → CF-Connecting-IP 및 X-Forwarded-For 헤더 값 overwrite + → NPM의 real_ip_header CF-Connecting-IP 설정 → $remote_addr에 240/4 기록 + → Vector VRL → VL의 client_ip 필드에 240/4 저장 +``` + +Netbis 조건과 완벽 일치: +- 백엔드 Apache 2.4.38 + PHP 5.6.40 (IPv6 지원 미흡한 레거시) +- 모바일 트래픽 비율 높음 (모바일 = IPv6 비율 높음) +- CF 대시보드에서 Pseudo IPv4 Overwrite headers 모드 활성 상태 + +## 검증 증거 + +로그 필드 샘플 5건 공통 패턴: +``` +client_ip = 240/4 범위 값 +xff_chain = 동일 값 (CF가 두 헤더 모두 overwrite) +cf_edge_ip = 172.70.x 등 CF 대역 +client_ip_source = remote_addr +``` + +UA 분포 (1h, 240/4 IP 한정): +``` +Android (Chrome): 15,469 (67%) +iPhone (Safari): 4,694 (20%) +Daum/Kakao 앱: 1,512 +KakaoTalk: 962 +NAVER: 573 +Windows: 591 (2.6%) +Mac: 3 (0.01%) +``` + +데스크톱 2.6%뿐 — 모바일 IPv6 비율 높음과 부합. + +## 함의 + +### 차단 영향 범위 + +- 240/4 IP 하나 = **단일 IPv6 /64 prefix 전체**의 해시 결과 +- 한 가정 IPv6 `/64`는 수십~수백 디바이스·수년 SLAAC 주소 포함 +- 따라서 **240/4 IP ban = 한 가정/기업의 모든 IPv6 디바이스 장기간 차단 위험** +- CGNAT(수천 유저)보다는 좁지만 단일 IP로 간주하면 오탐 증폭 + +### 봇 탐지 관점 + +- 역설적으로 현재 Overwrite 모드가 IPv6 봇넷 탐지에 유리 +- IPv6 프라이버시 확장으로 수천 주소 돌리는 봇도 같은 /64면 같은 240/4 하나로 collapse +- 진짜 IPv6를 받았다면 매번 다른 주소라 탐지 훨씬 어려웠을 것 +- 단 /64 차단 = 해당 prefix의 정상 유저 동반 차단 → ban duration을 짧게(10~30분) 유지 권장 + +### 시나리오/ML feature + +- 240/4 IP를 "가상 식별자"로 그대로 사용 가능 (결정적 해시이므로) +- CGNAT 플래그로 분류 금지 — 잘못된 분류로 처리 로직 왜곡됨 +- 필요 시 Pseudo IPv4 모드를 "Add header"로 전환해 실 IPv6 확보 가능 (백엔드 IPv6 처리 여부 선행 확인 필수) + +## 대안 경로 (추후 결정사항) + +| 조치 | 효과 | 리스크 | +|------|------|-------| +| 현상 유지 (Overwrite) | 레거시 백엔드 호환 유지 | 240/4 해석·ban 설계에 주의 필요 | +| Add header 모드 전환 | CF-Connecting-IP에 실 IPv6, 별도 헤더에 240/4 | PHP 5.6 IPv6 주소 문자열 처리 검증 필요 | +| 백엔드 IPv6 업그레이드 | 근본 해결 | 애플리케이션 호환 작업 대규모 | + +## 정본 반영 + +- `infra/security/cloudflare.md` — `Pseudo IPv4 (Class E 240/4)` 섹션 신규 +- `services/netbis.md` — client_ip 의미 설명 정정 (254.x → 240/4, 메커니즘 링크) + +## 참고 자료 + +- [CF Pseudo IPv4 공식 문서](https://developers.cloudflare.com/network/pseudo-ipv4/) +- [CF Support 문서](https://support.cloudflare.com/hc/en-us/articles/229666767) +- [APNIC Blog: Looking for 240/4 addresses](https://blog.apnic.net/2024/09/10/looking-for-240-4-addresses/) +- [benjojo: Class E Addresses in the Real World](https://blog.benjojo.co.uk/post/class-e-addresses-in-the-real-world) +- [IETF draft-schoen-intarea-unicast-240](https://datatracker.ietf.org/doc/draft-schoen-intarea-unicast-240/) + +## 오진 교훈 + +- UA 분포만으로 "CGNAT"로 단정 금지 — Pseudo IPv4가 모바일 IPv6 해시 결과이므로 같은 시그널이 나옴 +- 예약 대역 IP 관찰 시 먼저 CF 엣지 기능(Pseudo IPv4, IP anonymization 등) 가능성부터 확인 +- 공식 벤더 문서 확인이 UA·트래픽 패턴 추론보다 선행되어야 함 diff --git a/infra/security/cloudflare.md b/infra/security/cloudflare.md index d6241f8..323ece7 100644 --- a/infra/security/cloudflare.md +++ b/infra/security/cloudflare.md @@ -27,6 +27,46 @@ Syn 이 엣지 관점에서 소유. 일반 DNS 관리 협업은 Heimdall. ⚠️ **Rate Limit, WAF 규칙, Firewall 설정은 API로 조회/변경 불가 — 대시보드에서만 관리 가능**. 토큰 스코프 확장이 필요하면 CF 대시보드 > My Profile > API Tokens에서 편집. +## Pseudo IPv4 (Class E 240/4) + +레거시 IPv4-only 백엔드를 위한 CF 기능. **Netbis zone에서 활성 상태로 관찰됨** (Apache 2.4.38 + PHP 5.6.40). Kappa 계정 zone에는 현재 기본 Off로 추정. + +### 동작 원리 + +``` +클라이언트 (IPv6) → CF 엣지 + → IPv6 주소 상위 64비트를 MD5 해시 + → 결과를 240.0.0.0/4 (240~255.x.x.x) 범위의 IPv4로 매핑 + → CF-Connecting-IP 및 X-Forwarded-For 헤더를 해시된 IPv4로 overwrite + → 진짜 IPv6는 Cf-Connecting-IPv6 별도 헤더로 전달 +``` + +### 모드 3종 (CF 대시보드 Network → Pseudo IPv4) + +| 모드 | CF-Connecting-IP | 별도 헤더 | +|------|-----------------|----------| +| Off | 실 IPv6/IPv4 | - | +| Add header | 실 IP 그대로 | `Cf-Pseudo-IPv4`에 해시 | +| **Overwrite headers** | 해시(240/4) | `Cf-Connecting-IPv6`에 실 IPv6 | + +### 관찰 시그널 (Netbis) + +- VL `client_ip` 필드 중 240.0.0.0/4 대역 IP 등장 = IPv6 접속자 +- UA 분포: 95%+ 모바일 (Android Chrome / iPhone Safari / KakaoTalk / Daum / NAVER) — KR 모바일 IPv6 보급률 높음 +- 동일 IPv6 `/64` prefix → 항상 같은 240/4 IP로 매핑 (재현 가능한 식별자) +- **한 240/4 IP = 한 가정/ /64 단위 유저 집합** — ban 시 /64 전체가 차단됨 + +### 오해 주의 + +- 240/4는 KR 통신사 CGNAT 아님 (공식 문서 없음, 공개 라우팅도 안 됨) +- 실제로는 CF Pseudo IPv4 해시값 — "가짜 IP" +- CGNAT 오인하면 차단 효과 완전히 다르게 판단됨 + +### 관련 링크 + +- [CF Pseudo IPv4 docs](https://developers.cloudflare.com/network/pseudo-ipv4/) +- [CF Support: Pseudo IPv4 overview](https://support.cloudflare.com/hc/en-us/articles/229666767) + ## Zone | Zone | Zone ID | Status | Plan | NS | DNS rec | 비고 | diff --git a/services/netbis.md b/services/netbis.md index 696cef0..f662e63 100644 --- a/services/netbis.md +++ b/services/netbis.md @@ -245,7 +245,7 @@ NPM-1..6 호스트 Vector(0.55) | 라벨 | `host=npm-1..6`, `service=npm`, `log_type=access|error`, `zone=<서빙 zone CSV>`(npm-1/5/6 은 `shared`), `relay=zlambda`, `program=npm`, `proxy_host_id`(파일명에서 추출), `client_ip`(실 IP), `cf_edge_ip`, `xff_chain`, `client_ip_is_cf_edge`, `client_ip_source`, `remote_addr`(raw Client) | | 파싱 포맷 | NPM proxy/standard v2 (Real+XFF 포함, 2026-04-23 이후) 우선, v1(Real+XFF 없음) legacy fallback. 모두 실패 시 `log_format=raw` | | nginx 설정 | 6대 전부 `real_ip_header CF-Connecting-IP`, `real_ip_recursive on`, `set_real_ip_from` 에 CF + CloudFront IP 대역 포함. log_format에 `[Client $remote_addr] [Real $realip_remote_addr] [XFF $http_x_forwarded_for]` 3필드 기록 (2026-04-23 일괄 패치) | -| client_ip 의미 | nginx real_ip 재작성 후의 실 고객 IP (CF-Connecting-IP 경유). IPv6 방문자의 경우 CF Pseudo IPv4 254.x 범위로 나타남 — 시나리오/AI 분석에 식별자로 사용 가능. Rewrite 실패(Client == Real) 시 `client_ip_is_cf_edge=true` + `client_ip=null` + `client_ip_source=cf_edge_rewrite_failed` | +| client_ip 의미 | nginx real_ip 재작성 후의 실 고객 IP (CF-Connecting-IP 경유). **IPv6 방문자는 CF Pseudo IPv4 기능으로 `240.0.0.0/4` (240~255.x.x.x) 범위의 해시 값으로 나타남** — [[../infra/security/cloudflare#Pseudo IPv4 (Class E 240/4)]] 참조. Rewrite 실패(Client == Real) 시 `client_ip_is_cf_edge=true` + `client_ip=null` + `client_ip_source=cf_edge_rewrite_failed` | | zlambda relay | [[zlambda]] NixOS container `vector-relay` (Docker `timberio/vector:0.45.0-debian`, net `vector-net`, port 9999/tcp) | | zlambda 모듈 | `~/nixos-infra/vector.nix` — 전용 render/env systemd + Docker oci-container | | bearer token | zlambda agenix `secrets/vector-bearer-token.age` (kaffa + zlambda host key 복호화). NPM config 에는 평문, Vault 백업은 `secret/cloud/vector-relay-netbis` |