11 KiB
Traefik Production RP Bootstrap Design
Context
프로덕션 배포환경에서는 Baron SSO 앞단에 Traefik이 reverse proxy로 먼저 떠 있고, Traefik dashboard와 보호 대상 라우트도 Baron SSO 인증을 사용해야 한다.
현재 config/traefik-compose.yml은 Traefik과 traefik-forward-auth를 사전 구동하는 형태지만 다음 보완이 필요하다.
CLIENT_ID,CLIENT_SECRET,SECRET가 파일에 하드코딩되어 있다.- OIDC endpoint가 Baron/Ory Hydra가 아니라 Keycloak style path를 가리킨다.
traefik-publicexternal network가 선언만 되어 있고 서비스에 연결되어 있지 않다.- production boot flow에서 Traefik forward-auth RP가 Hydra에 자동 등록되지 않는다.
관련 이슈: #1221
Policy Alignment
- OAuth2/OIDC client SoT는 Ory Hydra다.
- client secret 원문은 Git에 커밋하지 않고
.env, 배포 host의 secret file, Gitea Actions secret, 또는 운영 secret store에서 주입한다. - Kratos는 identity SoT, Keto는 authorization relation SoT로 유지한다.
- Traefik과 forward-auth가 신뢰할 수 있는 last-hop proxy가 되며, 애플리케이션은 임의 외부 요청의 identity header를 신뢰하지 않는다.
- Wiki가 사용 중이므로 실제 Wiki 업데이트 전 검토본은
docs/문서로 둔다.
Target Architecture
flowchart TD
User[Browser] --> Traefik[Traefik edge proxy]
Traefik -->|ForwardAuth| TFA[traefik-forward-auth]
TFA -->|OIDC authorize/token/userinfo| Hydra[Ory Hydra public endpoint]
Hydra --> UserFront[Baron UserFront login/consent]
UserFront --> Backend[Baron Backend]
Backend --> Kratos[Ory Kratos]
Backend --> Keto[Ory Keto]
Traefik --> Dashboard[api@internal dashboard]
Traefik --> BaronRoute[Baron app routes]
traefik-forward-auth는 Hydra confidential client로 등록한다. SPA용 adminfront, devfront, orgfront와 달리 server-side middleware이므로 client_secret 기반 client로 다룬다.
Configuration Contract
운영 환경 변수는 다음 이름을 기준으로 둔다.
| Key | Required | Example | Note |
|---|---|---|---|
TRAEFIK_DASHBOARD_HOST |
yes | traefik.brsw.kr |
Traefik dashboard host |
TRAEFIK_FORWARD_AUTH_HOST |
yes | auth.brsw.kr |
forward-auth callback host |
TRAEFIK_FORWARD_AUTH_CLIENT_ID |
yes | traefik-forward-auth |
Hydra client id |
TRAEFIK_FORWARD_AUTH_CLIENT_SECRET |
yes in production | secret value | Git에 저장하지 않는다 |
TRAEFIK_FORWARD_AUTH_COOKIE_SECRET |
yes in production | 32+ byte random | forward-auth cookie signing secret |
TRAEFIK_FORWARD_AUTH_CALLBACK_URLS |
yes | https://auth.brsw.kr/_oauth |
comma-separated |
HYDRA_PUBLIC_URL |
yes | https://app.brsw.kr/oidc |
Baron/Ory public issuer base |
config/traefik-compose.yml은 위 값을 직접 박지 않고 ${...} placeholder만 사용한다. 여기서 금지하는 것은 "운영 secret 원문을 코드/문서/compose 파일에 커밋하는 것"이다. 배포 이후에는 발급된 client id/secret/cookie secret을 운영 설정 또는 저장소 secret에 고정해 재사용해야 한다.
Compose Design
config/traefik-compose.yml의 방향은 다음과 같다.
traefik와forward-auth모두traefik-publicexternal network에 붙인다.forward-authservice에는traefik.http.services.forward-auth.loadbalancer.server.port=4181label을 명시한다.- dashboard router에는
auth-forward@dockermiddleware를 적용한다. - forward-auth provider endpoint는
HYDRA_PUBLIC_URL기반generic-oauth설정으로 산출한다. Traefik이 Baron/Ory보다 먼저 떠야 하는 bootstrap 순서에서는 OIDC discovery가 시작 시점에 실패할 수 있으므로 명시 endpoint 방식을 사용한다.- auth:
${HYDRA_PUBLIC_URL}/oauth2/auth - token:
${HYDRA_PUBLIC_URL}/oauth2/token - userinfo:
${HYDRA_PUBLIC_URL}/userinfo
- auth:
INSECURE_COOKIE=false를 production 기본값으로 둔다.CLIENT_SECRET과 cookieSECRET는 운영 secret 주입만 허용한다.
Baron app compose 쪽에서도 production-facing 진입 서비스는 traefik-public에 붙어야 한다. 기존 정책상 외부 진입은 gateway/Oathkeeper 계층으로 수렴하는 것이 맞으므로, Traefik이 직접 backend admin endpoint로 붙는 구조는 피한다.
Hydra Client Bootstrap Design
현재 compose.ory.yaml의 init-rp는 Hydra 준비 후 기본 RP를 등록한다. Traefik forward-auth RP도 같은 부팅 경로에 넣되, production mode에서만 활성화한다.
권장 조건:
APP_ENV in production|prod
TRAEFIK_FORWARD_AUTH_ENABLED=true
등록 payload:
{
"client_id": "traefik-forward-auth",
"client_name": "Traefik Forward Auth",
"client_secret": "<from env>",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "openid offline_access profile email",
"redirect_uris": ["https://auth.brsw.kr/_oauth"],
"token_endpoint_auth_method": "client_secret_basic",
"metadata": {
"managed_by": "baron-sso-boot",
"system_client": true,
"purpose": "traefik-forward-auth",
"status": "active"
}
}
구현은 delete/create보다 idempotent upsert가 안전하다. 최초 부팅 시에는 값이 없으면 생성값을 발급할 수 있지만, RP가 한번 등록된 뒤에는 같은 client_id, client_secret, callback URI를 운영 설정으로 고정해야 한다. 재배포마다 secret이 바뀌면 forward-auth cookie/session과 Hydra client 인증이 깨질 수 있으므로 rotation은 별도 운영 작업으로만 수행한다.
hydra get oauth2-client <client_id>로 존재 여부 확인- 없으면 env/secret store 값을 사용해
hydra create oauth2-client - 없고 초기 생성 모드가 명시되어 있으면 random secret을 생성해 운영자가 저장할 수 있게 출력 또는 secret file에 기록
- 있으면
hydra update oauth2-client로 redirect URI, scope, metadata 같은 비밀값이 아닌 설정을 동기화 - client secret은 기본적으로 덮어쓰지 않고, rotation flag가 있을 때만 갱신
- secret mismatch나 필수 redirect URI 누락 시 실패
기본 RP 등록 shell block이 길어지고 있으므로, 구현 단계에서는 scripts/register_hydra_clients.sh 같은 별도 boot script로 분리하는 편이 유지보수에 유리하다.
Generated Auth Config
scripts/auth_config.sh는 다음을 확장한다.
TRAEFIK_FORWARD_AUTH_CALLBACK_URLSparsing- production mode에서 Traefik client id/secret/cookie secret 필수 검증
- generated
config/.generated/auth-config.env에 Traefik callback CSV 포함 KRATOS_ALLOWED_RETURN_URLS_JSON에 forward-auth callback URL 포함verifymode에서 runtime Hydra client에 Traefik callback이 들어 있는지 확인
운영 secret 보관 위치는 두 가지를 허용한다.
- 배포 host 기준:
.env또는 Docker secret/secret file로 주입 - 저장소 기준: Gitea Actions secret에 저장하고 배포 workflow에서
.env또는 secret file로 렌더링
두 경우 모두 Git tracked 파일에는 실제 secret 값을 남기지 않는다.
이렇게 하면 Ory 렌더링과 Hydra RP 등록이 같은 auth config contract를 공유한다.
Test Plan
구현 전에 RED test를 먼저 추가한다.
-
test/traefik_forward_auth_config_policy_test.shconfig/traefik-compose.yml에 literalCLIENT_SECRET=또는SECRET=값이 있으면 실패traefik와forward-auth가traefik-publicnetwork에 붙지 않으면 실패PROVIDER_GENERIC_*_URL이 Keycloak path를 사용하면 실패- dashboard router에
auth-forwardmiddleware가 없으면 실패
-
test/auth_config_traefik_rp_policy_test.shAPP_ENV=production에서 Traefik client secret이 없으면scripts/auth_config.sh validate실패- callback URL이
/_oauth형태가 아니거나 http URL이면 실패 - generated env에
TRAEFIK_FORWARD_AUTH_CALLBACK_URLS가 없으면 실패
-
test/compose_ory_traefik_rp_bootstrap_policy_test.shinit-rp또는 분리된 boot script가 Traefik RP를 등록하지 않으면 실패- boot flow가 delete/create only 방식이면 실패하고 upsert 방식을 요구
-
live verification
hydra get oauth2-client --endpoint "$HYDRA_ADMIN_URL" "$TRAEFIK_FORWARD_AUTH_CLIENT_ID"- dashboard 접근 시 Hydra authorize redirect 발생
- callback URL이 registered redirect URI와 정확히 일치
docker network inspect traefik-public에서traefik,forward-auth, production entry service 연결 확인
Rollout Sequence
.env.sample에 Traefik forward-auth 변수를 placeholder로 추가한다.- RED policy tests를 추가하고 실패를 확인한다.
config/traefik-compose.yml을 env-driven Baron/Ory 설정으로 교체한다.scripts/auth_config.sh와 Hydra RP bootstrap script를 확장한다.compose.ory.yaml의init-rp가 production에서 Traefik RP upsert를 실행하도록 연결한다.- Baron SSO production deployment template의 public-facing services에 Traefik labels와
traefik-publicexternal network를 연결한다. make validate-auth-config, 관련 shell policy tests, compose config 검증을 통과시킨다.- 운영 환경에서 live verification을 수행한다.
Baron SSO Deployment Labels
deploy/templates/docker-compose.yaml에서 외부 진입 서비스는 다음 Traefik router를 갖는다.
| Service | Host variable | Internal port | Purpose |
|---|---|---|---|
gateway |
PUBLIC_HOST |
80 | SSO main app, /api, /auth, /oidc |
adminfront |
ADMINFRONT_HOST |
5173 | Admin console |
devfront |
DEVFRONT_HOST |
5173 | Developer/RP console |
orgfront |
ORGFRONT_HOST |
5175 | Organization console |
각 public-facing service는 내부 app_net과 external traefik_public에 동시에 연결한다. traefik_public의 실제 Docker network name은 .env의 TRAEFIK_PUBLIC_NETWORK로 관리하며 기본값은 traefik-public이다.
deploy/create-instance.sh는 TARGET_DIR=/home/user/prod.baron-sso처럼 레포 밖 고정 경로에 생성할 수 있고, 이 경우에도 compose build context가 깨지지 않도록 .env의 SOURCE_ROOT를 실행 중인 레포 루트 절대 경로로 채운다.
Open Decisions
TRAEFIK_FORWARD_AUTH_HOST를auth.brsw.kr로 분리할지,app.brsw.kr/_oauth같은 동일 host callback으로 둘지 결정이 필요하다.External RP Ory IAM Foundation마일스톤은 현재 Due Date가 비어 있다. 구현 착수 전에 목표 Due Date를 정하는 것이 좋다.traefik-forward-auth를 계속 사용할지, Baron Backend/Oathkeeper에 first-party forward-auth endpoint를 만들지는 별도 장기 개선안으로 남긴다. 단기 목표는 현재 compose 구조를 안전하게 Baron/Ory에 맞추는 것이다.