forked from baron/baron-sso
oathkeeper 동작 확인
This commit is contained in:
@@ -96,6 +96,15 @@ HYDRA_ADMIN_URL=http://hydra:4445
|
|||||||
HYDRA_PUBLIC_URL=http://hydra:4444
|
HYDRA_PUBLIC_URL=http://hydra:4444
|
||||||
JWKS_URL=http://oathkeeper:4456/.well-known/jwks.json
|
JWKS_URL=http://oathkeeper:4456/.well-known/jwks.json
|
||||||
|
|
||||||
|
# Oathkeeper 실행 사용자/프로브 설정
|
||||||
|
OATHKEEPER_VERSION=v25.4.0
|
||||||
|
OATHKEEPER_UID=1001
|
||||||
|
OATHKEEPER_GID=1001
|
||||||
|
OATHKEEPER_HEALTH_URL=http://oathkeeper:4456/health/ready
|
||||||
|
OATHKEEPER_HEALTH_INTERVAL_SECONDS=10
|
||||||
|
OATHKEEPER_HEALTH_TIMEOUT_SECONDS=2
|
||||||
|
OATHKEEPER_HEALTH_ENABLED=true
|
||||||
|
|
||||||
# Kratos Selfservice UI required secrets (local only)
|
# Kratos Selfservice UI required secrets (local only)
|
||||||
COOKIE_SECRET=localcookie123
|
COOKIE_SECRET=localcookie123
|
||||||
CSRF_COOKIE_NAME=__HOST-baronSSO_csrf
|
CSRF_COOKIE_NAME=__HOST-baronSSO_csrf
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -207,36 +207,36 @@ docker compose -f compose.ory.yaml --profile mcp up -d hydra-mcp-server kratos-m
|
|||||||
```
|
```
|
||||||
|
|
||||||
- MCP 서버는 stdio 기반이라 외부 포트를 열지 않습니다.
|
- MCP 서버는 stdio 기반이라 외부 포트를 열지 않습니다.
|
||||||
- MCP 클라이언트에서 `npx`로 실행하는 설정 예시입니다.
|
- 최초 실행시거나 빌드된 이미지가 없으면 `docker compose -f mcp/compose.mcp.ory.yaml build' 후에 사용 가능합니다
|
||||||
- `hydra-mcp`는 첫 실행 시 캐시 디렉터리에 의존성을 자동 설치합니다(수동 `npm install` 불필요).
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[mcp_servers.kratos-mcp]
|
[mcp_servers.kratos-mcp]
|
||||||
command = "npx"
|
command = "docker"
|
||||||
args = ["-y", "mcp-ory-kratos"]
|
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "kratos-mcp-server"]
|
||||||
|
|
||||||
[mcp_servers.kratos-mcp.env]
|
[mcp_servers.kratos-mcp.env]
|
||||||
KRATOS_ADMIN_URL = "http://localhost:4434"
|
KRATOS_ADMIN_URL = "http://kratos:4434"
|
||||||
|
|
||||||
[mcp_servers.hydra-mcp]
|
[mcp_servers.hydra-mcp]
|
||||||
command = "npx"
|
command = "docker"
|
||||||
args = ["-y", "/home/lectom/repos/baron-sso/mcp/hydra-mcp"]
|
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "hydra-mcp-server"]
|
||||||
|
|
||||||
[mcp_servers.hydra-mcp.env]
|
[mcp_servers.hydra-mcp.env]
|
||||||
HYDRA_PUBLIC_URL = "http://localhost:4441"
|
HYDRA_PUBLIC_URL = "http://hydra:4444"
|
||||||
HYDRA_ADMIN_URL = "http://localhost:4445"
|
HYDRA_ADMIN_URL = "http://hydra:4445"
|
||||||
|
|
||||||
[mcp_servers.keto-mcp]
|
[mcp_servers.keto-mcp]
|
||||||
command = "npx"
|
command = "docker"
|
||||||
args = ["-y", "/home/lectom/repos/baron-sso/mcp/keto-mcp"]
|
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "keto-mcp-server"]
|
||||||
|
|
||||||
[mcp_servers.keto-mcp.env]
|
[mcp_servers.keto-mcp.env]
|
||||||
KETO_READ_URL = "http://localhost:4466"
|
KETO_READ_URL = "http://keto:4466"
|
||||||
KETO_WRITE_URL = "http://localhost:4467"
|
KETO_WRITE_URL = "http://keto:4467"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 로컬 개발 (Manual)
|
### 로컬 개발 (Manual)
|
||||||
Docker 없이 코드를 수정하며 개발하려면:
|
Docker 없이는 개발할 수 없지만 Backend 및 [user/admin/dev]Front 코드는 개발모드로 수정하며 개발가능.
|
||||||
|
백그라운드로 infra 및 ory stack이 구동중이라는 가정
|
||||||
|
|
||||||
**Backend:**
|
**Backend:**
|
||||||
```bash
|
```bash
|
||||||
@@ -292,6 +292,6 @@ baron_sso/
|
|||||||
## 📝 상태 및 로드맵 (Status & Roadmap)
|
## 📝 상태 및 로드맵 (Status & Roadmap)
|
||||||
- [x] **Phase 1**: 초기 설정 및 아키텍처 설계 (완료)
|
- [x] **Phase 1**: 초기 설정 및 아키텍처 설계 (완료)
|
||||||
- [x] **Phase 2**: Backend Audit API 구현 (일부 완료)
|
- [x] **Phase 2**: Backend Audit API 구현 (일부 완료)
|
||||||
- [x] **Phase 3**: userfront 로그인 UI 인증 로직 (완료)
|
- [ ] **Phase 3**: userfront 로그인 UI 인증 로직 (예정)
|
||||||
- [ ] **Phase 4**: adminfront 기능 추가 (예정)
|
- [ ] **Phase 4**: adminfront 기능 추가 (예정)
|
||||||
- [ ] **Phase 5**: 대시보드 및 통합 런처 구현 (예정)
|
- [ ] **Phase 5**: 대시보드 및 통합 런처 구현 (예정)
|
||||||
|
|||||||
132
backend/cmd/server/health_monitor.go
Normal file
132
backend/cmd/server/health_monitor.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPProbe struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
interval time.Duration
|
||||||
|
timeout time.Duration
|
||||||
|
client *http.Client
|
||||||
|
mu sync.RWMutex
|
||||||
|
status string
|
||||||
|
lastError string
|
||||||
|
lastChecked time.Time
|
||||||
|
lastSuccess time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProbeSnapshot struct {
|
||||||
|
Status string
|
||||||
|
Error string
|
||||||
|
LastChecked time.Time
|
||||||
|
LastSuccess time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPProbe(name, url string, interval, timeout time.Duration) *HTTPProbe {
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = 10 * time.Second
|
||||||
|
}
|
||||||
|
if timeout <= 0 {
|
||||||
|
timeout = 2 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTTPProbe{
|
||||||
|
name: name,
|
||||||
|
url: url,
|
||||||
|
interval: interval,
|
||||||
|
timeout: timeout,
|
||||||
|
client: &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start는 프로브를 백그라운드에서 주기적으로 실행합니다.
|
||||||
|
func (p *HTTPProbe) Start() {
|
||||||
|
go func() {
|
||||||
|
p.checkOnce()
|
||||||
|
ticker := time.NewTicker(p.interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
p.checkOnce()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPProbe) Snapshot() ProbeSnapshot {
|
||||||
|
p.mu.RLock()
|
||||||
|
defer p.mu.RUnlock()
|
||||||
|
return ProbeSnapshot{
|
||||||
|
Status: p.status,
|
||||||
|
Error: p.lastError,
|
||||||
|
LastChecked: p.lastChecked,
|
||||||
|
LastSuccess: p.lastSuccess,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPProbe) StatusText() string {
|
||||||
|
s := p.Snapshot()
|
||||||
|
if s.Status == "ok" {
|
||||||
|
return "ok"
|
||||||
|
}
|
||||||
|
if s.Status == "" {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
if s.Error == "" {
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
return "error: " + s.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPProbe) checkOnce() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, p.url, nil)
|
||||||
|
if err != nil {
|
||||||
|
p.update("error", fmt.Sprintf("request build failed: %v", err), false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := p.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
p.update("error", err.Error(), false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
p.update("error", fmt.Sprintf("status=%d", resp.StatusCode), false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.update("ok", "", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPProbe) update(status, errMsg string, success bool) {
|
||||||
|
p.mu.Lock()
|
||||||
|
prevStatus := p.status
|
||||||
|
p.status = status
|
||||||
|
p.lastError = errMsg
|
||||||
|
p.lastChecked = time.Now()
|
||||||
|
if success {
|
||||||
|
p.lastSuccess = p.lastChecked
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
if prevStatus == status {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status == "ok" {
|
||||||
|
slog.Info("Service probe recovered", "name", p.name, "url", p.url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.Error("Service probe failed", "name", p.name, "url", p.url, "error", errMsg)
|
||||||
|
}
|
||||||
@@ -158,6 +158,28 @@ func main() {
|
|||||||
slog.Warn("Failed to connect to Redis. Auth features may fail.", "error", err)
|
slog.Warn("Failed to connect to Redis. Auth features may fail.", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Oathkeeper 상태를 주기적으로 확인해 다운을 감지합니다.
|
||||||
|
var oathkeeperProbe *HTTPProbe
|
||||||
|
if strings.ToLower(getEnv("OATHKEEPER_HEALTH_ENABLED", "true")) != "false" {
|
||||||
|
intervalSec, err := strconv.Atoi(getEnv("OATHKEEPER_HEALTH_INTERVAL_SECONDS", "10"))
|
||||||
|
if err != nil || intervalSec <= 0 {
|
||||||
|
intervalSec = 10
|
||||||
|
}
|
||||||
|
timeoutSec, err := strconv.Atoi(getEnv("OATHKEEPER_HEALTH_TIMEOUT_SECONDS", "2"))
|
||||||
|
if err != nil || timeoutSec <= 0 {
|
||||||
|
timeoutSec = 2
|
||||||
|
}
|
||||||
|
oathkeeperProbe = NewHTTPProbe(
|
||||||
|
"oathkeeper",
|
||||||
|
getEnv("OATHKEEPER_HEALTH_URL", "http://oathkeeper:4456/health/ready"),
|
||||||
|
time.Duration(intervalSec)*time.Second,
|
||||||
|
time.Duration(timeoutSec)*time.Second,
|
||||||
|
)
|
||||||
|
oathkeeperProbe.Start()
|
||||||
|
} else {
|
||||||
|
slog.Info("Oathkeeper probe disabled")
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Initialize Handlers
|
// 2. Initialize Handlers
|
||||||
auditHandler := handler.NewAuditHandler(auditRepo)
|
auditHandler := handler.NewAuditHandler(auditRepo)
|
||||||
authHandler := handler.NewAuthHandler(redisService, idpProvider)
|
authHandler := handler.NewAuthHandler(redisService, idpProvider)
|
||||||
@@ -321,6 +343,32 @@ func main() {
|
|||||||
status = "degraded"
|
status = "degraded"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check Oathkeeper
|
||||||
|
if oathkeeperProbe != nil {
|
||||||
|
snapshot := oathkeeperProbe.Snapshot()
|
||||||
|
switch snapshot.Status {
|
||||||
|
case "ok":
|
||||||
|
checks["oathkeeper"] = "ok"
|
||||||
|
case "":
|
||||||
|
checks["oathkeeper"] = "unknown"
|
||||||
|
if status != "error" {
|
||||||
|
status = "degraded"
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if snapshot.Error == "" {
|
||||||
|
checks["oathkeeper"] = "error"
|
||||||
|
} else {
|
||||||
|
checks["oathkeeper"] = "error: " + snapshot.Error
|
||||||
|
}
|
||||||
|
status = "error"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checks["oathkeeper"] = "disabled"
|
||||||
|
if status != "error" {
|
||||||
|
status = "degraded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if status == "error" {
|
if status == "error" {
|
||||||
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
|
||||||
"status": status,
|
"status": status,
|
||||||
|
|||||||
@@ -71,22 +71,6 @@ services:
|
|||||||
- ory-net
|
- ory-net
|
||||||
- kratosnet
|
- kratosnet
|
||||||
|
|
||||||
kratos-mcp-server:
|
|
||||||
build:
|
|
||||||
context: ./mcp/kratos-mcp
|
|
||||||
container_name: mcp_ory_kratos
|
|
||||||
profiles:
|
|
||||||
- mcp
|
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
init: true
|
|
||||||
environment:
|
|
||||||
- KRATOS_ADMIN_URL=http://kratos:4434
|
|
||||||
depends_on:
|
|
||||||
- kratos
|
|
||||||
networks:
|
|
||||||
- ory-net
|
|
||||||
|
|
||||||
kratos-ui:
|
kratos-ui:
|
||||||
image: oryd/kratos-selfservice-ui-node:${KRATOS_UI_NODE_VERSION:-v25.4.0}
|
image: oryd/kratos-selfservice-ui-node:${KRATOS_UI_NODE_VERSION:-v25.4.0}
|
||||||
container_name: ory_kratos_ui
|
container_name: ory_kratos_ui
|
||||||
@@ -133,39 +117,7 @@ services:
|
|||||||
- ory-net
|
- ory-net
|
||||||
- hydranet
|
- hydranet
|
||||||
|
|
||||||
hydra-mcp-server:
|
|
||||||
build:
|
|
||||||
context: ./mcp/hydra-mcp
|
|
||||||
container_name: mcp_ory_hydra
|
|
||||||
profiles:
|
|
||||||
- mcp
|
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
init: true
|
|
||||||
environment:
|
|
||||||
- HYDRA_PUBLIC_URL=http://hydra:4444
|
|
||||||
- HYDRA_ADMIN_URL=http://hydra:4445
|
|
||||||
depends_on:
|
|
||||||
- hydra
|
|
||||||
networks:
|
|
||||||
- ory-net
|
|
||||||
|
|
||||||
keto-mcp-server:
|
|
||||||
build:
|
|
||||||
context: ./mcp/keto-mcp
|
|
||||||
container_name: mcp_ory_keto
|
|
||||||
profiles:
|
|
||||||
- mcp
|
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
init: true
|
|
||||||
environment:
|
|
||||||
- KETO_READ_URL=http://keto:4466
|
|
||||||
- KETO_WRITE_URL=http://keto:4467
|
|
||||||
depends_on:
|
|
||||||
- keto
|
|
||||||
networks:
|
|
||||||
- ory-net
|
|
||||||
|
|
||||||
# --- Keto ---
|
# --- Keto ---
|
||||||
keto-migrate:
|
keto-migrate:
|
||||||
@@ -197,17 +149,18 @@ services:
|
|||||||
|
|
||||||
# --- Oathkeeper ---
|
# --- Oathkeeper ---
|
||||||
oathkeeper:
|
oathkeeper:
|
||||||
image: oryd/oathkeeper:v0.40.6
|
image: oryd/oathkeeper:${OATHKEEPER_VERSION:-v25.4.0}
|
||||||
container_name: ory_oathkeeper
|
container_name: ory_oathkeeper
|
||||||
|
user: "${OATHKEEPER_UID:-1001}:${OATHKEEPER_GID:-1001}"
|
||||||
ports:
|
ports:
|
||||||
- "4457:4455" # Proxy
|
- "4457:4455" # Proxy
|
||||||
environment:
|
environment:
|
||||||
- LOG_LEVEL=debug
|
- LOG_LEVEL=${OATHKEEPER_LOG_LEVEL:info}
|
||||||
- APP_ENV=${APP_ENV:-development}
|
- APP_ENV=${APP_ENV:-development}
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/ory/oathkeeper:/etc/config/oathkeeper
|
- ./docker/ory/oathkeeper:/etc/config/oathkeeper
|
||||||
- ./docker/ory/oathkeeper/logs:/var/log/oathkeeper
|
- ./docker/ory/oathkeeper/logs:/var/log/oathkeeper
|
||||||
command: ["/etc/config/oathkeeper/entrypoint.sh"]
|
entrypoint: ["/etc/config/oathkeeper/entrypoint.sh"]
|
||||||
networks:
|
networks:
|
||||||
- ory-net
|
- ory-net
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,20 @@ export RULES_FILE
|
|||||||
|
|
||||||
echo "[oathkeeper] APP_ENV=$APP_ENV_VALUE rules=$RULES_FILE"
|
echo "[oathkeeper] APP_ENV=$APP_ENV_VALUE rules=$RULES_FILE"
|
||||||
|
|
||||||
exec /bin/sh -c "oathkeeper serve proxy -c /etc/config/oathkeeper/oathkeeper.yml 2>&1 | tee /var/log/oathkeeper/access.log"
|
RULES_ACTIVE="/etc/config/oathkeeper/rules.active.json"
|
||||||
|
if [ ! -f "$RULES_FILE" ]; then
|
||||||
|
echo "[oathkeeper] rules file not found: $RULES_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp "$RULES_FILE" "$RULES_ACTIVE"
|
||||||
|
|
||||||
|
LOG_DIR="/var/log/oathkeeper"
|
||||||
|
LOG_FILE="${LOG_DIR}/access.log"
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
if ! touch "$LOG_FILE" 2>/dev/null; then
|
||||||
|
echo "[oathkeeper] log file not writable: $LOG_FILE"
|
||||||
|
ls -ld "$LOG_DIR" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec /bin/sh -c "oathkeeper serve proxy -c /etc/config/oathkeeper/oathkeeper.yml 2>&1 | tee \"$LOG_FILE\""
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ errors:
|
|||||||
|
|
||||||
access_rules:
|
access_rules:
|
||||||
repositories:
|
repositories:
|
||||||
- file://${RULES_FILE:-/etc/config/oathkeeper/rules.json}
|
- file:///etc/config/oathkeeper/rules.active.json
|
||||||
|
|
||||||
authenticators:
|
authenticators:
|
||||||
noop:
|
noop:
|
||||||
@@ -34,6 +34,13 @@ authorizers:
|
|||||||
enabled: true
|
enabled: true
|
||||||
config:
|
config:
|
||||||
remote: http://keto:4466/check
|
remote: http://keto:4466/check
|
||||||
|
payload: |
|
||||||
|
{
|
||||||
|
"namespace": "permissions",
|
||||||
|
"object": "{{ print .Request.URL.Path }}",
|
||||||
|
"relation": "access",
|
||||||
|
"subject_id": "{{ print .Subject }}"
|
||||||
|
}
|
||||||
|
|
||||||
mutators:
|
mutators:
|
||||||
noop:
|
noop:
|
||||||
|
|||||||
92
docker/ory/oathkeeper/rules.active.json
Normal file
92
docker/ory/oathkeeper/rules.active.json
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "public-health",
|
||||||
|
"description": "공개 헬스체크",
|
||||||
|
"match": {
|
||||||
|
"url": "http://<.*>/health",
|
||||||
|
"methods": ["GET"]
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"url": "http://baron_backend:3000"
|
||||||
|
},
|
||||||
|
"authenticators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
],
|
||||||
|
"authorizer": { "handler": "allow" },
|
||||||
|
"mutators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "public-preflight",
|
||||||
|
"description": "CORS preflight",
|
||||||
|
"match": {
|
||||||
|
"url": "http://<.*>/api/v1/<.*>",
|
||||||
|
"methods": ["OPTIONS"]
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"url": "http://baron_backend:3000"
|
||||||
|
},
|
||||||
|
"authenticators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
],
|
||||||
|
"authorizer": { "handler": "allow" },
|
||||||
|
"mutators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "public-auth",
|
||||||
|
"description": "인증/회원가입 등 공개 엔드포인트",
|
||||||
|
"match": {
|
||||||
|
"url": "http://<.*>/api/v1/auth/<.*>",
|
||||||
|
"methods": ["GET", "POST", "OPTIONS"]
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"url": "http://baron_backend:3000"
|
||||||
|
},
|
||||||
|
"authenticators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
],
|
||||||
|
"authorizer": { "handler": "allow" },
|
||||||
|
"mutators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "backend-command",
|
||||||
|
"description": "Command 요청은 Backend로 전달 (Audit 강제)",
|
||||||
|
"match": {
|
||||||
|
"url": "http://<.*>/api/v1/<.*>",
|
||||||
|
"methods": ["POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"url": "http://baron_backend:3000"
|
||||||
|
},
|
||||||
|
"authenticators": [
|
||||||
|
{ "handler": "cookie_session" }
|
||||||
|
],
|
||||||
|
"authorizer": { "handler": "remote_json" },
|
||||||
|
"mutators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "backend-query",
|
||||||
|
"description": "Backend Query (admin/dev 포함)",
|
||||||
|
"match": {
|
||||||
|
"url": "http://<.*>/api/v1/<.*>",
|
||||||
|
"methods": ["GET"]
|
||||||
|
},
|
||||||
|
"upstream": {
|
||||||
|
"url": "http://baron_backend:3000"
|
||||||
|
},
|
||||||
|
"authenticators": [
|
||||||
|
{ "handler": "cookie_session" }
|
||||||
|
],
|
||||||
|
"authorizer": { "handler": "remote_json" },
|
||||||
|
"mutators": [
|
||||||
|
{ "handler": "noop" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
43
mcp/compose.mcp.ory.yaml
Normal file
43
mcp/compose.mcp.ory.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
services:
|
||||||
|
kratos-mcp-server:
|
||||||
|
build:
|
||||||
|
context: ./kratos-mcp
|
||||||
|
container_name: mcp_ory_kratos
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
init: true
|
||||||
|
environment:
|
||||||
|
- KRATOS_ADMIN_URL=http://kratos:4434
|
||||||
|
networks:
|
||||||
|
- ory-net
|
||||||
|
|
||||||
|
hydra-mcp-server:
|
||||||
|
build:
|
||||||
|
context: ./hydra-mcp
|
||||||
|
container_name: mcp_ory_hydra
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
init: true
|
||||||
|
environment:
|
||||||
|
- HYDRA_PUBLIC_URL=http://hydra:4444
|
||||||
|
- HYDRA_ADMIN_URL=http://hydra:4445
|
||||||
|
networks:
|
||||||
|
- ory-net
|
||||||
|
|
||||||
|
keto-mcp-server:
|
||||||
|
build:
|
||||||
|
context: ./keto-mcp
|
||||||
|
container_name: mcp_ory_keto
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
init: true
|
||||||
|
environment:
|
||||||
|
- KETO_READ_URL=http://keto:4466
|
||||||
|
- KETO_WRITE_URL=http://keto:4467
|
||||||
|
networks:
|
||||||
|
- ory-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ory-net:
|
||||||
|
external: true
|
||||||
|
name: ory-net
|
||||||
Reference in New Issue
Block a user