10 KiB
title, updated
| title | updated |
|---|---|
| APISIX 설정 및 운영 | 2026-03-27 |
아키텍처
서울 K3s 클러스터 구성: 고객 도메인 → [cloudflare] → OpenWrt HAProxy (80/443) → Traefik MetalLB 192.168.9.53 → 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 (포트 2222, Vault CA 인증서)
- 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 미사용)
- 방화벽: 22/tcp + 2201-2299/tcp + 443/tcp 개방, SSH는 Tailscale 경유 포트 2222
- 22 → iptables REDIRECT → 9022 (SFTPGo용, privileged 포트 우회)
- 443 → iptables REDIRECT → 8443 (Teleport용, privileged 포트 우회)
- 2026-03-17 AWS EC2에서 Lightsail nano($5/월)로 이전
- 주의: config.yaml의 stream_proxy.tcp에 privileged 포트(1-1023)를 넣으면 비특권 컨테이너에서 bind 실패로 크래시. 2026-03-27 포트 22 추가로 장애 발생, 제거하여 복구
포트 포워딩 (stream_routes)
| 포트 | 용도 | upstream | 비고 |
|---|---|---|---|
| 9022 (외부 22) | SFTPGo SFTP | 192.168.9.55:22 | K3s MetalLB → SFTPGo, iptables 22→9022 리다이렉트 |
| 2201 | inbest SSH | 10.100.1.158:22 | inbest 전용 SSH 포트, OpenWrt Tailscale 광고 경유 |
| 2202 | Gitea SSH | 192.168.9.54:22 | K3s MetalLB → Gitea SSH |
| 8443 (외부 443) | Teleport | 192.168.9.52:443 | K3s MetalLB → Teleport proxy, iptables 443→8443 리다이렉트 |
오사카 (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 네임스페이스) — SafeLine WAF 전용
외부 → OpenWrt HAProxy(:9080/:9443) → MetalLB 192.168.9.50(80/443) → APISIX(replica 1) → K3s 서비스
- 용도: SafeLine WAF 전용 리버스 프록시 (메인 라우팅은 Traefik으로 이전)
- 클러스터: K3s 새 클러스터 (Supabase PostgreSQL 백엔드, kr2+kr1+hp2)
- 배포: K3s apisix 네임스페이스, Deployment replica 1 (축소됨)
- APISIX: 3.15.0-ubuntu
- SafeLine WAF 연동:
plugin_attr.chaitin-waf→safeline-detector:8000(10.43.253.244) - global_rules:
chaitin-waf(mode: block) +limit-req(rate 20, burst 10) - 서비스: apisix-gateway LoadBalancer 192.168.9.50 (80→9080, 443→9443)
- etcd: apisix-etcd StatefulSet (K3s 내부)
- Admin API:
apisix-adminClusterIP :9180 (라우트/upstream/SSL 수동 관리) - HAProxy: OpenWrt에서 :9080→192.168.9.50:80, :9443→192.168.9.50:443 (MetalLB)
- 2026-03-25 메인 라우팅 역할을 Traefik으로 이전, APISIX는 SafeLine WAF 전용으로 축소
- Ingress Controller + Gateway API CRD 제거됨 (GatewayProxy 모드에서 ApisixRoute CRD 미지원, HTTPRoute에 플러그인 개별 적용 불가 문제)
plugin_metadata (etcd 직접 등록)
chaitin-waf 플러그인은 plugin_attr(config.yaml)이 아닌 **plugin_metadata(etcd)**에서 detector 노드를 읽음. 반드시 etcd에 등록해야 함.
etcdctl put /apisix/plugin_metadata/chaitin-waf '{"id":"chaitin-waf","nodes":[{"host":"10.43.253.244","port":8000}],"config":{"connect_timeout":1000,"send_timeout":1000,"read_timeout":1000,"req_body_size":1024,"real_client_ip":true}}'
등록된 라우트 (etcd 직접 등록)
| Route ID | 호스트 | upstream | chaitin-waf | 비고 |
|---|---|---|---|---|
| juiceshop | juiceshop.keepanker.cv | juiceshop:3000 (juiceshop ns) | block | WAF 테스트용 OWASP Juice Shop |
real_ip 설정 (ConfigMap 직접 수정)
real_ip_header: X-Forwarded-For, real_ip_from: 0.0.0.0/0 (BunnyCDN 경유이므로 전체 허용). Helm values로는 반영 안 되어 ConfigMap 직접 패치.
이전 사유 (2026-03-25)
- Ingress Controller 2.0 GatewayProxy 모드가 ApisixRoute CRD를 sync하지 않음
- Gateway API HTTPRoute에 플러그인 개별 적용 방법이 없음
- global_rules를 Ingress Controller가 덮어쓰려는 충돌 발생
- → Traefik으로 메인 라우팅 교체, APISIX는 SafeLine WAF 연동 전용으로 유지
BunnyCDN Pull Zone 매핑
| Zone | ID | Origin | 방향 | 주요 Hostnames |
|---|---|---|---|---|
| iron-jp | 5555247 | 172.233.93.180 | → 오사카 | inouter.com, n8n.inouter.com, tg.inouter.com, linode.actions.it.com |
| iron-kr | 5555227 | 220.120.65.245 | → 서울 (Traefik) | actions.it.com, gitea.inouter.com, n8n.inouter.com, jarvis.inouter.com, telegram-webhook.inouter.com, vault.inouter.com |
| iron-kr-waf | 5555224 | 220.120.65.245:9443 | → 서울 (APISIX WAF) | juiceshop.keepanker.cv |
참고: iron-kr zone은 DisableCookies: false (쿠키 허용). Gitea 웹 로그인 세션에 필요.
Edge Script: iron-jp, iron-kr에 CrowdSec bouncer middleware (ID 64811) 연결.
DNS 참고
*.inouter.com와일드카드 CNAME →iron-jp.b-cdn.net(오사카)- iron-kr zone 호스트는 전용 CNAME 필수 (와일드카드 오버라이드)
gitea.inouter.comCNAME →iron-kr.b-cdn.net
hcv.inouter.comCNAME →k3s.inouter.com(LAN 직접, BunnyCDN 우회)nocodb.inouter.comCNAME →k3s.inouter.com(LAN 직접, BunnyCDN 우회)- K3s CoreDNS:
gitea.inouter.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.inouter.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.