obsidian: 정본에서 변경 이력 분리, history/ 도입
- history/README.md: 변경 이력/인시던트 기록 규약 - history/2026-04-10-edge-cleanup.md: 오늘의 엣지 정리·인시던트·복구 전체 연대기 - infra/cloudflare.md: 연대기 주석/strikethrough/인시던트 서사 제거, 현재 사실만 - infra/crowdsec-safeline.md: 인시던트 bullet 제거, 과거 이력은 history/ 참조 - services/bunnycdn-security.md: sitekey 이력표 제거, 현재 위젯 정보만 - infra/nas-storage.md: reverse proxy 섹션의 날짜 주석 제거
This commit is contained in:
225
history/2026-04-10-edge-cleanup.md
Normal file
225
history/2026-04-10-edge-cleanup.md
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
date: 2026-04-10
|
||||
topic: Edge layer cleanup (BunnyCDN+Cloudflare 전수 감사·정리·인시던트·복구)
|
||||
areas:
|
||||
- infra/cloudflare.md
|
||||
- infra/crowdsec-safeline.md
|
||||
- infra/nas-storage.md
|
||||
- services/bunnycdn-security.md
|
||||
tags: [history, edge, bunnycdn, cloudflare, crowdsec, incident]
|
||||
---
|
||||
|
||||
하루 동안 여러 건의 엣지 레이어 정리 작업이 연쇄적으로 일어남. 감사 → 1차 정리 → nas 복원 → cleanup-2 시도 → 3-incident chain → 복구 → cron 영구 제거 → iron-kr IgnoreQS 통일 순서.
|
||||
|
||||
## 1. BunnyCDN+CF 도메인 전수 감사
|
||||
|
||||
### 배경
|
||||
|
||||
Obsidian 정본의 CF/BunnyCDN 정보가 오래되어 현재 상태와 불일치. 전체 도메인 인벤토리 재구축 필요.
|
||||
|
||||
### 조치
|
||||
|
||||
Syn 가 BunnyCDN MCP + CF MCP 로 실측 조회.
|
||||
|
||||
결과: BunnyCDN 풀존 5개 / 호스트 17개 (사용자 12 + 시스템 5). CF zone 6개 / DNS 60건 / Turnstile 8개 / Workers 9개. 상세는 Outline doc `51b963c3-b251-48b5-a57a-a2305959c470`.
|
||||
|
||||
Obsidian 정본 (`infra/cloudflare.md`, `services/bunnycdn-security.md`) 을 현재 상태로 전면 재작성.
|
||||
|
||||
### 발견된 조치 필요 항목
|
||||
|
||||
- `servidor.it.com` — CF zone 이지만 DNS 0건 (orphan zone). cert-manager wildcard + CrowdSec bouncer + Turnstile 위젯 설정은 살아있음. 죽은 도메인이 아니라 "배포 준비만 된 상태" 로 판단, 유지 결정.
|
||||
- `nas.inouter.com` CNAME → `actions.b-cdn.net` — dead 풀존 참조
|
||||
- `ironclad.jp.inouter.com` A `10.19.228.193`, `k8s.jp.inouter.com` A `10.253.103.124` — 사설 IP 공개 DNS 노출
|
||||
- `*.actions.it.com` CNAME → `actions.b-cdn.net` — dead 풀존 참조
|
||||
- `inouter.com` / `anvil.it.com` CF Worker bouncer 라우트가 DNS `proxied: false` 라서 실제 enforcement 안 됨 — BunnyCDN 미들웨어 64811 단독 방어
|
||||
- `vultr.actions.it.com/*`, `linode.actions.it.com/*` Worker 라우트 `script: null` (orphan)
|
||||
- Turnstile `inouter` (`0x4AAAAAACbmaudAjITah7y7`, 2026-02-13) — 이름·도메인 불일치 (이름은 inouter 인데 도메인은 anvil.it.com). legacy 후보
|
||||
- iron-kr `IgnoreQueryStrings: true` vs iron-jp `false` — 같은 미들웨어인데 캐시 키 정책 불일치
|
||||
- BunnyCDN 미들웨어 `TURNSTILE_SITE_KEY` 는 `inouter.com` zone 단일 키 → actions / anvil / `*.b-cdn.net` 에서 ban 발동 시 캡차 로드 실패 가능
|
||||
|
||||
## 2. CF DNS 정리 1차 (`cf-audit-cleanup-1`)
|
||||
|
||||
### 조치
|
||||
|
||||
Syn 가 CF API 로 아래 레코드 삭제:
|
||||
|
||||
- `nas.inouter.com` CNAME → `actions.b-cdn.net`
|
||||
- `ironclad.jp.inouter.com` A `10.19.228.193`
|
||||
- `k8s.jp.inouter.com` A `10.253.103.124`
|
||||
|
||||
검증: `dig @1.1.1.1` 로 3건 모두 삭제 확인.
|
||||
|
||||
### 참고
|
||||
|
||||
`nas.inouter.com` 은 `*.inouter.com` 와일드카드 (`→ k3s.inouter.com → 192.168.9.53`) 로 폴백되지만 LAN 전용 MetalLB VIP 라서 외부 접근 불가 — 실질적으로 의도한 결과.
|
||||
|
||||
## 3. `nas.inouter.com` Traefik 리버스 프록시 복원
|
||||
|
||||
### 배경
|
||||
|
||||
kappa 가 nas.inouter.com 을 Synology NAS (`192.168.9.100`) 의 DSM 접근 경로로 쓰고 싶어함.
|
||||
|
||||
### 조치
|
||||
|
||||
Heimdall 이 K3s 에 resources 생성:
|
||||
|
||||
- Namespace `lan-proxies` (LAN 장비 reverse proxy 전용)
|
||||
- Selector-less Service `nas` (ClusterIP 5000/TCP) + 수동 EndpointSlice `192.168.9.100:5000`
|
||||
- Traefik IngressRoute `nas` (web) + `nas-tls` (websecure), Middleware `redirect-https`
|
||||
- TLS: 기존 `wildcard-inouter-tls` 재사용
|
||||
|
||||
### 기술 노트
|
||||
|
||||
`ExternalName` Service 는 IP 를 받지 않고 DNS name 만 받음. LAN IP reverse proxy 에는 **selector-less Service + 수동 EndpointSlice** 패턴이 정석.
|
||||
|
||||
### 검증
|
||||
|
||||
`curl -sk --resolve nas.inouter.com:443:192.168.9.53 https://nas.inouter.com/` → 200, `<title>NAS - Synology DiskStation</title>`. HTTP 301 redirect 정상.
|
||||
|
||||
## 4. CF cleanup-2 3-incident chain
|
||||
|
||||
### 배경
|
||||
|
||||
감사에서 발견한 "inouter/anvil 의 CF Worker bouncer 라우트가 dead" 항목 정리. BunnyCDN 미들웨어가 단독 방어하고 있으니 CF 측 라우트와 위젯을 제거해 관리 면적 축소 목적.
|
||||
|
||||
### 인시던트 #1 — destructive widget cleanup
|
||||
|
||||
Syn 가 `cfb-manager` API `DELETE /domains/inouter.com` 호출. cfb-manager 는 bouncer 호스트의 yaml 을 편집하고 `systemctl restart` 호출. 재기동된 bouncer 가 **destructive cleanup** 으로 모든 zone 의 Turnstile 위젯을 새로 rotate 하면서 기존 위젯을 삭제. 이 과정에서 **BunnyCDN 미들웨어 64811 이 baked-in 으로 사용 중이던 `inouter.com` zone 위젯 `0x4AAAAAAC2cntUlRC3KKMKG` 가 함께 삭제**. 잠재 버그 (현재 ban 없으면 미발현).
|
||||
|
||||
잘못된 가정: "zone 을 bouncer config 에서 빼면 위젯은 freeze 된 채 남는다" — 실제로는 bouncer restart 가 force-rotation 사이클.
|
||||
|
||||
### 인시던트 #2 — 복구 중 토큰 권한 공백
|
||||
|
||||
복구 시도. 새 Turnstile 위젯 생성 필요. Obsidian 에 "현 CF API 토큰은 Turnstile read-only" 기록 → kappa 가 CF 대시보드에서 수동 생성 경로로 먼저 제안. 이후 "API 가 있는데 왜 대시보드?" 피드백 → jp1 `cs-cf-worker-bouncer` 의 CF 토큰 (`seUKZID4...`) 을 SSH 경유로 확인, 이 토큰이 **Turnstile read+write** 권한 보유 확인. 해당 토큰을 일시 차용해 API 로 새 위젯 생성:
|
||||
|
||||
- sitekey: `0x4AAAAAAC3otPWhldI96Aks`
|
||||
- secret: `0x4AAAAAAC3otP9NIYkJUVYdMjNLZsbWgpM`
|
||||
- name: `inouter-bunny-middleware` (bouncer auto 이름 패턴 `crowdsec-cloudflare-worker-bouncer-widget` 과 다름 → bouncer rotation 에서 freeze 보장)
|
||||
- domains: `[inouter.com]`, mode: managed
|
||||
|
||||
Vault `secret/cloud/cloudflare/turnstile-inouter-bunny` 에 `sitekey` / `secret` / `name` 저장. Syn 가 kappa 게이트웨이 ASK 프로토콜로 값을 받아 BunnyCDN 미들웨어 64811 env 변수 `TURNSTILE_SITE_KEY` / `TURNSTILE_SECRET_KEY` 갱신 후 publish (deployment `3816dbc5-...`, HTTP 204).
|
||||
|
||||
### 인시던트 #3 — cron-induced crashloop
|
||||
|
||||
Syn 가 `DONE: cf-audit-cleanup-2-recovery` 회신. kappa 검증 결과:
|
||||
|
||||
- `bouncer-managed` 위젯 4개 (keepanker / actions / ironclad / servidor) **사라짐** → CF Worker enforcement 4개 zone 다운
|
||||
- bouncer service **48회 crashloop** (`yaml: line 57: mapping values are not allowed`)
|
||||
- 4개 zone 의 CF Worker 라우트 부재
|
||||
- `cfb-manager /status` 도 YAML 파싱 에러로 500
|
||||
|
||||
Syn 의 narrative ("cfb-manager /status 200, bouncer_running") 는 **false positive** — cfb-manager 의 in-memory 상태나 일시적 normal window 만 보고 disk + systemd status 직접 검증 생략.
|
||||
|
||||
### 근본 원인 발견
|
||||
|
||||
`/etc/cron.d/cf-zone-sync` → `/usr/local/bin/auto-add-cf-zones.sh` (매 10분):
|
||||
|
||||
1. CF API 에서 account 의 zone 목록 fetch
|
||||
2. yaml 에 없는 zone 발견 → **12-space indent 로 자동 추가** (script bug, YAML 파괴)
|
||||
3. `systemctl restart crowdsec-cloudflare-worker-bouncer`
|
||||
|
||||
inouter / anvil 을 yaml 에서 뺐더니 10분 뒤 cron 이 다시 넣으면서 indent bug 로 YAML 깨뜨림 → bouncer 재기동 실패 → crashloop. 매 10분 루프.
|
||||
|
||||
Syn 가 yaml 을 수동 fix 해 disk 에 push 했다고 한 것도 `~10분 후` cron 이 다시 깨뜨려 무효화. Syn 는 push 직후 validation 에 `cfb-manager /status` 만 쓰고 disk 내용을 재확인하지 않아 detect 못 함.
|
||||
|
||||
### 복구 (kappa 직접, Syn 우회)
|
||||
|
||||
| 단계 | 조치 | 결과 |
|
||||
|---|---|---|
|
||||
| 1 | journalctl 로 crashloop 확인, cron 발견 | `auto-add-cf-zones.sh` root cause 식별 |
|
||||
| 2 | `mv /etc/cron.d/cf-zone-sync /etc/cron.d/cf-zone-sync.disabled.kappa-incident-20260410` | cron 비활성화 |
|
||||
| 3 | `cp yaml.bak.20260410004004 yaml` | 79 lines clean 복원 |
|
||||
| 4 | `crowdsec-cloudflare-worker-bouncer -c yaml -t` | `config is valid` |
|
||||
| 5 | `systemctl reset-failed && systemctl start` | active (running), PID 371770 |
|
||||
| 6 | 15 초 대기 후 CF API 검증 | 위젯 7개 (4개 bouncer-managed 자동 재생성 + `inouter-bunny-middleware` 생존 + `crowdsec-captcha` + `inouter` legacy), 라우트 4개 zone 재생성, inouter/anvil 은 0 routes |
|
||||
|
||||
재생성된 bouncer-managed 위젯:
|
||||
|
||||
- servidor: `0x4AAAAAAC3rgjWkt-40fI7d`
|
||||
- actions: `0x4AAAAAAC3rg3VTXEllaMhY`
|
||||
- keepanker: `0x4AAAAAAC3rhJWcBgQ5aPym`
|
||||
- ironclad: `0x4AAAAAAC3rhaCWqBq5Qgza`
|
||||
|
||||
`inouter-bunny-middleware` 가 rotation 에서 건드려지지 않음을 확인 — freeze 가설 증명 (이름 패턴 분리).
|
||||
|
||||
### 교훈
|
||||
|
||||
1. **DONE 회신 전에 ground truth 직접 검증**. API 응답·in-memory 상태가 아닌 disk + systemd + 외부 API 직접 조회.
|
||||
2. **destructive cron 은 recovery 윈도우의 적**. 복구 작업 전 모든 자동화 (cron, systemd timer, scheduled job) 를 먼저 멈추고 수동 mode 로 진입.
|
||||
3. **같은 시각 두 다른 크기의 백업 파일은 scheduled writer 의 흔적**. 이걸 일찍 감지했으면 근본 원인 파악이 빨랐음.
|
||||
4. **에이전트의 false-positive 보고는 신뢰 철회 신호**. 복구를 다시 같은 에이전트에 맡기지 않고 kappa 가 직접 수행한 결정은 옳음.
|
||||
|
||||
### 참조
|
||||
|
||||
- Outline incident report: `8f5c43f8-0b46-4032-b3f6-08106aa1e5e2`
|
||||
- OpenMemory: cf-audit-cleanup-2 3-incident chain
|
||||
- Vault: `secret/cloud/cloudflare/turnstile-inouter-bunny`
|
||||
|
||||
## 5. `cf-zone-sync` cron 영구 제거
|
||||
|
||||
### 배경
|
||||
|
||||
인시던트 #3 의 근본 원인이자 3 writer 구조 (`cfb-manager`, bouncer read, cron) 중 조정 없이 동작하는 writer. 재발 방지.
|
||||
|
||||
### 조치
|
||||
|
||||
- `/etc/cron.d/cf-zone-sync.disabled.kappa-incident-20260410` **완전 삭제**
|
||||
- `/usr/local/bin/auto-add-cf-zones.sh` → `.disabled.20260410-incident` 로 rename, `chmod 600` (실행 불가, post-mortem 용 보존)
|
||||
- `/usr/local/bin/sync-cf-zones.sh` (older detect-only 버전, cron 미참조, yaml 편집 없음) — 건드리지 않고 dormant 유지
|
||||
- `/var/log/cf-zone-sync.log` (274 KB) — history 로 유지
|
||||
|
||||
### 결정 근거
|
||||
|
||||
Exclusion list 지원 재작성보다 **자동 쓰기 자체를 제거** 가 옳음. yaml 에 쓰는 주체가 하나일 때만 coordination 문제 해소. 신규 zone 추가 시 cfb-manager `POST /domains` 또는 수동 yaml edit + systemctl restart.
|
||||
|
||||
### 영향
|
||||
|
||||
`cfb-manager` protected_domains 4, bouncer active (PID 371770, 유지). 추가 자동화 없음.
|
||||
|
||||
## 6. iron-kr `IgnoreQueryStrings` true → false
|
||||
|
||||
### 배경
|
||||
|
||||
iron-kr 과 iron-jp 의 설정 불일치. iron-kr `true` (query string 을 캐시 키에서 무시) 는 privilege 교차 / 배포 cache-bust 버그의 time bomb. 안전한 기본값은 `false`.
|
||||
|
||||
### 실측
|
||||
|
||||
BunnyCDN statistics 조회: iron-kr 의 월 전체 평균 cache hit rate `0.22973383260803562` (≈ 0.23%). 실질적으로 캐시 미사용 상태라 변경 영향 무시 수준.
|
||||
|
||||
### 조치
|
||||
|
||||
`bunny_update_pullzone` pullzone_id `5555227` → `{"IgnoreQueryStrings": false}`. API 응답에서 `"IgnoreQueryStrings": false` 확인.
|
||||
|
||||
### 결과
|
||||
|
||||
iron-kr / iron-jp 둘 다 `false` 로 통일. 안전한 기본값 확보.
|
||||
|
||||
## 종합 — 오늘의 end state
|
||||
|
||||
- BunnyCDN 풀존 5개 (iron-kr / iron-jp / iron-kr-waf / iron-git / i-gate), 호스트 17개 (유지)
|
||||
- BunnyCDN 미들웨어 64811 — `TURNSTILE_SITE_KEY` = `0x4AAAAAAC3otPWhldI96Aks` (`inouter-bunny-middleware`, freeze)
|
||||
- iron-kr `IgnoreQueryStrings: false` (iron-jp 와 통일)
|
||||
- CF zone 6개, DNS 레코드 57건 (60 - 3 삭제)
|
||||
- CF Worker bouncer 보호 zone 4개 (keepanker / actions / ironclad / servidor)
|
||||
- K3s `lan-proxies` ns 신설, `nas.inouter.com` reverse proxy 동작
|
||||
- `cf-zone-sync` cron 영구 제거, yaml writer = `cfb-manager` 단일
|
||||
- 미해결 별건: `cfb-manager` DELETE destructive 동작 문서화, Worker 라우트 null orphan (vultr/linode.actions.it.com) 정리, Turnstile `inouter` legacy 위젯 결정
|
||||
|
||||
### 관련 OpenMemory 엔트리
|
||||
|
||||
- 2026-04-10 BunnyCDN+Cloudflare 도메인 전수 감사
|
||||
- 2026-04-10 CF DNS 정리 1차 (cf-audit-cleanup-1)
|
||||
- 2026-04-10 nas.inouter.com LAN-only Traefik 리버스 프록시 복원
|
||||
- 2026-04-10 cf-audit-cleanup-2 3-incident chain
|
||||
- 2026-04-10 cf-zone-sync cron 영구 제거
|
||||
- 2026-04-10 iron-kr IgnoreQueryStrings true→false
|
||||
|
||||
### 관련 git commits
|
||||
|
||||
- `639c9b5` edge: 2026-04-10 BunnyCDN+CF 전수 감사 결과 정본 반영
|
||||
- `f5264bb` edge: CF inouter.com zone 정리 1차 (cf-audit-cleanup-1)
|
||||
- `9d71167` infra: nas.inouter.com Traefik 리버스 프록시 (LAN-only) 복원
|
||||
- `218c323` edge: cf-audit-cleanup-2 + recovery 정본 반영
|
||||
- `7a8230d` edge: cf-audit-cleanup-2 3차 인시던트 (cron-induced crashloop) + kappa 직접 복구 반영
|
||||
- `8fc6a34` edge: cf-zone-sync cron 영구 제거 (cleanup-2 인시던트 근본 원인)
|
||||
- `d16090a` edge: iron-kr IgnoreQueryStrings true→false (iron-jp 와 통일)
|
||||
Reference in New Issue
Block a user