Skip to content

[Security] Implement authorization model for OA API (JWT scope-based RBAC) #8

@1xp-dorami

Description

@1xp-dorami

Summary

현재 JWT 인증(HS256 + exp/iss/aud)은 구현되어 있으나, 인가(Authorization)가 없음.
유효한 토큰만 있으면 namespace/service/capability 구분 없이 전체 API 접근 가능.

"인증은 있는데 인가가 없는" 전형적인 Broken Access Control

현재 상태

  • src/auth.ts: JWT 유효성만 검사, payload를 request.user에 붙이기만 함
  • src/k8s/routes.ts: target.namespace 기본값 "*" — cluster-wide 조회
  • /v1/pods: podIP, labels, annotations, nodeName 전체 반환
  • /v1/bundles, /download: scope 제한 없음
  • src/standalone/routes.ts: /v1/services 서비스 메타(metrics URL, logs 경로) 전체 노출

요구사항

JWT Claim 기반 권한 설계

{
  "sub": "agent-01",
  "allowedNamespaces": ["prod", "monitoring"],
  "allowedServices": ["validator-*"],
  "capabilities": ["pods", "logs", "events", "metrics"],
  "admin": false
}

라우트별 적용

  • /v1/pods: allowedNamespaces + capabilitiespods 포함 필수
  • /v1/bundles: namespace/service scope 적용
  • /v1/bundles/:id/download: 동일 scope
  • /v1/services (standalone): allowedServices 필터링
  • namespace="*": admin: true 토큰에서만 허용

응답 축소 (관련 Medium 이슈 포함)

  • /v1/pods 기본 응답에서 annotations, nodeName, podIP 제거 (관리자 scope에서만)
  • /v1/services에서 metrics URL, logs 경로 등 민감 필드 축소

추가 보강 항목 (같은 이슈에서 추적)

  • trustProxy=true + OA_ALLOWED_IPS 동시 사용 시 경고/차단 로직
  • Rate limit 추가 (토큰 탈취 시 과부하 완화)
  • OA_SERVICES 기반 standalone logs 경로에 path.resolve() + allowlist 디렉토리 체크
  • OA_SERVICES 기반 standalone metrics URL에 protocol/host allowlist
  • JWT maxAge 옵션 추가 (토큰 수명 정책)
  • 에러 메시지 일반화 (내부 정보 노출 방지)

Acceptance Criteria

  • 권한 없는 토큰으로 ns=* 호출 시 403
  • 허용 namespace 내에서만 정상 조회
  • capabilities 미포함 리소스 요청 시 403
  • /v1/bundles/:id/download에도 동일 scope 적용
  • 기존 테스트 회귀 통과

Test Scenarios

  1. Token A (allowedNamespaces: ["prod"], capabilities: ["pods"]) -> ns=* 호출 -> 403
  2. Token B (allowedNamespaces: ["prod"], capabilities: ["logs"]) -> metrics 요청 -> 403
  3. Admin token -> ns=* 호출 -> 성공
  4. /v1/bundles/:id/download scope 정책 적용 확인

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions