From 35ce7853faa400386a19fe2e995dfeaa461ddef9 Mon Sep 17 00:00:00 2001 From: chan Date: Thu, 16 Apr 2026 14:04:37 +0900 Subject: [PATCH] =?UTF-8?q?Baron=20SSO=20=EB=8B=A4=EC=A4=91=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=20=EB=B0=B0=ED=8F=AC=20=ED=85=9C?= =?UTF-8?q?=ED=94=8C=EB=A6=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/create-instance.sh | 76 ++++++++++++ deploy/deploy_guide.md | 64 ++++++++++ deploy/templates/.env.template | 55 +++++++++ deploy/templates/adminfront/vite.config.ts | 29 +++++ deploy/templates/auth.template.ts | 21 ++++ deploy/templates/devfront/vite.config.ts | 29 +++++ deploy/templates/docker-compose.yaml | 135 +++++++++++++++++++++ deploy/templates/gateway/nginx.conf | 43 +++++++ deploy/templates/ory/kratos/kratos.yml | 114 +++++++++++++++++ deploy/templates/ory/oathkeeper/rules.json | 15 +++ deploy/templates/userfront/nginx.conf | 71 +++++++++++ 11 files changed, 652 insertions(+) create mode 100644 deploy/create-instance.sh create mode 100644 deploy/deploy_guide.md create mode 100644 deploy/templates/.env.template create mode 100644 deploy/templates/adminfront/vite.config.ts create mode 100644 deploy/templates/auth.template.ts create mode 100644 deploy/templates/devfront/vite.config.ts create mode 100644 deploy/templates/docker-compose.yaml create mode 100644 deploy/templates/gateway/nginx.conf create mode 100644 deploy/templates/ory/kratos/kratos.yml create mode 100644 deploy/templates/ory/oathkeeper/rules.json create mode 100644 deploy/templates/userfront/nginx.conf diff --git a/deploy/create-instance.sh b/deploy/create-instance.sh new file mode 100644 index 00000000..9ff61865 --- /dev/null +++ b/deploy/create-instance.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# ================================================================= +# Baron SSO 인스턴스 자동 생성 스크립트 (Full Infrastructure) +# ================================================================= + +if [ "$#" -ne 2 ]; then + echo "❌ Usage: $0 [INSTANCE_NAME] [PORT_PREFIX]" + exit 1 +fi + +INSTANCE_NAME=$1 +PORT_PREFIX=$2 +BASE_DIR=$(cd $(dirname $0); pwd) +TARGET_DIR="${BASE_DIR}/../instances/${INSTANCE_NAME}" + +echo "🚀 Creating instance: ${INSTANCE_NAME} (Port Prefix: ${PORT_PREFIX}xxx)" + +# 1. 폴더 구조 생성 +mkdir -p "${TARGET_DIR}/gateway" +mkdir -p "${TARGET_DIR}/ory/kratos" +mkdir -p "${TARGET_DIR}/ory/oathkeeper" +mkdir -p "${TARGET_DIR}/userfront" +mkdir -p "${TARGET_DIR}/adminfront" +mkdir -p "${TARGET_DIR}/devfront" + +# 2. .env 생성 및 변수 로드 +sed "s/{{INSTANCE_NAME}}/${INSTANCE_NAME}/g; s/{{PORT_PREFIX}}/${PORT_PREFIX}/g" \ + "${BASE_DIR}/templates/.env.template" > "${TARGET_DIR}/.env" + +# 포트 계산 (단순 치환) +BACKEND_PORT="${PORT_PREFIX}000" +USERFRONT_PORT="${PORT_PREFIX}500" +DOMAIN_SUFFIX=$(grep "DOMAIN_SUFFIX=" "${TARGET_DIR}/.env" | cut -d'=' -f2 | tr -d '\r') +ADMINFRONT_DOMAIN="${INSTANCE_NAME}-admin.${DOMAIN_SUFFIX}" +DEVFRONT_DOMAIN="${INSTANCE_NAME}-dev.${DOMAIN_SUFFIX}" + +# 3. Docker Compose & Config 복사 및 치환 +cp "${BASE_DIR}/templates/docker-compose.yaml" "${TARGET_DIR}/" + +# Gateway & UserFront Nginx +sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/gateway/nginx.conf" > "${TARGET_DIR}/gateway/nginx.conf" +sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/userfront/nginx.conf" > "${TARGET_DIR}/userfront/nginx.conf" + +# Oathkeeper Rules +sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/ory/oathkeeper/rules.json" > "${TARGET_DIR}/ory/oathkeeper/rules.json" +cp "${TARGET_DIR}/ory/oathkeeper/rules.json" "${TARGET_DIR}/ory/oathkeeper/rules.active.json" + +# Kratos Config +sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g; s/{{USERFRONT_PORT}}/${USERFRONT_PORT}/g" \ + "${BASE_DIR}/templates/ory/kratos/kratos.yml" > "${TARGET_DIR}/ory/kratos/kratos.yml" + +# Vite Configs +sed "s/{{ADMINFRONT_DOMAIN}}/${ADMINFRONT_DOMAIN}/g; s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" \ + "${BASE_DIR}/templates/adminfront/vite.config.ts" > "${TARGET_DIR}/adminfront/vite.config.ts" +sed "s/{{DEVFRONT_DOMAIN}}/${DEVFRONT_DOMAIN}/g; s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" \ + "${BASE_DIR}/templates/devfront/vite.config.ts" > "${TARGET_DIR}/devfront/vite.config.ts" + +# 4. 프론트엔드 auth.ts 주입 (하드코딩된 포트 해결) +sed "s/{{USERFRONT_PORT}}/${USERFRONT_PORT}/g; s/{{CLIENT_ID}}/adminfront/g" \ + "${BASE_DIR}/templates/auth.template.ts" > "${TARGET_DIR}/adminfront/auth.ts" +sed "s/{{USERFRONT_PORT}}/${USERFRONT_PORT}/g; s/{{CLIENT_ID}}/devfront/g" \ + "${BASE_DIR}/templates/auth.template.ts" > "${TARGET_DIR}/devfront/auth.ts" + +# 5. Ory 정적 설정 복사 +if [ -d "${BASE_DIR}/../docker/ory/kratos" ]; then cp -n "${BASE_DIR}/../docker/ory/kratos/"* "${TARGET_DIR}/ory/kratos/" 2>/dev/null || true; fi +if [ -d "${BASE_DIR}/../docker/ory/oathkeeper" ]; then cp -n "${BASE_DIR}/../docker/ory/oathkeeper/"* "${TARGET_DIR}/ory/oathkeeper/" 2>/dev/null || true; fi + +# 6. 마무리 +chmod +x "${TARGET_DIR}/.env" + +echo "--------------------------------------------------" +echo "✅ Success! ALL files (Infra/Ory/Apps/FrontConfigs) are ready." +echo "📂 Location: ${TARGET_DIR}" +echo "🚀 Run: cd ${TARGET_DIR} && docker compose up -d" +echo "--------------------------------------------------" diff --git a/deploy/deploy_guide.md b/deploy/deploy_guide.md new file mode 100644 index 00000000..fc6d48da --- /dev/null +++ b/deploy/deploy_guide.md @@ -0,0 +1,64 @@ +# Baron SSO 다중 인스턴스 배포 가이드 + +이 프로젝트는 한 대의 서버(머신)에서 여러 개의 독립된 서비스 인스턴스(예: stg, test2, prod 등)를 충돌 없이 실행하기 위한 **템플릿 기반 자동 배포 시스템**을 사용합니다. + +## 1. 폴더 구조 +- `deploy/templates/`: 모든 설정의 원천이 되는 마스터 템플릿. +- `deploy/create-instance.sh`: 새로운 인스턴스를 생성하는 마법사 스크립트. +- `instances/`: (자동 생성) 실제 실행되는 인스턴스별 설정 파일들이 저장되는 곳. + +## 2. 핵심 배포 전략 + +### 2.1 포트 Prefix 시스템 +모든 포트는 환경 변수 `P` (Port Prefix)를 기반으로 자동 계산됩니다. +- 예: 포트 Prefix를 `23`으로 설정 시: + - Backend: `23000` + - PostgreSQL: `23432` + - UserFront: `23500` + - AdminFront: `23173` + +### 2.2 네임스페이스 격리 +모든 리소스 이름에 `${INSTANCE_NAME}`이 자동으로 붙어 도커 엔진 내에서 격리됩니다. +- 컨테이너: `baron-sso-test2_backend` +- 네트워크: `baron-sso-test2_net` +- 볼륨: `db_data_test2` + +## 3. 새로운 인스턴스 배포 방법 + +### Step 1: 인스턴스 생성 +`deploy/` 폴더로 이동하여 스크립트를 실행합니다. +```bash +cd deploy +./create-instance.sh [인스턴스이름] [포트앞자리] +``` +> **예시**: `test3` 환경을 `24xxx` 포트 대역으로 만들고 싶을 때 +> ```bash +> ./create-instance.sh test3 24 +> ``` + +### Step 2: 서비스 실행 +생성된 인스턴스 폴더로 이동하여 도커 컴포즈를 실행합니다. +```bash +cd instances/test3 +docker compose up -d +``` + +## 4. 도메인 및 URL 규칙 +스크립트 실행 시 인스턴스 이름에 따라 다음 도메인이 자동으로 설정 파일에 주입됩니다. (`DOMAIN_SUFFIX`는 `.env`에서 수정 가능) +- **SSO/UserFront**: `https://[이름]-sso.hmac.kr` +- **AdminFront**: `https://[이름]-admin.hmac.kr` +- **DevFront**: `https://[이름]-dev.hmac.kr` + +## 5. 주요 설정 파일 관리 +- **Nginx (Gateway & UserFront)**: 각 인스턴스의 백엔드 포트를 자동으로 감지하여 리버스 프록시를 수행합니다. +- **Ory Kratos**: `allowed_origins`, `allowed_return_urls`, `webhook` 주소 등이 인스턴스 포트에 맞게 자동 치환됩니다. +- **Ory Oathkeeper (`rules.json`, `rules.active.json`)**: 인증 규칙 내의 업스트림 주소가 현재 인스턴스 포트에 맞게 자동 치환됩니다. +- **Environment (`.env`)**: 모든 시크릿과 URL 설정이 중앙에서 관리됩니다. + +## 6. Vite 프론트엔드 주의사항 +Vite 개발 서버나 Preview 모드를 사용하는 경우, `vite.config.ts`의 `allowedHosts` 설정이 새로운 도메인을 차단할 수 있습니다. +여러 인스턴스를 동적 도메인으로 운영할 경우, `vite.config.ts`에서 `allowedHosts: true` (Vite 5.1+) 설정을 권장합니다. + + +--- +**주의**: 새로운 인스턴스 생성 후 외부에서 접속하려면, 앞단의 메인 리버스 프록시(Nginx 등)나 DNS 설정에서 해당 인스턴스의 포트(`USERFRONT_PORT`)로 라우팅해주는 작업이 필요합니다. diff --git a/deploy/templates/.env.template b/deploy/templates/.env.template new file mode 100644 index 00000000..130f5fc7 --- /dev/null +++ b/deploy/templates/.env.template @@ -0,0 +1,55 @@ +# === [1] 프로젝트 식별 (중요: 인스턴스마다 다르게 설정) === +INSTANCE_NAME={{INSTANCE_NAME}} +COMPOSE_PROJECT_NAME=baron-sso-{{INSTANCE_NAME}} +APP_ENV=production + +# === [2] 포트 Prefix 설정 (예: 23 입력 시 23000, 23432 등 생성) === +P={{PORT_PREFIX}} + +# 인프라 포트 +DB_PORT=${P}432 +REDIS_PORT=${P}399 +CLICKHOUSE_PORT_HTTP=${P}123 +CLICKHOUSE_PORT_NATIVE=${P}000 + +# 서비스 포트 +BACKEND_PORT=${P}000 +USERFRONT_PORT=${P}500 +ADMINFRONT_PORT=${P}173 +DEVFRONT_PORT=${P}174 +OATHKEEPER_PROXY_PORT=${P}467 + +# === [3] 도메인 설정 (별도 도메인 구조) === +# {{INSTANCE_NAME}}이 stg면 sso-stg.hmac.kr 형식이 되도록 가이드 +DOMAIN_SUFFIX=hmac.kr +USERFRONT_URL=https://{{INSTANCE_NAME}}-sso.${DOMAIN_SUFFIX} +ADMINFRONT_URL=https://{{INSTANCE_NAME}}-admin.${DOMAIN_SUFFIX} +DEVFRONT_URL=https://{{INSTANCE_NAME}}-dev.${DOMAIN_SUFFIX} + +# OIDC/Auth URL +VITE_OIDC_AUTHORITY=${USERFRONT_URL}/oidc +ADMINFRONT_CALLBACK_URLS=${ADMINFRONT_URL}/auth/callback +DEVFRONT_CALLBACK_URLS=${DEVFRONT_URL}/auth/callback + +# Ory URL +KRATOS_UI_URL=${USERFRONT_URL}/auth +KRATOS_BROWSER_URL=${USERFRONT_URL}/auth +HYDRA_PUBLIC_URL=${USERFRONT_URL}/oidc +OATHKEEPER_PUBLIC_URL=${USERFRONT_URL} + +# === [4] IDP 및 DB Config === +IDP_PROVIDER=ory +DB_PASSWORD=password +ORY_POSTGRES_USER=ory +ORY_POSTGRES_PASSWORD=generated_secret_here +CLICKHOUSE_PASSWORD=password +REDIS_ADDR=redis:6379 + +# Secrets (At least 32 chars) +COOKIE_SECRET=at_least_32_characters_long_secret_12345 +JWT_SECRET=at_least_32_characters_long_secret_12345 +CSRF_COOKIE_SECRET=at_least_32_characters_long_secret_12345 + +# Admin 초기 계정 +ADMIN_EMAIL=admin@baron.co.kr +ADMIN_PASSWORD=adminPasswordIsNotSimple diff --git a/deploy/templates/adminfront/vite.config.ts b/deploy/templates/adminfront/vite.config.ts new file mode 100644 index 00000000..21277ec0 --- /dev/null +++ b/deploy/templates/adminfront/vite.config.ts @@ -0,0 +1,29 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [react()], + envPrefix: ["VITE_", "USERFRONT_"], + server: { + host: "127.0.0.1", + // 인스턴스별 도메인을 자동으로 허용 + allowedHosts: ["{{ADMINFRONT_DOMAIN}}", "localhost", "127.0.0.1"], + proxy: { + "/api": { + target: process.env.API_PROXY_TARGET || "http://backend:{{BACKEND_PORT}}", + changeOrigin: true, + }, + }, + }, + preview: { + host: "127.0.0.1", + port: 5173, + allowedHosts: ["{{ADMINFRONT_DOMAIN}}", "localhost", "127.0.0.1"], + proxy: { + "/api": { + target: process.env.API_PROXY_TARGET || "http://backend:{{BACKEND_PORT}}", + changeOrigin: true, + }, + }, + }, +}); diff --git a/deploy/templates/auth.template.ts b/deploy/templates/auth.template.ts new file mode 100644 index 00000000..b2e1dc2b --- /dev/null +++ b/deploy/templates/auth.template.ts @@ -0,0 +1,21 @@ +import { UserManager, WebStorageStateStore } from "oidc-client-ts"; +import type { AuthProviderProps } from "react-oidc-context"; + +export const oidcConfig: AuthProviderProps = { + authority: + import.meta.env.VITE_OIDC_AUTHORITY || `${window.location.protocol}//${window.location.hostname}:{{USERFRONT_PORT}}/oidc`, + client_id: import.meta.env.VITE_OIDC_CLIENT_ID || "{{CLIENT_ID}}", + redirect_uri: `${window.location.origin}/auth/callback`, + response_type: "code", + scope: "openid offline_access profile email", + post_logout_redirect_uri: window.location.origin, + userStore: new WebStorageStateStore({ store: window.localStorage }), + automaticSilentRenew: false, +}; + +export const userManager = new UserManager({ + ...oidcConfig, + authority: oidcConfig.authority || "", + client_id: oidcConfig.client_id || "", + redirect_uri: oidcConfig.redirect_uri || "", +}); diff --git a/deploy/templates/devfront/vite.config.ts b/deploy/templates/devfront/vite.config.ts new file mode 100644 index 00000000..f21fd889 --- /dev/null +++ b/deploy/templates/devfront/vite.config.ts @@ -0,0 +1,29 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [react()], + envPrefix: ["VITE_", "USERFRONT_"], + server: { + host: "127.0.0.1", + // 인스턴스별 도메인을 자동으로 허용 + allowedHosts: ["{{DEVFRONT_DOMAIN}}", "localhost", "127.0.0.1"], + proxy: { + "/api": { + target: process.env.API_PROXY_TARGET || "http://backend:{{BACKEND_PORT}}", + changeOrigin: true, + }, + }, + }, + preview: { + host: "127.0.0.1", + port: 5173, + allowedHosts: ["{{DEVFRONT_DOMAIN}}", "localhost", "127.0.0.1"], + proxy: { + "/api": { + target: process.env.API_PROXY_TARGET || "http://backend:{{BACKEND_PORT}}", + changeOrigin: true, + }, + }, + }, +}); diff --git a/deploy/templates/docker-compose.yaml b/deploy/templates/docker-compose.yaml new file mode 100644 index 00000000..c6d8ed3d --- /dev/null +++ b/deploy/templates/docker-compose.yaml @@ -0,0 +1,135 @@ +name: ${COMPOSE_PROJECT_NAME} + +services: + # --- Infrastructure --- + postgres: + image: postgres:17-alpine + container_name: ${COMPOSE_PROJECT_NAME}_db + environment: + - POSTGRES_PASSWORD=${DB_PASSWORD} + ports: + - "${DB_PORT}:5432" + volumes: + - db_data_${INSTANCE_NAME}:/var/lib/postgresql/data + networks: [app_net] + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + + redis: + image: redis:7-alpine + container_name: ${COMPOSE_PROJECT_NAME}_redis + ports: + - "${REDIS_PORT}:6379" + networks: [app_net] + + clickhouse: + image: clickhouse/clickhouse-server:latest + container_name: ${COMPOSE_PROJECT_NAME}_clickhouse + environment: + - CLICKHOUSE_USER=baron + - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD} + ports: + - "${CLICKHOUSE_PORT_HTTP}:8123" + - "${CLICKHOUSE_PORT_NATIVE}:9000" + volumes: + - clickhouse_data_${INSTANCE_NAME}:/var/lib/clickhouse + networks: [app_net] + + # --- Ory Stack --- + postgres_ory: + image: postgres:17-alpine + container_name: ${COMPOSE_PROJECT_NAME}_ory_db + environment: + - POSTGRES_USER=${ORY_POSTGRES_USER} + - POSTGRES_PASSWORD=${ORY_POSTGRES_PASSWORD} + volumes: + - ory_db_data_${INSTANCE_NAME}:/var/lib/postgresql/data + networks: [app_net] + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${ORY_POSTGRES_USER}"] + interval: 5s + + kratos: + image: oryd/kratos:v25.4.0 + container_name: ${COMPOSE_PROJECT_NAME}_kratos + env_file: .env + volumes: + - ./ory/kratos:/etc/config/kratos:ro + command: serve -c /etc/config/kratos/kratos.yml --dev + networks: [app_net] + depends_on: + postgres_ory: { condition: service_healthy } + + oathkeeper: + image: oryd/oathkeeper:v25.4.0 + container_name: ${COMPOSE_PROJECT_NAME}_oathkeeper + env_file: .env + ports: + - "${OATHKEEPER_PROXY_PORT}:4455" + volumes: + - ./ory/oathkeeper:/etc/config/oathkeeper:ro + networks: [app_net] + + # --- Application Services --- + backend: + image: baron-backend:latest + container_name: ${COMPOSE_PROJECT_NAME}_backend + env_file: .env + environment: + - PORT=${BACKEND_PORT} + - DB_HOST=postgres + - REDIS_ADDR=redis:6379 + - CLICKHOUSE_HOST=clickhouse + ports: + - "${BACKEND_PORT}:${BACKEND_PORT}" + networks: [app_net] + depends_on: + postgres: { condition: service_healthy } + redis: { condition: service_started } + + gateway: + image: nginx:alpine + container_name: ${COMPOSE_PROJECT_NAME}_gateway + ports: + - "${USERFRONT_PORT}:80" + volumes: + - ./gateway/nginx.conf:/etc/nginx/nginx.conf:ro + networks: [app_net] + + adminfront: + image: node:20-alpine + container_name: ${COMPOSE_PROJECT_NAME}_adminfront + working_dir: /app + env_file: .env + ports: + - "${ADMINFRONT_PORT}:5173" + volumes: + - ../../adminfront:/app + - ./adminfront/vite.config.ts:/app/vite.config.ts:ro + - ./adminfront/auth.ts:/app/src/lib/auth.ts:ro + command: npm run dev -- --host 0.0.0.0 + networks: [app_net] + + devfront: + image: node:20-alpine + container_name: ${COMPOSE_PROJECT_NAME}_devfront + working_dir: /app + env_file: .env + ports: + - "${DEVFRONT_PORT}:5173" + volumes: + - ../../devfront:/app + - ./devfront/vite.config.ts:/app/vite.config.ts:ro + - ./devfront/auth.ts:/app/src/lib/auth.ts:ro + command: npm run dev -- --host 0.0.0.0 + networks: [app_net] + +networks: + app_net: + name: ${COMPOSE_PROJECT_NAME}_net + +volumes: + db_data_${INSTANCE_NAME}: + ory_db_data_${INSTANCE_NAME}: + clickhouse_data_${INSTANCE_NAME}: diff --git a/deploy/templates/gateway/nginx.conf b/deploy/templates/gateway/nginx.conf new file mode 100644 index 00000000..5590645e --- /dev/null +++ b/deploy/templates/gateway/nginx.conf @@ -0,0 +1,43 @@ +worker_processes auto; +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + + # 인스턴스별로 계산된 포트 주입 + upstream backend_srv { + server backend:{{BACKEND_PORT}}; + } + + upstream oathkeeper_srv { + server oathkeeper:4455; + } + + server { + listen 80; + + # SSO 메인 도메인 및 API 처리 + location /api { + proxy_pass http://backend_srv; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /auth { + proxy_pass http://oathkeeper_srv; + proxy_set_header Host $host; + } + + location /oidc { + rewrite ^/oidc/(.*)$ /$1 break; + proxy_pass http://oathkeeper_srv; + proxy_set_header Host $host; + } + + # 기본 정적 파일 (UserFront) + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + } +} diff --git a/deploy/templates/ory/kratos/kratos.yml b/deploy/templates/ory/kratos/kratos.yml new file mode 100644 index 00000000..b70ca3f2 --- /dev/null +++ b/deploy/templates/ory/kratos/kratos.yml @@ -0,0 +1,114 @@ +version: v25.4.0 + +dsn: ${DSN} + +serve: + public: + base_url: http://localhost:4433/ + cors: + enabled: true + allowed_origins: + - http://backend:{{BACKEND_PORT}} + admin: + base_url: http://localhost:4434/ + +session: + cookie: + domain: hmac.kr + same_site: Lax + path: / + +selfservice: + default_browser_return_url: http://localhost:{{USERFRONT_PORT}}/ + allowed_return_urls: + - http://backend:{{BACKEND_PORT}} + - http://backend:{{BACKEND_PORT}}/ + - http://localhost:{{USERFRONT_PORT}} + - https://app.brsw.kr + - https://app.brsw.kr/ + - https://sss.hmac.kr + - https://sss.hmac.kr/ + - https://sso.hmac.kr + - https://sso.hmac.kr/ + - https://ssologin.hmac.kr + - https://ssologin.hmac.kr/ + - https://sso-test.hmac.kr + - https://sso-test.hmac.kr/ + - https://ssob.hmac.kr + - https://ssob.hmac.kr/ + - https://ssob.hmac.kr/ko + - https://ssob.hmac.kr/ko/ + - https://ssob.hmac.kr/en + - https://ssob.hmac.kr/en/ + - https://ssob.hmac.kr/auth/callback + - https://ssob.hmac.kr/ko/auth/callback + - https://ssob.hmac.kr/en/auth/callback + + methods: + password: + enabled: true + link: + enabled: true + code: + enabled: true + passwordless_enabled: true + + flows: + error: + ui_url: http://localhost:{{USERFRONT_PORT}}/error + settings: + ui_url: http://localhost:{{USERFRONT_PORT}}/error?error=settings_disabled + privileged_session_max_age: 15m + recovery: + ui_url: http://localhost:{{USERFRONT_PORT}}/recovery + use: code + verification: + ui_url: http://localhost:{{USERFRONT_PORT}}/verification + use: code + logout: + after: + default_browser_return_url: http://localhost:{{USERFRONT_PORT}}/login + login: + ui_url: http://localhost:{{USERFRONT_PORT}}/login + lifespan: 10m + registration: + ui_url: http://localhost:{{USERFRONT_PORT}}/registration + lifespan: 10m + +log: + level: debug + format: text + leak_sensitive_values: true + +secrets: + cookie: + - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE + cipher: + - 32-LONG-SECRET-NOT-SECURE-AT-ALL + +ciphers: + algorithm: xchacha20-poly1305 + +hashers: + algorithm: bcrypt + bcrypt: + cost: 8 + +identity: + default_schema_id: default + schemas: + - id: default + url: file:///etc/config/kratos/identity.schema.json + +courier: + template_override_path: /etc/config/kratos/courier-templates + delivery_strategy: http + http: + request_config: + url: http://backend:{{BACKEND_PORT}}/api/v1/auth/webhooks/kratos-courier + method: POST + body: file:///etc/config/kratos/courier-http.jsonnet + headers: + Content-Type: application/json + smtp: + connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true diff --git a/deploy/templates/ory/oathkeeper/rules.json b/deploy/templates/ory/oathkeeper/rules.json new file mode 100644 index 00000000..00fe02e3 --- /dev/null +++ b/deploy/templates/ory/oathkeeper/rules.json @@ -0,0 +1,15 @@ +[ + { + "id": "backend-api-rule", + "match": { + "url": "<.*>://<.*>/api/v1/<.*>", + "methods": ["GET", "POST", "PUT", "DELETE", "PATCH"] + }, + "upstream": { + "url": "http://backend:{{BACKEND_PORT}}" + }, + "authenticators": [{ "handler": "cookie_session" }], + "authorizer": { "handler": "remote_json" }, + "mutators": [{ "handler": "noop" }] + } +] diff --git a/deploy/templates/userfront/nginx.conf b/deploy/templates/userfront/nginx.conf new file mode 100644 index 00000000..73928f31 --- /dev/null +++ b/deploy/templates/userfront/nginx.conf @@ -0,0 +1,71 @@ +# ISO8601 시간을 "YYYY-MM-DD HH:mm:ss" 형식으로 변환 +map $time_iso8601 $time_custom { + "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})" "$1-$2-$3 $4:$5:$6"; +} + +# Go slog 포맷과 맞춘 JSON 액세스 로그 +log_format json_combined escape=json + '{' + '"time":"$time_custom",' + '"level":"INFO",' + '"msg":"http_access",' + '"svc":"baron-userfront",' + '"status":$status,' + '"method":"$request_method",' + '"path":"$request_uri",' + '"latency":"${request_time}s",' + '"ip":"$remote_addr",' + '"forwarded_for":"$http_x_forwarded_for",' + '"user_agent":"$http_user_agent"' + '}'; + +server { + listen 5000; + include /etc/nginx/mime.types; + types { + application/javascript mjs; + application/wasm wasm; + } + + error_log /dev/stderr warn; + access_log /var/log/nginx/access.log json_combined; + + # --- Backend API Proxy --- + location /api { + proxy_pass http://backend:{{BACKEND_PORT}}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # --- UserFront Static Files --- + + location ~* \.(js|css|html|json|mjs|wasm)$ { + root /usr/share/nginx/html; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + try_files $uri =404; + } + + location ~* \.mjs$ { + root /usr/share/nginx/html; + default_type application/javascript; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + try_files $uri =404; + } + + # dart2wasm 바이너리 MIME 명시 + location ~* \.wasm$ { + root /usr/share/nginx/html; + default_type application/wasm; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + try_files $uri =404; + } + + location / { + root /usr/share/nginx/html; + index index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + try_files $uri $uri/ /index.html; + } +}