From 21e7e9a526cd19861cd48643d5b8552f6b72acdb Mon Sep 17 00:00:00 2001 From: devmrko Date: Mon, 8 Jun 2026 13:03:41 +0900 Subject: [PATCH] Add Redmine/Gitea env placeholders and 2026-06-08 DDS Q&A session note - .env.example: REDMINE_URL/API_KEY and GITEA_URL/USER/PASSWORD/TOKEN placeholders so a fresh clone surfaces the optional tracker/mirror hookups without exposing real credentials. - docs/notes/2026-06-08-dds-qa-session.md: personal session reference covering DDS role model, scenario walkthrough, group/DB Link/Data Catalog patterns, and the mapping-table vs declarative-DDL decision. Co-Authored-By: Claude Opus 4.6 --- .env.example | 10 + docs/notes/2026-06-08-dds-qa-session.md | 370 ++++++++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 docs/notes/2026-06-08-dds-qa-session.md diff --git a/.env.example b/.env.example index 3872405..d6cb043 100644 --- a/.env.example +++ b/.env.example @@ -59,3 +59,13 @@ export MY_PASSWORD="" # --- (6) ADB → 원격 DB Link 이름 (관례적으로 고정 — 굳이 안 바꿔도 됨) --- export DBLINK_PG_NAME="RDS_POSTGRES_LINK" export DBLINK_MY_NAME="RDS_LINK" + +# --- (7) Redmine (project tracker) --- +export REDMINE_URL=https://redmine.example.com +export REDMINE_API_KEY= + +# --- (8) Gitea (self-hosted git) --- +export GITEA_URL=https://gittea.example.com +export GITEA_USER= +export GITEA_PASSWORD= +export GITEA_TOKEN= diff --git a/docs/notes/2026-06-08-dds-qa-session.md b/docs/notes/2026-06-08-dds-qa-session.md new file mode 100644 index 0000000..34490d4 --- /dev/null +++ b/docs/notes/2026-06-08-dds-qa-session.md @@ -0,0 +1,370 @@ +# 2026-06-08 — DDS 깊이 이해 Q&A 세션 기록 + +이 문서는 vpd-permission-poc 작업 중 DDS (Oracle 26ai Deep Data Security) 의 +운영 모델·시나리오·확장 가능성을 짚어본 Q&A 세션의 기록이다. 정식 docs 가 아니라 +개인 참조용 노트. + +--- + +## TL;DR — 핵심 7 가지 + +1. **DDS 의 권한 모델 = 3 객체**: `END USER` (개인) → `DATA ROLE` (그룹) → `DATA GRANT` (정책). +2. **DATA GRANT 한 줄**에 행 필터(WHERE) + 컬럼 마스킹(`ALL COLUMNS EXCEPT`) + 작업 단위(SELECT/INSERT/UPDATE) 가 다 들어간다. +3. **END USER ≠ 그룹**. 그룹은 `DATA ROLE`. 같은 권한 받을 사람들을 묶고, DATA GRANT 는 항상 DATA ROLE 대상. +4. **테이블 DDL 은 안 건드린다**. 정책은 별도 DDL 객체(`CREATE DATA GRANT ...`) 로 외부에서 붙는다. 변경/회수는 객체 단위 (`CREATE OR REPLACE`, `DROP`). +5. **DB Link / Data Catalog 등 외부 데이터도 통제 가능**. 패턴은 동일: `원격객체 → 로컬 VIEW → DATA GRANT`. 원격 객체에 직접은 못 붙임. +6. **권한 없는 유저** → `ORA-00942` (객체 자체 미노출). VPD 의 "0 rows" 보다 강한 enumeration 방어. +7. **권한 매핑 테이블 모델은 VPD 가 맞음**. DDS 는 권한을 "데이터 행" 이 아니라 "DDL 객체" 로 두는 모델. + +--- + +## 이번 세션에서 README 에 반영된 변경 + +| 커밋 | 내용 | +|---|---| +| `3045271` | README 에 `## 무엇을 통제하는가` 섹션 추가 — 행/컬럼/구현 비교 (드라이 톤) | +| `b80d119` | README 에 `## DDS 설정 핵심` 추가 — 사전조건·4스텝 DDL·함정 4종·변주 | + +두 섹션 모두 비유 없이 표·코드 위주. + +--- + +## 1. DDS 기본 모델 + +### 등장 인물 3 종 + +| | 역할 | 한 줄 | +|---|---|---| +| **END USER** | 데이터 조회자 (개인) | 스키마 없음 → 객체 못 만듦 | +| **DATA ROLE** | 권한 묶음 (그룹) | END USER 는 이걸 통해서만 권한 받음 | +| **DATA GRANT** | "누가 뭘 본다" 선언 | DDL 한 줄에 행·컬럼·작업 다 들어감 | + +흐름: `END USER → DATA ROLE → DATA GRANT → 테이블/뷰` + +### DATA GRANT 한 줄 예시 + +```sql +CREATE DATA GRANT admin.sales_apac + AS SELECT (ALL COLUMNS EXCEPT ssn, email) -- 컬럼 마스킹 + ON admin.customers -- 어느 객체 + WHERE region = 'APAC' -- 행 필터 + AND owner_id = ORA_END_USER_CONTEXT.username -- 세션 정체성 + TO sales_role; -- 누구에게 (그룹) +``` + +### VPD 와 비교 한 표 + +| | VPD | DDS | +|---|---|---| +| 정책 표현 | PL/SQL 함수 (절차형) | SQL DDL (선언형) | +| 컬럼 마스킹 | 별도 `DBMS_REDACT` | 그랜트에 내장 | +| 미권한 동작 | 0 rows | 객체 숨김 (`ORA-00942`) | +| 신원 | DB 유저 + LOGON 트리거 | END USER ± OAuth2 매핑 | +| 운영 코드 | 정책 함수 + 컨텍스트 패키지 + 트리거 | 그랜트 DDL 만 | +| 감사 | 함수 본문 까야 함 | 카탈로그 뷰 | + +--- + +## 2. 시나리오 — HR `employees` 테이블 9-STEP + +3 종 사용자: +- `hr_lead` — 전체 행, 모든 컬럼 +- `mgr_eng` — Engineering 부서만, SSN 마스킹 +- `auditor_ext` — 전체 행, salary/ssn/email 마스킹 + +### 두 가지 핵심 질문 직답 + +| Q | A | +|---|---| +| 유저는 어느 객체에 추가? | **어디에도 안 추가.** `CREATE END USER` 로 카탈로그(`dba_end_users`)에 생기는 독립 객체 | +| 일반 CREATE TABLE 에 같이 쓰나? | **아니오.** 테이블 DDL 은 평범하게. 정책은 별도 `CREATE DATA GRANT` | +| 테이블 정의를 바꿔야 하나? | **거의 안 바꿈.** 예외는 MAC 모드 켤 때 `ALTER TABLE ... SET USE DATA GRANTS ONLY ENABLED` 한 줄 | + +### 9 스텝 요약 + +```text +STEP 0 ADMIN 으로 접속 +STEP 1 CREATE TABLE employees (...) -- 평범한 DDL +STEP 2 CREATE END USER "hr_lead" / "mgr_eng" / "auditor_ext" +STEP 3 CREATE ROLE dds_db_role + GRANT CREATE SESSION + CREATE DATA ROLE hr_lead_role / eng_mgr_role / auditor_role + GRANT dds_db_role TO +STEP 4 GRANT DATA ROLE TO "" +STEP 5 CREATE DATA GRANT (행+컬럼+대상) × 3 +STEP 6 각 유저로 접속해 결과 확인 +STEP 7 CREATE OR REPLACE DATA GRANT 로 정책 라이브 변경 + DROP DATA GRANT 로 회수 (= 즉시 ORA-00942) +STEP 8 (선택) ALTER TABLE ... SET USE DATA GRANTS ONLY ENABLED -- MAC 모드 +STEP 9 감사 (dba_data_grants 등) + 역순 정리 +``` + +전체 스크립트는 세션 본문 참조. 이 POC 의 `sql/adb/13_dds_variant.sql` + `15_dds_cleanup.sql` 가 동일 패턴. + +--- + +## 3. 그룹 적용 + +### END USER 는 그룹이 아니다 — 그룹은 DATA ROLE + +마케팅팀 N 명에게 같은 권한: + +```sql +-- 1) 그룹 1 개 +CREATE DATA ROLE marketing_role; +GRANT dds_db_role TO marketing_role; + +-- 2) 정책 1 줄 — 그룹에 부여 +CREATE DATA GRANT admin.dg_marketing_view + AS SELECT (ALL COLUMNS EXCEPT ssn, salary) + ON admin.employees + WHERE department = 'Marketing' + TO marketing_role; + +-- 3) 멤버 N 명 추가 (반복) +GRANT DATA ROLE marketing_role TO "carol"; +GRANT DATA ROLE marketing_role TO "david"; + +-- 멤버 제거 +REVOKE DATA ROLE marketing_role FROM "frank"; +``` + +정책은 한 번, 멤버 변경은 GRANT/REVOKE. + +### DATA ROLE 중첩 (그룹 안에 그룹) + +```sql +GRANT all_employees_role TO marketing_role; -- 마케팅이 전사 권한 흡수 +GRANT marketing_role TO marketing_lead_role; -- 리드가 마케팅 권한 흡수 +``` + +### Federated Identity (외부 IdP 그룹 그대로) + +```sql +CREATE DATA ROLE marketing_role MAPPED TO 'AZURE_ROLE=Marketing'; +CREATE DATA ROLE finance_role MAPPED TO 'OKTA_GROUP=Finance-Read'; +``` + +→ DB 에 END USER 안 만들고, Azure/Okta 그룹 멤버십이 진실. SaaS / agentic AI 에 가장 큰 차별점. + +--- + +## 4. DB Link (heterogeneous) 데이터 통제 + +### 패턴 + +```text +[원격 PG/MySQL] + │ DB Link + ▼ +[ADB: CREATE VIEW v_dds_customers_pg AS SELECT ... FROM ...@RDS_POSTGRES_LINK] + │ CREATE DATA GRANT ON v_dds_customers_pg + ▼ +[DATA ROLE] ──GRANT──▶ [END USER] +``` + +### 핵심 4 가지 주의점 + +| 주의 | 내용 | +|---|---| +| 1. 원격 객체에 직접 ❌ | DATA GRANT 는 로컬 객체에만. 무조건 VIEW 한 겹 | +| 2. VPD 와 같은 뷰 공유 ❌ | VPD `1=0` 가 DDS 보다 먼저 적용 → silent 0 rows. **DDS 전용 뷰** 따로 만들 것 | +| 3. WHERE 푸시다운 보장 X | 옵티마이저 판단. 큰 테이블이면 `EXPLAIN PLAN` 으로 REMOTE 노드 확인 | +| 4. MV 는 다른 얘기 | MV 만들면 데이터 캐시됨 → fresh 정책 트레이드오프 | + +이 POC 의 `v_customers_*` (VPD용) ↔ `v_dds_customers_*` (DDS용) 분리가 정확히 #2 회피 때문. + +--- + +## 5. OCI Data Catalog 객체 통제 + +### 모델 — 2 층 권한 + +| 층 | 무엇 | 누가 관리 | +|---|---|---| +| ① OCI IAM | Object Storage 버킷 + Data Catalog 자산 자체 | OCI 콘솔 (IAM Policy) | +| ② DDS | ADB 안에 동기화된 External Table/View | ADB ADMIN | + +DDS 는 ②만. ①에서 ADB credential 이 가진 권한이 천장. + +### 흐름 + +```text +[Object Storage / RDB] + │ (등록) + ▼ +[OCI Data Catalog] + │ DBMS_DCAT.RUN_SYNC + ▼ +[ADB: External Table DCAT$XYZ] ← 평범한 ADB 객체 + │ + ▼ +[ADB: VIEW v_sales] ◀── CREATE DATA GRANT ON v_sales TO ... +``` + +### 스크립트 골격 + +```sql +-- 1) Catalog 연결 (최초 1회) +BEGIN + DBMS_DCAT.SET_DATA_CATALOG_CONN( + region => 'ap-seoul-1', + catalog_id => 'ocid1.datacatalog.oc1...', + catalog_tenancy => 'ocid1.tenancy.oc1...'); +END; +/ + +-- 2) 동기화 — External Table 생성됨 +BEGIN + DBMS_DCAT.RUN_SYNC( + synced_objects => '{"asset_list":[...]}', + sync_mode => 'AUTO', + target_schema => 'DCAT_LANDING', + sync_response => :resp); +END; +/ + +-- 3) sync-안전 VIEW 한 겹 (★) +CREATE OR REPLACE VIEW admin.v_customers AS +SELECT customer_id, full_name, email, region, signup_date +FROM dcat_landing."DCAT$CUSTOMERS"; + +-- 4) DDS 정책 +CREATE DATA GRANT admin.dg_sales_apac_customers + AS SELECT (ALL COLUMNS EXCEPT email) + ON admin.v_customers + WHERE region = 'APAC' + TO sales_apac_role; + +-- 5) (선택) MAC +ALTER TABLE admin.v_customers SET USE DATA GRANTS ONLY ENABLED; +``` + +### 왜 VIEW 한 겹이 중요한가 + +`DBMS_DCAT.RUN_SYNC` 가 자산 변경 시 External Table 을 DROP/CREATE 할 수 있다. +External Table 에 직접 DATA GRANT 붙이면 sync 한 번에 정책이 사라진다. +**VIEW 를 사이에 끼우면 DATA GRANT 는 VIEW 에 붙으니 sync 와 무관하게 살아남는다.** +(DB Link 패턴과 동일한 이유) + +--- + +## 6. "row 형태로 권한 적용" 이라는 표현 + +### 80% 맞지만 부족 + +| 통제 | 표현 | +|---|---| +| 행 | `WHERE region = 'APAC'` | +| 컬럼 | `(ALL COLUMNS EXCEPT email, ssn)` | +| 작업 | `AS SELECT/INSERT/UPDATE/DELETE` | +| 객체 노출 | 그랜트 없으면 `ORA-00942` | + +정확한 한 줄: **"행 + 컬럼 + 작업 단위 권한을, 그룹(DATA ROLE) 대상으로 선언형 DDL 한 줄에 표현."** + +### 흔한 오해 — 권한 저장 매체가 다름 + +| | VPD | DDS | +|---|---|---| +| 권한 어디 저장? | `permission` **테이블의 행** | DB 카탈로그의 `DATA GRANT` **객체** | +| 권한 추가 | `INSERT INTO permission ...` | `CREATE DATA GRANT ...` | +| 권한 회수 | `DELETE FROM permission ...` | `DROP DATA GRANT ...` | +| 권한 변경 | `UPDATE permission SET ...` | `CREATE OR REPLACE DATA GRANT ...` | + +DDS = "권한을 데이터처럼 다루는" 게 아니라 "**권한을 SQL 객체로 다룬다**" (GRANT 처럼). + +--- + +## 7. 권한 매핑 테이블 모델 — VPD vs DDS + +### 결론: **매핑 테이블 패턴은 VPD 가 맞다.** + +VPD 정책 함수는 PL/SQL → 그 안에서 임의의 SELECT 가능 → 매핑 테이블 lookup 자연스러움. +DDS 의 DATA GRANT 는 선언형 DDL → 동적 lookup 불가능. + +### 운영 패턴 비교 + +| 운영 | VPD | DDS | +|---|---|---| +| 권한 추가 | `INSERT INTO permission ...` | `CREATE DATA GRANT ...` 또는 `GRANT DATA ROLE` | +| 누가 변경? | 매핑 테이블에 INSERT 권한자 (앱/사람, DBA 아님) | DDL 권한자 (ADMIN) | +| 셀프서비스 (앱이 권한 위임) | 자연스러움 | 부자연 (앱이 DDL 실행은 부담) | +| 수만 명 운영 | 매핑 행 N → 함수가 동적 처리 (확장 잘 됨) | DATA GRANT/DATA ROLE 폭증 시 부담 | + +### DDS 우회 (권장 X) + +DATA GRANT 대상 객체를 **매핑 테이블과 JOIN 한 뷰** 로 만들 수 있다. + +```sql +CREATE OR REPLACE VIEW admin.v_customers_scoped AS +SELECT c.* +FROM admin.customers c +JOIN admin.permission p ... +JOIN admin.user_group ug ... +WHERE ug.user_name = ORA_END_USER_CONTEXT.username + AND INSTR(',' || p.allowed_regions || ',', ',' || c.region || ',') > 0; + +CREATE DATA GRANT admin.dg_scoped + AS SELECT ON admin.v_customers_scoped TO some_role; +``` + +→ 동작은 한다. 그러나: +- 선언형 DDS 의 장점 상실 (로직이 뷰 SQL 로 옮겨갔을 뿐) +- 매핑 테이블 별도 보호 필요 +- 디버깅이 두 곳 (뷰 + 그랜트) 으로 나뉨 +- **이럴 거면 그냥 VPD 가 깔끔** + +### 선택 가이드 + +| 상황 | 권장 | +|---|---| +| 권한이 **데이터 자체** (테넌트·고객·부서 매핑 자주 변경, 앱이 관리) | **VPD** | +| 권한이 **운영 정책** (소수 역할, DBA/SRE 가 DDL 관리, IdP 연동) | **DDS** | +| **수만 행 × 동적 lookup** (멀티테넌트 SaaS) | **VPD** | +| **선언형 단일 평면 + 카탈로그 감사** 우선 | **DDS** | +| **OAuth2 / Azure AD 그룹** 그대로 사용 | **DDS** (`MAPPED TO`) | + +### 이 POC 에서 보면 + +- `sql/adb/06_policy.sql` + `permission` 테이블 = "앱/운영자가 행으로 권한 관리" → **VPD 답안** +- `sql/adb/13_dds_variant.sql` = "DBA 가 DDL 로 4 종 역할 정의" → **DDS 답안** +- 같은 결과를 두 다른 모델로 표현한 비교 데모 + +**권한 매핑 테이블 중심으로 가겠다면 VPD 그대로 두는 게 정답.** +DDS 로 옮기려면 매핑 자체를 `DATA ROLE 멤버십` 으로 재구성해야 한다 (매핑 테이블 사라지고 `GRANT DATA ROLE` 이 그 자리 차지). + +--- + +## 부록 — DDS 운영 시 자주 보는 쿼리 + +```sql +-- 누가 뭘 볼 수 있는지 +SELECT * FROM dba_data_grants; + +-- 데이터 역할 (그룹) +SELECT * FROM dba_data_roles; + +-- 데이터 사용자 +SELECT * FROM dba_end_users; + +-- 일반 ROLE ↔ DATA ROLE 매핑 관계 +SELECT grantee, granted_role +FROM dba_role_privs +WHERE grantee LIKE '%role'; + +-- MAC 모드 켜진 객체 +SELECT owner, table_name +FROM dba_tables +WHERE use_data_grants_only = 'YES'; -- 정확한 컬럼명은 카탈로그 확인 +``` + +--- + +## 다음 세션 재개 시 확인할 것 + +- 이 POC 의 DDS 변형은 이미 E2E 검증 완료 (Task #28). `./run.sh dds` 한 번이면 재현됨. +- README 의 `## 무엇을 통제하는가` + `## DDS 설정 핵심` 섹션은 이번에 추가됨. +- 매핑 테이블 패턴 = VPD, 선언형 패턴 = DDS — POC 가 두 길 다 보여줌. +- 확장 검토 후보: + - DDS 변형에 `MAPPED TO` (federated identity) 데모 추가 + - DB Link 외에 OCI Data Catalog 싱크 자산을 같은 DDS 매트릭스로 보호하는 변형 + - `SET USE DATA GRANTS ONLY` 활성화 후 우회 시도 5종 재검증