--- title: Netbis 예비서버 ([[zlambda]]) updated: 2026-04-08 APISIX NixOS oci-containers로 재가동 aliases: [netbis-sandbox-tokyo] tags: [netbis, apisix, dr, cloudflare, nixos] --- ## 개요 Netbis 팀 도메인의 예비(DR) 리버스 프록시 서버. 평소에는 트래픽을 받지 않으며, 유사시 Cloudflare DNS를 수동 전환하여 활성화. 기존 Ironclad 인프라([[apisix]], [[crowdsec-safeline]])와는 별도 구성. APISIX/etcd는 NixOS `virtualisation.oci-containers`로 기동 완료. 설정은 [[zlambda|kaffa/nixos-infra]] 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 권한 세팅) - 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` → CrowdSec `http://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 (Worker bouncer용)~~ | ~~`crowdsec-cf-bouncer-netbis`~~ → 2026-04-23 Worker bouncer 폐기 시 token 삭제됨 | | API Token (Firewall bouncer용) | Vault `secret/cloud/cloudflare-netbis` (`firewall_bouncer_token`, id `firewall_bouncer_token_id`). 2026-04-25 발급. 권한: Account Firewall Access Rules Write + Account Rule Lists Write + 6 zone Firewall Services Write | | Workers 플랜 | Paid ($5/월, 1000만 요청 포함) — 현재 미사용 (Worker bouncer 폐기 후 재구축 안 함) | ### 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 Firewall Bouncer 재구축 (2026-04-25) 2026-04-23에 두 종류 bouncer (Worker + Firewall Rule) 모두 폐기됐다가 ([[../history/2026-04-23-netbis-bouncer-removal|history]]), 2026-04-25에 **Firewall Rule bouncer만 재구축**. | 항목 | 값 | |------|-----| | 컨테이너 | jp1 incus `crowdsec` (LAPI와 동거. 별도 컨테이너 X) | | 패키지 | `crowdsec-cloudflare-bouncer 0.3.0` (apt) | | LAPI bouncer 이름 | `cs-cloudflare-bouncer-1777082222` (자동 생성) | | 설정 파일 | `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` | | origin filter | `[crowdsec, cscli]` — CAPI 30k+/lists 무시 (10k 한도 회피 핵심) | | 적용 범위 | netbis 계정 전체 6개 zone, managed_challenge 액션 | | CF 리소스 | IP List `crowdsec_managed_challenge` + 6 Zone Firewall Rule 자동 생성 | | API token | Vault `secret/cloud/cloudflare-netbis` 의 `firewall_bouncer_token`, `firewall_bouncer_token_id` | | Token 권한 | Account Firewall Access Rules Write, Account Rule Lists Write, 6 zone Firewall Services Write | | 인증 방식 | Bearer token. global_api_key는 `Invalid request headers (6003)`로 거부 | | 비용 | $0 (Free plan IP List + Firewall Rule 사용) | 2026-04-23에 Worker Bouncer (`netbis-cf`) 도 폐기됐고 **2026-04-25 재구축에는 포함 안 됨** — Worker는 트래픽 비례 비용($14~50/월 추정)이라 origin filter로 회피 불가. 상세 history: [[../history/2026-04-25-netbis-cf-firewall-rebuild|2026-04-25 재구축]] ## 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|2026-03-31 대규모 봇 공격]] ## 로그 분석 ### 사용 가능 - **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|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 기능으로 `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|history]] | ## 유사시 전환 절차 1. Cloudflare DNS에서 각 도메인 A 레코드를 `139.162.71.52`로 변경 (수동) 2. APISIX 라우트/SSL 사전 등록 완료 상태이므로 즉시 서비스 가능 3. 전환 후 CrowdSec 로그 수신 및 바운서 차단 자동 동작 확인 이전 서비스 제거 이력: [[../history/2026-04-08-zlambda-nixos-migration|history]] ## 보안 방어 개발 중 - [[../projects/netbis-sigmatch]] — VL 로그 기반 자동 시그니처 합성 + CF IP Access Rules 차단 (개발 단계, kappa 직접 진행) ## 부트스트랩 체크리스트 (재가동 시) 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에 등록.