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 <noreply@anthropic.com>
This commit is contained in:
10
.env.example
10
.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=<your-redmine-api-key>
|
||||
|
||||
# --- (8) Gitea (self-hosted git) ---
|
||||
export GITEA_URL=https://gittea.example.com
|
||||
export GITEA_USER=<your-gitea-login>
|
||||
export GITEA_PASSWORD=<your-gitea-password>
|
||||
export GITEA_TOKEN=<minted-from-the-above>
|
||||
|
||||
370
docs/notes/2026-06-08-dds-qa-session.md
Normal file
370
docs/notes/2026-06-08-dds-qa-session.md
Normal file
@@ -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 <each data role>
|
||||
STEP 4 GRANT DATA ROLE <role> TO "<user>"
|
||||
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종 재검증
|
||||
Reference in New Issue
Block a user