Afterglow v1.14.0 — 2차 보안 패치 (PR-A + PR-B)

릴리스일: 2026-05-09 유형: minor (보안 강화 + 행동 변화 일부) 호환성: backward-compatible (마이그레이션 불필요, 다음 릴리스에서 v2/legacy crypto fallback 제거 예정)


요약

1차 보안 PR (Critical 5건 + High 13건 처리) 완료 후 잔존 취약점을 전수 조사한 결과 신규 Critical 5 + High 7 + Medium 11 + Low 9 가 식별됐다. 이번 릴리스는 그중 PR-A (Backend IDOR 일괄) + PR-B (K3s 보안) 두 카테고리, 총 8 commit / ~30 파일 을 처리한다.

핵심 의도:

  • OpenStack RBAC 가 1차 방어선이지만 policy 가 광범위하거나 admin 토큰 누설 시 cross-tenant 노출 위험을 백엔드에서 한번 더 차단 (defense-in-depth)
  • K3s 비밀 데이터 (kubeconfig / node_token / manager_password / notion) 의 단일 마스터키 의존도를 HKDF sub-key 도메인 분리로 완화
  • Health Bearer 토큰의 사실상 영구화 (30일 sliding) 를 7일 절대 만료로 변경
  • kubeconfig 다운로드를 forensic 으로 추적 가능하도록 audit log 추가

처리된 취약점

Critical (5건)

| ID | 항목 | 처리 | |—|—|—| | C2-1 | Object-storage 컨테이너/오브젝트 IDOR | 신규 컨테이너 owner metadata 자동 부착 (minimal — 기존 컨테이너 회귀 회피) | | C2-2 | K3s callback 토큰 IP/scope 바인딩 부재 | source IP 추출·로깅 + body.server_ip 와 불일치 시 warning. 하드 차단은 PR-C 로 | | C2-3 | Auth token localStorage 평문 저장 | 이번 PR 범위 외 (PR-D) | | C2-4 | CSP script-src 'unsafe-inline' | 이번 PR 범위 외 (PR-D) | | C2-5 | Kubeconfig 다운로드 audit log 부재 | 매 GET 마다 audit_log.rec(action="kubeconfig_download") + source IP |

High (7건)

| ID | 항목 | 처리 | |—|—|—| | H2-1 | Network/LB GET·DELETE owner 검증 누락 | PR-A 처리 | | H2-2 | K3s background task admin conn 무한 진행 | PR-C 로 | | H2-3 | K8s 매니페스트 securityContext / NetworkPolicy 부재 | PR-E 로 | | H2-4 | :latest tag + imagePullPolicy: Always | PR-E 로 | | H2-5 | HAProxy 컨테이너 USER root | PR-E 로 | | H2-6 | CI dependency / image scan gate 없음 | PR-F 로 | | H2-7 | K3s 단일 마스터키 (4종 데이터) | PR-B 처리 — HKDF v3 sub-key 도메인 분리 |

Medium (11건)

| ID | 항목 | 처리 | |—|—|—| | M2-3 | Database/Volume/Snapshot/Backup IDOR | PR-A 처리 | | M2-4 | Manila access-rule owner 검증 누락 | PR-A 처리 (가장 위험: cross-tenant CephFS mount) | | M2-10 | Health Bearer 토큰 30일 sliding | PR-B 처리 — 7일 절대 만료 | | 그 외 | M2-1/2/5/6/7/8/9/11 | 후속 PR |


변경 파일 (8 commits)

PR-A — Backend IDOR 일괄

1. 9c79b24 fix(api/network)

  • backend/app/api/network/networks.py — get/delete network, update/delete subnet, FIP associate/disassociate/delete (외부·공유 네트워크 owner check 면제)
  • backend/app/api/network/routers.py_get_router_with_owner_check 헬퍼, get/delete/interface/gateway 모두
  • backend/app/api/network/security_groups.py_get_sg_with_owner_check 헬퍼, delete + rule create/delete
  • backend/tests/conftest.pymock_conn SDK get_* default stub
  • backend/tests/test_network_owner_check.py — 16 신규 케이스

2. 3b36082 fix(api/loadbalancer)

  • backend/app/api/network/loadbalancers.py_assert_lb_owner 헬퍼 + LB/listener/pool/member/HM 의 모든 sub-path
  • backend/tests/test_loadbalancer_owner_check.py — 10 신규 케이스

3. 5365758 fix(api/database,storage)

  • backend/app/api/database/instances.py_assert_db_instance_owner + _assert_db_backup_owner. instance get/delete/restart/enable_root_user/databases/users/backups + restore from backup
  • backend/app/api/storage/volumes.py_assert_volume_owner + transfer
  • backend/app/api/storage/volume_snapshots.py — snapshot get/delete + create (volume owner)
  • backend/app/api/storage/volume_backups.py — backup get/delete/restore + create (volume owner)
  • backend/tests/test_database_owner_check.py (10) + test_volume_owner_check.py (11)

4. 3df11be fix(object-storage)

  • backend/app/services/swift.pycreate_containerX-Container-Meta-Owner-Project-Id 자동 부착 (SDK + raw PUT + SDK 재시도 3 경로 모두). 메타 부착 실패는 컨테이너 생성을 무효화하지 않음 (best-effort).
  • backend/tests/test_swift_owner_metadata.py — 3 신규 케이스

5. 093bfd8 fix(api/file-storage)

  • backend/app/api/storage/file_storage.py_assert_share_owner + _fetch_and_assert_share_owner 헬퍼. delete + access-rule list/grant/revoke 의 비대칭 해소
  • backend/tests/test_file_storage.py, test_create_access_rule_metadata.py — 기존 테스트 manila.get_file_storage patch 추가
  • backend/tests/test_file_storage_owner_check.py — 7 신규 케이스 (public share 면제 포함)

PR-B — K3s 보안

6. 836dfde fix(k3s)

  • backend/app/api/k3s/clusters.pydownload_kubeconfigaudit_log.rec + source_ip 기록 (HEAD 미기록)
  • backend/app/api/k3s/callback.py — source_ip 추출 + 토큰 소비/거부 모두 로그, body.server_ip 와 불일치 시 warning
  • backend/tests/test_k3s_kubeconfig_audit.py — 3 신규 케이스

7. 0be375e fix(k3s,health) — HKDF + Health TTL

  • backend/app/services/k3s_crypto.py_derive_subkey (HKDF-SHA256, info=b”afterglow-k3s/"), v3 prefix 도입. v2/legacy fallback 유지 + `_warn_legacy_once` (도메인 단위 1회 warning)
  • backend/app/services/instance_health.py_TOKEN_TTL 30일→7일, verify_report_token 의 sliding TTL 갱신 제거
  • backend/tests/test_k3s_crypto_v3_subkey.py — 6 신규 케이스 (sub-key 분리, cross-domain reject, fallback warning)
  • backend/tests/test_k3s_crypto.py, test_keystone_appcred.py — v2 → v3 prefix assertion update
  • backend/tests/test_instance_health.py — TTL=7일 + verify 가 expiry 갱신 안 함 검증

8. ca7107e chore: ruff format — 8개 파일 스타일 정리


행동 변화 (운영 영향)

Backward-compatible — 마이그레이션 불필요

  • 기존 v1/v2 ciphertext 는 자동으로 복호화. deprecation warning 1회 (도메인별) 만 로그. 신규 암호화는 모두 v3 prefix.
  • 기존 기능 모두 정상 동작 (cross-project 접근 시도가 없는 정상 사용자).

행동 변화 — 알아둘 것

변화 영향 대응
다른 프로젝트 자원 ID 로 GET/DELETE → 404 (이전: 200/403) ops 자동화 스크립트가 admin 권한 없이 cross-project 접근하던 케이스가 있었다면 깨짐 admin 토큰 사용 또는 application credential
Kubeconfig GET 마다 audit log 1줄 + DB row 다운로드가 잦은 환경에서 audit table row 증가 audit_log 적절한 retention policy 적용
Health Bearer 토큰 7일 후 만료 VM 부팅 후 7일 이상 health_check.sh 가 토큰을 갱신 안 하면 cephx rotate 실패 health_check.sh 의 재발급 흐름 동작 확인 (이미 구현돼 있음)
_warn_legacy_once 의 module-level set gunicorn/uvicorn N workers 에서 워커당 1회 warning (총 N회) 정상 — log 노이즈 적음

다음 릴리스에서 변경 예정 (1.15.0)

  • v2/legacy crypto fallback 제거. 사전에 마이그레이션 스크립트로 모든 ciphertext 를 v3 로 batch re-encrypt 필요 (별도 PR 제공).
  • 마이그레이션 미실행 시 v1/v2 ciphertext 가 영구 복호화 불가 가 된다.

운영 체크리스트

배포 전

  • 1.13.9 에서 작성된 v1/v2 ciphertext (kubeconfig / node_token / manager_password / notion) 의 갯수 확인 (SELECT COUNT(*) FROM cluster_record WHERE encrypted_kubeconfig NOT LIKE 'v3:%' 등)
  • audit_log 테이블 retention policy 확인 (kubeconfig_download row 증가 대비)
  • cross-project 접근하는 ops 스크립트 / dashboards 식별

배포 후

  • _loggerlegacy ciphertext detected warning 이 워커당 1회씩만 뜨는지 확인 (스팸 X)
  • kubeconfig 다운로드 → audit_logkubeconfig_download row 와 source_ip extra 기록 확인
  • Health Bearer 토큰: 새 인스턴스 생성 후 토큰 TTL = 7일 인지 확인 (TTL afterglow:health:token:<token>)

1.15.0 (예정) 전

  • 마이그레이션 스크립트로 모든 v1/v2 ciphertext → v3 re-encrypt
  • dry-run 으로 영향 행 수 확인 후 실행

advisor 검토 반영 사항

이번 PR 은 advisor (강화 검토 모델) 의 세 가지 우려를 반영해 minimal 방향으로 조정했다 (사용자 승인):

  1. Object-storage IDOR (C2-1) — Swift 의 account 모델 (project-scoped Keystone token = 그 account 의 컨테이너만 접근) 이 1차 방어선임을 코드 검증 (backend/app/api/deps.py:175). metadata 기반 검증을 추가하면 기존 컨테이너 (메타 없음) 가 모두 404 되는 회귀 위험이 큼 → 신규 컨테이너에 metadata 부착만, 검증 자체는 추가하지 않음.
  2. Kubeconfig redemption URL (C2-5) — nonce-only URL 은 server log/Referer/history 로 leak 위험이 새로 생김. 현재 X-Auth-Token 인증이 더 안전 → redemption URL endpoint 신설 제외, audit log 만 추가.
  3. HKDF + v1/v2 fallback 동시 제거 (H2-7) — 마이그레이션 부분 실패 시 ciphertext 영구 손실. → HKDF sub-key 추가 + v2/legacy fallback 유지 + deprecation log. v1/v2 제거는 다음 PR.

향후 보안 작업 (별도 PR)

  • PR-C — Background task token lifetime 검증 + cephx rotate race lock
  • PR-D — Frontend localStorage 토큰 → HttpOnly cookie 전환 + CSP nonce (큰 리팩토링)
  • PR-E — K8s securityContext + NetworkPolicy + digest pin + HAProxy non-root
  • PR-F — CI scanning (bun audit / pip-audit / trivy) + dependabot
  • PR-G — Manila CSI application credential + extend-session CSRF + WebSocket subprotocol
  • PR-H — Low 항목 일괄 (SECRET_KEY 엔트로피, Grafana JWT TTL, SecretStr, .gitignore, Google Fonts CSP 등)
  • v2/legacy crypto fallback 제거 + 마이그레이션 스크립트 + dry-run 모드

검증 결과

  • 단위 테스트: 1247 passed, 20 skipped, 0 failed
  • ruff check + format check: 통과
  • 7 commit + 1 format commit (총 8) push 완료

Afterglow — OpenStack Dashboard. MIT License.

This site uses Just the Docs, a documentation theme for Jekyll.