Skip to main content
Glama
gwanghun-choi

vault-mcp

vault-mcp

vault-mcp 는 단순한 HashiCorp Vault 관리 도구가 아니라, 사내 서버·웹사이트·API 접속 정보(Credential)를 중앙에서 인증·인가·감사하며 관리하는 Ops Broker 다.

LLM(Claude) 또는 CLI 가 사용자의 요청을 중앙 API 에 전달하면, 중앙 API 가 사용자 인증·인가 → 리소스 해석 → Vault Credential 조회 → 결과 마스킹 → 감사 기록을 통제한다. 실제 Secret 은 Vault 에만 있고, LLM·CLI·DB·API 응답 어디에도 새지 않는다.

현재 단계: STEP 8.7 — Resource 유형 확장(SERVER/WEBSITE/API) 완료. 다음은 STEP 9(인증 개선). SSH 실행(Runner)은 STEP 10 으로 아직 미구현이다. 단계별 상세는 docs/development-roadmap.md.


목차


Related MCP server: Better Auth MCP Server

핵심 원칙

원칙

내용

Secret 격리

실제 Secret 은 Vault 에만. DB·API 응답·로그·감사에는 credential_ref(논리 경로)만.

1 Resource = 1 Credential

하나의 등록 항목 = 하나의 접속 대상 = 하나의 Credential. credential_id 개념 없음(1:N 아님).

소유자 전용

리소스는 소유자만 관리. 타인 리소스는 403 이 아니라 404(존재 은닉).

클라이언트 입력 불신

요청 body 의 user_id/credential_ref/vault_path/host 를 신뢰하지 않는다. 서버가 결정.

LLM 은 별칭만

Claude 는 alias + operation 만 추출. 서버 해석·Credential 조회는 중앙 API 가 사용자 권한 안에서 수행.

감사 강제

모든 Credential 접근(등록/교체/삭제/reveal)은 감사 로그에 기록(Secret 원문 제외).


아키텍처

현재 구현된 데이터 흐름(등록 / 조회 / 삭제). SSH 실행(Runner)은 STEP 10 으로 아직 없다.

flowchart TD
    subgraph client [클라이언트]
      W[Web UI · Jinja2]
      S[Claude Skill · REST]
    end
    W -->|Cookie 세션 + CSRF| API
    S -->|API Key| API
    API[vault-mcp FastAPI]

    API -->|인증 4종 → users.id| AUTH{인증·인가}
    AUTH -->|비민감 메타데이터| PG[(PostgreSQL<br/>vault_mcp schema)]
    AUTH -->|credential_ref 로 Secret| VAULT[(HashiCorp Vault<br/>KV v2)]
    API --> AUDIT[(audit_logs)]

    PG -.->|credential_ref = 논리 경로| VAULT

흐름

경로

등록

리소스 등록(PG) → Credential 등록 → Vault write → credential_ref 를 PG 에 저장

조회(reveal)

소유권 확인(PG) → credential_ref 로 Vault read → 평문 Secret 반환(no-store)

삭제

Vault soft delete → PG 상태/credential_ref 갱신 → commit. commit 실패 시 Vault undelete 로 보상

컴포넌트 책임:

컴포넌트

한다

하지 않는다

Claude Skill

alias/operation 추출, 결과 분석, 비민감 요약만 출력

Secret 원문 출력(사용자 요청해도 금지), Vault/경로 접근

Web UI

로그인, 리소스 CRUD, Credential 등록/교체/삭제, API Key 발급

Secret 재표시(Web 에 reveal 없음)

FastAPI

인증·인가·리소스 해석·Vault 연동·마스킹·감사

Secret 을 응답/로그에 노출

Vault

실제 Secret 저장

PostgreSQL

비민감 메타데이터 + credential_ref

실제 Secret 저장


Resource 유형과 저장 책임

STEP 8.7 부터 등록 대상은 SERVER / WEBSITE / API 세 유형이다(OTHER 는 범위 밖). 유형은 등록 후 변경 불가(409). 정본: docs/resource-types.md.

사용 · 항상 NULL(보내면 400) · 굵게 = 필수

필드

저장소

SERVER

WEBSITE

API

alias / description / service_name

PG

/ ✓ /

/ ✓ / ✓

/ ✓ / ✓

status / environment

PG

✓ / ✓

✓ / —

✓ / —

host / ssh_port

PG

/

site_url

PG

base_url / auth_type(ApiAuthType)

PG

/

header_name / token_prefix / scopes / expires_at

PG

login_id

PG

(¹)

✓(= client_id)

memo

PG

username / port

Vault

/ ✓

password

Vault

✓(²)

private_key / passphrase

Vault

✓(²)

api_key / token / refresh_token / client_secret / webhook_secret

Vault

✓(³)

  • (¹) SERVER 의 SSH username 은 Vault 에만 있다(하위 호환). 그래서 목록 응답의 login_id 는 SERVER 에서 항상 null.

  • (²) SERVER 는 auth_type(SshAuthType) 에 따라 password 또는 private_key(+선택 passphrase).

  • (³) API 는 auth_type(ApiAuthType) 이 어떤 Secret 이 필수인지 결정한다. client_id 는 secret 이 아니므로 PG, client_secret 은 Vault — reveal 이 한 쌍으로 반환.

인증 유형 enum 은 분리 유지한다: SERVER = SshAuthType(PASSWORD/PRIVATE_KEY, Vault), API = ApiAuthType(API_KEY/ACCESS_TOKEN/BEARER_TOKEN/PERSONAL_ACCESS_TOKEN/CLIENT_CREDENTIALS/ OAUTH_TOKEN/WEBHOOK_SECRET/CUSTOM, PostgreSQL). WEBSITE 는 인증 유형 개념이 없다.


API 레퍼런스

Base prefix /api/v1. 스키마·Authorize·curl 예시: docs/swagger-guide.md (/docs, /redoc, /openapi.json 은 local/dev 에서만 노출).

Secret 반환 은 reveal 하나뿐이다. 나머지 Credential API 는 상태 메타데이터만 준다.

Method

Path

용도

인증 / scope

Secret

GET

/health · /ready

Liveness / Readiness(DB+Vault)

없음

POST

/auth/register · /auth/login

LOCAL 가입 / 로그인(JWT 발급)

없음

GET

/me

현재 사용자 프로필

인증

POST/GET/DELETE

/api-keys · /api-keys/{id}

API Key 발급(1회 노출)/목록/폐기

Web 사용자만(API Key 로 불가)

발급 시 1회

GET

/servers · /servers/{alias}

operation 권한 있는 서버 목록/상세

servers.read

POST/PUT/GET/DELETE

/resources/{id}/credentials

전 유형 Credential 등록/교체/상태/삭제

인증

POST/PUT/GET/DELETE

/servers/{id}/credentials/ssh

SSH Credential (SERVER 전용 하위 호환)

인증

GET

/my/resources · /my/resources/search

내 소유 리소스 목록/검색(전 유형)

resources:read

POST

/my/resources/{id}/credentials/reveal

⚠️ Credential 원문 조회

credentials:reveal

평문

  • /resources/{id}/credentials(전 유형, STEP 8.7 신규)와 /servers/{id}/credentials/ssh(SERVER 전용 하위 호환)는 SERVER 에 대해 동작·저장 결과가 동일하다. 후자는 WEBSITE/API 대상이면 409.

  • reveal 응답은 resource_type discriminator 로 갈리는 oneOf(SERVER/WEBSITE/API). SERVER 응답은 resource_type 키만 additive 로 추가돼 기존 Skill 이 그대로 동작한다.

  • scope 표기 불일치는 코드 그대로다: servers.read(점) vs resources:read/credentials:reveal(콜론).

Web UI(/web/*, HTML+Cookie, OpenAPI 미노출): 로그인, 대시보드, 리소스 CRUD(유형 선택 → 유형별 폼), Credential 등록/삭제(reveal 없음), API Key 발급/폐기. 상세: docs/web-ui.md.


인증 방식

Authorization 헤더에서 4종을 라우팅하며 모두 내부 users.id 로 귀결된다. 클라이언트가 보낸 user_id/tenant_id/oid 는 신뢰하지 않는다.

방식

헤더

알고리즘

사용자 식별

용도 · 활성 조건

LOCAL JWT

Bearer <JWT>

HS256

sub = 내부 user_id

개발 주 로그인 / Web UI. local_auth_enabled + issuer 일치

Microsoft Entra

Bearer <JWT>

RS256(only, JWKS)

(tid, oid) 매핑

사내 SSO. Bearer JWT 기본 경로

API Key

ApiKey <dv_...> (또는 Bearer dv_...)

HMAC-SHA256 해시

key_hash → user_id

Claude/CLI. 접두 dv_dev_/dv_live_

DEV

헤더 없음

dev_auth_user_id

로컬 편의. dev_auth_enabled + is_local(prod fail-fast)


Vault 경로와 삭제 정책

논리 경로(DB credential_ref 에 저장). DB 값이 경로의 정본 — 읽기/교체/삭제 시 재생성하지 않고 저장값을 그대로 쓴다(그래서 기존 행이 호환 로직 없이 동작). 경로는 UUID 로만 구성(사용자 입력·alias 미사용 → path traversal 차단).

구분

형식

신규(전 유형)

users/{user_id}/resources/{resource_id}/credential

legacy(STEP 6~8 SERVER)

users/{user_id}/servers/{server_resource_id}/ssh

KV v2 물리 경로 = {mount}/data/{credential_ref}. VaultClient 는 App Token Policy 가 실제로 허용하는 endpoint 만 쓴다:

동작

HTTP endpoint

필요 capability

write / read

POST/GET /v1/{mount}/data/{path}

data/* create·read·update

metadata read

GET /v1/{mount}/metadata/{path}

metadata/* read

soft delete

POST /v1/{mount}/delete/{path}

delete/* update

undelete(보상)

POST /v1/{mount}/undelete/{path}

undelete/* update

삭제는 soft delete 다(destroy 아님, 복구 가능). 정책이 data/* 에 delete 를 주지 않으므로 DELETE /data/ 가 아니라 POST /delete/(버전 지정, metadata 로 current_version 선조회)를 쓴다 — 전자를 쓰면 403 으로 삭제가 전부 실패한다. Resource 삭제도 Vault Secret 을 함께 soft delete 하고, DB commit 실패 시 undelete 로 보상한다. 정본: docs/vault-client.md, docs/credential-storage-flow.md.


데이터 모델

전용 PostgreSQL schema vault_mcp 에 격리(공유 DB vault_mcp 의 public·타 schema 미사용). enum 은 비네이티브 VARCHAR(32). 정본: docs/data-model.md.

테이블

역할

users

신원. LOCAL(login_id+password_hash) / MICROSOFT(tenant_id+entra_oid) 두 출처

api_keys

API Key 메타데이터. 원문 미저장(key_hash/key_prefix 만)

server_resources

등록 대상 Resource(SERVER/WEBSITE/API 전부). 이름은 하위 호환으로 유지

server_permissions

인가 단위: 사용자 × 리소스 × operation. Default Deny, DENY 우선

vault_connections

Vault 접속 메타데이터. 중앙 Vault(시스템 소유)면 owner NULL

audit_logs

감사 이력. Secret 컬럼 없음

주요 제약(server_resources):

  • uq_server_resources_owner_user_id_alias — UNIQUE(owner_user_id, alias) WHERE status ≠ 'DELETED'(partial). soft delete 는 행을 남기므로, 삭제한 별칭을 재사용할 수 있게 DELETED 를 제외한다(0006). → 같은 alias 의 DELETED 행이 여러 개일 수 있어 alias 조회는 DELETED 를 거른다.

  • resource_type_allowedresource_type IN ('SERVER','WEBSITE','API')

  • auth_type_api_onlyauth_type IS NULL OR resource_type = 'API'(SshAuthType/ApiAuthType 혼용 차단)

Migration 이력(revision id 는 alembic_version.version_num VARCHAR(32) 제약):

revision

내용

0001_initial

초기 스키마 5테이블(vault_mcp schema, enum=VARCHAR(32))

0002_local_auth_and_api_keys

LOCAL 로그인 필드 + api_keys 테이블, Entra 필드 nullable 전환

0003_credential_ref_nullable

server_resources.credential_ref nullable(미등록=NULL)

0004_server_environment

environment 컬럼(DEV/STAGING/PROD 표시용)

0005_resource_types

resource_type + 유형별 필드, host/ssh_port/service_name NOT NULL 해제, 기존 행 SERVER backfill

0006_alias_unique_active_only

alias unique 를 DELETED 제외 partial index 로 교체

SQLite(테스트)는 ALTER COLUMN / ADD CONSTRAINT 미지원 → NOT NULL 완화·CHECK 추가는 PostgreSQL 에서만 수행하고, SQLite 는 모델 create_all 이 동일한 최종 스키마를 만든다.


빠른 시작

요구: Python 3.12+, uv. DB/Vault 없이도 /api/v1/health 는 뜬다.

# [로컬 PC / WSL·zsh]
uv sync                                   # 가상환경 + 의존성
cp .env.example .env                      # 값 채우기(아래 환경 변수 참고)
uv run uvicorn app.main:app --reload      # 또는: bash scripts/run-local.sh
  • API 문서: http://127.0.0.1:8000/docs (local/dev 에서만)

  • Web UI: http://127.0.0.1:8000/web/login

  • PoC 로그인 계정 생성: docs/poc-user-seed.md 의 seed 스크립트

LOCAL 로그인은 .env 에서 LOCAL_AUTH_ENABLED=true 여야 활성화된다(기본 false). Credential 등록/조회는 Vault 연결이 필요하다 — 개발 Vault 터널·App Token 준비는 docs/vault-local-development.md.


환경 변수

.env 로 관리(Git 커밋 금지, .env.example 만 저장). Secret 값은 Vault 또는 K8S Secret 으로, .env 에는 서버별 Credential 을 넣지 않는다.

접두

예시 키

비고

APP_*

APP_ENV, APP_HOST, APP_PORT

APP_ENV = local/dev/test/production

DB_*

DB_HOST/PORT/NAME/USERNAME/PASSWORD/SCHEMA

공유 DB vault_mcp, schema vault_mcp

LOCAL_* / PASSWORD_*

LOCAL_AUTH_ENABLED, LOCAL_JWT_SECRET, LOCAL_JWT_ISSUER

HS256 자체 JWT

API_KEY_*

API_KEY_HASH_SECRET, API_KEY_MAX_ACTIVE_PER_USER

미설정 시 SHA-256 fallback

ENTRA_*

ENTRA_TENANT_ID, ENTRA_CLIENT_SECRET, ENTRA_AUDIENCE, ENTRA_ISSUER

Resource Server 검증

VAULT_*

VAULT_ADDR, VAULT_TOKEN / VAULT_TOKEN_FILE, VAULT_KV_MOUNT

Token 원문 또는 0600 파일

DEV_AUTH_*

DEV_AUTH_ENABLED, DEV_AUTH_USER_ID

local/dev 한정(prod fail-fast)

기타

LOG_LEVEL, DOCS_ENABLED, CORS_ALLOWED_ORIGINS

굵게 = SecretStr(로그/응답 미노출): DB_PASSWORD, LOCAL_JWT_SECRET, API_KEY_HASH_SECRET, ENTRA_CLIENT_SECRET, VAULT_TOKEN.


테스트·품질 게이트

uv run pytest              # 493 passed, 7 skipped (skip = 실 Vault 통합 테스트)
uv run ruff check .        # All checks passed
uv run ruff format --check .
uv run mypy app            # strict, no issues (87 files)
  • 테스트 32개 파일. 보안 경계(타 사용자 접근, 권한 없는 operation, Secret 노출, alias 위조, Vault 실패 일관성 등)를 필수로 테스트.

  • 실 Vault 통합 테스트는 기본 skip. 실행: VAULT_MCP_INTEGRATION=1 VAULT_ADDR=... VAULT_TOKEN_FILE=... uv run pytest tests/vault/test_real_vault_integration.py.


디렉터리 구조

app/
  main.py                       FastAPI 앱 조립 + OpenAPI 설정
  core/                         config, exceptions, openapi, api_docs, logging, middleware, security
  api/
    dependencies.py             인증/서비스 DI
    v1/router.py, v1/endpoints/ health, auth, me, api_keys, servers,
                                credentials(SSH 하위호환), resource_credentials(전 유형),
                                my_resources(목록/검색/reveal)
    internal/                   dev 전용 라우터(local/dev 에서만 등록)
  auth/                         verifier(Entra), local_token/local_service, dependencies, context, jwks, password
  api_keys/                     generator, hasher, service, repository, schemas
  credentials/                  service, compensation, validation, schemas, errors
  server_resources/             crud, service(alias 해석/인가), repository, validation, schemas
  vault/                        client(KV v2), factory, token_file, credential_ref, errors
  db/                           base(schema 격리), session, models/{user,api_key,server_resource,...}
  domain/                       enums, errors, resolved
  audit/                        service(감사 기록)
  web/                          router(Jinja2), csrf, dependencies
  templates/  static/{css,js}   Web UI
  scripts/seed_poc_user.py      PoC 사용자 seed
  ssh/  operations/             SSH Runner·운영 API 자리(STEP 10~11, 미구현)
migrations/versions/            Alembic 0001~0006
tests/                          api, api_keys, auth, services, vault, web, skills, db, models
skills/vault-mcp/             전사 공통 Claude Skill(REST 호출, Secret 미출력)
deploy/                         gitops-repo(Kustomize+ArgoCD), k8s/dev(수동 kubectl 대안)
docs/                           설계·운영 문서(아래 인덱스)
Dockerfile  Jenkinsfile         멀티스테이지 이미지 / CI 파이프라인

개발 단계

STEP

내용

상태

1

FastAPI 골격

2

DB 모델·권한·서버 해석(Alembic, schema 격리)

3

Microsoft Entra 인증

4

LOCAL 로그인 + API Key + 사용자별 Vault 경로 모델

5

개발 Vault 설치·단독 검증(NCP)

6

FastAPI ↔ Vault 연동 + Credential 등록

7

Skill 기반 REST API PoC

8

사용자 Web UI(Jinja2)

8.5 / 8.6

개발 K8S 배포 / Jenkins+Harbor+ArgoCD GitOps

8.7

Resource 유형 확장(SERVER/WEBSITE/API)

9

사용자 인증 개선(Device Flow/짧은 Access Token)

예정

10

SSH Runner 등 Credential 활용

미구현

11 / 12 / 13

읽기 전용 운영 API / CLI+Keyring / K8S 배포 정리

미구현

아직 미구현: SSH 실행(Runner), 운영 API(logs/status/disk), CLI/OS Keyring. Entra 실제 App Registration 통합 검증은 조직 환경에서 수행 필요.


배포

Jenkins(CI) → Harbor(registry) → ArgoCD(GitOps). 정본: docs/ci-gitops.md, deploy/gitops-repo/README.md.

flowchart LR
    GIT[소스 push] -->|pollSCM| J[Jenkins]
    J -->|build| IMG[이미지 sha-태그]
    IMG -->|push| H[Harbor]
    J -->|newTag 갱신 commit| D[vault-mcp-deploy repo]
    D -->|watch| A[ArgoCD]
    A -->|PreSync: migration Job| K[K8S vault-mcp ns]
    A -->|sync: Deployment| K
  • 이미지: Dockerfile 멀티스테이지, non-root(uid 10001), uvicorn :8000. Secret 미포함(K8S Secret 주입).

  • Jenkinsfile: Init(short SHA) → Build → Push Harbor → deploy repo 의 newTagsha-<SHA> 로 갱신. Deployment 와 migration Job 이 같은 SHA 로 반영(불일치 방지).

  • GitOps: deploy/gitops-repo/(Kustomize + ArgoCD Application). migration 은 PreSync Hook, seed 는 수동 1회.

  • deploy/k8s/dev/ 는 GitOps 없이 수동 kubectl apply 로 검증하는 대안이다.


기술 스택

영역

스택

언어

Python 3.12+

FastAPI ≥0.115, Uvicorn ≥0.34

검증

Pydantic v2 ≥2.9, pydantic-settings ≥2.6

DB

SQLAlchemy 2.x async, asyncpg ≥0.30, Alembic ≥1.14, PostgreSQL

인증

PyJWT[crypto] ≥2.9, pwdlib[argon2] ≥0.2

Vault/HTTP

httpx ≥0.28 (KV v2 직접 호출)

Web

Jinja2 ≥3.1, python-multipart

개발

pytest + pytest-asyncio, aiosqlite, ruff(line 100, py312), mypy(strict)

패키지

uv(비패키지 애플리케이션 모드)


문서 인덱스


Git 정책: 기본 브랜치 main, 명시적 요청 없이 commit/push 하지 않는다. .env·Token·인증서· Private Key 는 커밋 금지. 프로젝트 원칙과 작업 하네스는 CLAUDE.md.

F
license - not found
-
quality - not tested
-
maintenance - not tested

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gwanghun-choi/vault-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server