refs #744: configure OCI IAM DDS context test
This commit is contained in:
@@ -84,6 +84,8 @@ public class DdsMcpVectorSearchService {
|
|||||||
log.info("DDS vector query completed with {} rows", rows.size());
|
log.info("DDS vector query completed with {} rows", rows.size());
|
||||||
return new DdsMcpVectorSearchResult(vector.query(), vector.embeddingMode(), rows.size(), List.copyOf(rows));
|
return new DdsMcpVectorSearchResult(vector.query(), vector.embeddingMode(), rows.size(), List.copyOf(rows));
|
||||||
} catch (java.sql.SQLException exception) {
|
} catch (java.sql.SQLException exception) {
|
||||||
|
log.warn("DDS vector query failed: sqlState={}, errorCode={}",
|
||||||
|
exception.getSQLState(), exception.getErrorCode());
|
||||||
throw new AppException("DDS_QUERY_FAILED: DDS 보호 객체를 조회할 수 없습니다.");
|
throw new AppException("DDS_QUERY_FAILED: DDS 보호 객체를 조회할 수 없습니다.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 설계서: Windows AD 기반 DDS END USER 매핑 테스트 (#744)
|
# 설계서: Windows AD 기반 DDS END USER 매핑 테스트 (#744)
|
||||||
|
|
||||||
> **상태**: OIDC JWT → MCP 매핑 검증 완료 · DDS data grant 검증 대기
|
> **상태**: OIDC JWT → MCP → OCI IAM → DDS context 및 Alice/Bob 권한 차이 검증 완료
|
||||||
> **최종수정**: 2026-08-04
|
> **최종수정**: 2026-08-04
|
||||||
> **추적성**: Redmine #744 · 선행 설계: [DDS MCP END USER Context](../617-dds-mcp-end-user-context/README.md)
|
> **추적성**: Redmine #744 · 선행 설계: [DDS MCP END USER Context](../617-dds-mcp-end-user-context/README.md)
|
||||||
|
|
||||||
@@ -44,6 +44,32 @@ ORA_END_USER_CONTEXT + DDS DATA GRANT / 권한 함수
|
|||||||
|
|
||||||
`sub`는 Keycloak이 발급하는 안정적인 federated-user 식별자다. AD objectGUID는 원천 디렉터리의 감사 식별자로 유지하되, 실제 권한 키는 **검증된 JWT의 `iss + sub`**로 고정한다. UPN은 로그인 화면·감사 표시용으로 보관할 수 있지만 권한 키로 신뢰하지 않는다. `iss + sub` 조합이 유일하지 않거나, 매핑이 없거나, 사용자가 비활성이면 요청은 fail-closed로 거부한다.
|
`sub`는 Keycloak이 발급하는 안정적인 federated-user 식별자다. AD objectGUID는 원천 디렉터리의 감사 식별자로 유지하되, 실제 권한 키는 **검증된 JWT의 `iss + sub`**로 고정한다. UPN은 로그인 화면·감사 표시용으로 보관할 수 있지만 권한 키로 신뢰하지 않는다. `iss + sub` 조합이 유일하지 않거나, 매핑이 없거나, 사용자가 비활성이면 요청은 fail-closed로 거부한다.
|
||||||
|
|
||||||
|
### 사용자 인증과 DB 접속 신뢰의 분리
|
||||||
|
|
||||||
|
이 PoC에는 서로 다른 두 신뢰 체인이 있다. 둘을 같은 OAuth token으로 혼동하지 않는다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
[업무 사용자 인증]
|
||||||
|
AD user -> Keycloak (AD LDAP 인증) -> Keycloak JWT
|
||||||
|
-> MCP validates iss + sub -> DDS_U_1 / DDS_U_2 선택
|
||||||
|
|
||||||
|
[DB 접속 신뢰]
|
||||||
|
MCP service -> OCI IAM DDS_MCP_SERVICE_TEST (client credentials)
|
||||||
|
-> database-access token -> Oracle DB
|
||||||
|
-> 선택된 local DDS END USER context attach
|
||||||
|
```
|
||||||
|
|
||||||
|
| 역할 | 현재 담당 | 하는 일 |
|
||||||
|
|---|---|---|
|
||||||
|
| AD 사용자 인증 | Keycloak | AD LDAP의 계정·비밀번호를 확인하고 Keycloak JWT를 발급 |
|
||||||
|
| 업무 사용자 식별 | MCP | 검증된 Keycloak `iss + sub`를 HMM application user와 local DDS END USER에 매핑 |
|
||||||
|
| DB 접속 애플리케이션 신뢰 | OCI IAM credential app | MCP service가 database-access token을 받아 Oracle DB에 신뢰된 application임을 증명 |
|
||||||
|
| 최종 데이터 권한 | Oracle Deep Sec | attach된 local DDS END USER의 DATA ROLE과 DATA GRANT로 행·열을 제한 |
|
||||||
|
|
||||||
|
따라서 OCI IAM credential app은 현재 AD 사용자를 직접 로그인시키지 않는다. OCI IAM은 MCP service의 database-access token 발급자이며, Oracle DB가 해당 service client를 application identity로 신뢰하게 한다. AD 사용자가 어떤 DDS END USER가 되는지는 MCP가 결정한다.
|
||||||
|
|
||||||
|
향후 OCI IAM을 사용자 인증의 중심으로 바꾸려면 Keycloak을 OCI IAM의 외부 IdP(OIDC 또는 SAML)로 federation하고, AIPF/MCP가 OCI IAM user token을 받도록 변경한다. 그 경우 Oracle DB도 OCI IAM user token의 issuer·group claim을 직접 검증할 수 있다. 현재 PoC의 local DDS END USER 매핑과는 별도의 확장 경로다.
|
||||||
|
|
||||||
## 4. 구현 단계
|
## 4. 구현 단계
|
||||||
|
|
||||||
1. Windows Server에 AD DS를 설치하고 `dds.test` forest를 생성한다.
|
1. Windows Server에 AD DS를 설치하고 `dds.test` forest를 생성한다.
|
||||||
@@ -64,7 +90,7 @@ ORA_END_USER_CONTEXT + DDS DATA GRANT / 권한 함수
|
|||||||
|
|
||||||
`dds-mcp` client는 access token audience에 `dds-mcp`를 포함한다. MCP는 Keycloak JWKS에서 JWT 서명과 `iss`, `aud`, 만료 시간을 검증한 뒤에만 위 매핑을 조회한다. 실제 `/dds/mcp/messages` 초기화 요청에 Alice JWT를 넣어 성공 응답을 확인했다.
|
`dds-mcp` client는 access token audience에 `dds-mcp`를 포함한다. MCP는 Keycloak JWKS에서 JWT 서명과 `iss`, `aud`, 만료 시간을 검증한 뒤에만 위 매핑을 조회한다. 실제 `/dds/mcp/messages` 초기화 요청에 Alice JWT를 넣어 성공 응답을 확인했다.
|
||||||
|
|
||||||
이 단계는 END USER와 DATA ROLE만 게시한다. 각 DATA ROLE에는 data grant를 만들지 않았으므로 데이터 도구 호출은 여전히 **default deny**다.
|
초기 게시 단계에서는 END USER와 DATA ROLE만 만들고 default deny로 시작한다. 현재 테스트에서는 Alice에만 vector view SELECT data grant를 추가했고 Bob은 grant 없이 유지한다.
|
||||||
|
|
||||||
## 4.2 AIPF MCP 등록 값
|
## 4.2 AIPF MCP 등록 값
|
||||||
|
|
||||||
@@ -131,7 +157,36 @@ AIPF의 **Edit MCP server** 화면에서 다음 값으로 등록한다. AIPF는
|
|||||||
| Alice/Bob 데이터 차이 | 같은 `dds_vector_search` 호출에서 반환 행이 다름 | 대기 — OCI IAM DB service credential 및 DDS DATA GRANT 필요 |
|
| Alice/Bob 데이터 차이 | 같은 `dds_vector_search` 호출에서 반환 행이 다름 | 대기 — OCI IAM DB service credential 및 DDS DATA GRANT 필요 |
|
||||||
| 미매핑 토큰 거부 | 매핑 없는 `iss + sub`의 MCP 호출이 `AUTHORIZATION_DENIED` | 구현 완료, 회귀 검증 대기 |
|
| 미매핑 토큰 거부 | 매핑 없는 `iss + sub`의 MCP 호출이 `AUTHORIZATION_DENIED` | 구현 완료, 회귀 검증 대기 |
|
||||||
|
|
||||||
`dds_vector_search`는 현재 유일한 DDS MCP 도구이며 입력값은 `query`(필수), `limit`(1~100), `embeddingMode`(`DEMO` 또는 `AI`)다. 데이터 권한 차이 검증은 서비스 identity의 OCI IAM 설정과 Alice 허용/Bob 거부 DDS grant를 반영한 뒤에 완료한다.
|
`dds_vector_search`는 현재 유일한 DDS MCP 도구이며 입력값은 `query`(필수), `limit`(1~100), `embeddingMode`(`DEMO` 또는 `AI`)다. service identity와 Alice 허용/Bob 거부 DDS grant를 반영해 권한 차이까지 검증했다.
|
||||||
|
|
||||||
|
## 4.4 OCI IAM database-access token 구성
|
||||||
|
|
||||||
|
Keycloak access token은 MCP의 업무 사용자 식별과 `iss + sub` 매핑에만 사용한다. Oracle Deep Sec context를 열 때는 별도의 OCI IAM database-access token이 필요하다. 이 토큰은 `DDS_MCP_SERVICE_TEST` confidential client가 client credentials flow로 받고, DB는 해당 client ID를 application identity로 신뢰한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
AD user -> Keycloak token -> MCP user mapping -> local DDS END USER
|
||||||
|
\
|
||||||
|
OCI IAM DDS_MCP_SERVICE_TEST -- database-access token --> Oracle Deep Sec context
|
||||||
|
```
|
||||||
|
|
||||||
|
구성 순서는 OCI IAM database resource(`DDS_ORACLE_DB_TEST`)와 scope(`DB_ACCESS_SCOPE`) 생성, OCI IAM service client 생성, Autonomous DB의 OCI IAM identity provider/credential 등록, 그리고 `CREATE APPLICATION IDENTITY ... MAPPED TO 'IAM_OAUTH_CLIENT_ID=...'` 순서다. 이 단계는 Autonomous DB의 외부 인증 구성을 변경할 수 있으므로 기존 설정을 먼저 조회하고 Redmine에 기록한다.
|
||||||
|
|
||||||
|
### 적용 결과와 검증
|
||||||
|
|
||||||
|
| 구성 요소 | 적용값 | 상태 |
|
||||||
|
|---|---|---|
|
||||||
|
| OCI IAM database resource | `DDS_ORACLE_DB_TEST` / `DB_ACCESS_SCOPE` | 적용 완료 |
|
||||||
|
| OCI IAM service client | `DDS_MCP_SERVICE_TEST`, client credentials만 허용 | 적용 완료 |
|
||||||
|
| Autonomous DB identity provider | `OCI_IAM`, database resource app ID와 OCI IAM domain URL 등록 | 적용 완료 |
|
||||||
|
| DB signing-key credential | `OCI_IAM_DOMAIN_DB_CRED$` | 적용 완료 |
|
||||||
|
| DB application identity | `DDS_MCP_SERVICE_TEST` → OCI IAM service client ID | 적용 완료 |
|
||||||
|
| HMM DDS MCP runtime | client ID·secret·scope를 권한 제한 환경 파일로 로드 | 적용 완료 |
|
||||||
|
| Alice data grant | `DDS_U_1_ROLE` → `CB_VECTOR_SEARCH_DOCUMENTS` SELECT | 적용 완료 |
|
||||||
|
| Bob data grant | 없음 | default deny |
|
||||||
|
|
||||||
|
database-access token은 `resource_app_id`, `tenant_iss`, scope가 DB identity provider 등록과 모두 일치해야 한다. OCI IAM domain URL은 token의 issuer와 같은 정규형(`:443` 포함)을 사용했다. 포트가 빠진 URL로 등록하면 Oracle이 `ORA-52602`(invalid database access token)으로 context 사용을 거부한다.
|
||||||
|
|
||||||
|
2026-08-04 검증 결과는 다음과 같다. 동일한 `dds_vector_search` 호출에서 Alice는 DDS context attach 후 query가 성공했고, Bob은 data grant가 없어 보호 객체 조회가 거부됐다. 현재 HMM knowledge chunk 데이터가 없으므로 Alice의 성공 응답 행 수는 `0`이다. 이는 권한 허용과 데이터 존재 여부를 구분한 결과다.
|
||||||
|
|
||||||
## 5. Entra ID로 전환할 때
|
## 5. Entra ID로 전환할 때
|
||||||
|
|
||||||
@@ -142,7 +197,7 @@ Entra tenant가 확보되면 AD DS/bridge 테스트에서 확인한 `issuer + im
|
|||||||
- [x] `dds.test` AD forest와 두 테스트 사용자가 생성되었다.
|
- [x] `dds.test` AD forest와 두 테스트 사용자가 생성되었다.
|
||||||
- [x] Keycloak LDAP bridge가 HTTPS OIDC JWT를 발급하고 각 사용자가 서로 다른 안정 subject를 가진다.
|
- [x] Keycloak LDAP bridge가 HTTPS OIDC JWT를 발급하고 각 사용자가 서로 다른 안정 subject를 가진다.
|
||||||
- [x] MCP가 JWT signature/issuer/audience를 검증하고 `issuer + sub` 매핑을 해석한다.
|
- [x] MCP가 JWT signature/issuer/audience를 검증하고 `issuer + sub` 매핑을 해석한다.
|
||||||
- [ ] subject 매핑을 통해 각 요청에 대응하는 DDS END USER context만 attach된다.
|
- [x] subject 매핑을 통해 각 요청에 대응하는 DDS END USER context만 attach된다.
|
||||||
- [ ] 서로 다른 권한의 동일 MCP 호출에서 데이터 행/열 결과가 달라진다.
|
- [x] 서로 다른 권한의 동일 MCP 호출에서 데이터 행/열 결과가 달라진다.
|
||||||
- [ ] 미매핑·만료·issuer/audience 불일치 요청은 데이터 접근 전에 거부된다.
|
- [ ] 미매핑·만료·issuer/audience 불일치 요청은 데이터 접근 전에 거부된다.
|
||||||
- [ ] 종료 시 테스트 VM과 전용 네트워크 리소스의 정리 여부 및 비용 상태를 Redmine에 기록한다.
|
- [ ] 종료 시 테스트 VM과 전용 네트워크 리소스의 정리 여부 및 비용 상태를 Redmine에 기록한다.
|
||||||
|
|||||||
Reference in New Issue
Block a user