From 792b9b9c72243647ac5903ccffbc2f904a4554fe Mon Sep 17 00:00:00 2001 From: 0310700 Date: Mon, 22 Jun 2026 17:53:07 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8F=AC=ED=84=B8=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=A7=81=ED=81=AC=EC=97=90=20SSO=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=A0=81=EC=9A=A9=20(Co?= =?UTF-8?q?der/Gitea=20OIDC=20=EC=8B=9C=EC=9E=91=20URL=20+=20=EB=B1=83?= =?UTF-8?q?=EC=A7=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- .project-env.example | 6 +++++- Dockerfile | 17 +++++++++++++++++ deploy/portal-route.yaml | 26 ++++++++++++++++++++++++++ public/style.css | 1 + src/sites.js | 11 +++++++++-- views/dashboard.ejs | 2 +- 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 Dockerfile create mode 100644 deploy/portal-route.yaml diff --git a/.project-env.example b/.project-env.example index 5b64915..73275c1 100644 --- a/.project-env.example +++ b/.project-env.example @@ -10,7 +10,11 @@ SESSION_SECRET=change-me-to-a-long-random-string # --- DB (OpenEverest appdb, portal schema 전용 role) --- # host 는 클러스터 내부 OpenEverest pgbouncer (워크스페이스/Kubero 양쪽 도달). -DATABASE_URL=postgresql://portal_app:7e2950ce6ed0c5a1457ea5b5@postgresql-6ox-pgbouncer.everest.svc.cluster.local:5432/appdb?options=-c%20search_path%3Dportal +# 주의: pgbouncer 가 SSL 필수 → sslmode=require. node pg 최신은 require 를 verify-full 로 취급하므로 +# 자체서명 인증서 통과를 위해 uselibpqcompat=true 동반 필요. +# search_path 는 portal_app role 기본값(ALTER ROLE ... SET search_path=portal)으로 고정돼 있어 +# URL 의 options 파라미터는 불필요(pgbouncer 가 startup options 거부함). +DATABASE_URL=postgresql://portal_app:7e2950ce6ed0c5a1457ea5b5@postgresql-6ox-pgbouncer.everest.svc:5432/appdb?uselibpqcompat=true&sslmode=require # --- Keycloak OIDC (realm bokdev, client portal) --- OIDC_ISSUER=https://keycloak.bokdev.in/realms/bokdev diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..02ed437 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# AI DEV 포털 이미지. Kubero buildpack(node22 + npm install + node index.js)과 동일 구성. +# Kubero v3.1.1 buildpack 빌드잡이 harbor 단일세그먼트 경로로 push 실패하는 이슈 우회용: +# 이 이미지를 harbor.bokdev.in/kubero/ai-dev-portal-portal:main 으로 직접 push 하면 +# Kubero 가 만든 web 파드가 그대로 pull 한다. +FROM node:22-slim + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm install --omit=dev + +COPY . . + +ENV PORT=8080 +EXPOSE 8080 + +CMD ["node", "index.js"] diff --git a/deploy/portal-route.yaml b/deploy/portal-route.yaml new file mode 100644 index 0000000..c32782f --- /dev/null +++ b/deploy/portal-route.yaml @@ -0,0 +1,26 @@ +# portal.bokdev.in → Kubero 가 배포한 portal Service. +# Kubero 파이프라인 ai-dev, production phase → ns ai-dev-production. +# Service 이름=portal-kuberoapp, port=80. +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: portal-route + namespace: ai-dev-production +spec: + hostnames: + - portal.bokdev.in + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: bokdev-gateway + namespace: istio-ingress + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - group: "" + kind: Service + name: portal-kuberoapp + port: 80 diff --git a/public/style.css b/public/style.css index 1628b4b..a0de808 100644 --- a/public/style.css +++ b/public/style.css @@ -82,6 +82,7 @@ a:hover { text-decoration: underline; } .site-card .ico { font-size: 22px; line-height: 1; } .site-card .name { font-weight: 600; color: var(--fg); } .site-card .desc { color: var(--fg-muted); font-size: 12px; } +.sso-badge { display: inline-block; font-size: 10px; font-weight: 600; color: #1a7f37; background: #dafbe1; border: 1px solid #aceebb; border-radius: 999px; padding: 1px 7px; vertical-align: middle; margin-left: 4px; } .group-title { font-size: 13px; font-weight: 600; color: var(--fg-muted); margin: 20px 0 8px; text-transform: uppercase; letter-spacing: .04em; } /* 버튼 */ diff --git a/src/sites.js b/src/sites.js index 7581467..44f74b9 100644 --- a/src/sites.js +++ b/src/sites.js @@ -1,11 +1,18 @@ // 포털에 모아둘 사이트 카탈로그. 카테고리: 개발 핵심 / AI·데이터. // icon 은 이모지(추가 의존성 없이 GitHub 감성 카드에 표시). +// +// url: 각 앱의 'OIDC 로그인 바로 시작' 엔드포인트를 쓰면, Keycloak SSO 세션이 있을 때 +// 로그인 폼 없이 즉시 통과(자동 로그인)된다. 포털도 같은 Keycloak realm 으로 로그인하므로 +// 포털 로그인 후 이 링크를 누르면 바로 들어간다. +// - Coder: /api/v2/users/oidc/callback (code 없으면 keycloak authorize 로 307) +// - Gitea: /user/oauth2/keycloak (auth source 명=keycloak, 307 로 keycloak authorize) +// SSO 미지원(Kubero=oauth2 미완성, Harbor 등)은 기본 URL 유지. export const siteGroups = [ { group: "개발 핵심", sites: [ - { name: "Coder", url: "https://coder.bokdev.in", icon: "💻", desc: "웹 개발 워크스페이스 (VS Code + AI CLI)" }, - { name: "Gitea", url: "https://gitea.bokdev.in", icon: "🍵", desc: "사내 Git 저장소" }, + { name: "Coder", url: "https://coder.bokdev.in/api/v2/users/oidc/callback", icon: "💻", desc: "웹 개발 워크스페이스 (VS Code + AI CLI)", sso: true }, + { name: "Gitea", url: "https://gitea.bokdev.in/user/oauth2/keycloak", icon: "🍵", desc: "사내 Git 저장소", sso: true }, { name: "Kubero", url: "https://kubero.bokdev.in", icon: "🚀", desc: "앱 배포 PaaS (*.apps.bokdev.in)" }, { name: "Harbor", url: "https://harbor.bokdev.in", icon: "📦", desc: "컨테이너 이미지 레지스트리" }, ], diff --git a/views/dashboard.ejs b/views/dashboard.ejs index b755431..f9e67d1 100644 --- a/views/dashboard.ejs +++ b/views/dashboard.ejs @@ -13,7 +13,7 @@ <%= s.icon %> - <%= s.name %>
+ <%= s.name %><% if (s.sso) { %> SSO 자동로그인<% } %>
<%= s.desc %>