- 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 오진 교훈 포함)
14 KiB
title, updated, aliases, tags
| title | updated | aliases | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Netbis 예비서버 (zlambda) | 2026-04-08 APISIX NixOS oci-containers로 재가동 |
|
|
개요
Netbis 팀 도메인의 예비(DR) 리버스 프록시 서버. 평소에는 트래픽을 받지 않으며, 유사시 Cloudflare DNS를 수동 전환하여 활성화.
기존 Ironclad 인프라(apisix, crowdsec-safeline)와는 별도 구성.
APISIX/etcd는 NixOS virtualisation.oci-containers로 기동 완료. 설정은 zlambda flake의 apisix.nix 모듈. 라우트/SSL은 비어 있으므로 DR 역할 부트스트랩은 별도 작업 필요.
서버 정보
| 항목 | 값 |
|---|---|
| 호스트명 | zlambda (구 sandbox-tokyo, 2026-04-08 변경) |
| Linode 라벨 | zlambda (id 47271589) |
| 공인 IP | 139.162.71.52 |
| Tailscale IP | 100.78.51.18 (2026-04-08 변경, 이전 100.79.87.48) |
| 위치 | Linode Tokyo (ap-northeast, lish-tokyo2) |
| OS | NixOS 25.05 (Warbler), x86_64-linux |
| Linode kernel | linode/direct-disk (NixOS 자체 GRUB) |
| 디스크 | sda 49.5G ext4 (/), sdb 510M swap |
| SSH | ssh root@zlambda (또는 alias ssh root@sandbox-tokyo, 공인 IP ssh root@139.162.71.52) |
| LISH 사용자 | netbis@lish-tokyo2.linode.com (kaffa-Macmini SSH key) |
APISIX 구성
NixOS virtualisation.oci-containers로 선언 — ~/nixos-infra/apisix.nix. 재배포는 nixos-rebuild switch --flake .#zlambda.
| 컨테이너 | 이미지 | 포트 |
|---|---|---|
| apisix | apache/apisix:3.15.0-debian | 80(→9080), 443(→9443), 9180 |
| apisix-etcd | quay.io/coreos/etcd:v3.5.11 | 2379 (내부) |
- Docker 네트워크:
apisix-net(172.22.222.0/24), etcd=172.22.222.3, apisix=172.22.222.20 - Admin API:
http://127.0.0.1:9180(admin key 필수, Linode 방화벽이 public 차단 → Tailscale 경유) - Admin Key: agenix로 암호화 (
secrets/apisix-admin-key.age). activation 시/run/agenix/apisix-admin-key로 복호화, render 서비스가/run/apisix/config.yaml에 sed 치환. 회전 시nix run github:ryantm/agenix -- -e secrets/apisix-admin-key.age후nixos-rebuild switch. - APISIX 컨테이너 ulimits: nofile 655360
- 영속 데이터:
- etcd: bind mount
/var/lib/apisix/etcd(root 소유) - apisix 로그: docker named volume
apisix-logs(이미지 VOLUME 선언이 uid 1001 권한 세팅)
- etcd: bind mount
- config.yaml: 템플릿(Nix store,
__ADMIN_KEY__자리표시자) →apisix-render-config.service(oneshot)가/run/apisix/config.yaml에 렌더링 → 컨테이너에 read-only bind mount - systemd 의존:
init-apisix-net.service(oneshot) →docker-apisix-etcd.service;apisix-render-config.service(oneshot) +init-apisix-net.service→docker-apisix.service - 플러그인: real-ip, cors, ip-restriction, proxy-rewrite, response-rewrite, redirect, limit-*, prometheus, http-logger, file-logger 등 (config.yaml 참조). crowdsec-bouncer 플러그인은 미포함 (커스텀 lua 이전 필요 시 별도 작업).
global_rules
real-ip— source:http_cf_connecting_ip, trusted: Cloudflare IP 대역http-logger→ CrowdSechttp://10.253.100.240:8085/apisix-logs(auth:apisix-crowdsec-log-2024)
커널 튜닝
/etc/sysctl.d/99-apisix-tuning.conf 적용 완료:
- TCP BBR, conntrack 262144, fin_timeout 10s, keepalive 300s
- syncookies, netdev_backlog 16384, port range 1024-65535
- fs.file-max 1048576
Cloudflare 계정 (Netbis)
| 항목 | 값 |
|---|---|
| 이메일 | netbis@netbis.io |
| Account ID | 8fcf3c7876332aba33e974cbbfdad951 |
| Global API Key | Vault secret/cloud/cloudflare-netbis (global_api_key) |
| Linode API Key | e7cd3103ca76b865df2533b32eee5c8d7799c963fb29848274245dee142d21b0 |
| API Token (바운서용) | crowdsec-cf-bouncer-netbis (Workers, Turnstile, WAF, Zone 권한) |
| Workers 플랜 | Paid ($5/월, 1000만 요청 포함) |
Zone 목록
| Zone | Zone ID | 플랜 | 현재 오리진 |
|---|---|---|---|
| fall-vip.com | 662312b0ca619d1d5c8f4c112150d749 | Pro | 42.125.196.86 |
| fall-mvp.com | 6c171579912a271c0fc89c8187493b0f | Free | 139.162.73.240 |
| fall-vip7.com | a8832b9d3b546f96505abeadea4750d1 | Free | 139.162.73.240 |
| psd777.com | a14533c2937b19e5b7ed19cbecd58679 | Pro | 139.162.114.197 |
| rss-555.com | 6d4b084940520c1f820927e5d8ade2c6 | Pro | 139.162.73.17 |
| rss-7790.com | d9db9e50e202339326498baa340a9d16 | Pro | 139.162.73.17 |
모든 zone은 Cloudflare Flexible SSL, 프록시(오렌지 구름) 사용.
라우트 및 업스트림
| 라우트 ID | 호스트 | 업스트림 (오리진 IP) |
|---|---|---|
| fall-vip-com | fall-vip.com | 42.125.196.86 |
| fall-mvp-com | fall-mvp.com | 139.162.73.240 |
| fall-vip7-com | fall-vip7.com | 139.162.73.240 |
| psd777-com | psd777.com | 139.162.114.197 |
| rss-555-com | rss-555.com | 139.162.73.17 |
| rss-7790-com | rss-7790.com | 139.162.73.17 |
| ev-fall-vip-com | ev.fall-vip.com | 42.125.196.115 |
| ev-fall-vip7-com | ev.fall-vip7.com | 42.125.196.115 |
| ev-psd777-com | ev.psd777.com | 42.125.196.115 |
| vi-rss-555-com | vi.rss-555.com | 42.125.196.115 |
| vi-rss-7790-com | vi.rss-7790.com | 42.125.196.115 |
모든 라우트에 proxy-rewrite (Host 전달) 적용.
SSL 인증서
acme.sh + Cloudflare DNS-01 챌린지로 발급. 크론 자동 갱신.
| 도메인 | SAN | 발급기관 | 만료 |
|---|---|---|---|
| fall-vip.com | *.fall-vip.com | ZeroSSL | 2026-07-02 |
| fall-mvp.com | *.fall-mvp.com | ZeroSSL | 2026-07-02 |
| fall-vip7.com | *.fall-vip7.com | ZeroSSL | 2026-07-02 |
| psd777.com | *.psd777.com | ZeroSSL | 2026-07-02 |
| rss-555.com | *.rss-555.com | ZeroSSL | 2026-07-02 |
| rss-7790.com | *.rss-7790.com | ZeroSSL | 2026-07-02 |
인증서 경로: /root/.acme.sh/{domain}_ecc/
CrowdSec 연동
http-logger
APISIX global_rule로 모든 요청 로그를 CrowdSec(jp1)로 전송.
- 엔드포인트:
http://10.253.100.240:8085/apisix-logs - 인증:
auth_header: apisix-crowdsec-log-2024 - 파서:
custom/apisix-json-logs(기존 파서 공유)
Cloudflare Worker Bouncer (netbis-cf)
| 항목 | 값 |
|---|---|
| 컨테이너 | jp1 incus netbis-cf-bouncer (10.253.103.33) |
| 바운서 이름 | netbis-cf (CrowdSec LAPI 등록) |
| LAPI 키 | FR/PbHA110b6+m/gkByp9itNOaQMdbM8BwKR3DerCTI |
| 설정 파일 | /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml |
| 동작 | CrowdSec ban → Cloudflare Worker + Turnstile captcha |
| 적용 범위 | Netbis 계정 전체 6개 zone |
기존 cs-cf-worker-bouncer (Kappa 계정용)와 별도 컨테이너로 분리 운영.
Cloudflare 보안 설정
Rate Limiting (120/분)
| Zone | 제한 | 액션 | 차단시간 |
|---|---|---|---|
| fall-vip.com | 120/분 | managed_challenge | 60초 |
| psd777.com | 120/분 | managed_challenge | 60초 |
| rss-555.com | 120/분 | managed_challenge | 60초 |
| rss-7790.com | 120/분 | managed_challenge | 60초 |
| fall-mvp.com | 20/10초 (~120/분) | block (Free 제한) | 10초 |
| fall-vip7.com | 20/10초 (~120/분) | block (Free 제한) | 10초 |
정상 사용자 IP당 ~10 req/분 기준, 12배 여유. 공격 IP(230+/분)는 확실히 차단.
Super Bot Fight Mode
Pro zone 4개(fall-vip.com, psd777.com, rss-555.com, rss-7790.com)에 적용:
- Definitely automated → managed_challenge
- Verified bots → allow
- Static resource protection → true
Free zone(fall-mvp.com, fall-vip7.com)은 미적용.
DDoS Protection
기본 활성화 상태 (Cloudflare managed ruleset). 감도는 기본값(Medium).
트래픽 기준
일 평균 약 140만 요청. 월 환산 약 4200만.
공격 이력: ../history/2026-03-31-netbis-ddos-attack
로그 분석
사용 가능
- CF GraphQL Analytics API — 시간별/국가별/threat 데이터 조회 (보관 30일)
- APISIX http-logger → CrowdSec — 오리진 도달 요청 분석
Logpush (Enterprise 전용, 현재 불가)
Cloudflare Logpush(HTTP 요청 로그를 R2 등으로 전송)는 Enterprise 플랜 전용. Pro 플랜에서는 사용 불가.
Workers Logpush (준비만 완료)
Workers Paid에 포함. CrowdSec Worker Bouncer 요청 로그를 R2에 저장 가능.
- R2 버킷
cf-logs생성 완료 (APAC region) - R2 API Token:
r2-logpush-cf-logs(id: 6450de0b6fc95f6d47affa8be3804a75) - 비용: 월 2000만 건 포함, 초과분 $0.60/100만 건
- 정상 트래픽 기준 약 $13/월 추가 발생 예상
- 공격 시 비용 폭증 가능 → 필요 시에만 활성화 권장, 현재 미활성
Workers 비용 예상
| 시나리오 | 월 요청 | Workers 비용 |
|---|---|---|
| 정상 | ~4200만 | ~$14.6 |
| 폭주 포함 | ~1.6억 | ~$50 |
| Rate Limit 적용 후 정상 | ~4200만 | ~$14.6 |
| Rate Limit 적용 후 폭주 | 대폭 감소 예상 | ~$15-20 |
NPM 서버 (오리진)
| 호스트명 | IP | 비고 |
|---|---|---|
| NPM-1 | 172.104.100.11 | |
| NPM-2 | 139.162.114.197 | SSH 비밀번호 인증 → 키 등록 완료 |
| NPM-3 | 139.162.73.17 | rss-555.com, rss-7790.com 오리진 |
| NPM-4 | 139.162.73.240 | fall-vip.com, fall-mvp.com, fall-vip7.com 오리진 |
| NPM-5 | 172.104.70.137 | |
| npm-6 | 172.105.226.218 |
6대 모두 커널 튜닝 완료 (/etc/sysctl.d/99-proxy-tuning.conf):
- TCP BBR, conntrack 262144, fin_timeout 10s, keepalive 300s, port range 1024-65535
- limits.conf nofile 655360 (Docker 컨테이너 반영은 compose ulimits 추가 필요, 서비스 중이라 미적용)
NPM-4 추가 튜닝
- 커널: tcp_tw_reuse=1, rmem_max/wmem_max 16MB, tcp_max_tw_buckets 131072, tcp_max_orphans 32768
- Nginx: worker_connections 10240, proxy_buffers 16 32k, keepalive_requests 1000, open_file_cache
- real_ip_header: CF-Connecting-IP (컨테이너 내 sed, 재시작 시 초기화 주의)
로그 수집 (Vector → zlambda → VictoriaLogs)
6대 모두 호스트-레벨 Vector 0.55 가 NPM 로그 파일을 tail → zlambda(vector-relay 0.45, 컨테이너) → VictoriaLogs(vl.inouter.com). 2026-04-23 구축.
NPM-1..6 호스트 Vector(0.55)
source: file tail /root/data/logs/proxy-host-*_access.log 등 (NPM-6만 /home/kaffa/npm/data/log/)
transform: remap (VRL, NPM proxy/standard log 포맷 파싱 → ip/method/path/status/bytes/user_agent/referer/domain/upstream)
sink: http POST http://139.162.71.52:9999/ (zlambda public IP) basic auth (user=npm-relay, password=zlambda agenix bearer)
└─ zlambda Vector-relay(0.45, Docker, net vector-net)
source: http_server 0.0.0.0:9999 basic auth
transform: remap — `.relay = "zlambda"` 태그
sink: elasticsearch bulk https://vl.inouter.com/insert/elasticsearch
└─ VictoriaLogs (index `npm-netbis`, stream fields: host, service, log_type)
| 항목 | 값 |
|---|---|
| NPM Vector 설치 | sh.vector.dev 공식 스크립트 → /usr/local/bin/vector, systemd unit vector.service |
| NPM Vector 설정 | /etc/vector/vector.yaml (mode 600, bearer 평문 포함), checkpoints /var/lib/vector/npm_{access,error}/ |
| 라벨 | host=npm-1..6, service=npm, `log_type=access |
| 파싱 포맷 | 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 기능으로 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 |
| Linode 방화벽 (zlambda 691875) | inbound allow TCP 9999 from 6 NPM /32 IPs (allow-npm-relay-9999 rule) |
| VL 샘플 쿼리 | service:npm host:"npm-4" / service:npm zone:"fall-vip.com" / service:npm log_type:error |
| 로그 이력 | [[../history/2026-04-23-netbis-npm-vl-collection |
유사시 전환 절차
- Cloudflare DNS에서 각 도메인 A 레코드를
139.162.71.52로 변경 (수동) - APISIX 라우트/SSL 사전 등록 완료 상태이므로 즉시 서비스 가능
- 전환 후 CrowdSec 로그 수신 및 바운서 차단 자동 동작 확인
이전 서비스 제거 이력: ../history/2026-04-08-zlambda-nixos-migration
부트스트랩 체크리스트 (재가동 시)
DR 활성화 전에 필요한 작업:
- Linode 방화벽/네트워크: Cloudflare IP 대역이 80/443에 도달하는지 확인
- acme.sh 재설치 (NixOS 환경에서
services.acme또는 docker로 별도 실행). 기존 cert 경로/root/.acme.sh/{domain}_ecc/는 삭제됨 - 6개 도메인 × wildcard cert 재발급 (ZeroSSL, Cloudflare DNS-01)
- admin API로 11개 라우트 + 업스트림 + SSL 등록 (위 "라우트 및 업스트림" 표 참고)
- global_rules: real-ip(Cloudflare IPs) + http-logger(CrowdSec jp1 10.253.100.240)
- CrowdSec LAPI 키 재확인 — netbis 전용 바운서용 (vault에 저장)
- Cloudflare DNS 전환 (각 zone A 레코드 → 139.162.71.52)
crowdsec-bouncer 플러그인이 필요하면 osaka에서 crowdsec-bouncer.lua 파일 가져와 apisix.nix 볼륨에 추가하고 config.yaml plugins에 등록.