Add optional Oracle 26ai Deep Data Security variant
Reimplements the same 4-user source-access matrix using Oracle AI Database 26ai's Deep Data Security (DDS) — VPD's declarative SQL successor. Coexists with the VPD demo (ddsuser_* / dds_* prefixes, MAC intentionally not enabled on shared views). - sql/adb/13_dds_variant.sql: CREATE END USER + CREATE DATA ROLE + CREATE DATA GRANT for the same 4-user matrix; row-filter and column-mask variants shown as commented examples. - docs/05-dds-variant.md: prereqs (23.26.2+, COMPATIBLE>=20.0), VPD <-> DDS 1:1 mapping table, run + teardown snippets. - .env.example: DDSUSER_*_PASSWORD block (3b). - README.md: tree + "더 깊이" link. Not wired into run.sh — kept manual since DDS requires 26ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,14 @@ export VPDUSER_PG_PASSWORD="RowFilter#Pg2026"
|
||||
export VPDUSER_BOTH_PASSWORD="RowFilter#Both26"
|
||||
export VPDUSER_NONE_PASSWORD="RowFilter#None26"
|
||||
|
||||
# --- (3b) OPTIONAL — DDS 변형 데모용 엔드유저 비밀번호 ---
|
||||
# sql/adb/13_dds_variant.sql (Oracle 26ai Deep Data Security 재구현) 에서만 사용.
|
||||
# 동일한 4-user 매트릭스를 declarative Data Grants 로 재현. VPD 데모와 공존.
|
||||
export DDSUSER_MY_PASSWORD="DdsGrant#My2026"
|
||||
export DDSUSER_PG_PASSWORD="DdsGrant#Pg2026"
|
||||
export DDSUSER_BOTH_PASSWORD="DdsGrant#Both26"
|
||||
export DDSUSER_NONE_PASSWORD="DdsGrant#None26"
|
||||
|
||||
# --- (4) 원격 Postgres (AWS RDS, Cloud SQL, ...) ---
|
||||
# sql/source/postgres_setup.sql 가 여기로 customers 테이블/seed 생성.
|
||||
# ADB 의 RDS_POSTGRES_LINK 가 이 인스턴스를 가리킴.
|
||||
|
||||
@@ -128,9 +128,11 @@ $EDITOR .env
|
||||
│ ├── 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
|
||||
│ ├── 12_tests_admin_audit.sql
|
||||
│ └── 13_dds_variant.sql # (선택) 같은 4-user 매트릭스를 26ai Deep Data Security 로 재구현
|
||||
└── docs/
|
||||
└── 03-detailed-guide.md # 한국어 상세 설명 (아키텍처, 정책 로직, 운영 고려사항)
|
||||
├── 03-detailed-guide.md # 한국어 상세 설명 (아키텍처, 정책 로직, 운영 고려사항)
|
||||
└── 05-dds-variant.md # (선택) VPD ↔ DDS 1:1 매핑 + 26ai 변형 사용법
|
||||
```
|
||||
|
||||
---
|
||||
@@ -139,6 +141,9 @@ $EDITOR .env
|
||||
|
||||
* [docs/03-detailed-guide.md](docs/03-detailed-guide.md) — 권한 매핑 모델, 정책
|
||||
함수의 동적 SQL, Secure Context 사용 이유, 운영 시 주의점 등 전체 해설.
|
||||
* [docs/05-dds-variant.md](docs/05-dds-variant.md) — Oracle AI Database 26ai
|
||||
의 신기능 **Deep Data Security** 로 같은 데모를 재구현 (선택). 선언형 SQL
|
||||
Data Grant 가 VPD 의 PL/SQL 정책 함수를 어떻게 한 줄로 대체하는지 비교.
|
||||
|
||||
---
|
||||
|
||||
|
||||
149
docs/05-dds-variant.md
Normal file
149
docs/05-dds-variant.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# 05 · DDS Variant — 같은 데모를 Oracle Deep Data Security 로
|
||||
|
||||
> 본 문서는 **선택사항** 입니다. 메인 데모(`run.sh all`) 는 전통적 **VPD** 경로로
|
||||
> 그대로 동작합니다. 이 문서는 동일한 4-user 매트릭스를 Oracle AI Database
|
||||
> **26ai 신기능 Deep Data Security (DDS)** 로 재구현한 `sql/adb/13_dds_variant.sql`
|
||||
> 의 사용법과, VPD ↔ DDS 1:1 매핑을 다룹니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 왜 DDS 도 같이 다루는가
|
||||
|
||||
Oracle 은 **2026-04-09** 에 VPD/RAS 의 공식 후계자로 **Deep Data Security**
|
||||
(이하 DDS) 를 발표했습니다 (Oracle AI Database 26ai). 공식 한 줄:
|
||||
|
||||
> "Extends and modernizes Oracle Virtual Private Database and Real Application
|
||||
> Security, moving from procedural PL/SQL and API-based controls **to
|
||||
> declarative policies in SQL**."
|
||||
|
||||
본 POC 는 의도적으로 **VPD 를 직접 구현하는 경로** 를 보여주는데, 같은 요건을
|
||||
DDS 로 표현하면 코드량과 메커니즘이 어떻게 줄어드는지 함께 보여주면 의사결정
|
||||
근거가 명확해집니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 전제 조건
|
||||
|
||||
| 항목 | 요건 |
|
||||
|---|---|
|
||||
| Oracle 버전 | AI Database **23.26.2 이상** (`v$version` 확인) |
|
||||
| 초기화 파라미터 | `COMPATIBLE >= 20.0` (`SELECT value FROM v$parameter WHERE name='compatible'`) |
|
||||
| 관리자 권한 | `CREATE END USER`, `CREATE DATA ROLE`, `GRANT DATA ROLE`, `CREATE DATA GRANT` (ADB ADMIN 은 기본 보유) |
|
||||
|
||||
> ADB 가 아직 26ai 로 업그레이드되지 않은 환경이라면 이 스크립트는 `ORA-00942`
|
||||
> 류로 실패합니다. 본 디렉토리의 VPD 경로는 그대로 동작합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 실행
|
||||
|
||||
```bash
|
||||
source .env # DDSUSER_*_PASSWORD 로드
|
||||
sqlplus "$ADB_USER/$ADB_PASSWORD@$ADB_TNS" @sql/adb/13_dds_variant.sql
|
||||
```
|
||||
|
||||
스크립트가 만드는 객체:
|
||||
|
||||
```
|
||||
END USER ddsuser_my ddsuser_pg ddsuser_both ddsuser_none
|
||||
ROLE dds_db_role (CREATE SESSION 보유)
|
||||
DATA ROLE my_only_role pg_only_role both_sources_role
|
||||
DATA GRANT
|
||||
dds_my_only_grant_mysql -> v_customers_my TO my_only_role
|
||||
dds_pg_only_grant_pg -> v_customers_pg TO pg_only_role
|
||||
dds_both_grant_pg -> v_customers_pg TO both_sources_role
|
||||
dds_both_grant_mysql -> v_customers_my TO both_sources_role
|
||||
```
|
||||
|
||||
검증:
|
||||
|
||||
```bash
|
||||
sqlplus '"ddsuser_pg"'/"$DDSUSER_PG_PASSWORD"@"$ADB_TNS" <<'EOF'
|
||||
SELECT ORA_END_USER_CONTEXT.username FROM dual;
|
||||
SELECT COUNT(*) FROM admin.v_customers_pg; -- 12 (기대값)
|
||||
SELECT COUNT(*) FROM admin.v_customers_my; -- ORA-... 권한 없음 (기대)
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. VPD ↔ DDS 1:1 매핑
|
||||
|
||||
| 관심사 | VPD 경로 (이 POC 본체) | DDS 경로 (`13_dds_variant.sql`) |
|
||||
|---|---|---|
|
||||
| 사용자 생성 | `CREATE USER vpduser_my IDENTIFIED BY ...` | `CREATE END USER "ddsuser_my" IDENTIFIED BY ...` |
|
||||
| 사용자 = 스키마? | 예 (Oracle 의 전통적 모델) | **아니오** — END USER 는 스키마/객체 없음 |
|
||||
| 그룹/역할 매핑 | `app_group` + `user_group` 매핑 테이블 | `CREATE DATA ROLE` + `GRANT DATA ROLE x TO "user"` |
|
||||
| 정책 표현 | `dbms_rls.add_policy` + PL/SQL 함수 `vpd_region_filter` | `CREATE DATA GRANT ... AS SELECT ON tbl WHERE ... TO role` (선언형 SQL) |
|
||||
| 식별자 전파 | LOGON 트리거 → `DBMS_SESSION.SET_CONTEXT('VPD_CTX', ...)` | 세션 자동: `ORA_END_USER_CONTEXT.username` |
|
||||
| 행 필터 | 정책 함수가 `WHERE region IN (...)` 반환 | `CREATE DATA GRANT ... WHERE region IN ('APAC','EMEA')` |
|
||||
| 컬럼 마스킹 | 별도 `DBMS_REDACT.ADD_POLICY` (06a_redaction.sql) | 같은 grant 안에 `AS SELECT (ALL COLUMNS EXCEPT email)` |
|
||||
| 권한 부여 단위 | `permission` 테이블 INSERT | DDL 한 줄 (`CREATE DATA GRANT`) |
|
||||
| 우회 차단 | GRANT 안 주기 + EXEMPT ACCESS POLICY 안 주기 + DBMS_RLS 안 주기 | `SET USE DATA GRANTS ONLY ON tbl ENABLED` (MAC) 한 줄 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 행/컬럼 변형 — 한 줄 비교
|
||||
|
||||
**행 필터 (region='APAC' 만):**
|
||||
|
||||
```sql
|
||||
-- VPD: 정책 함수 안에서 IN-list 빌드 + permission 테이블 UPDATE
|
||||
UPDATE permission SET allowed_regions = 'APAC' WHERE group_id = 30 ...;
|
||||
|
||||
-- DDS: grant 한 줄
|
||||
CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
WHERE region = 'APAC'
|
||||
TO both_sources_role;
|
||||
```
|
||||
|
||||
**컬럼 마스킹 (email 을 NULL 로):**
|
||||
|
||||
```sql
|
||||
-- VPD: 별도 Redaction 정책
|
||||
DBMS_REDACT.ADD_POLICY(object_schema=>'ADMIN', object_name=>'V_CUSTOMERS_PG',
|
||||
column_name=>'EMAIL', function_type=>DBMS_REDACT.NULLIFY, ...);
|
||||
|
||||
-- DDS: 같은 grant 안에서
|
||||
CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT (ALL COLUMNS EXCEPT email)
|
||||
ON admin.v_customers_pg
|
||||
TO both_sources_role;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 한계와 주의
|
||||
|
||||
* DDS Data Grant 는 **테이블·뷰** 모두에 적용 가능. 본 POC 의 `v_customers_*`
|
||||
뷰는 DB Link 패스스루이므로 grant 가 뷰 단에서 평가됩니다. 원격 push-down 은
|
||||
여전히 ADB → 원격 옵티마이저 협상에 의존합니다 (VPD 와 동일한 성능 특성).
|
||||
* 본 스크립트는 의도적으로 `USE DATA GRANTS ONLY` (MAC) 를 **켜지 않습니다**.
|
||||
켜면 `vpduser_*` (regular GRANT 경로) 가 같은 뷰에 접근하지 못해 VPD 데모가
|
||||
깨집니다. 운영에서는 MAC 활성화가 권장됩니다.
|
||||
* OAuth2 identity propagation (OCI IAM / Microsoft Entra ID claim) 은 본
|
||||
문서에선 다루지 않습니다. 직접 로그온 시나리오만 다룹니다. agentic AI / ORDS
|
||||
중간 계층에서 사용자 ID 를 전파하는 경우 4장 (OCI IAM) / 5장 (Entra ID) 의
|
||||
공식 가이드 (`G50191`) 를 참고하세요.
|
||||
|
||||
---
|
||||
|
||||
## 7. 정리/삭제
|
||||
|
||||
```sql
|
||||
-- DDS 변형만 정리 (VPD 데모는 그대로 유지)
|
||||
DROP DATA GRANT admin.dds_my_only_grant_mysql;
|
||||
DROP DATA GRANT admin.dds_pg_only_grant_pg;
|
||||
DROP DATA GRANT admin.dds_both_grant_pg;
|
||||
DROP DATA GRANT admin.dds_both_grant_mysql;
|
||||
DROP DATA ROLE my_only_role;
|
||||
DROP DATA ROLE pg_only_role;
|
||||
DROP DATA ROLE both_sources_role;
|
||||
DROP ROLE dds_db_role;
|
||||
DROP END USER "ddsuser_my";
|
||||
DROP END USER "ddsuser_pg";
|
||||
DROP END USER "ddsuser_both";
|
||||
DROP END USER "ddsuser_none";
|
||||
```
|
||||
165
sql/adb/13_dds_variant.sql
Normal file
165
sql/adb/13_dds_variant.sql
Normal file
@@ -0,0 +1,165 @@
|
||||
-- ============================================================
|
||||
-- 13_dds_variant.sql (OPTIONAL — Oracle 26ai Deep Data Security)
|
||||
-- ============================================================
|
||||
-- Reimplements the same 4-user source-access matrix as the VPD
|
||||
-- path (06_policy.sql + 07_end_users.sql), but using Oracle Deep
|
||||
-- Data Security (DDS) — the declarative SQL successor to VPD/RAS
|
||||
-- introduced in Oracle AI Database 26ai (announced 2026-04-09).
|
||||
--
|
||||
-- Matrix (identical to VPD demo):
|
||||
-- user PG view MySQL view
|
||||
-- -------------- ----------- -----------
|
||||
-- DDSUSER_MY blocked ALL rows
|
||||
-- DDSUSER_PG ALL rows blocked
|
||||
-- DDSUSER_BOTH ALL rows ALL rows
|
||||
-- DDSUSER_NONE blocked blocked (no data role = default deny)
|
||||
--
|
||||
-- ============================================================
|
||||
-- PREREQUISITES (verify before running)
|
||||
-- ------------------------------------------------------------
|
||||
-- * Oracle AI Database 23.26.2 or later
|
||||
-- * COMPATIBLE init parameter >= 20.0
|
||||
-- * ADMIN holds: CREATE END USER, CREATE DATA ROLE, GRANT DATA
|
||||
-- ROLE, CREATE DATA GRANT system privileges (ADMIN gets them
|
||||
-- by default on ADB; on on-prem you may need to grant).
|
||||
--
|
||||
-- Check version + COMPATIBLE before running:
|
||||
-- SELECT banner_full FROM v$version;
|
||||
-- SELECT value FROM v$parameter WHERE name = 'compatible';
|
||||
-- ============================================================
|
||||
-- COEXISTENCE NOTE
|
||||
-- ------------------------------------------------------------
|
||||
-- This script creates *new* end users with the `ddsuser_` prefix
|
||||
-- so it does NOT collide with the VPD demo users (`vpduser_*`).
|
||||
-- Both paths can coexist on the same ADB. The shared objects
|
||||
-- (views v_customers_pg / v_customers_my, db_source table, etc.)
|
||||
-- are reused — DDS adds a parallel access path, it does not
|
||||
-- replace anything.
|
||||
--
|
||||
-- We deliberately do NOT enable `SET USE DATA GRANTS ONLY` on
|
||||
-- the views — doing so would block the existing VPD users who
|
||||
-- rely on the regular GRANT SELECT path. The DDS users get their
|
||||
-- access exclusively through DATA GRANTs (no regular SELECT
|
||||
-- grant is issued), so MAC is not required for this demo.
|
||||
--
|
||||
-- DEFINE: &DDSUSER_MY_PASSWORD, &DDSUSER_PG_PASSWORD,
|
||||
-- &DDSUSER_BOTH_PASSWORD, &DDSUSER_NONE_PASSWORD
|
||||
-- ============================================================
|
||||
SET ECHO OFF
|
||||
SET FEEDBACK ON
|
||||
SET DEFINE ON
|
||||
|
||||
PROMPT === 1. Creating local END USERs (schemaless, no objects) ===
|
||||
-- End users in DDS do not own a schema and cannot create objects.
|
||||
-- They authenticate, then receive access purely via DATA GRANTs.
|
||||
CREATE END USER "ddsuser_my" IDENTIFIED BY "&DDSUSER_MY_PASSWORD";
|
||||
CREATE END USER "ddsuser_pg" IDENTIFIED BY "&DDSUSER_PG_PASSWORD";
|
||||
CREATE END USER "ddsuser_both" IDENTIFIED BY "&DDSUSER_BOTH_PASSWORD";
|
||||
CREATE END USER "ddsuser_none" IDENTIFIED BY "&DDSUSER_NONE_PASSWORD";
|
||||
|
||||
PROMPT === 2. Creating DATA ROLEs + connection role for direct logon ===
|
||||
-- Per the DDS direct-logon pattern: a standard ROLE carries
|
||||
-- CREATE SESSION, then that standard role is granted to each
|
||||
-- data role so end users can actually connect.
|
||||
CREATE ROLE dds_db_role;
|
||||
GRANT CREATE SESSION TO dds_db_role;
|
||||
|
||||
CREATE DATA ROLE my_only_role;
|
||||
CREATE DATA ROLE pg_only_role;
|
||||
CREATE DATA ROLE both_sources_role;
|
||||
-- (No role for the "none" user — absence of a data role = default deny.)
|
||||
|
||||
GRANT dds_db_role TO my_only_role;
|
||||
GRANT dds_db_role TO pg_only_role;
|
||||
GRANT dds_db_role TO both_sources_role;
|
||||
|
||||
PROMPT === 3. Mapping end users to data roles ===
|
||||
GRANT DATA ROLE my_only_role TO "ddsuser_my";
|
||||
GRANT DATA ROLE pg_only_role TO "ddsuser_pg";
|
||||
GRANT DATA ROLE both_sources_role TO "ddsuser_both";
|
||||
-- ddsuser_none gets nothing — they can authenticate (no, actually
|
||||
-- they cannot, because they have no data role carrying dds_db_role).
|
||||
-- Grant CREATE SESSION directly so they can prove "logged in but
|
||||
-- denied at the data layer" — same UX as VPDUSER_NONE in the VPD demo.
|
||||
GRANT dds_db_role TO "ddsuser_none";
|
||||
|
||||
PROMPT === 4. Creating DATA GRANTs (the declarative equivalent of the VPD policy) ===
|
||||
-- These five lines replace the entire vpd_region_filter PL/SQL
|
||||
-- function + the per-row lookups against the `permission` table.
|
||||
-- Each grant is a single declarative SQL statement.
|
||||
|
||||
-- DDSUSER_MY -> ALL rows from v_customers_my, no PG access.
|
||||
CREATE DATA GRANT admin.dds_my_only_grant_mysql
|
||||
AS SELECT
|
||||
ON admin.v_customers_my
|
||||
TO my_only_role;
|
||||
|
||||
-- DDSUSER_PG -> ALL rows from v_customers_pg, no MY access.
|
||||
CREATE DATA GRANT admin.dds_pg_only_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
TO pg_only_role;
|
||||
|
||||
-- DDSUSER_BOTH -> both views.
|
||||
CREATE DATA GRANT admin.dds_both_grant_pg
|
||||
AS SELECT
|
||||
ON admin.v_customers_pg
|
||||
TO both_sources_role;
|
||||
|
||||
CREATE DATA GRANT admin.dds_both_grant_mysql
|
||||
AS SELECT
|
||||
ON admin.v_customers_my
|
||||
TO both_sources_role;
|
||||
|
||||
-- DDSUSER_NONE: NO data grant -> default deny.
|
||||
|
||||
PROMPT === 5. (Optional) Region row-level filter — DDS-style ===
|
||||
-- The VPD path stores allowed_regions in a `permission` table and
|
||||
-- builds the predicate at query time. DDS makes this a declarative
|
||||
-- WHERE clause directly on the grant. Example: restrict
|
||||
-- both_sources_role to APAC only on the PG view. Uncomment to try.
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
-- AS SELECT
|
||||
-- ON admin.v_customers_pg
|
||||
-- WHERE region = 'APAC'
|
||||
-- TO both_sources_role;
|
||||
--
|
||||
-- For multi-region, use IN-list (no CSV-splitting helper needed,
|
||||
-- unlike the VPD policy):
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_mysql
|
||||
-- AS SELECT
|
||||
-- ON admin.v_customers_my
|
||||
-- WHERE region IN ('APAC','EMEA')
|
||||
-- TO both_sources_role;
|
||||
|
||||
PROMPT === 6. (Optional) Column masking — DDS-style ===
|
||||
-- The VPD path uses a separate Data Redaction policy
|
||||
-- (06a_redaction.sql) to NULL out the email column. DDS folds this
|
||||
-- into the grant itself with ALL COLUMNS EXCEPT — one statement
|
||||
-- handles both row filter AND column mask.
|
||||
--
|
||||
-- CREATE OR REPLACE DATA GRANT admin.dds_both_grant_pg
|
||||
-- AS SELECT (ALL COLUMNS EXCEPT email)
|
||||
-- ON admin.v_customers_pg
|
||||
-- TO both_sources_role;
|
||||
--
|
||||
-- Excluded columns return NULL (same UX as redaction), but it's
|
||||
-- one declarative grant instead of (policy function + redaction
|
||||
-- policy + permission table row).
|
||||
|
||||
PROMPT === DDS variant ready ===
|
||||
-- ------------------------------------------------------------
|
||||
-- Verify with:
|
||||
-- sqlplus '"ddsuser_pg"'/<pw>@<service>
|
||||
-- SQL> SELECT ORA_END_USER_CONTEXT.username FROM dual;
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_customers_pg; -- expect 12
|
||||
-- SQL> SELECT COUNT(*) FROM admin.v_customers_my; -- expect 0 (no grant)
|
||||
--
|
||||
-- Audit the grants:
|
||||
-- SELECT * FROM dba_data_grants;
|
||||
-- SELECT * FROM dba_data_roles;
|
||||
-- SELECT * FROM dba_end_users;
|
||||
-- ------------------------------------------------------------
|
||||
EXIT;
|
||||
Reference in New Issue
Block a user