- infra/compute/infra-hosts.md: jp1 default 20→19, cs-cf-worker-bouncer 컨테이너 라인에서 제거 - services/bunnycdn-security.md: Edge Script 64811 / bloom filter / 국가차단 / Turnstile inouter-bunny 폐기 반영. 현재 layer (Bunny Shield + Rate Limit + 대역폭 한도) 중심 재작성 - infra/network/apisix.md: Edge Script 64811 attach 라인 폐기 표시 - infra/security/cloudflare.md: Workers 인벤토리 + Worker 라우트 + CF proxy 패턴 + cfb-manager 절 모두 폐기 반영 - infra/security/crowdsec-safeline.md: cs-cf-worker-bouncer 운영 중 문장 폐기 표시 - ops-agents/overview.md: Syn 영역 정의에서 폐기 자산 명시 - history/_index.md: 누락된 2026-04-25-netbis-npm-vector-msg-rewrite, 2026-04-26-bouncer-consolidation 등록 + frontmatter updated
14 KiB
title, updated
| title | updated |
|---|---|
| APISIX 설정 및 운영 | 2026-04-04 |
아키텍처
서울 K3s 클러스터 구성: 고객 도메인 → [cloudflare] → OpenWrt HAProxy (80/443) → Traefik MetalLB 192.168.9.53 → K3s 서비스 → pods
오사카 구성: 고객 도메인 → [cloudflare] → crowdsec-safeline → APISIX(라우팅) → 고객 오리진
3개의 독립 APISIX 인스턴스.
서울 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 포트 우회)
- 주의: config.yaml의 stream_proxy.tcp에 privileged 포트(1-1023)를 넣으면 비특권 컨테이너에서 bind 실패로 크래시
포트 포워딩 (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(iron-jp, ID 5555247) → 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(:9080/:9443) → MetalLB 192.168.9.50(80/443) → APISIX(replica 2) → K3s 서비스
- 용도: Traefik(VIP 192.168.9.53)과 동등한 병렬 독립 LoadBalancer gateway. "SafeLine WAF 전용" 이 아니라, 2026-03-25 에 메인 HTTP 라우팅을 Traefik 으로 이전한 이후 현재 APISIX 에는
juiceshop.keepanker.cv1건만 남아 있고 그것이 chaitin-waf 플러그인으로 SafeLine 통합 테스트 용도라 결과적으로 현재만 SafeLine 테스트 전용처럼 보일 뿐 — 언제든 새 ApisixRoute 추가 가능한 범용 gateway. - 클러스터: K3s 새 클러스터 (Supabase PostgreSQL 백엔드, kr2+kr1+hp2)
- 배포: K3s apisix 네임스페이스, Deployment replica 2
- APISIX: 3.15.0-ubuntu
- SafeLine WAF 연동:
plugin_attr.chaitin-waf→safeline-detector:8000(10.43.253.244) - global_rules:
http-logger(CrowdSec 로그 전송) +limit-req(rate 20, burst 10).chaitin-waf는 global_rule에 없음 — 라우트별 적용. 두 global_rule 모두 GatewayProxy CR(spec.plugins)로 선언되어 ingress controller가 관리. - 서비스: apisix-gateway LoadBalancer 192.168.9.50 (80→9080, 443→9443)
- etcd: K3s 내부 apisix-etcd StatefulSet 3 replicas (Bitnami etcd 차트, Longhorn PVC 5Gi × 3), prefix
/apisix. ClusterIPapisix-etcd.apisix.svc.cluster.local:2379. - Admin API:
apisix-adminClusterIP :9180 (adminKey: edd1c9f034335f136f87ad84b625c8f1). admin allow IPs:127.0.0.1/24,10.42.0.0/16(pod),10.43.0.0/16(svc),192.168.9.0/24(LAN),100.64.0.0/10(Tailscale) - HAProxy: OpenWrt에서 :9080→192.168.9.50:80, :9443→192.168.9.50:443 (MetalLB)
plugin_metadata (GatewayProxy CR로 관리)
chaitin-waf 플러그인은 plugin_attr(config.yaml)이 아닌 **plugin_metadata(etcd)**에서 detector 노드를 읽음. 옛날에는 etcd에 직접 PUT했으나, 2026-04-08부터 ingress controller가 destructive sync로 미관리 객체를 1분마다 삭제하므로 반드시 GatewayProxy CR의 spec.pluginMetadata로 선언해야 함. helm values의 gatewayProxy.pluginMetadata 항목 참고.
등록된 라우트 / TLS (ApisixRoute / ApisixTls CRD)
모든 라우트와 SSL은 K8s CRD로 선언됨. ingress controller가 watch하여 APISIX admin API로 push.
| Kind | Name | Namespace | 설명 |
|---|---|---|---|
| ApisixRoute | juiceshop | juiceshop | juiceshop.keepanker.cv → juiceshop:3000, plugins.chaitin-waf (block) |
| ApisixTls | wildcard-keepanker-cv | apisix | *.keepanker.cv + keepanker.cv, secret wildcard-keepanker-cv-tls (cert-manager 발급) |
⚠️ etcd 직접 등록 금지 (1분 이내 controller가 삭제). 모든 신규 객체는 CRD로 선언해야 함.
real_ip 설정 (2026-04-08 patch 반영)
⚠️ helm values로는 반영 안 됨 — APISIX helm chart의 nginx.http.realIpFrom/realIpHeader 옵션이 K3s에 배포된 차트(2.13.0) 구조에서 nginx_config.http로 전달이 안 됨. 그래서 cm/apisix 직접 patch가 필요하고, helm upgrade 시 reset됨.
현재 적용 값 (cm/apisix config.yaml):
nginx_config:
http:
real_ip_header: "X-Forwarded-For"
real_ip_from:
- 0.0.0.0/0 # BunnyCDN/OpenWrt/K3s 모든 hop 신뢰 (Tailnet+LAN+CDN 경유)
real_ip_recursive: "on"
access_log_format: '$remote_addr - $remote_user [$time_local] $http_host \"$request\" $status $body_bytes_sent $request_time \"$http_referer\" \"$http_user_agent\" $upstream_addr $upstream_status $upstream_response_time \"$upstream_scheme://$upstream_host$upstream_uri\" xff=\"$http_x_forwarded_for\" xrip=\"$http_x_real_ip\"'
→ access log의 첫 필드 $remote_addr이 X-Forwarded-For에서 추출한 진짜 클라이언트 IP가 됨. 끝에 xff="..." xrip="..." 디버그 필드 추가.
적용 절차:
kubectl get cm apisix -n apisix -o jsonpath='{.data.config\.yaml}' > /tmp/apisix-config.yaml
# /tmp/apisix-config.yaml 편집 (real_ip_*, access_log_format)
kubectl create cm apisix --from-file=config.yaml=/tmp/apisix-config.yaml -n apisix --dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart deploy/apisix -n apisix
⚠️ Vector parse_apisix 정규식과 짝: APISIX log format을 변경할 때마다 vector helm values의 parse_apisix 정규식도 같이 업데이트해야 victorialogs에 구조화 필드가 정상 추출됨. 현재 정규식은 xff/xrip 필드를 optional 그룹으로 처리.
Ingress Controller 설정
apisix-ingress-controller Helm release (chart 1.1.2, controller v2.0.1). GatewayProxy 모드에서 ApisixRoute CRD(v2)도 정상 동작. ApisixRoute에 ingressClassName: apisix만 명시하면 controller가 자동으로 admin API에 push.
helm values 핵심:
gatewayProxy:
createDefault: true
provider:
type: ControlPlane
controlPlane:
service:
name: apisix-admin
port: 9180
auth:
type: AdminKey
adminKey:
value: "edd1c9f034335f136f87ad84b625c8f1"
config:
disableGatewayAPI: false
kubernetes:
ingressClass: apisix
라우팅 전환/복구 이력: 2026-03-25-apisix-to-traefik-routing
ApisixRoute 예시 (라우트별 chaitin-waf):
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: <name>
namespace: <ns>
spec:
ingressClassName: apisix
http:
- name: rule1
match:
hosts: [<host>]
paths: ["/*"]
backends:
- serviceName: <svc>
servicePort: <port>
plugins:
- name: chaitin-waf
enable: true
config:
mode: block
append_waf_resp_header: true
WAF가 문제 시 plugins 항목만 빼면 즉시 비활성화됨.
Destructive sync 주의사항 (v2.x controller)
새 controller는 CRD/GatewayProxy로 선언되지 않은 모든 APISIX 객체를 1분마다 자동 삭제함. 영향:
| 객체 | 관리 방법 |
|---|---|
| routes | ApisixRoute CRD (또는 HTTPRoute) |
| upstreams | ApisixUpstream CRD |
| ssls | ApisixTls CRD |
| consumers | ApisixConsumer CRD |
| plugin_metadata | GatewayProxy.spec.pluginMetadata (helm values) |
| global_rules | GatewayProxy.spec.plugins (helm values) |
⚠️ etcd 직접 PUT은 임시 디버깅 외에는 금지. 1분 안에 삭제됨. 옛 운영 메모에 있는 etcdctl put 예제는 모두 deprecated.
etcd 설정
K3s 내부 apisix-etcd StatefulSet 3 replicas (Bitnami etcd, Longhorn PVC 5Gi x 3, ClusterIP apisix-etcd.apisix.svc:2379, prefix /apisix).
helm values 핵심:
etcd:
enabled: true
replicaCount: 3
persistence:
enabled: true
size: 5Gi
업그레이드 시 주의:
- Bitnami etcd 차트의 pre-upgrade hook 실패 시
helm upgrade --no-hooks사용 - helm upgrade 전 release values nesting 점검 필수:
apisix.admin.allow.ipList(O) vsadmin.allow.ipList(X) 등. 잘못된 nesting은 차트가 조용히 무시함. - ingress controller는 자동으로 모든 K8s CRD 객체를 etcd에 재push (rollout restart로 즉시 동기화)
etcd 통합/분리 이력: 2026-04-06-apisix-etcd-consolidation
BunnyCDN Pull Zone 매핑 (2026-04-09 API 실측)
| Zone | ID | Origin | 방향 | Hostnames (전수) |
|---|---|---|---|---|
| iron-kr | 5555227 | 220.120.65.245 | → 서울 (OpenWrt → Traefik) | iron-kr.b-cdn.net, actions.it.com, n8n.inouter.com, jarvis.inouter.com, telegram-webhook.inouter.com, vault.inouter.com, outline.inouter.com (사용자 6 + 시스템 1 = 7) |
| iron-jp | 5555247 | 172.233.93.180 | → 오사카 (osaka-gw APISIX) | iron-jp.b-cdn.net, anvil.it.com, n8n.anvil.it.com, tg.anvil.it.com, linode.actions.it.com (사용자 4 + 시스템 1 = 5) |
| iron-kr-waf | 5555224 | 220.120.65.245:9443 | → 서울 (HAProxy :9443 → K3s APISIX SafeLine WAF) | iron-kr-waf.b-cdn.net, juiceshop.keepanker.cv |
| iron-git | 5584382 | 220.120.65.245 | → 서울 (gitea, 미들웨어 미장착) | iron-git.b-cdn.net, gitea.inouter.com |
| i-gate | 5557897 | 172.233.93.180 | (미사용 슬롯) | i-gate.b-cdn.net |
참고:
- iron-kr / iron-jp 모두
IgnoreQueryStrings: false(통일) - iron-kr / iron-jp
BlockNoneReferrer: true, iron-git 는false(git smart HTTP 호환) - 모든 zone
EdgeRules: [](Edge Rules 미사용) Edge Script 64811: 2026-04-26 bouncer 단일화로 폐기. 전 풀존crowdsec-bouncer-middlewareattachMiddlewareScriptId: null(../../history/2026-04-26-bouncer-consolidation)- gitea.inouter.com 은 더 이상 iron-kr 가 아니라 iron-git 풀존 소속 (분리됨)
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 우회 처리.
CrowdSec 로그 연동
오사카/서울 양쪽 APISIX → CrowdSec (10.253.100.240:8085) http-logger로 전송. 서울은 global_rule, 오사카는 글로벌 적용.
인증: auth_header: apisix-crowdsec-log-2024 (주의: headers 필드가 아님)
커스텀 파서: custom/apisix-json-logs (403 응답만 필터)
시나리오 매칭으로 반복 공격자 탐지.
과거 트러블슈팅 이력: 2026-03-15-apisix-git-push-500
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.