Replaces the original APAC-vs-all 2-user demo (vpduser_a/b on KR_ANALYSTS/GLOBAL_ADMINS groups) with a 2x2 source-access matrix: vpduser_my -> MY_ONLY group -> MySQL view only vpduser_pg -> PG_ONLY group -> Postgres view only vpduser_both -> BOTH_SOURCES group -> both views vpduser_none -> (no group) -> nothing (default deny) Why: source-level segmentation is the more common production permission story than region-level filtering. Region filtering remains available as an opt-in variant via commented UPDATE in sql/adb/03_seed.sql. Key changes: - 03_seed.sql, 07_end_users.sql, 00_cleanup.sql, .env.example, run.sh updated for the new 4-user model. All 4 users get identical view GRANTs; the only differentiator is the permission table (proves the model is "data-driven, not GRANT-driven"). - 08-11 split into one file per user: my (+ 5 bypass attempts), pg, both, none (default-deny verification). - 12_tests_admin_audit.sql uses LEFT JOIN so vpduser_none shows up as NULL permissions, and filters by object_owner=USER to exclude cross-schema policies. - Removed inline "-- comment" after ";" lines in 03_seed.sql: SQL*Plus silently skipped the inserts (documented gotcha). - README.md + docs/01,02 updated for the 4-user matrix. docs/03 detailed guide keeps the region-filter example but now has a preface explaining it's a variant of the default 4-user model. - docs/04: db_type='mysql_community' note added (RDS MySQL). E2E verified: PG=0/MY=17, PG=12/MY=0, PG=12/MY=17, PG=0/MY=0 plus all 5 bypass attempts blocked. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
148 lines
6.4 KiB
Markdown
148 lines
6.4 KiB
Markdown
# VPD Permission POC — ADB 중심 행 단위 접근 제어
|
||
|
||
Oracle Autonomous Database (26ai) 의 **VPD (Virtual Private Database)** 와
|
||
**Data Redaction** 을 이용해, ADB 한 곳에서 여러 원본 DB (AWS RDS for PostgreSQL,
|
||
MySQL) 의 데이터를 **유저 → 그룹 → 권한 매핑 테이블** 만으로 행/열 단위 통제하는
|
||
End-to-End 데모입니다.
|
||
|
||
> 핵심: 원본 DB 는 단순한 데이터 소스로 두고, 접근 권한은 ADB 의 VPD 정책 함수와
|
||
> Secure Application Context 로 통합 관리합니다. 애플리케이션 코드 변경 없이
|
||
> 로그인하는 DB 유저에 따라 자동으로 행이 필터링됩니다.
|
||
|
||
---
|
||
|
||
## 구성요소
|
||
|
||
| 계층 | 객체 | 역할 |
|
||
|---|---|---|
|
||
| 원격 | `public.customers` (PG), `ecommerce_poc.customers` (MySQL) | 원본 데이터 |
|
||
| ADB - 연결 | `RDS_POSTGRES_LINK`, `RDS_LINK` | `DBMS_CLOUD_ADMIN.CREATE_DATABASE_LINK` 로 만든 heterogeneous DB Link |
|
||
| ADB - 매핑 | `app_customer`, `app_user`, `app_group`, `user_group`, `db_source`, `permission` | 누가 어느 소스의 어느 region 을 볼 수 있는지 |
|
||
| ADB - 컨텍스트 | `vpd_ctx` (Secure Application Context) + `ctx_pkg` | 로그인 시 권한을 세션 컨텍스트로 로딩 |
|
||
| ADB - 뷰 | `v_customers_pg`, `v_customers_my` | DB Link 위의 통합 뷰. **이 뷰에만 VPD/Redaction 정책이 붙음** |
|
||
| ADB - 정책 | `CUSTOMERS_PG_POLICY`, `CUSTOMERS_MY_POLICY`, `PII_REDACT_PG/MY` | VPD 행 필터 + 이메일 마스킹 |
|
||
| ADB - 엔드유저 | `vpduser_my`, `vpduser_pg`, `vpduser_both`, `vpduser_none` | 최소 권한. 뷰만 SELECT 가능. LOGON 트리거로 컨텍스트 자동 로딩 |
|
||
|
||
---
|
||
|
||
## 빠른 시작 (One-Click)
|
||
|
||
```bash
|
||
git clone https://github.com/<you>/vpd-permission-poc.git
|
||
cd vpd-permission-poc
|
||
|
||
# 1) 환경값 채우기
|
||
cp .env.example .env
|
||
$EDITOR .env
|
||
|
||
# 2) 끝
|
||
./run.sh
|
||
```
|
||
|
||
`./run.sh` 가 다음을 순서대로 실행합니다:
|
||
|
||
1. **prereq** — `sqlplus`, `psql`, `mysql` 존재 + `.env` 변수 검증
|
||
2. **source** — 원격 PG/MySQL 에 `customers` 테이블 + seed (멱등)
|
||
3. **adb** — ADB 측 cleanup → DB Link → 권한 테이블/seed → context/view/policy → 엔드유저
|
||
4. **tests** — 4 명 (`vpduser_my`/`vpduser_pg`/`vpduser_both`/`vpduser_none`) 로 접속해 행 필터/마스킹/default-deny 검증
|
||
5. **audit** — ADMIN 으로 정책/뷰/유저 상태 점검
|
||
|
||
세부 단계만 따로 돌리려면:
|
||
|
||
```bash
|
||
./run.sh prereq # 환경만 검증
|
||
./run.sh source # 원격 DB 만 준비
|
||
./run.sh adb # ADB 만 준비
|
||
./run.sh tests # 엔드유저 테스트만
|
||
./run.sh audit # ADMIN audit 만
|
||
./run.sh teardown # ADB 측 객체 + DB Link/credential 정리
|
||
```
|
||
|
||
---
|
||
|
||
## 사전 준비
|
||
|
||
| 항목 | 필요 사항 |
|
||
|---|---|
|
||
| ADB | Autonomous Database (Always Free 도 가능). Wallet 다운로드 + Instant Client 에 풀어둠 |
|
||
| PostgreSQL | 호스트/포트 도달 가능. `vpdpoc` (혹은 원하는 이름) DB 가 미리 생성되어 있어야 함 |
|
||
| MySQL | 호스트/포트 도달 가능. `ecommerce_poc` (혹은 원하는 이름) DB 가 미리 생성되어 있어야 함 |
|
||
| 클라이언트 도구 | `sqlplus` (Instant Client), `psql`, `mysql` |
|
||
|
||
`.env.example` 의 각 항목 의미는 같은 파일 안 주석 참고.
|
||
|
||
---
|
||
|
||
## 데모 시나리오 — 2×2 source access matrix
|
||
|
||
`sql/adb/03_seed.sql` 의 매핑 (4 유저, 4 케이스):
|
||
|
||
| DB 유저 | 그룹 | PG 뷰 | MySQL 뷰 | VPD 결과 |
|
||
|---|---|---|---|---|
|
||
| `vpduser_my` | `MY_ONLY` | ✗ 0 rows | ✓ ALL | PG=`1=0` / MY=`NULL` |
|
||
| `vpduser_pg` | `PG_ONLY` | ✓ ALL | ✗ 0 rows | PG=`NULL` / MY=`1=0` |
|
||
| `vpduser_both` | `BOTH_SOURCES` | ✓ ALL | ✓ ALL | PG=`NULL` / MY=`NULL` |
|
||
| `vpduser_none` | (그룹 없음) | ✗ 0 rows | ✗ 0 rows | PG=`1=0` / MY=`1=0` (default deny) |
|
||
|
||
핵심 포인트:
|
||
|
||
* 네 유저 모두 **양쪽 뷰에 SELECT GRANT 는 동일하게 주어집니다.** 차이를 만드는 건
|
||
GRANT 가 아니라 `permission` 테이블의 행. 즉 **권한 회수 = GRANT 해제가 아니라
|
||
permission row 삭제**.
|
||
* `vpduser_none` 처럼 매핑이 아예 없는 유저는 자동으로 fail-closed
|
||
(`1=0` predicate) — **deny by default**.
|
||
* 누구든 원본 테이블 직접 접근 시도 (`@RDS_POSTGRES_LINK` 등) → 권한 없음.
|
||
|
||
`sql/adb/08_tests_user_my.sql` 가 우회 시도 5개 (원격 직접 SELECT, 컨텍스트
|
||
스푸핑, DBMS_RLS 변경, 매핑 테이블 SELECT) 를 시도하고 모두 ORA-xxxxx 로 실패하는 것을
|
||
보여줍니다. 09/10/11 은 각 유저의 expected 행 수를 가볍게 확인합니다.
|
||
|
||
> Row-level region 필터링 (예: `vpduser_both` 가 PG 는 APAC 만) 도 정책 함수에
|
||
> 그대로 살아있습니다. `03_seed.sql` 하단의 주석 처리된 `UPDATE permission ...
|
||
> allowed_regions='APAC'` 한 줄이면 활성화됩니다.
|
||
|
||
---
|
||
|
||
## 디렉토리
|
||
|
||
```
|
||
.
|
||
├── run.sh # 원클릭 엔트리포인트
|
||
├── .env.example
|
||
├── scripts/lib/common.sh # log/ok/warn/die + env 검증 헬퍼
|
||
├── sql/
|
||
│ ├── source/
|
||
│ │ ├── postgres_setup.sql # 원격 PG: customers + 12 rows
|
||
│ │ └── mysql_setup.sql # 원격 MySQL: customers + 12 rows
|
||
│ └── adb/
|
||
│ ├── 00_cleanup.sql # 멱등 teardown
|
||
│ ├── 01_dblinks.sql # DB Link + credential 생성
|
||
│ ├── 02_perm_tables.sql # 6개 권한 매핑 테이블
|
||
│ ├── 03_seed.sql # 데모 매핑 데이터
|
||
│ ├── 04_secure_ctx.sql # ctx_pkg + vpd_ctx (Secure App Context)
|
||
│ ├── 05_views.sql # DB Link 통합 뷰
|
||
│ ├── 06_policy.sql # VPD 정책 + 정책 함수
|
||
│ ├── 06a_redaction.sql # Data Redaction (이메일 마스킹)
|
||
│ ├── 07_end_users.sql # 4 유저 + LOGON 트리거
|
||
│ ├── 08_tests_user_my.sql # MY only — 우회 시도 5종 포함
|
||
│ ├── 09_tests_user_pg.sql # PG only
|
||
│ ├── 10_tests_user_both.sql # both
|
||
│ ├── 11_tests_user_none.sql # default deny (fail-closed) 검증
|
||
│ └── 12_tests_admin_audit.sql
|
||
└── docs/
|
||
└── 03-detailed-guide.md # 한국어 상세 설명 (아키텍처, 정책 로직, 운영 고려사항)
|
||
```
|
||
|
||
---
|
||
|
||
## 더 깊이 알고 싶으면
|
||
|
||
* [docs/03-detailed-guide.md](docs/03-detailed-guide.md) — 권한 매핑 모델, 정책
|
||
함수의 동적 SQL, Secure Context 사용 이유, 운영 시 주의점 등 전체 해설.
|
||
|
||
---
|
||
|
||
## 라이선스
|
||
|
||
MIT.
|