Clustara
요약. Clustara: 자율 개선 회차 30회, 릴리즈 19건. 최근 릴리즈 v0.9.281 (자산 3개). 건강 C
- 30회차
- 1프로젝트
- 19배포 준비 완료
- 2릴리즈 진행 중
- 1병합 완료
- 1검토 대기
- 0검증 실패
- 7변경 없음
- 0실행 오류
- $75.95비용
- 3시간 55분에이전트 시간
현황
- 저장소
- https://github.com/hkjang/clustara
- 마지막 회차
- 2026-09-10 17:50 KST — 🚀 릴리즈 merged PR #20, released v0.9.281
- 최근 릴리즈
- v0.9.281 — released · 자산 3개 (이전 v0.9.280: 3개) 전체 릴리즈 →
회차 이력
| 일시 | 프로젝트 | 결과 |
|---|---|---|
| 2026-09-10 17:50 | Clustara | 배포 준비 완료 merged PR #20, released v0.9.281 |
| 2026-09-10 09:11 | Clustara | 배포 준비 완료 merged PR #19, released v0.9.280 |
| 2026-09-09 13:30 | Clustara | 배포 준비 완료 merged PR #18, released v0.9.279 |
| 2026-09-09 05:03 | Clustara | 배포 준비 완료 merged PR #17, released v0.9.278 |
| 2026-09-08 23:21 | Clustara | 배포 준비 완료 merged PR #16, released v0.9.277 |
| 2026-09-08 18:10 | Clustara | 배포 준비 완료 merged PR #15, released v0.9.276 |
| 2026-09-08 11:18 | Clustara | 배포 준비 완료 assets-only, assets for v0.9.275 |
| 2026-09-08 11:15 | Clustara | 릴리즈 진행 중 assets-only, assets for v0.9.275, asset manifest failed, ASSETS MISSING |
| 2026-09-08 10:56 | Clustara | 배포 준비 완료 merged PR #14, released v0.9.275, asset manifest failed |
| 2026-09-07 07:07 | Clustara | 배포 준비 완료 merged PR #13, released v0.9.274, asset manifest failed |
| 2026-09-06 21:45 | Clustara | 릴리즈 진행 중 merged PR #12, released v0.9.273, asset manifest failed, ASSETS MISSING |
| 2026-09-06 00:17 | Clustara | 검토 대기 review held, PR open PR #11 |
| 2026-09-05 17:13 | Clustara | 배포 준비 완료 merged PR #10, released v0.9.271 |
| 2026-09-05 11:08 | Clustara | 배포 준비 완료 merged PR #9, released v0.9.270 +3 assets |
| 2026-09-04 22:19 | Clustara | 배포 준비 완료 merged PR #8, released v0.9.269 +3 assets |
| 2026-09-04 11:23 | Clustara | 변경 없음 assets-only v0.9.262, assets for v0.9.262 +3 assets |
| 2026-09-04 11:21 | Clustara | 변경 없음 assets-only v0.9.263, assets for v0.9.263 +3 assets |
| 2026-09-04 11:20 | Clustara | 변경 없음 assets-only v0.9.264, assets for v0.9.264 +3 assets |
| 2026-09-04 11:18 | Clustara | 변경 없음 assets-only v0.9.265, assets for v0.9.265 +3 assets |
| 2026-09-04 11:16 | Clustara | 변경 없음 assets-only v0.9.266, assets for v0.9.266 +3 assets |
| 2026-09-04 11:14 | Clustara | 변경 없음 assets-only v0.9.267, assets for v0.9.267 +3 assets |
| 2026-09-04 07:02 | Clustara | 변경 없음 assets-only, assets for v0.9.268 +3 assets |
| 2026-09-04 03:41 | Clustara | 배포 준비 완료 merged PR #7, released v0.9.268 |
| 2026-09-03 20:00 | Clustara | 배포 준비 완료 merged PR #6, released v0.9.267 |
| 2026-09-03 12:04 | Clustara | 배포 준비 완료 merged PR #5, released v0.9.266 |
| 2026-09-03 06:29 | Clustara | 배포 준비 완료 merged PR #4, released v0.9.265 |
| 2026-09-03 00:29 | Clustara | 배포 준비 완료 merged PR #3, released v0.9.264 |
| 2026-09-02 18:00 | Clustara | 배포 준비 완료 merged PR #2, released v0.9.263 |
| 2026-09-02 17:41 | Clustara | 배포 준비 완료 release-only, released v0.9.262 |
| 2026-09-02 12:45 | Clustara | 병합 완료 merged (옛 러너 경로에서 수행된 회차 원장 이관) |
비용·사용량
| 시각 | 프로젝트 | 단계 | 시간 | 턴 | 비용 | 토큰 입력/출력 | 종료 |
|---|---|---|---|---|---|---|---|
| 17:48 | Clustara | 릴리즈 | 5분 | 30 | $1.32 | 1.1M / 12K | success |
| 17:43 | Clustara | review | 2분 | 18 | $0.94 | 740K / 8K | success |
| 17:40 | Clustara | 개선 | 10분 | 65 | $4.01 | 4.2M / 38K | success |
| 09:09 | Clustara | 릴리즈 | 4분 | 28 | $1.39 | 1.2M / 10K | success |
| 09:05 | Clustara | review | 5분 | 17 | $0.97 | 642K / 11K | success |
| 08:58 | Clustara | 개선 | 8분 | 48 | $3.34 | 3.2M / 29K | success |
| 13:28 | Clustara | 릴리즈 | 4분 | 26 | $1.13 | 854K / 11K | success |
| 13:20 | Clustara | 개선 | 7분 | 40 | $3.11 | 2.8M / 30K | success |
| 05:01 | Clustara | 릴리즈 | 4분 | 26 | $1.24 | 947K / 11K | success |
| 04:56 | Clustara | review | 6분 | 37 | $2.09 | 2.0M / 18K | success |
| 04:49 | Clustara | 개선 | 10분 | 72 | $4.22 | 4.8M / 36K | success |
| 23:19 | Clustara | 릴리즈 | 4분 | 24 | $1.12 | 779K / 11K | success |
| 23:14 | Clustara | review | 3분 | 16 | $1.03 | 684K / 11K | success |
| 23:10 | Clustara | 개선 | 11분 | 55 | $3.63 | 3.6M / 36K | success |
| 18:08 | Clustara | 릴리즈 | 5분 | 29 | $1.38 | 1.1M / 11K | success |
| 18:03 | Clustara | review | 3분 | 13 | $0.71 | 437K / 8K | success |
| 17:59 | Clustara | 개선 | 9분 | 43 | $2.92 | 2.3M / 34K | success |
| 11:18 | Clustara | 자산 | 2분 | 21 | $0.87 | 544K / 7K | success |
| 11:01 | Clustara | 자산 | 2분 | 21 | $0.86 | 606K / 6K | success |
| 10:54 | Clustara | 릴리즈 | 5분 | 28 | $1.51 | 1.1M / 13K | success |
| 10:49 | Clustara | review | 7분 | 24 | $1.51 | 805K / 20K | success |
| 10:41 | Clustara | 개선 | 11분 | 55 | $4.19 | 4.1M / 44K | success |
| 07:07 | Clustara | 릴리즈 | 4분 | 20 | $0.91 | 600K / 8K | success |
| 07:03 | Clustara | review | 6분 | 23 | $1.48 | 1.1M / 16K | success |
| 06:56 | Clustara | 개선 | 28분 | 53 | $4.24 | 4.3M / 40K | success |
| 21:31 | Clustara | 릴리즈 | 4분 | 19 | $0.99 | 676K / 9K | success |
| 21:26 | Clustara | review | 5분 | 26 | $1.46 | 1.2M / 14K | success |
| 21:21 | Clustara | 개선 | 11분 | 47 | $4.07 | 4.0M / 36K | success |
| 00:17 | Clustara | review | 5분 | 26 | $1.92 | 1.5M / 18K | success |
| 00:11 | Clustara | 개선 | 12분 | 62 | $4.71 | 4.8M / 40K | success |
아이디어 백로그 — 대기 13 / 전체 14
| 아이디어 | 가치/위험/크기 | 상태 | 메모 | 갱신 |
|---|---|---|---|---|
| .github 에 CI 워크플로 없음 — build/vet/test 게이트 추가 | 3/1/S | 대기 | 2026-09-10 재확인, 여전히 .github 에는 FUNDING.yml 만. go test ./... 는 캐시 없이 약 80초(proxy 62s + store 16s)라 CI 게이트에 충분. gofmt -l 은 기존 미포맷 파일이 60개 이상이라 fmt 게이트는 넣지 말 것(별도 정리 필요). | 2026-09-10 |
| PSS Restricted 검사에 seccompProfile 항목이 없음 | 3/2/S | 대기 | restrictedProfileViolations 는 runAsNonRoot·allowPrivilegeEscalation·capabilities drop ALL 만 본다. 추가하면 사실상 모든 기존 Pod 가 baseline 으로 내려가고 enforce_pss_restricted Deny 게이트가 기존 Stack 을 막을 수 있으므로 포스처 점수·게이트 영향을 먼저 확인할 것. | 2026-09-10 |
| 취약점 import 가 파싱하지 못한 아티팩트를 '취약점 0건 완료' 스캔으로 저장 | 3/3/S | 대기 | v0.9.274 는 summary.parse_notice·scanner_detected 로 신호만 남기고 import 는 성공시킨다. 형식 미인식 + findings 0건을 400 으로 거절하거나 Status 를 parse_failed 로 저장하는 편이 Admission 게이트에는 안전 — 기존 CI 파이프라인 영향 확인 필요. AnalyzeTLS(v0.9.276)가 같은 원칙을 파싱 실패 Secret 에 적용했다. | 2026-09-10 |
| AnalyzeCapacity 가 종료된(Succeeded/Failed) Pod 를 노드 packing·비용에 계속 셈 | 3/3/M | 대기 | 2026-09-10 재확인 — 스케줄러가 계산하지 않는 완료 Job Pod 가 인벤토리에 남아 packing/GPU/비용에 잡힌다. 2026-09-06 에 같은 취지의 변경(+init 컨테이너 max 계산)을 담은 PR 이 사람에게 반려된 이력이 있으므로 같은 접근을 반복하지 말 것. 다시 시도한다면 집계 의미를 바꾸는 대신 '종료된 Pod 제외' 를 별도 표시/옵션으로 드러내는 등 다른 접근이 필요하다. | 2026-09-10 |
| PodSecurityResult 에 cluster_id 가 없음 | 2/1/S | 대기 | SecFinding 은 2026-09-09 에, 용량 리포트 5종은 2026-09-10 에 cluster_id 를 채웠지만 PodSecurityResult(SEC-01 Pod Security 표)와 DW export 는 아직 없다. /admin/k8s/security 도 cluster_id 가 선택 파라미터라 두 클러스터의 동명 워크로드가 구분되지 않는다. 추가는 omitempty 로 additive. | 2026-09-10 |
| capacity 의 GPU 집계는 nvidia 만, node_monitoring 은 amd·intel 도 셈 | 2/1/S | 대기 | capacity.podRequestGPU 와 nodePackingAndGPU 의 allocatable 이 nvidia.com/gpu 만 읽는다(2026-09-10 확인). 벤더 키를 늘리려면 requests·allocatable 을 함께 맞춰야 하고, cost.go 가 같은 podRequestGPU 를 쓰므로 AMD/Intel GPU 가 0원에서 GPU 단가로 바뀌는 비용 영향도 함께 확인할 것. 벤더 키 목록을 node_monitoring 과 공유하는 최소 변경으로. | 2026-09-10 |
| podControllerOwned 가 라벨 휴리스틱만 봐서 ownerReferences 로만 소유된 Pod 를 standalone 으로 판정 | 2/1/S | 대기 | pod-template-hash / controller-revision-hash / job-name 라벨이 없는 커스텀 컨트롤러 소유 Pod 에 '자동 복구 없음' 승인 사유가 붙는다(fail-safe 방향이라 위험 낮음). 수집기가 ownerReferences 를 Spec 에 저장하는지 먼저 확인 필요. | 2026-09-10 |
| securityScanRunViews/DW export 가 scanner fallback 여부를 표시하지 않음 | 2/1/S | 대기 | v0.9.274 가 summary.scanner_detected 를 저장하므로 스캔 목록 UI·export 에 '형식 미인식 fallback' 배지를 붙일 수 있다. 저장은 되고 표시만 없음. | 2026-09-10 |
| Exposure Center 가 Gateway·HTTPRoute 를 실제로는 수집하지 않음 | 2/1/S | 대기 | AnalyzeExposure 는 Kind "Gateway"/"HTTPRoute" 를 채점할 수 있고 문서·주석도 그렇게 적었지만, handleK8sExposures 의 switch 는 Ingress·Service 만 본다. 수집기가 gateway.networking.k8s.io 리소스를 인벤토리에 담는지 먼저 확인하고, 담지 않는다면 문서 문구를 사실에 맞추는 편이 정직하다. | 2026-09-10 |
| isSensitivePath 가 substring 매칭이라 참조 이름까지 마스킹 | 2/2/S | 대기 | imagePullSecrets·volumes[].secret.secretName 같은 참조 이름까지 *** 로 덮어 manifest 원장 diff 에 잡음이 생긴다. 마스킹을 좁히는 방향이므로 노출 위험이 없는지 경로별로 확인 필요. | 2026-09-10 |
| AnalyzeExposure 의 wildcard host 점수가 host 수만큼 누적 | 2/2/S | 대기 | host 하나당 +15 라 와일드카드 host 4개면 그것만으로 60점(high)이 된다. 같은 성격의 위험이 host 수로 등급을 밀어 올리는 셈. 다만 RiskScore/RiskLevel 은 UI 정렬과 저장된 기대치라 조정이 곧 등급 변동이므로 영향 확인 후. | 2026-09-10 |
| Runtime Security Profile 의 AllowPrivEsc 는 명시적 true 만 셈 | 2/2/S | 대기 | Kubernetes 는 allowPrivilegeEscalation 미설정을 true 로 취급하므로 아무것도 적지 않은 컨테이너가 런타임 보안 점수에서 0점을 받는다. restrictedProfileViolations 는 이미 미설정을 위반으로 본다(같은 Pod 를 두 화면이 다르게 표현). 다만 미설정이 압도적 다수라 그대로 켜면 거의 모든 Pod 가 medium 으로 올라가 화면이 무의미해질 수 있어 점수 배분을 먼저 설계할 것. | 2026-09-10 |
| 터미널 allowlist 는 명령의 첫 프로그램만 검사 — 체이닝된 뒤 명령은 대조되지 않음 | 2/3/M | 대기 | terminalCommandMatches 는 prefix/substring 이라 'cat x && mv /data /tmp' 가 allowlist "cat" 으로 통과한다. 다만 Pod exec 는 셸 없이 argv 를 그대로 실행하므로 sh -c 없이는 뒤 명령이 실제로 실행되지 않고, 체이닝은 ParseCommandRisk 가 medium → guided 승인으로 올린다. 실효 위험은 낮고 allow 쪽을 세그먼트 단위로 바꾸면 기존 정책이 깨질 수 있어 보류. | 2026-09-10 |
| 용량 리포트가 노드·Pod 를 이름만으로 교차 참조해 다른 클러스터 값이 섞임 (SCALE-03/04/05/07/08) | 4/1/M | 완료 | 2026-09-10 구현. /admin/k8s/capacity 는 cluster_id 가 선택 파라미터. nodePackingAndGPU·ProjectNodeCapacity·allocFinding 이 모두 이름만으로 조인해 동명 노드가 한 행으로 합쳐지고 다른 클러스터 메트릭이 이 클러스터 Pod 의 request 와 비교됐다. 같은 패키지 node_monitoring 은 이미 nodeKey(cluster,node) 사용. 결과 타입 5종에 cluster_id(omitempty) 추가, map 순회 순서를 cluster→node 정렬로 고정, UI 노드 표 조인과 YAML 딥링크도 행의 클러스터를 쓰게 함. | 2026-09-10 |
교훈 (깨졌던 변경)
- 2026-09-07 rejected-by-human — 사람이 PR 을 반려함. 같은 접근은 피할 것. (링크)
원장 (에이전트가 남긴 기록)
2026-09-02
- 선택: 스캔/SBOM 정규화기가 버리던 필드 복구 + 첫 단위 테스트 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
internal/analyzer/vulnerability.go(576줄, 단위 테스트 0개)가 Trivy·Grype·trivy-operator 스캔과 CycloneDX·SPDX SBOM import의 유일한 파싱 경로인데, 아티팩트에 실제로 들어 있는 값 4가지를 못 읽고 있었다 — Grype CVSS(cvss[].metrics.baseScore가 두 단계 아래라 항상 0), SBOMgenerated_at(존재하지 않는 최상위created를 보고creationInfo객체에 문자열 변환을 걸어 두 포맷 모두 빈 값), CycloneDX 1.4 generator(metadata.tools배열 형태 미지원 + 빈 값을 그대로 return해 SPDX fallback 차단), Grype EPSS(객체 배열). 기존 Trivy V3Score 경로와 요청이 준 defaults 우선순위는 그대로 두고 누락분만 추가했으며, 신규 테스트 6개를 고치기 전 코드에 되돌려 붙여 4개가 정확히 해당 값을 지목하며 실패하는 것을 확인했다. 검증:go build ./...,go vet ./...,go test ./...전부 통과(17 패키지). 저장소 관례대로 AppVersion·changelog·docs/K8S_OPERATIONS_HUB.md버전 마커를 v0.9.262로 올렸다(release gate 테스트가 강제함). - 보류 아이디어: ①
collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨서 kube-bench 결과의 Section이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ② Grype의Negligible심각도가Unknown으로 접혀 Low보다 낮은 등급 정보가 사라짐 (가치 2 / 위험 2 / S) ③.github에 FUNDING.yml만 있고 CI 워크플로가 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ④detectScanner가 아무것도 못 맞히면scanner="unknown"으로 저장하면서 trivy 파서를 돌림 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ⑤internal/harbor,internal/servicecatalog테스트 0개 — 커버리지 공백 (가치 3 / 위험 1 / M) - 릴리즈: v0.9.262 (2026-09-02)
2026-09-02
- 선택: Harbor manifest·정책 게이트 결함 수정 +
internal/harbor첫 단위 테스트 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
internal/harbor(460줄, 단위 테스트 0개)는 운영자가 그대로kubectl apply하는 imagePullSecret/Deployment manifest 와 그 앞의 배포 정책 게이트를 만드는 유일한 경로인데 4가지 결함이 있었다 — ① note 가 하나의 double-quoted 스칼라인데 그 안의 robot 이름·registry 를yamlScalar로 따로 인용해서, Harbor 가 실제로 발급하는robot$project+name이름이면 따옴표가 note 안쪽에 박혀 Secret 문서 전체가 YAML 파싱 실패 ②expires_at을 RFC3339 로만 파싱하고 실패 시 조용히 무시 → 날짜·unix seconds(Harbor v2 robot API 의 실제 반환형)·-1이 전부 “만료 없음”으로 읽혀 allow 통과 (이제 해석하고, 그래도 못 읽으면robot_expiry_unreadable승인 필요) ③DefaultSecretName("")→harbor--pull(가드를 이어붙인 문자열에 걸어둬 미동작) ④RegistryHost(" ")이 공백을 host 로 반환. 검증: 신규 테스트 8개(생성 manifest 를 gopkg.in/yaml.v3 로 실제 디코드)를 고치기 전 코드에 되돌려 붙여 4개가 각 결함을 지목하며 실패함을 확인했고,go build ./...·go vet ./...·go test ./...전부 통과. 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.263 으로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ③ GrypeNegligible심각도가Unknown으로 접혀 Low 미만 등급 정보 소실 (가치 2 / 위험 2 / S) ④collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨 kube-bench Section 이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ⑤internal/servicecatalog테스트 0개 — 커버리지 공백 (가치 3 / 위험 1 / M) - 릴리즈: v0.9.263 (2026-09-02)
- 릴리즈: v0.9.263 (2026-09-02)
2026-09-03
- 선택: 셀프서비스 카탈로그 검증 게이트의 구멍 4개 수정 +
internal/servicecatalog첫 단위 테스트 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
internal/servicecatalog(158줄, 단위 테스트 0개)의ValidateInput은 셀프서비스 요청 본문의values(전부 사용자 입력)가 운영자가 승인·apply 하는 manifest 로 바뀌는 사이의 유일한 게이트인데 4가지가 그냥 통과했다 — ①port미검증(그대로containerPort:에 렌더 → 0·-1·70000 이 검증·정책 미리보기·승인을 다 통과하고 apply 시점에만 거절) ② 태그 없는 이미지가Contains(":latest")가드 우회(k8s 는 무태그를 latest 로 해석), 게다가 호출부 운영 digest 게이트도Contains(image, ":")전제 때문에 콜론 없는 이미지는 digest 요구 자체를 건너뜀 ③ 같은 substring 검사가tomcat:latest-jdk21같은 고정 태그와 digest 로 고정된repo:latest@sha256:...을 오탐 거절 ④0·0Gi가 유효 수량(storage 0 은 PVC 검증기가 거절, memory limit 0 은 첫 할당에서 OOM). 참조를[registry[:port]/]repo[:tag][@digest]로 분해하는 방식으로 바꾸고 port 범위·0 초과 수량을 추가했으며, 호출부(admin_k8s_services.go)의 운영 digest 게이트에서 콜론 전제를 제거했다. 검증: 신규 테스트 7개를 고치기 전 코드에 되돌려 붙여 4개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.264 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ③ GrypeNegligible심각도가Unknown으로 접혀 Low 미만 등급 정보 소실 (가치 2 / 위험 2 / S) ④collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨 kube-bench Section 이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ⑤ 카탈로그 caller 의num()헬퍼가 문자열"0"·비숫자를 조용히 기본값으로 되돌림 — 잘못된 입력이 기본값으로 성공 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.264 (2026-09-03)
- 릴리즈: v0.9.264 (2026-09-03)
2026-09-03
- 선택: 액션 영향도 산출기의 승인 게이트 결함 4개 수정 +
internal/action테스트 보강 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
internal/action/impact.go(272줄, 테스트 4개)는 클러스터를 바꾸는 모든 액션이 승인 전에 통과하는 유일한 영향도 산출기이고 그 결과가DryRunDiff로 저장돼 운영자는 그 텍스트만 보고 승인한다. 넷이 잘못돼 있었다 — ① preview 의num()은 float/int 만 읽는데 실행기의intFromParams는 JSON 문자열도Atoi로 파싱해서,{"replicas":"5"}요청이 승인기록·감사로그에replicas 2 → 0 (-2)로 남고 실제로는 5로 스케일됐다(승인한 값 ≠ 실행된 값) ②replicas부재·음수도 똑같이 “0으로 축소”로 렌더 — 실행기가 거절할 요청에 전면 중단 diff 가 남았다 ③ replica 0 축소에 승인 게이트가 없음:scale은Classify가 유일하게RequiresApproval:false로 두는 액션인데 0 은 워크로드 전면 중단이고, 서비스 플랫폼 stop 경로는 같은 요청을 이미approval_required로 기록 중이라POST /admin/k8s/actions만pending으로 통과시키고 있었다 ④cordon/uncordon이 한case를 공유해 uncordon 승인자가 “cordon은 신규 스케줄만 차단합니다” 라는 반대 동작 설명을 읽었다. 덤으로 patch 미허용 필드 목록이 map 순회 순서라 같은 요청이 매번 다른 승인 문구를 만들던 것도 정렬했다. 검증: 신규 테스트 5개를 고치기 전 코드에 되돌려 붙여 다섯 개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(20 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.265 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ③ GrypeNegligible심각도가Unknown으로 접혀 Low 미만 등급 정보 소실 (가치 2 / 위험 2 / S) ④collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨 kube-bench Section 이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ⑤AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 — 미관측 대상임을 표시해야 함 (가치 3 / 위험 1 / S) - 릴리즈: v0.9.265 (2026-09-03)
- 릴리즈: v0.9.265 (2026-09-03)
2026-09-03
- 선택: kubectl 의 last-applied 주석이 Secret 값을 저장·응답으로 실어나르던 경로 차단 + 마스킹 3곳 정합 (가치 5 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약: 수집기(
internal/kube/client.go)는 Secret 의data를 절대 저장하지 않고tls.key조차 버리는데,metadata.annotations는 그대로 저장돼kubectl apply가 남긴kubectl.kubernetes.io/last-applied-configuration(적용 객체 전체 = Secret 의 base64data, 워크로드의 모든 env 값)이 그 정책을 우회하고 있었다. 그 주석은GET /admin/k8s/inventory가 가공 없이 돌려주고 Manifest Viewer 는"masked": true라고 말하면서 함께 내보냈다. 수집 경로 셋(실시간·에이전트 push·스냅샷 import)이 전부UpsertK8sInventory로 모이므로 저장 시점에 떨어내고, 기존 행 때문에 읽기 시점(scanK8sInventory)에서도 떨어낸다. 같은 테마로 둘 더 고쳤다 — Manifest Viewer 의maskStringMap이 주석을 키 이름으로만 판단해 값(토큰 붙은 webhook URL·DSN)을 그대로 복사하던 것을 Pod 지문 경로와 동일하게analyzer.MaskSensitive로 맞췄고,DetectStackFieldDrift가DB_PASSWORD=...를 선언/실제 양쪽 평문으로 관리자 UI 에 렌더하던 것을 비교는 원본·출력은 마스킹으로 바꿨다(드리프트 검출은 그대로, 이름은 남김). 검증: 신규 테스트 5개를 고치기 전 코드에 되돌려 붙여 넷이 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.266 으로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②analyzer.MaskSensitive(Pod 로그 마스킹)가Authorization: Basic <base64>를 못 잡음 — audit redactor 는 잡는데 로그 경로만 구멍 (가치 3 / 위험 1 / S) ③isSensitivePath가 substring 매칭이라imagePullSecrets·volumes[].secret.secretName같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음이 생김 (가치 2 / 위험 2 / S) ④detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ⑤AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 (가치 3 / 위험 1 / S) - 릴리즈: v0.9.266 (2026-09-03)
- 릴리즈: v0.9.266 (2026-09-03)
2026-09-03
- 선택: Pod 로그 마스크(
analyzer.MaskSensitive)가 놓친 자격증명 8가지 수정 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
MaskSensitive는 Pod 로그·exec stdout/stderr·터미널 스트림·Pod env 지문·Manifest Viewer 주석 값이 나가기 전 통과하는 유일한 마스크인데, 바로 앞 틱(v0.9.266)이 주석 값을 이 함수로 넘기며 근거로 적은 “토큰 붙은 webhook URL·DSN·bearer 헤더” 중 둘을 실제로는 못 잡고 있었다. 실제 유입 형태를 하나씩 넣어 8가지를 확인했다 — ① DSN 비밀번호(postgres://u:pw@h) 무마스킹(key=value 규칙은DATABASE_URL을 모름; 이제 비밀번호만 가리고 host/db 는 남김) ②Authorization: Basic규칙 부재(audit 리댁터는 처음부터 잡고 있었음 — 로그 경로만 구멍) ③ JSON 으로 덤프된 헤더는 이름과 콜론 사이 닫는 따옴표 때문에 미매치(audit 이 자기 쪽에서 이미 고친 것과 같은 처리) ④ Bearer 토큰 문자집합에+/=가 없어 base64 토큰을 앞부분만 가림 —Bearer ***REDACTED***+Z/gh==로 “가렸다고 표시하면서” 뒷부분 노출 ⑤SECRET_KEY=·PRIVATE_KEY=통과(구분자가 키 이름 바로 뒤여야 하므로secret항목이 못 덮음) ⑥ 키 이름 없이 값만 찍힌 토큰(sk-·gh[pousr]_·xox[abprs]-·glpat-·AIza) 통과 — 기존엔AKIA만 이 형태를 다룸 ⑦ PEM 개인키 블록 통과. 표적 규칙 원칙은 유지(넓은 base64 휴리스틱 없음)하고 정상 로그가 한 글자도 안 바뀌는 것을 회귀 테스트로 고정했으며, 인덱스switch로 치환을 고르던 구조를 audit 과 같은{정규식, 치환}표로 바꿨다(가운데 규칙 삽입 시 뒤쪽 치환이 어긋나던 구조). 검증: 신규 테스트 10개(+오탐 회귀 1개)를 고치기 전 코드에 되돌려 붙여 열 개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). AppVersion·changelog·docs 버전 마커를 v0.9.267 로 올렸다. - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 (가치 3 / 위험 1 / S) ③isSensitivePath가 substring 매칭이라imagePullSecrets·volumes[].secret.secretName같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) ④detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ⑤ GrypeNegligible심각도가Unknown으로 접혀 Low 미만 등급 정보 소실 (가치 2 / 위험 2 / S) - 릴리즈: v0.9.267 (2026-09-03)
- 릴리즈: v0.9.267 (2026-09-03)
2026-09-04
- 선택: 정책 팩 가드레일 4종의 오탐·미탐 수정 (
analyzer.EvaluatePolicies) (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
analyzer.EvaluatePolicies는 Admission 시뮬레이터·컴플라이언스 스캔·GitOps Stack 배포 게이트 셋이 공유하는 유일한 룰 평가기이고Deny는 Stack apply 전체를 막는다. 넷이 잘못돼 있었다 — ① 이미지 공급망 룰 3종(disallow_unsigned_image·require_sbom·require_vuln_scan_attestation)이 attestation 주석의 부재로 발화하는데 kind 범위가 없어 Service·ConfigMap·ClusterRole 처럼 이미지를 pull 하지 않는 리소스를 전부 위반 처리 → Stack 안의 Service 하나가 apply 전체를 차단하고, ClusterRole 하나마다 findings 3건이 쌓여 실제 위반이 묻힘(룰 카탈로그와 내보내는 Kyverno/Rego 는 이미 대상을 “워크로드” 로 적고 있었음) ②disallow_latest_tag가:하나만 있으면 “태그 있음” 으로 읽어registry.corp.local:5000/app(kubelet 이:latest로 pull) 이 통과 — 포트 붙은 사설 레지스트리는 이 제품이 대상으로 하는 폐쇄망의 기본 형태라[registry[:port]/]repo[:tag][@digest]분해로 교체 ③require_resource_limits가 ephemeral container 까지 순회 — K8s API 는 ephemeral container 에resources를 허용하지 않으므로 승인된 디버그 세션이 붙어 있는 동안 그 Pod 는 만족 불가능한 조건으로 계속 위반 표시(securityRelevantContainers주석이 이미 경고해 둔 경우) ④ 컨테이너 securityContext 가 Pod 것을 덮어쓰는데require_run_as_non_root가 Pod 값만 봐서, PodrunAsNonRoot: true+ 컨테이너false(= root 로 실행)를 준수로 판정. 검증: 신규 테스트 4개를 고치기 전 코드에 되돌려 붙여 넷 모두가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.268 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②require_resource_limits는limits가 비어있지 않기만 하면 통과 — cpu 만 있고 memory limit 이 없는(OOM 무제한) 흔한 실패 형태를 놓침 (가치 3 / 위험 2 / S) ③AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 (가치 3 / 위험 1 / S) ④isSensitivePath가 substring 매칭이라imagePullSecrets·volumes[].secret.secretName같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) ⑤detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.268 (2026-09-04)
- 릴리즈: v0.9.268 (2026-09-04)
2026-09-04
- 선택: 터미널 명령 위험 분류기(
analyzer.ParseCommandRisk)의 우회·오탐 4종 수정 (가치 5 / 위험 2 / 작업량 M) - 결과: 성공
- 요약:
ParseCommandRisk는 Pod 터미널/exec 요청의 등급기이지만 결과는 라벨이 아니라 게이트다 —evaluateTerminalPolicy는critical이면 정책을 한 줄도 읽기 전에Allowed=false로 돌려보내고,ClassifyTerminalAccessMode는low를 넘는 순간 guided 티어와 승인을 강제한다. 구현이 명령 전체 문자열 substring 매칭이라 양방향으로 틀렸다 — ① 루트 삭제가rm -rf /라는 정확한 바이트열만 critical 이어서 플래그를 나눈rm -f -r /는 아무 규칙에도 안 걸려low(승인 불필요한 read_only), 공백 하나 다른rm -rf /도low,rm -rf /·rm -rf "/"·sh -c "rm -rf /"는 하드 블록에서high로 내려앉아 allowlist 항목 하나면 실행 ②reboot·shutdown·halt·mkfs를 substring 으로 찾아cat /var/log/reboot-analysis.log·grep -i halt app.log가 critical 하드 블록 — 지난 재부팅 로그를 읽는 것이 재부팅을 시키는 것으로 취급됨 ③ pipe-to-shell 이 “명령 어딘가에|+sh+curl” 조건이라curl -s http://api/health | grep crash(crash 안의 sh)가 원격 코드 실행으로 차단 ④ 규칙 표가 map 이라 findings 순서와CommandRiskReason(evaluator 가 저장하는 차단 사유이자 감사 문구)이 실행마다 달라짐. 명령을 토큰화해 프로그램 자리/플래그/대상으로 판정하도록 바꿨고(프록시 deny 경로의terminalProgramTokenMatches가 이미 쓰던 판정), 규칙 표를 정렬된 슬라이스로 교체했다. 덤으로>/dev/sda(공백 없는 리다이렉트) critical 승격,a || b를 파이프로 세던 것, 재귀 삭제 1건이rm -rf·rm -r2건으로 중복 보고되던 것도 고쳤다. 검증: 신규 테스트 8개를 고치기 전 코드에 되돌려 붙여 7개가 각 결함을 지목하며 실패함을 확인(8번째는 정상 조회가low로 남는지 지키는 오탐 회귀)했고go build ./...·go vet ./...·go test ./...전부 통과. AppVersion·changelog·docs 버전 마커를 v0.9.269 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
require_resource_limits는limits가 비어있지 않기만 하면 통과 — cpu 만 있고 memory limit 이 없는(OOM 무제한) 흔한 실패 형태를 놓침 (가치 3 / 위험 2 / S) ②AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 (가치 3 / 위험 1 / S) ③.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ④kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 (가치 2 / 위험 1 / S) ⑤isSensitivePath가 substring 매칭이라imagePullSecrets같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) - 릴리즈: v0.9.269 (2026-09-04)
- 릴리즈: v0.9.269 (2026-09-04)
2026-09-05
- 선택: 실클러스터 write 경로(
internal/kubeexecutor·stack applier)의 대상 조립 결함 5개 수정 (가치 5 / 위험 2 / 작업량 M) - 결과: 성공
- 요약:
internal/kube의 executor 는 Action Center 승인 뒤 실제로 클러스터를 바꾸는 곳이고 stack applier 는 GitOps Stack 의 server-side apply 경로인데, 둘 다 요청 URL 을 문자열로 조립하면서 다섯 가지가 틀렸다 — ①DeletePod는 name 이 비면/api/v1/namespaces/{ns}/pods/를 만드는데 이건 Kubernetes 가 deletecollection 으로 처리하는 URL 이라 이름 없는 delete_pod 한 건이 네임스페이스의 Pod 를 전부 지운다. 그리고 그런 요청이 실제로 만들어질 수 있었다:analyzer.PlanDevRequest가in.ResourceName != ""로만 검증하는데 핸들러는strings.TrimSpace한 값을 저장하므로 공백만 있는resource_name이 대상 없는 액션 요청으로 적재됐다(검증기는 trim 후 비교, executor 는 요청 경로를 믿지 않고 빈 namespace·name·node 를 전송 전에 거절) ②normalizeWorkloadKind가 switch 앞에서 복수형s를 무조건 떼어내sts→st,ds→d가 되면서 바로 아래case "sts"·case "ds"가 실행 불가능한 코드였다 —ResourceKind는 자유 입력이고 단축 이름은 운영자·Ops Agent 가 쓰는 표기(deploy는s로 안 끝나 우연히 동작 중이었음) ③ apps/v1 DaemonSet 에는/scale서브리소스가 없는데workloadResourcePlural이 scale 대상으로 받아들여 요청→영향도→승인을 다 통과한 뒤 실행 시점에 404 로 끝났다 ④resolveStackTargets가 모든 문서에 stack namespace 를 채우므로clusterScopedKinds에 없는 cluster-scoped kind 는/namespaces/{stack}/...로 조립돼 실패 —IngressClass·PodSecurityPolicy는pluralizeKind의 불규칙 목록에 이미 있으면서 이 목록엔 없었고, webhook configuration 2종·APIService·RuntimeClass·CSIDriver·VolumeSnapshotClass·ValidatingAdmissionPolicy(Binding)도 추가 ⑤ 같은 패키지 읽기 경로(podLogRequest·podExecURL)는url.PathEscape를 쓰는데apiResourcePath만 날것으로 이어붙여, 저장된 manifest 값의 슬래시가force=trueapply 를 다른 리소스로 돌릴 수 있었다. 검증: 신규 테스트 5개를 고치기 전 코드에 되돌려 붙여 다섯 개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과. 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.270 으로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
delete_pod실행기가act.ResourceKind를 아예 보지 않아 Deployment 를 대상으로 만든 요청이 같은 이름의 Pod 삭제로 나감 (가치 3 / 위험 1 / S) ②require_resource_limits는limits가 비어있지 않기만 하면 통과 — cpu 만 있고 memory limit 이 없는(OOM 무제한) 형태를 놓침 (가치 3 / 위험 2 / S) ③AssessImpact가 인벤토리에 없는 대상을 zero value 로 받아 “replicas 0 → N” 처럼 현재 상태를 아는 척함 (가치 3 / 위험 1 / S) ④.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ⑤kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 —podExecArgs의 CommandArg 경로도 같음 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.270 (2026-09-05)
- 릴리즈: v0.9.270 (2026-09-05)
2026-09-05
- 선택: 액션 대상 kind 미검증 + 미관측 대상의 지어낸 현재 상태 수정 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: Action Center 실행기는 액션마다 하나의 고정된 리소스 종류를 addressing 한다 —
delete_pod은DELETE /api/v1/namespaces/{ns}/pods/{resource_name},cordon/uncordon은PATCH /api/v1/nodes/{resource_name}. 그런데resource_kind는 아무도 검사하지 않는 자유 입력이라 “Deployment/web 에 delete_pod” 로 적히고 그렇게 검토·승인된 요청이 실제로는web이라는 이름의 Pod 를 지웠고(같은 이름의 Pod 가 있으면 승인 화면에 없던 객체가 사라지고 감사 로그에는 Deployment 이름으로 남는다), 같은 요청이cordon이면 Pod 이름과 같은 노드를 차단했다. 세 지점에서 대조하도록 고쳤다 — 영향도 산출기가 kind 불일치를 승인 사유로 올리고, 실행 디스패치가 API 로 아무것도 보내기 전에 거절하며,kube.DeletePod이 Scale·RolloutRestart 처럼 kind 를 받아 자기 앞에서 확인한다(Pod/pods/po/v1/Pod,deploy/sts/ds표기 차이는 통과, 빈 kind 는 주장하는 바가 없어 미판정). 둘째로 두 요청 핸들러 모두GetK8sInventoryItem실패 시 zero value 를 그대로 영향도 산출기에 넘겨, 미수집 대상 scale 요청이replicas 0 → 5 (+5)라는 관측한 적 없는 diff 로 승인 기록에 남고(실제 5개가 돌고 있어도),delete_pod은 라벨이 없다는 이유로 “standalone Pod 이라 자동 재생성되지 않습니다” 라고 단정했다 — 이제 미확인으로 표시하고 승인 대상으로 넘긴다. 검증: 신규 테스트 6개(action 4 + kube 1 + proxy 종단 1, 오탐 회귀 포함)를 고치기 전 동작으로 되돌려 실행해 여섯 개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과. 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.271 로 올렸다(release gate 테스트가 강제). -
보류 아이디어: ①
require_resource_limits는limits가 비어있지 않기만 하면 통과 — cpu 만 있고 memory limit 이 없는(OOM 무제한) 형태를 놓침 (가치 3 / 위험 2 / S) ②.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ③kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 —podExecArgs의 CommandArg 경로도 같음 (가치 2 / 위험 1 / S) ④podControllerOwned가 라벨 휴리스틱만 봐서ownerReferences로만 소유된 Pod(Job/커스텀 컨트롤러)를 standalone 으로 판정 (가치 2 / 위험 1 / S) ⑤isSensitivePath가 substring 매칭이라imagePullSecrets같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) - 릴리즈: v0.9.271 (2026-09-05, run 2026-09-05-165039-Clustara-improve)
2026-09-06
- 선택: 용량·비용·GPU 집계가 스케줄러가 실제로 예약한 양과 어긋난 결함 3종 수정 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
internal/analyzer의 용량 리포트(SCALE-06/07/08)·비용 예측·네임스페이스 청구 롤업·rightsizing 권고는 모두 “이 Pod 가 노드에서 얼마를 잡고 있나” 를 계산하는데, 세 지점이 Kubernetes 의 실제 의미와 달랐다 — ① 종료된 Pod 를 계속 셌다: 인벤토리에는 Succeeded(완료된 Job/CronJob Pod)·Failed(evicted 포함) Pod 가 API 서버가 지울 때까지 그대로 남고 수집기가 전부 저장하는데, 스케줄러는 그 Pod 를 노드에 계산하지 않고 kubelet 도 cgroup 을 반납한 상태다. CronJob 하나로 노드 패킹이 125% 로 뜨고 GPU 가 유휴인데Idle: -3이 되며, 비용은 아무것도 소비하지 않는 Pod 를 request 정가로 계속 청구했다(스케줄된 채 아픈 CrashLoopBackOff·Pending Pod 는 여전히 자리를 잡으므로 그대로 센다). ② init 컨테이너 요청을 앱 컨테이너와 더했다: 유효 요청량은max(앱 합, 가장 큰 init)인데(init 은 앱보다 먼저 하나씩 끝까지 돈다) 그냥 합산해서, 4Gi 를 내려받는 init 옆의 256Mi 앱이 4.25Gi 로 장부에 올랐다. 네이티브 사이드카(restartPolicy: Always인 initContainer)만 합에 남긴다. 반대 방향으로, 화면용 요약(SummarizePodResources)은.spec.containers만 읽어 init 이 최대 소비자인 Pod 를 실제보다 작게 표시했다 — 같은 패키지의 두 함수가 같은 질문에 다른 답을 하고 있었다. ③qtyMem단위표에T·P·E·Pi·Ei와 소문자k(API 가 받는 유일한 십진 1000 표기)가 없어 그런 접미사로 적힌 메모리 요청이 조용히 0 바이트, 즉 공짜로 집계됐다. 검증: 신규 테스트 6개를 고치기 전 코드에 되돌려 붙여 다섯 개가 각 결함을 지목하며 실패함을 확인했고(여섯 번째는 사이드카 합산 회귀),go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.272 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
require_resource_limits는limits가 비어있지 않기만 하면 통과 — cpu 만 있고 memory limit 이 없는(OOM 무제한) 형태를 놓침, 내보내는 Kyverno 패턴은 이미 memory+cpu 둘 다 요구 (가치 3 / 위험 2 / S) ②.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ③capacity.podRequestGPU는nvidia.com/gpu만 세는데node_monitoring.podGPURequests는 amd·intel 도 셈 — 같은 클러스터의 GPU 요청량이 화면마다 다름 (가치 2 / 위험 1 / S) ④kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 (가치 2 / 위험 1 / S) ⑤podControllerOwned가 라벨 휴리스틱만 봐서ownerReferences로만 소유된 Pod 를 standalone 으로 판정 (가치 2 / 위험 1 / S)
2026-09-06
- 선택: PSS 등급·Restricted 게이트·리소스 limits 가드레일의 “준수” 오판 4종 수정 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: Pod Security Standards 는 누적인데
classifyPodSecurity의 등급 판정이 privileged·baseline 위반만 보고 자기가 방금 계산한 restricted 위반 목록을 등급에 반영하지 않아, host namespace 를 안 쓰고privileged: true만 아니면 곧바로 최고 등급이 됐다 — 즉 securityContext 가 아예 없는 평범한 Pod(root, 기본 capability, 권한 상승 허용)가 전부restricted로 분류됐다. 이 라벨은 소비자가 보는 유일한 값이라(Pod Security 표는level !== 'restricted'로 걸러 그리고, DW export 도 restricted 행을 건너뛰며, 요약은 목표 상태로 센다) 하드닝이 가장 안 된 워크로드가 화면에서 사라지고 이미Violations에 들어 있던 세 위반이 아무 데서도 표시되지 않았다. 같은 테마로 셋 더 — ②enforce_pss_restricted가deny_privileged_runtime과case를 공유한 복사본이라 “PSS Restricted 강제” Deny 게이트가 Restricted 항목을 하나도 검사하지 않았다(같은 Pod 를 포스처 리포트는 위반으로 적고 있어 제품의 두 부분이 정반대 판정) — 이제 둘이restrictedProfileViolations헬퍼를 공유한다 ③ 포스처 경로가 runAsNonRoot 를!컨테이너 && !Pod로 읽어 Pod 의true가 컨테이너의 명시적false(= root 실행)를 가렸다 — v0.9.268 이 룰 쪽에만 고친 결함 ④require_resource_limits가len(limits) == 0만 봐서limits: {cpu: "500m"}(메모리 무제한, 다른 Pod 를 evict 시키는 형태)가 준수로 통과했다 — 내보내는 Kyverno 패턴과 룰 설명은 이미 cpu·memory 를 둘 다 요구하고 있었으므로 세 표현 중 구현만 어긋나 있었고, 빈 값(memory: ""·null)도 limit 이 아니게 했다. 내보내는 Rego/Kyverno 본문도 함께 맞췄다. 검증: 신규 테스트 6개를 고치기 전 코드에 되돌려 붙여 네 결함을 각각 지목하며 실패함을 확인했고(하드닝된 Pod 가 계속restricted인지, 컨테이너 없는 리소스에 발화하지 않는지 지키는 오탐 회귀 포함)go build ./...·go vet ./...·go test ./...전부 통과. 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.273 으로 올렸다(v0.9.272 는 아직 머지되지 않은 다른 브랜치가 이미 사용 중이라 건너뜀). -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③capacity.podRequestGPU는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) ④kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 (가치 2 / 위험 1 / S) ⑤podControllerOwned가 라벨 휴리스틱만 봐서ownerReferences로만 소유된 Pod 를 standalone 으로 판정 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.273 (2026-09-06, run 2026-09-06-211047-Clustara-improve)
2026-09-07
- 선택: 보안 스캔 결과 ingest 정규화(
analyzer.NormalizeVulnerabilityScan·NormalizeKubeBench)의 오분류 5종 (가치 4 / 위험 2 / 작업량 M) - 결과: 성공
- 요약: 취약점·CIS 스캔 import 는 CI 나 Trivy Operator 가 만든 JSON 을 그대로 받아 Admission 게이트·컴플라이언스 화면·DW export 가 쓰는 원장으로 바꾸는 경로인데, 다섯 지점이 틀려 있었다 — ①
detectScanner가 형식을 못 맞히면"unknown"을 돌려주고 그 값이switch의default로 떨어져 Trivy 파서가 실행되는데 스캔 행에는scanner="unknown"이 저장됐다(호출자가"snyk"처럼 지원 안 하는 이름을 적어도 동일). 스캔 목록·감사 로그가 실행된 적 없는 파서를 말했고, 더 중요하게는 읽히지 않은 아티팩트와 정말 깨끗한 이미지가 둘 다 findings 0건이라 게이트가 구분할 수 없었다 — 이제 실행된 리더로 라벨링하고summary.scanner_detected·requested_scanner·parse_notice로 구분한다. ②NormalizeSeverity매핑표에 Grype 의Negligible과 RPM 권고의Important·Moderate가 없어 셋 다Unknown(랭크 0)으로 접혔다 — 벤더가 High 로 매긴ImportantCVE 가 Admission 승인 임계(SeverityRank >= 3)에 걸리지 않고 통과했다. 게이트 임계값은 저장된 계약이므로 등급 번호는 바꾸지 않고Negligible을 Low 와 같은 랭크로 뒀다. ③ severity 카운트 맵 두 곳이 손으로 적은 키 목록 +m[sev].(int)+1이라 정규화가 표에 없는 등급을 하나라도 내보내면 nil 타입 단언 패닉(요약 API 500)이었다 — 새analyzer.SeverityLevels()에서 키를 만들게 했다. ④ kube-bench 는Controls[{id,text}] > tests[{section}] > results[]로 중첩되는데 수집기가 상속값을 먼저 채택해 가장 바깥 노드의 자유 문구가 모든 control 의 Section 에 눌러앉아1.1을 잃었다. ⑤scored는 JSON bool 인데 문자열로 읽어(strV(true)="") 언제나 scored 였고,BenchmarkVersion은 핸들러가 읽고 있는데 정규화기가 설정한 적이 없어 cis-1.7 과 cis-1.23 결과를 구분할 수 없었다. 검증: 신규 테스트 8개(analyzer 7 + proxy 1)를 고치기 전 코드에 되돌려 붙여 다섯 결함을 각각 지목하며 실패함을 확인했고(인식된 형식의 라벨·section 없는 평평한 문서의 기존 동작을 지키는 오탐 회귀 포함)go build ./...·go vet ./...·go test ./...전부 통과. 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.274 로 올렸다(release gate 테스트가 강제). -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③capacity.podRequestGPU는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) ④kube.splitCommandLine이 빈 인자(sh -c "")를 버려 argv 가 밀림 (가치 2 / 위험 1 / S) ⑤podControllerOwned가 라벨 휴리스틱만 봐서ownerReferences로만 소유된 Pod 를 standalone 으로 판정 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.274 (2026-09-07, run 2026-09-07-063049-Clustara-improve)
2026-09-08
- 선택: 터미널 게이트가 읽는 명령 문자열과 executor 가 만드는 argv 의 파서 불일치 5종 수정 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: 하나의 명령 문자열을 두 파서가 읽는다 — 게이트(Command Risk Parser·터미널 denylist·access mode 분류기)와 Kubernetes exec argv 를 조립하는 executor. 게이트는 원본 바이트를 보면서 토큰의 바깥쪽 따옴표만 떼어냈고 executor 는 따옴표·백슬래시를 제대로 해석해 실제 프로그램을 실행했으므로, 셸에서 흔한 표기 하나로 모든 게이트를 동시에 통과했다 — ①
r"m" -rf /·\rm -rf /·re"boot"·shut'down' -h now·mkfs".ext4" /dev/sda가 전부critical이 아니라low로 채점됐다. critical 은 정책을 한 줄도 읽기 전에 걸리는 하드 블록(실행 핸들러에서 한 번 더)이고 low 는 승인이 필요 없는 read_only 티어라, 첫 토큰을 받아주는 allowlist 하나만 있으면 evaluateTerminalPolicy 가 루트 삭제에Allowed=true, RequireApproval=false, access_mode=read_only를 돌려줬다(신규 종단 테스트가 이 응답을 그대로 재현). ② denylist 도 같은 표기를 놓쳤다 —"rm -rf"는 원본 문자열 substring 검사인데r"m" -rf /data에는 그 부분문자열이 없다. 이제 deny 쪽만 executor 가 해석한 형태로도 대조한다(allow 쪽은 원본 유지 — 정규화하면 allowlist 를 더 쉽게 만족시키는 반대 방향이 된다). ③ full TTY 는 항상 승인을 강제하는 티어인데isInteractiveShell이 원본 Fields 를 써서"bash"·\bash·'sh'가 read_only 로 분류됐다. ④ executor 의 분해기 자체도 승인된 텍스트와 두 곳에서 어긋났다 — 명시적 빈 인자(sh -c "" ls)를 버려 뒤 인자가 한 칸씩 당겨지고(=sh -c ls실행), 작은따옴표 안의 백슬래시를 이스케이프로 처리해grep 'a\.b' f가a.b를 찾았다. ⑤podExecArgs가 호출자 argv 의 빈 요소를 버리고 각 요소를 trim 해서sh -c <script> <argv0> <path> <query> <n>의 위치 파라미터가 밀렸다(evidence search 경로가 이 형태). 따옴표 해석을analyzer.ShellWords한 곳에 POSIX 규칙으로 문서화하고 executor 분해기가 같은 규칙을 따르게 했다. 검증: 신규 테스트 8개(analyzer 5 + kube 2 + proxy 3, 정상 조회가 계속 low·미차단인지 지키는 오탐 회귀와 allow 쪽이 넓어지지 않았는지 지키는 테스트 포함)를 고치기 전 코드에 되돌려 붙여 각 결함을 지목하며 실패함을 확인했고(analyzer 16건·kube 5건·proxy 8건 실패),go build ./...·go vet ./...·go test ./...전부 통과. 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④capacity.podRequestGPU는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) ⑤podControllerOwned가 라벨 휴리스틱만 봐서ownerReferences로만 소유된 Pod 를 standalone 으로 판정 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.275 (2026-09-08, run 2026-09-08-103054-Clustara-improve)
2026-09-08
- 선택: TLS 인증서 만료 점검(SEC-07)이 만료 직후를 “유효” 로, 정상 인증서를 “만료 임박” 으로 읽던 결함 5종 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
analyzer.AnalyzeTLS는 kubernetes.io/tls Secret 의 공개 인증서로 만료를 판정해 보안 화면(SEC-07 표)과 멀티 클러스터 보안 posture 에 쓰이는데, 다섯 지점이 틀려 있었다 — ① 남은 일수를int(dur.Hours()/24)로 계산해 0 쪽으로 잘라내는 바람에 24시간 이내에 만료된 인증서가daysLeft=0→critical이 아니라high+ “0일 후 만료” 로 분류됐다. 장애가 막 시작된 그 구간이 정확히 여기고, UI 는 이 값을 “0일” 로 그려 아직 여유가 있는 것처럼 보였다(−inf 방향 내림 + 실제 시각 기준 판정, 경과는 시/분 단위 표기). ②tls.crt는 leaf + 발급 체인 번들인데parseFirstCert가 첫 인증서만 봤다 — 이 제품이 겨냥하는 폐쇄망의 내부 CA 처럼 체인 쪽이 먼저 만료되면 Secret 은 이미 handshake 를 못 하는데 리포트는 “유효 (300일 남음)” 이었다(번들에서 가장 먼저 만료되는 인증서로 판정하되 CN/SAN 은 leaf 유지, 체인이 먼저 만료되면 메시지에 그 CN 을 적는다). ③notBefore를 아무도 읽지 않아 아직 유효하지 않은 인증서(미래 발급·시계 오차)가 만료 인증서와 똑같이 실패하면서 “유효” 로 남았다. ④tls.crt를 인증서로 못 읽으면continue로 조용히 건너뛰어 파싱 실패한 Secret 이 인증서가 전부 정상인 클러스터와 구분되지 않았다(v0.9.274 가 취약점 import 에서 정한 “읽히지 않은 아티팩트를 깨끗함으로 저장하지 않는다” 와 같은 원칙). ⑤ posture 롤업이TLSExpiring: len(tls)라 TLS Secret 이 하나라도 있으면 전부 만료 임박으로 집계됐고, 권고 사다리의TLSExpiring > 0때문에 모든 인증서가 1년 남은 클러스터에도 “TLS 인증서 갱신 일정 확인” 이 붙어 “현재 주요 보안 조치 없음” 이 사실상 도달 불가였다 — 조치가 필요한 건수만 세고, 지금 사용할 수 없는 인증서를tls_expired로 분리해 권고 상단과needs_attention에 반영했다. 덤으로 리포트를 심각도·남은 일수 순으로 정렬해(UI 가 입력 순서 그대로 그림) 만료된 인증서가 목록 맨 앞에 오게 했다. 검증: 신규 테스트 5개 중 앞 4개를 고치기 전 코드에 되돌려 붙여 각 결함을 지목하며 실패함을 확인했고(각각high/”0일 후 만료”,low/”유효 (400일 남음)”,low/300일, finding 0건), 5번째는 새TLSAttentionCounts로 집계·정렬을 지킨다.go build ./...·go vet ./...·go test ./...전부 통과(19 패키지). 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③analyzeServices의 endpoint 판정이 Succeeded/Failed Pod 까지 세어 종료된 Job Pod 만 남은 Service 를 “endpoint 있음” 으로 봄 (가치 3 / 위험 2 / S) ④ 같은 host 를 rule 두 번에 쓴 하나의 Ingress 가IngressDuplicateHost로 잡히고,defaultBackend의 없는 Service 는 검사되지 않음 (가치 2 / 위험 1 / S) ⑤SummarizeExposure의 Plaintext/Wildcard 가 finding 이 아니라 reason 수를 세어 Total 을 넘길 수 있음 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.276 (2026-09-08, run 2026-09-08-175100-Clustara-improve)
2026-09-08
- 선택: 외부 노출(Exposure Center)·Ingress/PVC 연결성 점검의 오탐·집계 결함 6종 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약: 같은 Ingress 를 두 화면이 서로 다르게 틀리게 읽고 있었다 — ①
AnalyzeExposure는spec.tls[].hosts를 문자열 그대로 대조하는데 Kubernetes 는 여기에 와일드카드를 허용하고 그것이 한 DNS 라벨을 덮는다. 그래서*.example.com인증서로 정상 서비스되는app.example.com이 “TLS 미적용(평문 노출)” +30점으로 채점돼, 노출 위험 목록에서 진짜 평문 Ingress 와 나란히 올라왔다(라벨 하나 규칙으로 대조하고 DNS 이름이므로 대소문자 무시,a.b.example.com은 계속 미커버로 남는지 오탐 회귀로 고정). ②SummarizeExposure의Plaintext/Wildcard는 finding 이 아니라RiskReasons를 세어, 와일드카드 host 3개짜리 Ingress 하나가Total=1인데Wildcard=3을 만들었다 — 이유 문자열을 상수로 묶어 문구를 바꾸면 조용히 0이 되던 결합도 함께 끊었다. ③IngressDuplicateHost는 rule 단위로 owners 에 append 해서, 같은 host 아래 path 를 rule 두 개로 나눠 적은 하나의 Ingress 가 자기 자신과 충돌하는 것으로 잡혔다(ingresses: default/solo, default/solo); 겸사겸사 이 finding 만cluster_id가 비어 있던 것과 map 순회라 실행마다 findings 순서가 달라지던 것을 고쳤다. ④IngressBackendMissing은 rule 에 일치하지 않는 모든 요청을 받는spec.defaultBackend를 아예 보지 않았고(노출 분석의TargetServices에서도 빠져 있었다), 같은 Service 를 여러 path 가 참조하면 동일한 finding 을 그 수만큼 반복해 응답의count를 부풀렸다. ⑤ PVC Pending 증적 조건이 (볼륨 실패 reason) OR (message 에 이름 포함) 이라, 네임스페이스에 PVC 가 여러 개면 A 의 증적에 B 의ProvisioningFailed가 그대로 붙었다 — 이제 이벤트의 involved object 로 대상을 확인한다(PVC 자신에 기록되는 provisioning/binding 이벤트 + 청구 이름을 message 에 적는 Pod 의 mount/attach 실패). 검증: 신규 테스트 11개(analyzer 10 + proxy 1)를 고치기 전 코드에 되돌려 붙여 9개가 각 결함을 지목하며 실패함을 확인했고(나머지 2개는 와일드카드가 깊은 host 를 덮지 않는지·존재하는 defaultBackend 가 깨끗한지 지키는 오탐 회귀),go build ./...·go vet ./...·go test ./...전부 통과. 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④isSensitivePath가 substring 매칭이라imagePullSecrets같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) ⑤ 스캔 목록 UI·DW export 에summary.scanner_detectedfallback 배지가 없음 — 저장은 되고 표시만 없음 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.277 (2026-09-08, run 2026-09-08-230105-Clustara-improve)
2026-09-09
- 선택: 연결성 점검·SEC-06 이 네임스페이스 이름만으로 교차 참조해 다른 클러스터가 이쪽 검사를 대신 통과시키던 결함 5종 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
/admin/k8s/connectivity와/admin/k8s/security는cluster_id가 선택 파라미터라 전 클러스터 보기에서 인벤토리·이벤트가 여러 클러스터를 담는데, 이 두 분석의 교차 참조가 전부 네임스페이스 이름만 맞췄다 — 클러스터를 건너면 같은 네임스페이스가 아니므로 다른 클러스터의 동명 네임스페이스가 검사를 대신 통과시켰다. ①analyzeServices가 selector 를 네임스페이스만으로 대조해 dr 클러스터의 Pod 가 prod Service 의 endpoint 로 계산되면서 실제로 비어 있는 Service 가 목록에서 사라졌고 ② 같은 이유로 다른 클러스터에 동명 Service 가 있으면IngressBackendMissing이 조용히 사라졌으며 ③IngressDuplicateHost는 반대로 오탐 — 액티브/스탠바이 한 쌍이 같은 host 를 서비스하는 DR 구성이 라우팅 충돌로 잡혔다 ④ PVC Pending 증적도 이벤트 피드가 전 클러스터를 담으므로 다른 클러스터의 같은 네임스페이스 이벤트가 이 청구의 오류로 붙었다 ⑤ 영향이 가장 큰 것은 SEC-06(NetworkPolicy 공백) — 네임스페이스 집합의 차집합이라 한 클러스터의prodNetworkPolicy 가 다른 모든 클러스터의prod를 덮어 보호되지 않은 네임스페이스가 리포트에서 통째로 빠졌다(fail-open). 어느 클러스터인지 알 수 있도록SecFinding에cluster_id를omitempty로 추가하고 map 순회라 실행마다 달라지던 순서를 정렬로 고정했다. 덤으로 같은 함수의 endpoint 판정이 종료된 Pod 까지 세던 것을 이 검사에만 좁게 고쳤다(Succeeded/Failed 는 endpoints 컨트롤러가 빼지만 GC 전까지 인벤토리에 남아, 완료된 Job Pod 만 남은 Service 가 “endpoint 있음” 으로 통과했다 — Pending·CrashLoopBackOff 는 not-ready 주소로 게시되므로 그대로 센다). 검증: 신규 테스트 6개를 고치기 전 코드에 되돌려 붙여 5개가 각 결함을 지목하며 실패함을 확인했고(6번째는 정상이 아닐 뿐인 Pod 가 계속 endpoint 로 남는지 지키는 오탐 회귀),go build ./...·go vet ./...·go test ./...전부 통과(20 패키지). 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④PodSecurityResult에cluster_id가 없어 다중 클러스터 포스처 표가 어느 클러스터인지 말하지 못함 — SecFinding 은 이번에 채웠음 (가치 2 / 위험 1 / S) ⑤isSensitivePath가 substring 매칭이라imagePullSecrets같은 참조 이름까지***로 덮어 manifest 원장 diff 에 잡음 (가치 2 / 위험 2 / S) - 릴리즈: v0.9.278 (2026-09-09, run 2026-09-09-044102-Clustara-improve)
2026-09-09
-
(원장 항목에 비밀/내부 정보 의심 문자열이 있어 비공개 기록으로 옮김 — run 2026-09-09-131238-Clustara-improve)
- 릴리즈: v0.9.279 (2026-09-09, run 2026-09-09-131238-Clustara-improve)
2026-09-10
- 선택: Pod 레벨
securityContext.runAsUser: 0이 root 로 판정되지 않던 결함 4종 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
spec.securityContext.runAsUser: 0를 Pod 레벨에 적고 컨테이너는 아무것도 적지 않는 것이 워크로드를 root 로 돌리는 가장 흔한 형태인데(Pod 값은 덮어쓰지 않은 모든 컨테이너의 기본값), 세 보안 화면이 컨테이너 securityContext 만 읽어 그 워크로드를 root 아님으로 보고했다 — ① SEC-01classifyPodSecurity는runAsUser=0위반을 아예 만들지 않았고 ② Runtime Security Profile(CLU-OCP-03)의podSecurityInput은 Pod 레벨을 읽긴 하지만 무조건 적용해 컨테이너가 실제 UID 로 덮어쓴 Pod 까지 root 로 점수를 매겼으며(반대 방향 오탐), 동시에containers만 순회해 privileged init 컨테이너·그 추가 capability·지금 붙어 있는 privileged 디버그(ephemeral) 컨테이너가 위험 설정이 하나도 없는 것으로 채점됐다(같은 Pod 를 정책 엔진과 SEC-01 은 이미 위반으로 적고 있어 제품의 두 부분이 반대 판정) ③ Workspace 건강도의podHasRuntimeSecurityRisk는 Pod 레벨을 통째로 무시하고 역시containers만 봐서 같은 Pod 를 두고 런타임 보안 화면과 답이 달랐다. 판정을 한 곳으로 모았다 —analyzer.EffectiveRunAsUser가 컨테이너 → Pod 우선순위를 양방향으로 적용하고(명시적 null 은 미설정),PodRunsAsRoot가 세 호출자 모두에게 한 번만 답하며, 두 핸들러는 포스처·정책 엔진이 이미 쓰던analyzer.SecurityRelevantContainers(regular+init+ephemeral)를 순회한다. 검증: 신규 테스트 8개를 고치기 전 코드에 되돌려 붙여 6개가 각 결함을 지목하며 실패함을 확인했고(나머지 2개는 컨테이너가 Pod 의 0 을 덮어쓴 경우와 Pod 의 non-root 를 컨테이너가 덮어쓴 경우를 지키는 오탐 회귀),go build ./...·go vet ./...·go test ./...전부 통과(20 패키지). 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④PodSecurityResult에cluster_id가 없어 다중 클러스터 포스처 표가 어느 클러스터인지 말하지 못함 (가치 2 / 위험 1 / S) ⑤capacity.podRequestGPU는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.280 (2026-09-10, run 2026-09-10-085117-Clustara-improve)
2026-09-10
- 선택: 용량 리포트(SCALE-03/04/05/07/08)가 노드·Pod 를 이름만으로 교차 참조해 다른 클러스터 값이 섞이던 결함 5종 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
/admin/k8s/capacity는cluster_id가 선택 파라미터라 전 클러스터 보기에서 인벤토리·메트릭이 여러 클러스터를 담는데, 이 리포트의 모든 교차 참조가 이름만 맞췄다 — 같은 패키지의node_monitoring이 이미nodeKey(cluster, node)로 같은 조인을 하고 있어 제품의 두 화면이 같은 노드를 다르게 계산했다. ① SCALE-07/08 은 노드를 이름으로만 키잉해 동명 노드 둘이 한 행으로 합쳐졌고(allocatable 은 인벤토리에서 마지막에 온 클러스터 것),.spec.nodeName매칭이 그 행에 두 클러스터 Pod 를 모두 더했다 — 4코어 노드 두 개(1000m·5000m 사용)가 “75% 패킹된 노드 하나” 로 나오고 다른 클러스터 노드는 표에서 사라졌다 ② SCALE-03/04 의 Pod 최신 메트릭 맵이 namespace/name 키라, newest-first 중복 제거가 한 클러스터 샘플만 남긴 뒤 그것을 동명 Pod 전부의 request 와 비교했다 — request 1000m 중 600m 을 쓰는 Pod 가 다른 클러스터 Pod 의 250m 때문에over_provisioned(비용 절감 후보)로 보고됐다 ③ SCALE-05 노드 예측은 두 클러스터 샘플을 한 추세선에 섞어(가장 오래된 샘플과 최신 샘플이 서로 다른 머신일 수 있다) 실재하지 않는 노드의 days-to-full 을 그렸고 allocatable 도 마지막에 본 클러스터 것을 썼다 ④ 어느 클러스터인지 말할 수 있도록 다섯 결과 타입에cluster_id를omitempty로 추가했다(v0.9.278 의SecFinding과 같은 방식) ⑤ 두 노드 표가 map 순회로 만들어져 요청마다 순서가 달라지던 것을 cluster→node 정렬로 고정했다. UI 쪽도 노드 표가 packing 을 이름만으로 조인하고 있었고(바로 옆 Pod 조인은 이미 클러스터를 넘기고 있었다) 용량 페이지 YAML 딥링크가 전 클러스터 보기에서 빈 cluster_id 를 쓰던 것을 행 자신의 클러스터로 바꿨다. 검증: 신규 테스트 5개를 고치기 전 키잉으로 되돌려 붙여 각 결함을 지목하며 실패함을 확인했고(합쳐진 단일 노드 행, 사라진 GPU 노드, 잘못된over_provisioned, 섞인 예측, 세 번 실행 모두 다른 순서),go build ./...·go vet ./...·go test ./...전부 통과(20 패키지). 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④PodSecurityResult에cluster_id가 없어 다중 클러스터 포스처 표가 어느 클러스터인지 말하지 못함 (가치 2 / 위험 1 / S) ⑤capacity.podRequestGPU(+ allocatable)는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.281 (2026-09-10, run 2026-09-10-173116-Clustara-improve)