Files
obsidian/infra/apisix.md

8.0 KiB

title, updated
title updated
APISIX 설정 및 운영 2026-03-18

아키텍처

서울 K3s 클러스터 구성: 고객 도메인 → [cloudflare] → OpenWrt HAProxy (80/443) → APISIX hostNetwork (9080/9443, 3노드) → K3s 서비스 → pods

오사카 구성: 고객 도메인 → [cloudflare]crowdsec-safeline → APISIX(라우팅) → 고객 오리진

3개의 독립 APISIX 인스턴스. kr1의 Docker APISIX는 2026-03-15 제거 완료.

서울 relay (relay4wd)

  • 용도: 포트 포워딩 게이트웨이
  • 호스트: relay4wd (AWS Lightsail, Tailscale 100.103.161.4, 공인 52.79.45.166, Debian 12), Docker APISIX 3.15.0, SSH: admin + lightsail.pem
  • Admin API: http://100.103.161.4:9180 (Tailscale 100.64.0.0/10에서만 접근 가능)
  • Admin Key: edd1c9f034335f136f87ad84b625c8f1
  • etcd: incus-jp1 db 프로젝트 etcd-1 (10.253.102.11:2379), prefix /apisix-sandbox
  • 설정 파일: /opt/apisix/ (config.yaml, docker-compose.yml)
  • 모드: stream only (HTTP proxy 비활성화, 9080 미사용)
  • 방화벽: 2201-2299/tcp만 개방, SSH는 Tailscale 경유
  • 2026-03-17 AWS EC2에서 Lightsail nano($5/월)로 이전

포트 포워딩 (stream_routes)

포트 용도 upstream 비고
2201 inbest SSH 10.100.1.158:22 inbest 전용 SSH 포트, OpenWrt Tailscale 광고 경유

오사카 (apisix-osaka)

BunnyCDN(inouter, ID 5316471) → apisix-osaka(172.233.93.180) → 백엔드
  • 용도: Ironclad 인프라 서비스 (ironclad.it.com, n8n, twilio 등)
  • Docker: APISIX 3.15.0 (waf-apisix) + etcd v3.5.11 (waf-etcd)
  • 보안: SafeLine WAF + CrowdSec 연동
  • upstream: incus-jp1 내부(10.253.x), K3s Traefik

서울 (K3s, apisix 네임스페이스)

인터넷 → OpenWrt HAProxy(:80/:443) → 3노드 hostNetwork(:9080/:9443) → APISIX → K3s 서비스 → pods
  • 용도: KR존 리버스 프록시
  • 배포: K3s apisix 네임스페이스, hostNetwork 모드로 3노드(hp2/kr1/kr2)에 배포
  • upstream: K3s Traefik (192.168.9.134/214/135:443), LAN 서비스(192.168.9.x)
  • SSL: cert-manager wildcard *.inouter.com 인증서
  • Admin API: apisix-admin ClusterIP 서비스 (10.43.70.216:9180)
  • etcd: apisix-etcd StatefulSet (K3s 내부, ClusterIP 10.43.20.100:2379)
  • NodePort: apisix-gateway 80:32020/TCP, 443:30675/TCP (HAProxy는 hostNetwork :9080/:9443 직접 사용)
  • 이전: kr2 incus 컨테이너(10.179.99.126)에서 K3s로 이전

global_rules

  • real-ip — source: http_x_real_ip, trusted: 0.0.0.0/0
  • http-logger → CrowdSec (10.253.100.240:8085, auth_header: apisix-crowdsec-log-2024)

라우트 및 플러그인

라우트 ID 호스트 upstream chaitin-waf limit-req (rate/burst)
gitea-anvil-it-com gitea.anvil.it.com K3s Traefik :443 (roundrobin) block, match: .git/ 제외 50/30
hcv-inouter-com hcv.inouter.com K3s Traefik :443 (roundrobin) block 20/10
nocodb nocodb.inouter.com K3s Traefik :443 (roundrobin) block 100/50
nocodb-nuxt nocodb.inouter.com K3s Traefik :443 (roundrobin) block 100/50

BunnyCDN Pull Zone 매핑

Zone Origin 방향 주요 Hostnames
inouter (5316471) 172.233.93.180 → 오사카 anvil.it.com, vault.inouter.com, n8n, kroki, tg.anvil.it.com
actions (5330178) 220.120.65.245 → 서울 actions.it.com, gitea.anvil.it.com

참고: actions zone은 DisableCookies: false (쿠키 허용, 2026-03-15). Gitea 웹 로그인 세션에 필요.

DNS 참고

  • *.anvil.it.com 와일드카드 CNAME → inouter.b-cdn.net (오사카)
  • actions zone으로 이전한 호스트는 전용 CNAME 필수 (와일드카드 오버라이드)
    • gitea.anvil.it.com CNAME → actions.b-cdn.net
  • hcv.inouter.com CNAME → k3s.inouter.com (LAN 직접, BunnyCDN 우회)
  • nocodb.inouter.com CNAME → k3s.inouter.com (LAN 직접, BunnyCDN 우회)
  • K3s CoreDNS: gitea.anvil.it.com → Traefik ClusterIP (10.43.205.207) 헤어핀 방지

ironclad.it.com 라우트

ironclad.it.com Cloudflare DNS origin: 172.233.93.180 (osaka), zone ID: bc8761b398cc52cf731f804bd3cbf388. APISIX 라우트 ironclad-it-com → web 컨테이너 10.253.100.159:80. SSL: Google Trust Services wildcard cert (*.ironclad.it.com) in APISIX. → cert-manager

SSL ID 규칙

APISIX SSL ID는 도메인 MD5 해시 앞 16자리

플러그인

APISIX 연동: ip-restriction + geoip-restriction 플러그인

Twilio 라우트

APISIX 라우트 ID: twilio-jp-inouter-com → twilio

Gitea POST 변환

gitea가 POST 미지원(AuthenticateNotImplemented, 404)하므로 APISIX에서 POST body 파라미터를 GET query string으로 변환

hcv.inouter.com 라우트

APISIX 서울 라우트 hcv-inouter-com → K3s Traefik (192.168.9.134/214/135:443, roundrobin, scheme https). upstream ID: hcv-inouter-com. vault UI/API 서빙. DNS: k3s.inouter.com CNAME (LAN 직접 연결, BunnyCDN 우회). BunnyCDN pull zone actions (ID 5330178)에 hostname 등록. 오사카에서 서울로 이전 (2026-03-15).

nocodb.inouter.com 라우트

트래픽 흐름: Cloudflare DNS (A 220.120.65.245, BunnyCDN 우회) → OpenWrt(:443) → hp2(:9443, incus proxy device) → APISIX(10.179.99.126, 라우트 nocodb/nocodb-nuxt) → K3s Traefik (192.168.9.134/214/135:443, roundrobin, scheme https) → nocodb svc:8080 (namespace tools).

BunnyCDN WAF가 NocoDB JS를 오탐 차단하여 CDN 우회 처리 (2026-03-15).

CrowdSec 로그 연동

오사카/서울 양쪽 APISIX → CrowdSec (10.253.100.240:8085) http-logger로 전송. 서울은 global_rule, 오사카는 글로벌 적용.

인증: auth_header: apisix-crowdsec-log-2024 (주의: headers 필드가 아님)

커스텀 파서: custom/apisix-json-logs (403 응답만 필터)

시나리오 매칭으로 반복 공격자 탐지.

트러블슈팅 기록

git push 500 에러 (2026-03-15)

증상: gitea.anvil.it.com으로 git push 시 BunnyCDN에서 403 반환 (오리진 500)

원인 1 — client_body_temp 퍼미션:

  • nginx가 큰 POST body(git pack ~20KB)를 /usr/local/apisix/client_body_temp/에 임시 파일로 쓸 때 퍼미션 거부
  • 디렉토리 소유자가 nobody인데 APISIX는 apisix 사용자로 실행
  • 작은 요청은 메모리 버퍼로 처리되어 정상, 큰 요청만 실패
  • 수정: chown apisix:apisix /usr/local/apisix/client_body_temp

원인 2 — chaitin-waf 글로벌 적용:

  • chaitin-waf가 global_rules에 있어서 모든 요청에 WAF 검사 적용
  • SafeLine detector가 git pack 바이너리를 파싱 실패 시 mode: block이면 500 반환 (fail-closed)
  • 라우트 레벨에서 match 제외를 넣어도 global_rule이 먼저 적용되어 무시됨
  • 수정: global_rules에서 chaitin-waf 제거, 각 라우트에 개별 적용. gitea 라우트는 match: [{"vars": [["uri", "!", "~*", "\\.git/"]]}]로 git 경로 WAF 제외

교훈:

  • SafeLine WAF는 바이너리 프로토콜(git pack 등)을 처리할 수 없음 → 해당 경로는 WAF에서 제외 필요
  • global_rules의 WAF는 예외 처리가 어려움 → 라우트별 개별 적용이 유연함
  • BunnyCDN errorcode: 112는 오리진 에러를 나타냄 → 실제 원인은 오리진 로그에서 확인

http-logger 401 에러 (2026-03-15)

증상: 서울 APISIX http-logger가 CrowdSec으로 로그 전송 시 401 Unauthorized

원인: http-logger 플러그인은 Authorization 헤더를 auth_header 필드로 설정해야 하는데, headers.Authorization으로 설정되어 있었음 (무시됨)

수정: "headers": {"Authorization": "..."}"auth_header": "apisix-crowdsec-log-2024"

jarvis.inouter.com 라우트

jarvis.inouter.com → APISIX(오사카) → jarvis(10.100.2.162:18789). OpenClaw 게이트웨이 웹 UI 및 webhook 엔드포인트. enable_websocket: true.

telegram-webhook.inouter.com 라우트

telegram-webhook.inouter.com → APISIX(오사카) → jarvis(10.100.2.162:8787). 텔레그램 봇 webhook 수신. Cloudflare proxied, *.inouter.com 와일드카드 SSL.