Wire DDS variant into run.sh as optional subcommands
Adds dds / dds-setup / dds-tests / dds-teardown subcommands so the 26ai Deep Data Security variant can be run from the same one-click entry point. Not part of `./run.sh all` since DDS requires 26ai (23.26.2+) which not every ADB has. - sql/adb/14_tests_dds_user.sql: shared verification script for all 4 ddsuser_*; uses WHENEVER SQLERROR CONTINUE so the expected ORA-00942 (deny-by-hiding) doesn't abort the script. Includes bypass attempts against the underlying VPD views, raw DB Links, and the VPD permission tables. - sql/adb/15_dds_cleanup.sql: idempotent teardown for DDS objects (data grants, end users, data roles, dds_db_role, DDS-only views). - run.sh: do_dds_prereq / do_dds_setup / do_dds_tests / do_dds_teardown helpers; dispatch case extended. Also fixes a pre-existing secrets-leak gap: both 07_end_users.sql and 13_dds_variant.sql had SET DEFINE ON without SET VERIFY OFF, which causes sqlplus to echo the substituted DDL (including the IDENTIFIED BY <password> clause) on the `new 1:` line. Added SET VERIFY OFF. E2E re-verified on ADB 23.26.2.2.0: matrix identical to manual run (MY=17 / PG=12 / BOTH=12+17 / NONE=ORA-00942 on both), no password in logs, dds-teardown leaves no residue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
15
README.md
15
README.md
@@ -58,6 +58,17 @@ $EDITOR .env
|
|||||||
./run.sh teardown # ADB 측 객체 + DB Link/credential 정리
|
./run.sh teardown # ADB 측 객체 + DB Link/credential 정리
|
||||||
```
|
```
|
||||||
|
|
||||||
|
(선택) Oracle 26ai **Deep Data Security** 변형도 같은 시나리오를 declarative SQL 로
|
||||||
|
재현합니다. VPD 데모와 공존하며 별도 ddsuser_*/v_dds_* 객체로 깔립니다.
|
||||||
|
([docs/05-dds-variant.md](docs/05-dds-variant.md))
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run.sh dds # dds-setup + dds-tests
|
||||||
|
./run.sh dds-setup # 13_dds_variant.sql 적용
|
||||||
|
./run.sh dds-tests # ddsuser_* 4명 매트릭스 검증
|
||||||
|
./run.sh dds-teardown # DDS 객체만 정리 (VPD 데모는 보존)
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 사전 준비
|
## 사전 준비
|
||||||
@@ -129,7 +140,9 @@ $EDITOR .env
|
|||||||
│ ├── 10_tests_user_both.sql # both
|
│ ├── 10_tests_user_both.sql # both
|
||||||
│ ├── 11_tests_user_none.sql # default deny (fail-closed) 검증
|
│ ├── 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 로 재구현
|
│ ├── 13_dds_variant.sql # (선택) 같은 4-user 매트릭스를 26ai Deep Data Security 로 재구현
|
||||||
|
│ ├── 14_tests_dds_user.sql # (선택) ddsuser_* 공용 매트릭스 검증 + bypass 시도
|
||||||
|
│ └── 15_dds_cleanup.sql # (선택) DDS 객체 멱등 정리
|
||||||
└── docs/
|
└── docs/
|
||||||
├── 03-detailed-guide.md # 한국어 상세 설명 (아키텍처, 정책 로직, 운영 고려사항)
|
├── 03-detailed-guide.md # 한국어 상세 설명 (아키텍처, 정책 로직, 운영 고려사항)
|
||||||
└── 05-dds-variant.md # (선택) VPD ↔ DDS 1:1 매핑 + 26ai 변형 사용법
|
└── 05-dds-variant.md # (선택) VPD ↔ DDS 1:1 매핑 + 26ai 변형 사용법
|
||||||
|
|||||||
68
run.sh
68
run.sh
@@ -12,6 +12,12 @@
|
|||||||
# ./run.sh all # source → adb → tests → audit
|
# ./run.sh all # source → adb → tests → audit
|
||||||
# ./run.sh teardown # ADB 측 객체 + 원격 link/cred 만 정리 (원격 PG/MySQL 데이터는 보존)
|
# ./run.sh teardown # ADB 측 객체 + 원격 link/cred 만 정리 (원격 PG/MySQL 데이터는 보존)
|
||||||
#
|
#
|
||||||
|
# (선택) 26ai Deep Data Security 변형 — VPD 데모와 공존하며 같은 4-user 매트릭스를 재구현:
|
||||||
|
# ./run.sh dds-setup # 13_dds_variant.sql 적용 (END USER + DATA ROLE + DATA GRANT)
|
||||||
|
# ./run.sh dds-tests # 4명 ddsuser_* 로 매트릭스 검증
|
||||||
|
# ./run.sh dds # dds-setup → dds-tests
|
||||||
|
# ./run.sh dds-teardown # DDS 객체만 정리 (VPD 데모는 보존)
|
||||||
|
#
|
||||||
# 환경설정:
|
# 환경설정:
|
||||||
# cp .env.example .env → 값 채우고 → ./run.sh
|
# cp .env.example .env → 값 채우고 → ./run.sh
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -65,6 +71,10 @@ DEFINE VPDUSER_MY_PASSWORD = "${VPDUSER_MY_PASSWORD}"
|
|||||||
DEFINE VPDUSER_PG_PASSWORD = "${VPDUSER_PG_PASSWORD}"
|
DEFINE VPDUSER_PG_PASSWORD = "${VPDUSER_PG_PASSWORD}"
|
||||||
DEFINE VPDUSER_BOTH_PASSWORD = "${VPDUSER_BOTH_PASSWORD}"
|
DEFINE VPDUSER_BOTH_PASSWORD = "${VPDUSER_BOTH_PASSWORD}"
|
||||||
DEFINE VPDUSER_NONE_PASSWORD = "${VPDUSER_NONE_PASSWORD}"
|
DEFINE VPDUSER_NONE_PASSWORD = "${VPDUSER_NONE_PASSWORD}"
|
||||||
|
DEFINE DDSUSER_MY_PASSWORD = "${DDSUSER_MY_PASSWORD:-}"
|
||||||
|
DEFINE DDSUSER_PG_PASSWORD = "${DDSUSER_PG_PASSWORD:-}"
|
||||||
|
DEFINE DDSUSER_BOTH_PASSWORD = "${DDSUSER_BOTH_PASSWORD:-}"
|
||||||
|
DEFINE DDSUSER_NONE_PASSWORD = "${DDSUSER_NONE_PASSWORD:-}"
|
||||||
@${sql_file}
|
@${sql_file}
|
||||||
SQLEOF
|
SQLEOF
|
||||||
}
|
}
|
||||||
@@ -164,16 +174,51 @@ do_teardown() {
|
|||||||
ok "teardown 완료"
|
ok "teardown 완료"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# (선택) DDS 단계 — Oracle 26ai Deep Data Security
|
||||||
|
# 메인 VPD 데모와 공존. 26ai 미지원 ADB 에서는 ORA-00942 등으로 실패함.
|
||||||
|
# ============================================================
|
||||||
|
do_dds_prereq() {
|
||||||
|
require_env \
|
||||||
|
DDSUSER_MY_PASSWORD DDSUSER_PG_PASSWORD \
|
||||||
|
DDSUSER_BOTH_PASSWORD DDSUSER_NONE_PASSWORD
|
||||||
|
log "DDS 변형용 비밀번호 4개 확인됨"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_dds_setup() {
|
||||||
|
log "=== dds-setup: 13_dds_variant.sql (END USER + DATA ROLE + DATA GRANT) ==="
|
||||||
|
# 멱등성: 부분 적용 상태를 먼저 청소한 뒤 재생성
|
||||||
|
run_sqlplus_file "$ROOT/sql/adb/15_dds_cleanup.sql"
|
||||||
|
run_sqlplus_file "$ROOT/sql/adb/13_dds_variant.sql"
|
||||||
|
ok "DDS 셋업 완료"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_dds_tests() {
|
||||||
|
log "=== dds-tests: 4-user (ddsuser_*) 매트릭스 검증 ==="
|
||||||
|
# END USER 이름이 lowercase 라 사용자명을 큰따옴표로 감싸서 전달.
|
||||||
|
run_sqlplus_as '"ddsuser_my"' "$DDSUSER_MY_PASSWORD" "$ROOT/sql/adb/14_tests_dds_user.sql"
|
||||||
|
run_sqlplus_as '"ddsuser_pg"' "$DDSUSER_PG_PASSWORD" "$ROOT/sql/adb/14_tests_dds_user.sql"
|
||||||
|
run_sqlplus_as '"ddsuser_both"' "$DDSUSER_BOTH_PASSWORD" "$ROOT/sql/adb/14_tests_dds_user.sql"
|
||||||
|
run_sqlplus_as '"ddsuser_none"' "$DDSUSER_NONE_PASSWORD" "$ROOT/sql/adb/14_tests_dds_user.sql"
|
||||||
|
ok "DDS 4명 (MY / PG / BOTH / NONE) 테스트 실행 완료"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_dds_teardown() {
|
||||||
|
log "=== dds-teardown: DDS 객체만 정리 (VPD 데모는 보존) ==="
|
||||||
|
run_sqlplus_file "$ROOT/sql/adb/15_dds_cleanup.sql"
|
||||||
|
ok "DDS teardown 완료"
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 디스패치
|
# 디스패치
|
||||||
# ============================================================
|
# ============================================================
|
||||||
case "$CMD" in
|
case "$CMD" in
|
||||||
prereq) do_prereq ;;
|
prereq) do_prereq ;;
|
||||||
source) do_prereq; do_source ;;
|
source) do_prereq; do_source ;;
|
||||||
adb) do_prereq; do_adb ;;
|
adb) do_prereq; do_adb ;;
|
||||||
tests) do_prereq; do_tests ;;
|
tests) do_prereq; do_tests ;;
|
||||||
audit) do_prereq; do_audit ;;
|
audit) do_prereq; do_audit ;;
|
||||||
teardown) do_prereq; do_teardown ;;
|
teardown) do_prereq; do_teardown ;;
|
||||||
all)
|
all)
|
||||||
do_prereq
|
do_prereq
|
||||||
do_source
|
do_source
|
||||||
@@ -182,7 +227,16 @@ case "$CMD" in
|
|||||||
do_audit
|
do_audit
|
||||||
ok "=== ALL DONE — VPD POC 전체 파이프라인 통과 ==="
|
ok "=== ALL DONE — VPD POC 전체 파이프라인 통과 ==="
|
||||||
;;
|
;;
|
||||||
|
dds-setup) do_prereq; do_dds_prereq; do_dds_setup ;;
|
||||||
|
dds-tests) do_prereq; do_dds_prereq; do_dds_tests ;;
|
||||||
|
dds-teardown) do_prereq; do_dds_teardown ;;
|
||||||
|
dds)
|
||||||
|
do_prereq; do_dds_prereq
|
||||||
|
do_dds_setup
|
||||||
|
do_dds_tests
|
||||||
|
ok "=== DDS DONE — Deep Data Security 변형 셋업 + 검증 통과 ==="
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
die "알 수 없는 명령: $CMD (사용: prereq|source|adb|tests|audit|all|teardown)"
|
die "알 수 없는 명령: $CMD (사용: prereq|source|adb|tests|audit|all|teardown | dds|dds-setup|dds-tests|dds-teardown)"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
SET ECHO OFF
|
SET ECHO OFF
|
||||||
SET FEEDBACK ON
|
SET FEEDBACK ON
|
||||||
SET DEFINE ON
|
SET DEFINE ON
|
||||||
|
SET VERIFY OFF -- 비번이 'new 1:' 라인으로 echo 되는 것을 막음 (leak 방지)
|
||||||
|
|
||||||
PROMPT === Creating end-user accounts ===
|
PROMPT === Creating end-user accounts ===
|
||||||
-- Passwords come from .env (DEFINE) so they aren't hardcoded in source.
|
-- Passwords come from .env (DEFINE) so they aren't hardcoded in source.
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
SET ECHO OFF
|
SET ECHO OFF
|
||||||
SET FEEDBACK ON
|
SET FEEDBACK ON
|
||||||
SET DEFINE ON
|
SET DEFINE ON
|
||||||
|
SET VERIFY OFF -- 비번이 'new 1:' 라인으로 echo 되는 것을 막음 (leak 방지)
|
||||||
|
|
||||||
PROMPT === 0. Creating DDS-only views (no VPD policy attached) ===
|
PROMPT === 0. Creating DDS-only views (no VPD policy attached) ===
|
||||||
-- Functionally identical to v_customers_pg / v_customers_my but
|
-- Functionally identical to v_customers_pg / v_customers_my but
|
||||||
|
|||||||
61
sql/adb/14_tests_dds_user.sql
Normal file
61
sql/adb/14_tests_dds_user.sql
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 14_tests_dds_user.sql
|
||||||
|
-- Shared verification script for all 4 DDS end-users.
|
||||||
|
-- Run as ddsuser_my / ddsuser_pg / ddsuser_both / ddsuser_none.
|
||||||
|
--
|
||||||
|
-- Expected matrix (DDS):
|
||||||
|
-- user v_dds_customers_pg v_dds_customers_my
|
||||||
|
-- --------------- --------------------- ---------------------
|
||||||
|
-- ddsuser_my ORA-00942 (hidden) 17 rows
|
||||||
|
-- ddsuser_pg 12 rows ORA-00942 (hidden)
|
||||||
|
-- ddsuser_both 12 rows 17 rows
|
||||||
|
-- ddsuser_none ORA-00942 (hidden) ORA-00942 (hidden)
|
||||||
|
--
|
||||||
|
-- Note: where VPD silently returns "0 rows", DDS returns
|
||||||
|
-- `ORA-00942 (table or view does not exist)` — the object is
|
||||||
|
-- not visible at all. `WHENEVER SQLERROR CONTINUE` is set so the
|
||||||
|
-- script keeps going past expected ORA-00942 errors.
|
||||||
|
-- ============================================================
|
||||||
|
WHENEVER SQLERROR CONTINUE
|
||||||
|
SET FEEDBACK ON
|
||||||
|
SET LINESIZE 200
|
||||||
|
SET PAGESIZE 100
|
||||||
|
|
||||||
|
PROMPT
|
||||||
|
PROMPT === Who am I? (DDS end-user context) ===
|
||||||
|
SELECT ORA_END_USER_CONTEXT.username AS end_user_name FROM dual;
|
||||||
|
|
||||||
|
PROMPT
|
||||||
|
PROMPT === Row counts on the DDS-only views ===
|
||||||
|
PROMPT (ORA-00942 here means "no DATA GRANT" — that's the expected deny path)
|
||||||
|
SELECT 'v_dds_customers_pg' AS view_name, COUNT(*) AS rows_visible
|
||||||
|
FROM admin.v_dds_customers_pg;
|
||||||
|
SELECT 'v_dds_customers_my' AS view_name, COUNT(*) AS rows_visible
|
||||||
|
FROM admin.v_dds_customers_my;
|
||||||
|
|
||||||
|
PROMPT
|
||||||
|
PROMPT === Sample (first 3 rows from each view, if visible) ===
|
||||||
|
COLUMN customer_id FORMAT 9999
|
||||||
|
COLUMN full_name FORMAT A20
|
||||||
|
COLUMN email FORMAT A30
|
||||||
|
COLUMN region FORMAT A8
|
||||||
|
SELECT customer_id, full_name, email, region
|
||||||
|
FROM admin.v_dds_customers_pg
|
||||||
|
ORDER BY customer_id FETCH FIRST 3 ROWS ONLY;
|
||||||
|
SELECT customer_id, full_name, email, region
|
||||||
|
FROM admin.v_dds_customers_my
|
||||||
|
ORDER BY customer_id FETCH FIRST 3 ROWS ONLY;
|
||||||
|
|
||||||
|
PROMPT
|
||||||
|
PROMPT === Bypass attempts ===
|
||||||
|
PROMPT -- 1) underlying VPD view (no GRANT given) -> must fail
|
||||||
|
SELECT COUNT(*) FROM admin.v_customers_pg;
|
||||||
|
SELECT COUNT(*) FROM admin.v_customers_my;
|
||||||
|
PROMPT -- 2) raw remote table via DB Link (no GRANT on the link) -> must fail
|
||||||
|
SELECT COUNT(*) FROM "public"."customers"@RDS_POSTGRES_LINK;
|
||||||
|
SELECT COUNT(*) FROM "ecommerce_poc"."customers"@RDS_LINK;
|
||||||
|
PROMPT -- 3) permission / app_user tables (VPD plane) -> must fail
|
||||||
|
SELECT COUNT(*) FROM admin.permission;
|
||||||
|
SELECT COUNT(*) FROM admin.app_user;
|
||||||
|
|
||||||
|
EXIT;
|
||||||
53
sql/adb/15_dds_cleanup.sql
Normal file
53
sql/adb/15_dds_cleanup.sql
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 15_dds_cleanup.sql
|
||||||
|
-- Idempotent teardown for the DDS variant (13_dds_variant.sql).
|
||||||
|
-- Safe to run before 13_dds_variant.sql to wipe partial state.
|
||||||
|
-- Errors are ignored (objects may not exist on first run).
|
||||||
|
-- Run as ADMIN.
|
||||||
|
-- ============================================================
|
||||||
|
WHENEVER SQLERROR CONTINUE NONE;
|
||||||
|
SET ECHO OFF
|
||||||
|
SET FEEDBACK OFF
|
||||||
|
|
||||||
|
PROMPT === Dropping DDS data grants (if present) ===
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_my_only_grant_mysql'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_pg_only_grant_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_both_grant_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_both_grant_mysql'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
|
||||||
|
PROMPT === Dropping DDS end users (no CASCADE — END USERs own no objects) ===
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_my"'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_pg"'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_both"'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_none"'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
|
||||||
|
PROMPT === Dropping DDS data roles (if present) ===
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE my_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE pg_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE both_sources_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE connect_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
|
||||||
|
PROMPT === Dropping DDS regular role (session-carrier) ===
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP ROLE dds_db_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
|
||||||
|
PROMPT === Dropping DDS-only views ===
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP VIEW v_dds_customers_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
BEGIN EXECUTE IMMEDIATE 'DROP VIEW v_dds_customers_my'; EXCEPTION WHEN OTHERS THEN NULL; END;
|
||||||
|
/
|
||||||
|
|
||||||
|
PROMPT === DDS cleanup complete ===
|
||||||
|
EXIT;
|
||||||
Reference in New Issue
Block a user