From b55cb2c4bf7e45bdf644a542a6898bf540ffa2fd Mon Sep 17 00:00:00 2001 From: Lectom C Han Date: Wed, 28 Jan 2026 12:19:56 +0900 Subject: [PATCH] =?UTF-8?q?ory=20selfservice=20=EC=83=98=ED=94=8C=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.sample | 3 ++ compose.ory.yaml | 25 ++++++++- devfront/src/lib/apiClient.ts | 5 +- docker/ory/kratos/kratos.yml | 22 ++++---- docs/ory-usage.md | 97 +++++++++++++++++++++++++++++++++++ test/ory-network-check.sh | 28 ++++++++++ 6 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 docs/ory-usage.md create mode 100755 test/ory-network-check.sh diff --git a/.env.sample b/.env.sample index 67ad0f1e..2495893c 100644 --- a/.env.sample +++ b/.env.sample @@ -89,6 +89,9 @@ KETO_WRITE_PORT=4467 ORY_SDK_URL=http://kratos:4433 KRATOS_PUBLIC_URL=http://kratos:4433 KRATOS_ADMIN_URL=http://kratos:4434 +# 브라우저가 접근할 Kratos Public/UI 외부 URL (리버스 프록시/도메인 환경 고려) +KRATOS_BROWSER_URL=http://localhost:4433 +KRATOS_UI_URL=http://localhost:4455 HYDRA_ADMIN_URL=http://hydra:4445 HYDRA_PUBLIC_URL=http://hydra:4444 JWKS_URL=http://oathkeeper:4456/.well-known/jwks.json diff --git a/compose.ory.yaml b/compose.ory.yaml index f76ab284..0d132440 100644 --- a/compose.ory.yaml +++ b/compose.ory.yaml @@ -22,6 +22,17 @@ services: image: oryd/kratos:${KRATOS_VERSION:-v25.4.0} environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB}?sslmode=disable&max_conns=20 + - KRATOS_SERVE_PUBLIC_BASE_URL=${KRATOS_BROWSER_URL:-http://localhost:4433} + - KRATOS_SERVE_ADMIN_BASE_URL=${KRATOS_ADMIN_URL:-http://kratos:4434} + - KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL:-http://localhost:4455} + - KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=["${KRATOS_UI_URL:-http://localhost:4455}","${USERFRONT_URL:-http://localhost:5000}"] + - KRATOS_SELFSERVICE_FLOWS_ERROR_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/error + - KRATOS_SELFSERVICE_FLOWS_SETTINGS_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/settings + - KRATOS_SELFSERVICE_FLOWS_RECOVERY_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/recovery + - KRATOS_SELFSERVICE_FLOWS_VERIFICATION_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/verification + - KRATOS_SELFSERVICE_FLOWS_LOGIN_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/login + - KRATOS_SELFSERVICE_FLOWS_REGISTRATION_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/registration + - KRATOS_SELFSERVICE_FLOWS_LOGOUT_AFTER_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL:-http://localhost:4455}/login volumes: - ./docker/ory/kratos:/etc/config/kratos command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes @@ -36,10 +47,20 @@ services: container_name: ory_kratos ports: - "${KRATOS_PUBLIC_PORT:-4433}:4433" - - "${KRATOS_ADMINFRONT_PORT:-4434}:4434" environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB}?sslmode=disable&max_conns=20 - COOKIE_SECRET=${COOKIE_SECRET:-localcookie123} + - KRATOS_SERVE_PUBLIC_BASE_URL=${KRATOS_BROWSER_URL:-http://localhost:4433} + - KRATOS_SERVE_ADMIN_BASE_URL=${KRATOS_ADMIN_URL:-http://kratos:4434} + - KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL:-http://localhost:4455} + - KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=["${KRATOS_UI_URL:-http://localhost:4455}","${USERFRONT_URL:-http://localhost:5000}"] + - KRATOS_SELFSERVICE_FLOWS_ERROR_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/error + - KRATOS_SELFSERVICE_FLOWS_SETTINGS_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/settings + - KRATOS_SELFSERVICE_FLOWS_RECOVERY_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/recovery + - KRATOS_SELFSERVICE_FLOWS_VERIFICATION_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/verification + - KRATOS_SELFSERVICE_FLOWS_LOGIN_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/login + - KRATOS_SELFSERVICE_FLOWS_REGISTRATION_UI_URL=${KRATOS_UI_URL:-http://localhost:4455}/registration + - KRATOS_SELFSERVICE_FLOWS_LOGOUT_AFTER_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL:-http://localhost:4455}/login volumes: - ./docker/ory/kratos:/etc/config/kratos command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier @@ -73,6 +94,7 @@ services: - "${KRATOS_UI_PORT:-4455}:4455" environment: - KRATOS_PUBLIC_URL=${KRATOS_PUBLIC_URL:-http://kratos:4433/} + - KRATOS_BROWSER_URL=${KRATOS_BROWSER_URL:-http://localhost:${KRATOS_PUBLIC_PORT:-4433}} - KRATOS_ADMIN_URL=${KRATOS_ADMIN_URL:-http://kratos:4434/} - NODE_ENV=development - PORT=${KRATOS_UI_PORT:-4455} @@ -99,7 +121,6 @@ services: container_name: ory_hydra ports: - "${HYDRA_PUBLIC_PORT:-4441}:4444" - - "${HYDRA_ADMINFRONT_PORT:-4445}:4445" environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB}?sslmode=disable&max_conns=20 - URLS_SELF_ISSUER=${BACKEND_URL:-http://127.0.0.1:3000} diff --git a/devfront/src/lib/apiClient.ts b/devfront/src/lib/apiClient.ts index 80927a89..f136f91b 100644 --- a/devfront/src/lib/apiClient.ts +++ b/devfront/src/lib/apiClient.ts @@ -1,7 +1,10 @@ import axios from "axios"; const apiClient = axios.create({ - baseURL: import.meta.env.VITE_ADMIN_API_BASE ?? "/api/admin", + baseURL: + import.meta.env.VITE_DEV_API_BASE ?? + import.meta.env.VITE_ADMIN_API_BASE ?? + "/api/v1/dev", }); apiClient.interceptors.request.use((config) => { diff --git a/docker/ory/kratos/kratos.yml b/docker/ory/kratos/kratos.yml index 3ee3b617..27ed71a2 100644 --- a/docker/ory/kratos/kratos.yml +++ b/docker/ory/kratos/kratos.yml @@ -4,16 +4,16 @@ dsn: memory serve: public: - base_url: http://127.0.0.1:4433/ + base_url: http://localhost:4433/ cors: enabled: true admin: - base_url: http://127.0.0.1:4434/ + base_url: http://localhost:4434/ selfservice: - default_browser_return_url: http://127.0.0.1:4455/ + default_browser_return_url: http://localhost:4455/ allowed_return_urls: - - http://127.0.0.1:4455 + - http://localhost:4455 - http://localhost:5000 methods: @@ -26,24 +26,24 @@ selfservice: flows: error: - ui_url: http://127.0.0.1:4455/error + ui_url: http://localhost:4455/error settings: - ui_url: http://127.0.0.1:4455/settings + ui_url: http://localhost:4455/settings privileged_session_max_age: 15m recovery: - ui_url: http://127.0.0.1:4455/recovery + ui_url: http://localhost:4455/recovery use: code verification: - ui_url: http://127.0.0.1:4455/verification + ui_url: http://localhost:4455/verification use: code logout: after: - default_browser_return_url: http://127.0.0.1:4455/login + default_browser_return_url: http://localhost:4455/login login: - ui_url: http://127.0.0.1:4455/login + ui_url: http://localhost:4455/login lifespan: 10m registration: - ui_url: http://127.0.0.1:4455/registration + ui_url: http://localhost:4455/registration lifespan: 10m log: diff --git a/docs/ory-usage.md b/docs/ory-usage.md new file mode 100644 index 00000000..ed3ecf12 --- /dev/null +++ b/docs/ory-usage.md @@ -0,0 +1,97 @@ +# Ory Stack 사용 가이드 + +이 문서는 Baron SSO 로컬/도메인 환경에서 Ory Stack(Kratos/Hydra/Keto/Oathkeeper) 사용법과 내부/외부 엔드포인트 구성을 정리합니다. + +## 1) 구성 요약 +- **Kratos**: Identity/Session 관리(SoT) +- **Hydra**: OAuth2/OIDC 토큰 엔진 +- **Keto**: 권한/정책 +- **Kratos UI**: Self-service UI (login/registration 등) + +## 2) 실행 방법 +```bash +# 인프라 + Ory Stack +docker compose -f compose.infra.yaml -f compose.ory.yaml up -d + +# 앱 스택(backend/userfront/adminfront/devfront) +docker compose -f docker-compose.yaml up -d +``` + +## 3) 내부 통신 vs 브라우저 접근용 URL 분리 +Ory 구성은 **컨테이너 내부 통신 URL**과 **브라우저 접근 URL**을 분리해야 합니다. + +### 내부 통신용 URL(컨테이너 네트워크) +- `KRATOS_PUBLIC_URL=http://kratos:4433` +- `KRATOS_ADMIN_URL=http://kratos:4434` +- `HYDRA_PUBLIC_URL=http://hydra:4444` +- `HYDRA_ADMIN_URL=http://hydra:4445` + +### 브라우저 접근용 URL(외부 도메인/프록시) +- `KRATOS_BROWSER_URL` : Kratos Public의 외부 URL +- `KRATOS_UI_URL` : Kratos UI의 외부 URL + +예시(로컬): +```env +KRATOS_BROWSER_URL=http://localhost:4433 +KRATOS_UI_URL=http://localhost:4455 +``` + +예시(리버스 프록시/도메인): +```env +KRATOS_BROWSER_URL=https://sso.example.com +KRATOS_UI_URL=https://sso-ui.example.com +``` + +### 포트 노출 정책 +- **Kratos/Hydra Admin 포트는 호스트에 노출하지 않음** (내부 네트워크 전용) +- MCP 서버는 동일 네트워크에서 `http://kratos:4434`, `http://hydra:4445`로 접근 +- Backend는 `ory-net`에 연결되어 있어 Admin 포트 접근 가능 +- 브라우저/Frontend는 Backend API를 통해서만 IDP 기능을 호출 + +## 4) Kratos Self-service UI 리다이렉트 설정 +Kratos는 self-service UI URL을 설정값으로 사용합니다. 브라우저에서 접근 가능한 URL이어야 정상 동작합니다. + +- `KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL` +- `KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS` +- `KRATOS_SELFSERVICE_FLOWS_*_UI_URL` + +compose에서 기본적으로 다음과 같이 오버라이드합니다: +- `KRATOS_SELFSERVICE_FLOWS_LOGIN_UI_URL=${KRATOS_UI_URL}/login` +- `KRATOS_SELFSERVICE_FLOWS_REGISTRATION_UI_URL=${KRATOS_UI_URL}/registration` +- `KRATOS_SELFSERVICE_FLOWS_SETTINGS_UI_URL=${KRATOS_UI_URL}/settings` +- `KRATOS_SELFSERVICE_FLOWS_RECOVERY_UI_URL=${KRATOS_UI_URL}/recovery` +- `KRATOS_SELFSERVICE_FLOWS_VERIFICATION_UI_URL=${KRATOS_UI_URL}/verification` + +## 5) 트러블슈팅 +### 5.1 로그인 클릭 시 동작 없음 +- 원인: Kratos 기동 실패(설정 파싱 실패 등) 또는 브라우저용 URL이 내부 도메인(`kratos:...`)으로 설정됨 +- 확인: + - `docker logs ory_kratos`에서 config 오류 여부 확인 + - 브라우저 네트워크 탭에서 `/self-service/login/browser` 응답 확인(302 Location 헤더) + +### 5.2 kratos.yml에 ${...} 환경 변수 치환 실패 +- Kratos 설정 파일은 `${ENV}` 치환을 지원하지 않음 +- 해결: compose 환경 변수로 `KRATOS_SELFSERVICE_*`, `KRATOS_SERVE_*` 오버라이드 사용 + +## 6) 네트워크 접근 테스트 +아래 스크립트는 **ory-net에서 Admin 포트 접근 가능** / **baron_net(Frontend 영역)에서 접근 불가**를 검증합니다. + +```bash +bash test/ory-network-check.sh +``` + +직접 확인하려면: +```bash +# ory-net에서는 성공해야 함 +docker run --rm --network ory-net curlimages/curl:8.10.1 -fsS http://hydra:4445/health/ready +docker run --rm --network ory-net curlimages/curl:8.10.1 -fsS http://kratos:4434/health/ready + +# baron_net에서는 실패해야 함 +docker run --rm --network baron_net curlimages/curl:8.10.1 -fsS http://hydra:4445/health/ready +docker run --rm --network baron_net curlimages/curl:8.10.1 -fsS http://kratos:4434/health/ready +``` + +## 7) 참고 파일 +- `compose.ory.yaml` +- `docker/ory/kratos/kratos.yml` +- `.env.sample` diff --git a/test/ory-network-check.sh b/test/ory-network-check.sh new file mode 100755 index 00000000..ebc59f22 --- /dev/null +++ b/test/ory-network-check.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Kratos/Hydra admin endpoints should be reachable only on ory-net. +# Frontend network (baron_net) must NOT reach admin endpoints. + +IMAGE="curlimages/curl:8.10.1" + +# ory-net should succeed +# 한국어: ory-net에서는 admin 포트 접근이 가능해야 함 + +docker run --rm --network ory-net "$IMAGE" -fsS http://hydra:4445/health/ready > /dev/null + +docker run --rm --network ory-net "$IMAGE" -fsS http://kratos:4434/health/ready > /dev/null + +# baron_net should fail +# 한국어: baron_net에서는 admin 포트 접근이 불가능해야 함 +if docker run --rm --network baron_net "$IMAGE" -fsS http://hydra:4445/health/ready > /dev/null 2>&1; then + echo "ERROR: hydra admin is reachable from baron_net" + exit 1 +fi + +if docker run --rm --network baron_net "$IMAGE" -fsS http://kratos:4434/health/ready > /dev/null 2>&1; then + echo "ERROR: kratos admin is reachable from baron_net" + exit 1 +fi + +echo "OK: admin endpoints are reachable on ory-net only"