Compare commits

...

1 Commits
v0.2.2 ... main

Author SHA1 Message Date
ebbf2c297c docs: add Docker Compose setup for OAuth multi-user mode
- Update Dockerfile to default to streamable-http transport
- Add docker-compose.oauth-standalone.yml for existing OIDC providers
- Add .env.oauth.example template with all required settings
- Update README_DOCKER.md with OAuth deployment instructions

OAuth mode requires streamable-http transport (not stdio) since
authentication happens via browser redirect flow.
2026-01-12 14:40:50 -07:00
4 changed files with 231 additions and 9 deletions

82
.env.oauth.example Normal file
View File

@ -0,0 +1,82 @@
# mcvsphere OAuth Configuration Template
# Copy to .env and configure for your environment
#
# Usage:
# cp .env.oauth.example .env
# # Edit .env with your values
# docker compose -f docker-compose.oauth-standalone.yml up -d
# ─────────────────────────────────────────────────────────────────────────────
# Docker Compose
# ─────────────────────────────────────────────────────────────────────────────
COMPOSE_PROJECT_NAME=mcvsphere
# ─────────────────────────────────────────────────────────────────────────────
# vCenter Connection (Required)
# ─────────────────────────────────────────────────────────────────────────────
VCENTER_HOST=vcenter.example.com
VCENTER_USER=mcpservice@vsphere.local
VCENTER_PASSWORD=your-secure-password
# Optional: Skip SSL verification (dev only - use false in production)
VCENTER_INSECURE=false
# Optional: Specify defaults (auto-detected if not set)
# VCENTER_DATACENTER=Datacenter
# VCENTER_CLUSTER=Cluster
# VCENTER_DATASTORE=datastore1
# VCENTER_NETWORK=VM Network
# ─────────────────────────────────────────────────────────────────────────────
# MCP Transport (Required for OAuth)
# ─────────────────────────────────────────────────────────────────────────────
MCP_TRANSPORT=streamable-http
MCP_HOST=0.0.0.0
MCP_PORT=8080
# Your public domain (must match Caddy proxy)
MCP_DOMAIN=mcp.example.com
# TLS mode: 'internal' for self-signed (dev), remove for auto HTTPS (prod)
MCP_TLS_MODE=internal
# ─────────────────────────────────────────────────────────────────────────────
# OAuth / OIDC Provider (Required)
# ─────────────────────────────────────────────────────────────────────────────
OAUTH_ENABLED=true
# OIDC Discovery URL (ends with /.well-known/openid-configuration)
# Examples:
# Authentik: https://auth.example.com/application/o/mcvsphere/
# Keycloak: https://keycloak.example.com/realms/myrealm
# Auth0: https://myapp.auth0.com/
# Okta: https://myorg.okta.com/oauth2/default
OAUTH_ISSUER_URL=https://auth.example.com/application/o/mcvsphere/
# OAuth Client Credentials (from your OIDC provider)
OAUTH_CLIENT_ID=your-client-id
OAUTH_CLIENT_SECRET=your-client-secret
# Public callback URL (must be accessible from browser)
OAUTH_BASE_URL=https://mcp.example.com
# ─────────────────────────────────────────────────────────────────────────────
# RBAC Permission Groups
# ─────────────────────────────────────────────────────────────────────────────
# Create these groups in your OIDC provider and assign users:
#
# | Group | Access Level |
# |------------------------|---------------------------------|
# | vsphere-super-admins | Full control (all 94 tools) |
# | vsphere-host-admins | Host operations + VM management |
# | vsphere-admins | VM lifecycle management |
# | vsphere-operators | Power ops + snapshots |
# | vsphere-readers | Read-only |
#
# Users without any vsphere-* group will be denied access (default-deny).
# ─────────────────────────────────────────────────────────────────────────────
# Optional Settings
# ─────────────────────────────────────────────────────────────────────────────
# LOG_LEVEL=INFO
# MCVSPHERE_VERSION=0.2.2

View File

@ -48,8 +48,11 @@ ENV PATH="/app/.venv/bin:$PATH"
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Default to SSE transport for Docker
ENV MCP_TRANSPORT=sse
# Default transport - override with MCP_TRANSPORT env var
# stdio: Direct CLI usage (default for local)
# sse: Server-Sent Events (legacy HTTP)
# streamable-http: HTTP with OAuth support (required for multi-user)
ENV MCP_TRANSPORT=streamable-http
ENV MCP_HOST=0.0.0.0
ENV MCP_PORT=8080
@ -58,10 +61,9 @@ USER mcpuser
EXPOSE 8080
# Health check
# Health check - works with both SSE and streamable-http
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080')" || exit 1
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/.well-known/oauth-authorization-server')" || exit 1
# Run the MCP server
# Run the MCP server - transport configured via environment
ENTRYPOINT ["mcvsphere"]
CMD ["--transport", "sse"]

View File

@ -235,15 +235,89 @@ MCP_LOG_LEVEL=DEBUG
log_level: "DEBUG"
```
## OAuth Multi-User Mode
For shared infrastructure or production deployments, mcvsphere supports OAuth 2.1 with any OIDC provider. This enables:
- **Browser-based authentication** via Authentik, Keycloak, Auth0, Okta, etc.
- **Group-based RBAC** with 5 permission levels
- **Audit logging** with user identity
### Quick Start (Existing OIDC Provider)
If you already have an OIDC provider:
```bash
# 1. Copy and configure environment
cp .env.oauth.example .env
# Edit .env with your OIDC provider details
# 2. Start mcvsphere with OAuth
docker compose -f docker-compose.oauth-standalone.yml up -d
# 3. Add to Claude Code
claude mcp add -t http vsphere https://mcp.example.com/mcp
```
### Quick Start (New Authentik Deployment)
To deploy mcvsphere with a complete Authentik identity provider:
```bash
# 1. Generate secrets and configure
./scripts/setup-oauth.sh
# 2. Start full stack (mcvsphere + Authentik + PostgreSQL + Redis)
docker compose -f docker-compose.oauth.yml up -d
# 3. Configure Authentik at https://auth.yourdomain.com
# - Create OAuth2 provider
# - Create vsphere-* groups
# - Assign users to groups
```
### RBAC Permission Groups
Create these groups in your OIDC provider:
| Group | Access Level |
|-------|--------------|
| `vsphere-super-admins` | Full control (all 94 tools) |
| `vsphere-host-admins` | Host operations + VM management |
| `vsphere-admins` | VM lifecycle management |
| `vsphere-operators` | Power ops + snapshots |
| `vsphere-readers` | Read-only |
Users without any `vsphere-*` group are denied access (default-deny security).
### Required Environment Variables
```bash
# Transport (must be streamable-http for OAuth)
MCP_TRANSPORT=streamable-http
# OIDC Provider
OAUTH_ENABLED=true
OAUTH_ISSUER_URL=https://auth.example.com/application/o/mcvsphere/
OAUTH_CLIENT_ID=your-client-id
OAUTH_CLIENT_SECRET=your-client-secret
OAUTH_BASE_URL=https://mcp.example.com
```
See [OAUTH-ARCHITECTURE.md](OAUTH-ARCHITECTURE.md) for detailed setup and troubleshooting.
---
## Production Deployment
### Security Recommendations
1. Use a dedicated user account for vCenter access
2. Enable API key authentication
3. Use valid SSL certificates (set `insecure: false`)
1. Use a dedicated service account for vCenter access
2. Enable OAuth authentication (not API keys)
3. Use valid SSL certificates (set `VCENTER_INSECURE=false`)
4. Limit container resources
5. Use Docker secrets for sensitive data
6. Deploy behind a reverse proxy with HTTPS (Caddy recommended)
### High Availability

View File

@ -0,0 +1,64 @@
# mcvsphere with OAuth - Standalone Mode
# For users who already have an OIDC provider (Authentik, Keycloak, Auth0, Okta, etc.)
#
# Usage:
# 1. Copy .env.oauth.example to .env
# 2. Configure your OIDC provider settings
# 3. docker compose -f docker-compose.oauth-standalone.yml up -d
#
# Requires:
# - External OIDC provider with OAuth 2.1 support
# - Caddy network (caddy-docker-proxy) for HTTPS termination
#
# For full Authentik deployment, use docker-compose.oauth.yml instead
services:
mcvsphere:
build:
context: .
dockerfile: Dockerfile
image: mcvsphere:${MCVSPHERE_VERSION:-latest}
container_name: mcvsphere
restart: unless-stopped
env_file:
- .env
environment:
# Transport - streamable-http required for OAuth
MCP_TRANSPORT: streamable-http
MCP_HOST: 0.0.0.0
MCP_PORT: 8080
# OAuth - set in .env file
OAUTH_ENABLED: ${OAUTH_ENABLED:-true}
volumes:
- ./logs:/app/logs
networks:
- caddy
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/.well-known/oauth-authorization-server')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
labels:
# Caddy reverse proxy - configure domain in .env
caddy: ${MCP_DOMAIN:-mcp.localhost}
caddy.reverse_proxy: "{{upstreams 8080}}"
# TLS - use 'internal' for local dev, remove for production (auto HTTPS)
caddy.tls: ${MCP_TLS_MODE:-internal}
# WebSocket/streaming support
caddy.reverse_proxy.flush_interval: "-1"
caddy.reverse_proxy.transport: http
caddy.reverse_proxy.transport.read_timeout: "0"
caddy.reverse_proxy.transport.write_timeout: "0"
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
reservations:
memory: 256M
cpus: '0.25'
networks:
caddy:
external: true