1
0
forked from baron/baron-sso
Files
baron-sso/docs/traefik-production-rp-bootstrap-design.md
2026-06-18 11:02:48 +09:00

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-public external 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의 방향은 다음과 같다.

  • traefikforward-auth 모두 traefik-public external network에 붙인다.
  • forward-auth service에는 traefik.http.services.forward-auth.loadbalancer.server.port=4181 label을 명시한다.
  • dashboard router에는 auth-forward@docker middleware를 적용한다.
  • 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
  • INSECURE_COOKIE=false를 production 기본값으로 둔다.
  • CLIENT_SECRET과 cookie SECRET는 운영 secret 주입만 허용한다.

Baron app compose 쪽에서도 production-facing 진입 서비스는 traefik-public에 붙어야 한다. 기존 정책상 외부 진입은 gateway/Oathkeeper 계층으로 수렴하는 것이 맞으므로, Traefik이 직접 backend admin endpoint로 붙는 구조는 피한다.

Hydra Client Bootstrap Design

현재 compose.ory.yamlinit-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은 별도 운영 작업으로만 수행한다.

  1. hydra get oauth2-client <client_id>로 존재 여부 확인
  2. 없으면 env/secret store 값을 사용해 hydra create oauth2-client
  3. 없고 초기 생성 모드가 명시되어 있으면 random secret을 생성해 운영자가 저장할 수 있게 출력 또는 secret file에 기록
  4. 있으면 hydra update oauth2-client로 redirect URI, scope, metadata 같은 비밀값이 아닌 설정을 동기화
  5. client secret은 기본적으로 덮어쓰지 않고, rotation flag가 있을 때만 갱신
  6. 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_URLS parsing
  • 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 포함
  • verify mode에서 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를 먼저 추가한다.

  1. test/traefik_forward_auth_config_policy_test.sh

    • config/traefik-compose.yml에 literal CLIENT_SECRET= 또는 SECRET= 값이 있으면 실패
    • traefikforward-authtraefik-public network에 붙지 않으면 실패
    • PROVIDER_GENERIC_*_URL이 Keycloak path를 사용하면 실패
    • dashboard router에 auth-forward middleware가 없으면 실패
  2. test/auth_config_traefik_rp_policy_test.sh

    • APP_ENV=production에서 Traefik client secret이 없으면 scripts/auth_config.sh validate 실패
    • callback URL이 /_oauth 형태가 아니거나 http URL이면 실패
    • generated env에 TRAEFIK_FORWARD_AUTH_CALLBACK_URLS가 없으면 실패
  3. test/compose_ory_traefik_rp_bootstrap_policy_test.sh

    • init-rp 또는 분리된 boot script가 Traefik RP를 등록하지 않으면 실패
    • boot flow가 delete/create only 방식이면 실패하고 upsert 방식을 요구
  4. 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

  1. .env.sample에 Traefik forward-auth 변수를 placeholder로 추가한다.
  2. RED policy tests를 추가하고 실패를 확인한다.
  3. config/traefik-compose.yml을 env-driven Baron/Ory 설정으로 교체한다.
  4. scripts/auth_config.sh와 Hydra RP bootstrap script를 확장한다.
  5. compose.ory.yamlinit-rp가 production에서 Traefik RP upsert를 실행하도록 연결한다.
  6. Baron SSO production deployment template의 public-facing services에 Traefik labels와 traefik-public external network를 연결한다.
  7. make validate-auth-config, 관련 shell policy tests, compose config 검증을 통과시킨다.
  8. 운영 환경에서 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은 .envTRAEFIK_PUBLIC_NETWORK로 관리하며 기본값은 traefik-public이다.

deploy/create-instance.shTARGET_DIR=/home/user/prod.baron-sso처럼 레포 밖 고정 경로에 생성할 수 있고, 이 경우에도 compose build context가 깨지지 않도록 .envSOURCE_ROOT를 실행 중인 레포 루트 절대 경로로 채운다.

Open Decisions

  • TRAEFIK_FORWARD_AUTH_HOSTauth.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에 맞추는 것이다.