Skip to content

feat(database): PostgreSQL RLS 접근 거부 관측과 제한 역할 E2E 검증 - #99

Merged
hywznn merged 8 commits into
mainfrom
feat/34-rls-observability-http-e2e
Aug 6, 2026
Merged

feat(database): PostgreSQL RLS 접근 거부 관측과 제한 역할 E2E 검증#99
hywznn merged 8 commits into
mainfrom
feat/34-rls-observability-http-e2e

Conversation

@krestar

@krestar krestar commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

관련 Issue

이번 PR은 운영·staging runtime role 발급이나 RLS 활성화 migration까지 포함하지 않습니다.
해당 작업은 #9와 staging 검증에서 이어서 진행합니다.

무엇을 바꾸나요?

  • PostgreSQL SQLSTATE 42501을 cause chain과 SQLException#getNextException() chain에서 분류합니다.
  • 확인된 DB 접근 거부를 기존 내부 서버 오류 응답 형태로 안전하게 반환하고,
    route pattern 기반 구조화 로그와 저카디널리티 metric으로 관측합니다.
  • 일반적인 ObjectOptimisticLockingFailureException은 기존 CONCURRENT_MODIFICATION 계약을 유지합니다.
  • 공유 PostgreSQL 보안 테스트에 bounded advisory lock과 테이블별 RLS/FORCE 상태 snapshot·복원을 적용합니다.
  • 제한 역할과 명시적 최소 GRANT로 Worker Repository의 silent filtering, INSERT 안전 실패, UPDATE·DELETE 불변성을 검증합니다.
  • 제한 역할로 실제 Spring 애플리케이션을 기동해 Login, Refresh Token, Worker Link, Worker API의 정상 흐름과 tenant A/B 격리를 검증합니다.
  • test-only unbound Worker INSERT를 통해 HTTP 500, 안전한 응답·로그·metric, DB 불변성을 함께 확인합니다.
  • PostgreSQL rollout 문서에 CI fixture와 실제 staging 역할의 경계, 적용 순서, smoke test 및 forward-only 복구 절차를 기록합니다.

주요 보안 계약

  • 42501DATABASE_ACCESS_DENIED 관측 대상으로 분류합니다.
  • SQL, bound parameter, token, email, company ID, DB 객체명과 원본 예외 메시지를 응답·일반 로그·metric tag에 노출하지 않습니다.
  • RLS USING에 따른 SELECT empty 및 UPDATE·DELETE 0건은 silent filtering으로 취급하며 42501로 오분류하지 않습니다.
  • 제한 역할은 NOSUPERUSER, NOBYPASSRLS, NOINHERIT이고 table owner나 migration role member가 아닙니다.
  • 테스트 종료 시 Spring context와 connection pool을 먼저 닫고, fixture 데이터·권한·역할·RLS 상태·advisory lock 순으로 안전하게 정리합니다.

어떻게 검증했나요?

아래 테스트는 사용자가 로컬에서 직접 실행해 통과를 확인했습니다.

.\gradlew.bat test --tests "*PostgreSqlWorkerRepositoryRlsTest"
.\gradlew.bat test --tests "*PostgreSqlRestrictedRoleHttpE2ETest"
.\gradlew.bat test --tests "*OutboxIntegrationTest"
.\gradlew.bat clean test
  • 전체 suite: 297개 테스트 통과
  • 전체 suite는 최종적으로 연속 2회 통과했습니다.
  • 최초 전체 실행에서 Outbox 시간 경계 실패가 발견되어 테스트 전용 고정 UTC Clock으로 안정화했습니다.
  • 이후 한 차례 기존 AuthSecurityIntegrationTest.replayingAUsedRefreshTokenRevokesTheWholeFamily()가 간헐 실패했으나, 같은 코드로 이어진 전체 실행 2회는 통과했습니다.

API·DB·운영 영향

  • 외부 API schema와 정상 응답 계약은 변경하지 않습니다.
  • 확인된 PostgreSQL 접근 거부는 기존 내부 서버 오류 계열 응답을 유지하면서 관측 정보만 추가합니다.
  • Flyway migration, 실제 runtime credential, Secret, 환경별 GRANT와 RLS 활성화는 포함하지 않습니다.
  • staging에서는 실제 runtime role의 최소 GRANT와 bootstrap 함수 실행 권한을 별도로 확정해야 합니다.

후속 작업

  • #9에서 실제 dev·staging·prod runtime role, 최소 GRANT, credential와 Secret을 구성합니다.
  • 별도 forward migration으로 RLS를 활성화하고 staging smoke test를 수행합니다.
  • staging 검증 전에는 #34를 자동 종료하지 않습니다.

krestar added 6 commits August 6, 2026 18:22
- SQLSTATE 42501을 cause 및 next-exception 체인에서 안전하게 분류한다.
- 접근 거부를 내부 500 응답, route-pattern 로그와 단일 metric으로 관측한다.
- 일반 낙관적 락은 기존 CONCURRENT_MODIFICATION 계약을 유지한다.
제한 시간 advisory lock으로 공유 PostgreSQL 보안 테스트를 직렬화한다.

테이블별 RLS와 FORCE 초기 상태를 저장해 정확히 복원하고, setup/cleanup 실패 시에도 역할과 연결 정리를 끝까지 수행하며 오류를 보존한다.
제한된 PostgreSQL 역할로 Worker 저장소의 조회·생성·수정·삭제 실패 폐쇄 동작을 검증한다.

역할과 Spring 애플리케이션 수명주기, Worker 데이터와 스냅샷을 두 fixture로 분리하고 기존 RLS 잠금·상태 복원 및 안전한 정리 순서를 유지한다.
임시 제한 runtime role과 명시적 최소 GRANT로 Spring 애플리케이션을 기동해 Login, Refresh Token, Worker Link 및 Worker API의 tenant 격리를 검증한다.

테스트 전용 unbound INSERT로 SQLSTATE 42501의 안전한 HTTP 응답·로그·metric 계약을 확인하고, fixture ID 충돌 방지와 RLS 상태 복원 및 역할 정리 순서를 보장한다.
CI·로컬 임시 제한 역할과 staging runtime 역할의 책임을 구분하고, SQLSTATE 42501 관측과 silent filtering의 차이를 정리한다.

HTTP E2E, advisory lock, RLS 상태 복원, production DELETE 비범위와 staging 후속 검증 조건을 rollout 가이드에 기록한다.
Outbox 통합 테스트에 고정 UTC Clock을 주입해 최초 발행과 즉시 claim의 시간 비교를 결정적으로 만든다.

lease 만료 시각도 애플리케이션 Clock 기준으로 설정해 H2 DB 시계와의 경계 차이로 발생하는 간헐 실패를 방지한다.
@krestar krestar added the status:in-progress 담당자가 현재 구현 중인 작업 label Aug 6, 2026
@hywznn

hywznn commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

ㅎㅇ

공유 PostgreSQL 테스트가 Flyway migration 전에 advisory lock을 획득하도록 수명주기 순서를 통일한다.

HTTP 및 RLS isolation fixture가 직접 변경하는 task와 user_account 테이블의 RLS 상태를 정확히 저장·복원하고, 제한 runtime role에 task 권한이 없음을 검증한다.
@krestar krestar added status:in-review 구현을 마치고 리뷰 또는 병합을 기다리는 작업 and removed status:in-progress 담당자가 현재 구현 중인 작업 labels Aug 6, 2026
@hywznn

hywznn commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

확인했습니다!
DB 접근 권한 오류 발생 시 SQL이나 테이블명 같은 내부 정보가 응답과 로그에 노출되지 않고, 기존 낙관적 락 응답도 그대로 유지되는 것을 확인했습니다.
데모 기준으로 기존 기능에 영향을 주는 부분은 없어 보입니다. 👍

참고로 코드 문제는 아니지만, 열린 PR인 #100·#102도 PostgreSqlRlsIsolationTest.java를 수정하고 있어서 나중에 병합하는 PR에서 충돌 정리는 필요합니다.

# Conflicts:
#	src/test/java/com/fowoco/server/common/security/PostgreSqlRlsIsolationTest.java
@hywznn
hywznn requested a review from chaeliki August 6, 2026 15:21

@hywznn hywznn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

최신 main 반영 후 충돌 해결 내용을 확인
RLS 상태 보관·복원 구조와 약관 동의·비밀번호 재설정 테이블 검증이 함께 유지되며, 로컬 전체 테스트와 PostgreSQL 기반 Server CI가 통과 확인

@hywznn
hywznn merged commit 7717a67 into main Aug 6, 2026
4 checks passed
@hywznn
hywznn deleted the feat/34-rls-observability-http-e2e branch August 6, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:in-review 구현을 마치고 리뷰 또는 병합을 기다리는 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants