Skip to content

fix(auth): fail closed without write-capable admin on public bind - #94

Closed
seonghobae wants to merge 27 commits into
mainfrom
fix/issue-78-fail-closed-credentials
Closed

fix(auth): fail closed without write-capable admin on public bind#94
seonghobae wants to merge 27 commits into
mainfrom
fix/issue-78-fail-closed-credentials

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the operator-visible half of issue #78: a non-loopback listener no longer becomes ready with open management writes when no write-capable admin principal is configured.

  • require_write_auth_for_bind refuses 0.0.0.0 / :: / LAN binds without ADMIN_TOKEN, ADMIN_TOKENS (write role), or WAF_IDS_CREDENTIALS_PATH.
  • Loopback development still starts without a token and reports auth_mode=development on /healthz.
  • Management writes return 401 (unauthenticated) vs 403 (authenticated, not permitted to write) without naming the expected role.
  • Presented admin secrets compare in constant time, including lengths that differ by a multiple of 256; ADMIN_TOKENS rejects blank, duplicate, and unknown roles at startup; readonly-only maps fail closed.
  • Blank WAF_IDS_STATE_PATH is in-memory state. Blank WAF_IDS_CREDENTIALS_PATH is unset.
  • scripts/smoke.sh generates a per-process admin token (no shared source-tree secret) and sends X-Admin-Token on audit-log reads. Strix CWE-798 on dev-secret is closed without weakening the scanner.
  • Destination-policy ([P0] Enforce a fail-closed destination policy for all outbound traffic #79) and proven-engine payload ([P0] Put proven WAF/IDS engines in the enforcement path and publish detection-quality evidence #86) remain separate PRs after concurrent scoping.

Doctoring (APA 7th): docs/doctoring/fail-closed-management-auth.md.

Merge note

Org ruleset CWL Central required workflows (id 18156473) requires two approving reviews. Do not --admin merge. Do not self-approve.

Fixes #78.

Summary by CodeRabbit

  • 보안

    • 비루프백 주소에서는 쓰기 권한이 있는 관리자 인증 없이는 서비스가 준비되지 않습니다.
    • 읽기 전용 토큰은 조회만 가능하며 변경 요청에는 403 Forbidden이 반환됩니다.
    • 중복·빈 관리자 토큰과 알 수 없는 역할이 거부되며, 인증 실패와 권한 부족이 구분됩니다.
    • 상태 및 헬스 체크에 인증 모드가 표시됩니다.
  • 새 기능

    • CISA KEV 카탈로그를 가져와 위협 지표로 등록할 수 있습니다.
    • 운영자가 등록한 위협 지표가 피드 동기화로 덮어쓰이거나 삭제되지 않습니다.
  • 문서

    • 운영·배포·보안 문서와 관리자 콘솔 디자인 시스템 및 접근성 안내가 보강되었습니다.
  • 테스트

    • 비루프백 무자격 실행, 손상된 상태 경로, 관리자 인증 동작 검증이 강화되었습니다.

Non-loopback BIND_ADDR refuses readiness unless ADMIN_TOKEN, ADMIN_TOKENS,
or WAF_IDS_CREDENTIALS_PATH supplies a write-capable principal. Loopback
development still starts and reports auth_mode=development. Management
writes return 401 vs 403; presented secrets compare in constant time.
Blank WAF_IDS_STATE_PATH is in-memory state, not a post-ready crash.

Fixes #78.
The fail-closed credentials change opened as #94; leftover merge
blockers remain the org 2-approval ruleset and unread strix 404s.
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 55 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 79451ee7-b87c-4c1f-83a4-936ee448c2b6

📥 Commits

Reviewing files that changed from the base of the PR and between 0d2c495 and a0cbcd5.

📒 Files selected for processing (3)
  • CLAUDE.md
  • src/lib.rs
  • tests/fuzz_invariants.rs
📝 Walkthrough

Walkthrough

관리자 인증을 RBAC 기반으로 확장했습니다. 비루프백 바인딩은 쓰기 권한 인증 없이는 시작되지 않습니다. CISA KEV 가져오기와 피드 소유권 처리를 추가하고, health 상태·테스트·운영 문서를 갱신했습니다.

Changes

관리 인증 및 위협 피드 관리

Layer / File(s) Summary
자격 증명과 시작 게이트
src/credentials.rs, src/lib.rs, tests/binary.rs
자격 증명 정규화, constant-time 비교, loopback 판별과 비루프백 바인딩 게이트를 추가했습니다.
RBAC 관리 요청과 health 상태
src/lib.rs, fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs
엄격한 토큰 파싱과 읽기·쓰기 권한 분리를 적용했습니다. 읽기 전용 토큰의 쓰기 요청은 403을 반환합니다. health 응답에 auth_mode를 추가했습니다.
CISA KEV 가져오기와 피드 소유권
src/lib.rs
서버 측 CISA KEV 가져오기, URL 검증, CVE 매핑, 랜섬웨어 연계 심각도 승격과 피드·운영자 소유권 처리를 추가했습니다.
인증·자격 증명 검증
src/credentials.rs, src/lib.rs, tests/binary.rs, tests/fuzz_invariants.rs, fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs, scripts/smoke.sh
자격 증명 JSON, strict 토큰 파서, 시작 순서, readiness, KEV 가져오기와 smoke 흐름을 검증했습니다.
보안·운영·제품 문서
README.md, CLAUDE.md, docs/*, CHANGELOG.md
비루프백 인증 요구, development·production 모드, 401·403 응답, 복구 절차와 디자인 기준을 문서화했습니다.
저장소 및 탐색 지침
.gitignore, AGENTS.md
.codegraph를 무시 목록에 추가하고 CodeGraph 인덱스 사용 지침을 변경했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🟠 High · up to 0d2c4

The change improves the standard startup path, but current validation can still accept whitespace-only credentials or misclassify a localhost name before the actual listener address is checked, allowing management writes to become reachable without a valid write-capable administrator. Alternate application launch paths can also bypass the listener safeguard, so the PR is not safe to merge until these authentication-boundary issues are fixed or explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant run_from_env
  participant CredentialRegistry
  participant ManagementAPI
  participant KEVSource
  participant ThreatStore
  Operator->>run_from_env: BIND_ADDR 및 관리자 자격 증명 제공
  run_from_env->>CredentialRegistry: 토큰과 credentials 파일 로드
  run_from_env->>ManagementAPI: 인증된 서버 시작
  Operator->>ManagementAPI: KEV import 요청과 관리자 토큰 전달
  ManagementAPI->>KEVSource: 서버 설정 URL에서 카탈로그 가져오기
  KEVSource-->>ManagementAPI: KEV 카탈로그 반환
  ManagementAPI->>ThreatStore: CVE 위협 지표와 피드 소유권 적용
  ThreatStore-->>Operator: 가져오기 결과 반환
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 인증 변경과 직접 관련이 없는 CISA KEV import 및 threat-feed 소유권 변경, SOC LLM 설정, Figma·UI/UX·design-system 문서, product-technical gap 문서가 포함되어 있습니다. 이 변경은 issue #78의 범위를 벗어납니다. 인증 fail-closed 변경에 직접 필요한 코드, 테스트, 운영 문서만 유지하십시오. CISA KEV·threat-feed·SOC LLM·Figma·UI/UX·product-gap 변경은 관련 issue를 연결한 별도 PR로 분리하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 70.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 65 functions across 4 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 비루프백 바인드에서 쓰기 가능한 관리자 인증이 없을 때 fail-closed하는 핵심 변경을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed PR은 issue #78의 핵심 요구사항을 충족합니다. 비루프백 시작 차단, 루프백 개발 모드, credential 검증, constant-time 비교, readonly/write 권한 분리, 401/403 응답, 상태 표시, 관련 테스트와 운영 문서를 구현했습니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 70.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 65 functions across 4 files. (4 skipped: 4 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-78-fail-closed-credentials

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

devin-ai-integration[bot]

This comment was marked as resolved.

One DestinationPolicy mediates gateway upstreams, threat-intel fetches,
Clearfolio, and SOC LLM. Private, loopback, link-local, CGNAT, and
metadata classes are denied unless DESTINATION_ALLOWLIST (or loopback
development) permits them; DESTINATION_DENYLIST wins. Clients ignore
ambient HTTP proxies and do not follow redirects.

Refs #79.
devin-ai-integration[bot]

This comment was marked as resolved.

Coraza/Suricata ingest already fed IP and path hints. Also persist the
audit URI query as an engine_payload indicator so the same CRS payload
is blocked on /gateway for a different client, keeping route-scoped
block mode.

Refs #86.
devin-ai-integration[bot]

This comment was marked as resolved.

Restore PR #94 to its issue #78 fail-closed admin-auth scope. Destination-policy (#79) and proven-engine payload (#86) work remain separate responsibility lanes; their review findings must not block the auth repair.
RED: preserve the Devin findings as executable regressions before changing production behavior. The constant-time helper must reject length deltas that alias through u8, and blank credential paths must be normalized explicitly rather than relying on platform-specific read errors.
GREEN: normalize blank credential bootstrap paths before filesystem access and retain a full length-inequality bit in constant-time secret comparison. This closes the current auth-scope Devin findings without restoring unrelated destination/WAF drift.
RED: a corrupt persisted-state file currently binds and prints the listening readiness line before AppState::load rejects the file. Preserve the review finding as an executable binary regression before changing startup ordering.
Strix failed PR #94 as CWE-798 on the hardcoded smoke.sh secret.
The process still sets ADMIN_TOKEN from that value before listen;
generating it at runtime removes the shared source-tree credential
without weakening the scanner.
devin-ai-integration[bot]

This comment was marked as resolved.

Corrupt WAF_IDS_STATE_PATH must fail closed before TcpListener bind and
the "waf-ids-ai-soc listening on" line, so a supervisor cannot treat a
dying process as ready.
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Exact-head f31d960a0b52bd037c6611a59a07e425c35e642b: rust and most checks green; Strix still FAIL (infrastructure model path, not product finding). Destination-policy remains on #96/#79. Requesting independent review for ruleset 18156473. Do not weaken fail-closed public bind.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae
seonghobae enabled auto-merge (squash) August 26, 2026 17:37
devin-ai-integration[bot]

This comment was marked as resolved.

@opencode-agent
opencode-agent Bot disabled auto-merge August 26, 2026 17:44
@seonghobae
seonghobae enabled auto-merge (squash) August 26, 2026 18:10
@opencode-agent
opencode-agent Bot disabled auto-merge August 26, 2026 18:46
@seonghobae
seonghobae enabled auto-merge (squash) August 26, 2026 19:19
devin-ai-integration[bot]

This comment was marked as resolved.

@opencode-agent
opencode-agent Bot disabled auto-merge August 26, 2026 20:26
@seonghobae
seonghobae enabled auto-merge (squash) August 26, 2026 20:55
@opencode-agent
opencode-agent Bot disabled auto-merge August 31, 2026 01:06
Adopt protected main@b2bcee3 (CISA KEV catalog) without dropping the
issue #78 gate: non-loopback binds still refuse readiness without a
write-capable admin principal. Credential-file bootstrap keeps
parse_credentials_json fail-closed on blank/null keys while recording
admin_from_file provenance. Hostname localhost is not a loopback
exception; only numeric 127.0.0.0/8 and ::1 skip the write-capable
requirement.

Signed-off-by: Seongho Bae <me@seonghobae.me>

Copy link
Copy Markdown
Contributor Author

@opencode-agent review | Independent exact-current-head formal review requested for 0d2c4952ab1ed731f05690ca4787f5ca76cd0ad9 (merge of protected main@b2bcee3 KEV catalog into the issue #78 fail-closed public-bind gate). Read the full current diff and current-head checks only; do not reuse predecessor approvals or hosted evidence. Destination-policy (#79/#96) and engine-payload (#86) stay out. Do not weaken fail-closed public bind. Hostname localhost is not a loopback exception.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 new potential issues.

Devin Review

Comment thread AGENTS.md
Comment thread src/lib.rs Outdated
Comment thread src/lib.rs
Comment thread src/lib.rs Outdated
Comment thread docs/adr/0001-figma-and-design-system.md
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Hour-20 exact-head review request.

Current head 0d2c4952ab1ed731f05690ca4787f5ca76cd0ad9. Devin/CodeRabbit/author COMMENTED is not independent APPROVE. OpenCode DISMISSED/REQUEST_CHANGES on predecessor heads does not transfer. Ruleset 18156473 needs two independent APPROVE reviews on this exact SHA.

Issue #78 fail-closed public bind. Destination #79/#96 and engine-payload #86 stay out. Localhost is not a loopback exception.

Do not self-approve. Do not --admin merge. Checks/reviews are not a reason to weaken fail-closed gates.

Copy link
Copy Markdown
Contributor Author

Hour-21 exact-head review request.

Current head 0d2c4952ab1ed731f05690ca4787f5ca76cd0ad9.
Devin/CodeRabbit/author COMMENTED is not independent APPROVE. Copilot review requests are not APPROVE. Ruleset 18156473 needs two independent approvals. Do not self-approve. Do not merge without two independent APPROVE reviews on this exact head.

@opencode-agent review

Copy link
Copy Markdown
Contributor Author

Hour-22 exact-head review request.

Current head 7b13e0e9086fa448490df9fe1847750896450c85 (moved from 0d2c4952ab1ed731f05690ca4787f5ca76cd0ad9 by fix(auth): preserve state and write-gate semantics). Predecessor Checks/reviews do not transfer.

Issue #78 fail-closed public bind. Blank/null credentials fail closed. Localhost is not a loopback exception. Destination #79/#96 and engine-payload #86 stay out.

Devin/CodeRabbit/author COMMENTED is not independent APPROVE. Copilot review requests are not APPROVE. Ruleset 18156473 needs two independent approvals on this exact SHA. Do not self-approve. Do not --admin merge.

@opencode-agent review

Copy link
Copy Markdown
Contributor Author

Hour-23 exact-head review request.

Current head 7b13e0e9086fa448490df9fe1847750896450c85.
Devin/CodeRabbit/author COMMENTED is not independent APPROVE. Copilot review requests are not APPROVE. Ruleset 18156473 needs two independent approvals on this exact SHA. Do not self-approve. Do not merge without two independent APPROVE reviews on this exact head.

Issue #78 fail-closed public bind. Destination #79/#96 and engine-payload #86 stay out. Localhost is not a loopback exception.

@opencode-agent review

Copy link
Copy Markdown
Contributor Author

Superseded by the clean current-main replacement #138. Fresh comparison shows both branches share the issue #78 runtime-auth scope, while #138 carries the current focused auth implementation plus the newer admin_auth_properties and smoke-script security regressions. The one unique issue-78 artifact worth preserving from this older branch, docs/doctoring/fail-closed-management-auth.md, has been ported to #138 in commit efca66ab86d9f8ec6cfd7e48d3471effd437df15. The remaining #94-only files are unrelated/stale branch-history documentation and are already owned by protected main or dedicated successor work (including the product-gap baseline). No predecessor checks/reviews transfer to #138.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P0] Fail closed when management credentials are absent

2 participants