Skip to content

Commit 1337c0d

Browse files
committed
Add proposal for shared key authentication for local deployments
Introduces a lightweight authentication mechanism for localhost MCP server deployments that provides defense-in-depth security without requiring external identity providers. The proposal includes: - Cryptographically-secure shared key generation per workload - OS keychain storage via existing encrypted secrets provider - New middleware for key validation - Transparent integration with thv proxy stdio bridge - Zero-configuration user experience with --shared-key-auth flag This addresses security concerns around unauthenticated localhost ports while maintaining simplicity for single-user local deployments.
1 parent b58742c commit 1337c0d

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Shared Key Authentication for Local MCP Servers
2+
3+
## Problem Statement
4+
5+
Local ToolHive deployments bind MCP servers to localhost (`127.0.0.1`) for network-level security, but have no authentication layer. While localhost binding prevents remote access, customers want defense-in-depth protection against:
6+
7+
- Malicious local processes connecting to open localhost ports
8+
- Port forwarding or SSH tunneling accidentally exposing endpoints
9+
- Privilege escalation attacks targeting unauthenticated local services
10+
- Compliance requirements mandating authentication even for localhost
11+
12+
Current authentication options (OIDC, local user auth) require external identity providers or manual password management, making them unsuitable for single-user local deployments.
13+
14+
## Goals
15+
16+
- Provide lightweight authentication for localhost MCP server deployments
17+
- Require zero or minimal configuration from users
18+
- Leverage existing ToolHive infrastructure (OS keychain, middleware, stdio bridge)
19+
- Work transparently with the `thv proxy stdio` bridge used by MCP clients
20+
- Maintain backward compatibility (opt-in feature)
21+
22+
## Non-Goals
23+
24+
- Replace network isolation or container-based security
25+
- Support multi-user local deployments (use OIDC instead)
26+
- Protect against root/administrator access or OS keychain compromise
27+
- Store credentials in configuration files
28+
29+
## Proposed Solution
30+
31+
Implement a shared key authentication middleware that generates unique cryptographic keys per MCP server workload, stores them in the OS keychain, and validates them on incoming requests.
32+
33+
### Architecture
34+
35+
```
36+
┌─────────────────────────────────────────────────────────────┐
37+
│ MCP Client │
38+
│ (Claude Desktop, etc.) │
39+
└──────────────────────┬──────────────────────────────────────┘
40+
│ stdio
41+
42+
┌─────────────────────────────────────────────────────────────┐
43+
│ thv proxy stdio Bridge │
44+
│ • Reads shared key from OS keychain │
45+
│ • Injects X-ToolHive-Auth header in HTTP requests │
46+
└──────────────────────┬──────────────────────────────────────┘
47+
│ HTTP + X-ToolHive-Auth header
48+
49+
┌─────────────────────────────────────────────────────────────┐
50+
│ ToolHive HTTP Proxy │
51+
│ • Shared Key Auth Middleware validates header │
52+
│ • Constant-time comparison with env variable │
53+
│ • Other middleware (Parser, Authz, Audit) │
54+
└──────────────────────┬──────────────────────────────────────┘
55+
│ Forwarded request
56+
57+
┌─────────────────────────────────────────────────────────────┐
58+
│ MCP Server Container │
59+
│ • Receives TOOLHIVE_SHARED_KEY env variable │
60+
└─────────────────────────────────────────────────────────────┘
61+
```
62+
63+
### Key Components
64+
65+
**1. Key Generation and Storage** (`pkg/auth/sharedkey/`)
66+
- Generate cryptographically-secure 32-byte keys using `crypto/rand`
67+
- Store in OS keychain via existing encrypted secrets provider
68+
- Key naming: `workload:<workload-name>:shared-key`
69+
- Lifecycle: Generate on deployment, delete on workload removal
70+
71+
**2. Shared Key Authentication Middleware** (`pkg/auth/sharedkey/middleware.go`)
72+
- Implements standard `types.Middleware` interface
73+
- Validates `X-ToolHive-Auth` header against `TOOLHIVE_SHARED_KEY` environment variable
74+
- Uses `crypto/subtle.ConstantTimeCompare` to prevent timing attacks
75+
- Returns `401 Unauthorized` for missing/invalid keys
76+
- Adds identity to context for audit trail
77+
78+
**3. Workload Integration** (`pkg/workloads/manager.go`)
79+
- Generate and store shared key when `SharedKeyAuth.Enabled` is set in RunConfig
80+
- Inject key into container and proxy via `TOOLHIVE_SHARED_KEY` environment variable
81+
- Configure middleware chain automatically
82+
- Clean up key on workload deletion
83+
84+
**4. Stdio Bridge Enhancement** (`cmd/thv/app/proxy_stdio.go`)
85+
- Retrieve shared key from secrets provider on startup
86+
- Inject `X-ToolHive-Auth` header in all HTTP requests via custom transport wrapper
87+
- Handle 401 responses with informative error messages
88+
89+
**5. RunConfig Extension** (`pkg/runner/config.go`)
90+
91+
```go
92+
type SharedKeyAuthConfig struct {
93+
Enabled bool `json:"enabled"`
94+
KeyRotationDays int `json:"keyRotationDays,omitempty"` // 0 = disabled
95+
}
96+
97+
// Add to RunConfig struct
98+
SharedKeyAuth *SharedKeyAuthConfig `json:"sharedKeyAuth,omitempty"`
99+
```
100+
101+
## High-Level Design
102+
103+
### Request Flow
104+
105+
1. **Workload Deployment**: User runs `thv run my-server --shared-key-auth`
106+
2. **Key Generation**: Workload manager generates 32-byte random key
107+
3. **Key Storage**: Key stored as `workload:my-server:shared-key` in OS keychain
108+
4. **Key Injection**: Key injected via `TOOLHIVE_SHARED_KEY` environment variable to container and proxy
109+
5. **Middleware Configuration**: Shared key auth middleware added to chain
110+
6. **Client Configuration**: MCP client configured to use `thv proxy stdio my-server`
111+
112+
### Authentication Flow
113+
114+
1. **Client Request**: MCP client sends JSON-RPC over stdio to `thv proxy stdio`
115+
2. **Key Retrieval**: Stdio bridge retrieves key from OS keychain
116+
3. **Header Injection**: Bridge adds `X-ToolHive-Auth: <key>` to HTTP request
117+
4. **Validation**: Middleware validates header against environment variable
118+
5. **Forwarding**: On success, request forwarded to MCP server container
119+
6. **Rejection**: On failure, return `401 Unauthorized`
120+
121+
### Security Properties
122+
123+
- **Key Strength**: 32 bytes (256 bits), base64-encoded
124+
- **Key Generation**: Crypto-secure random via `crypto/rand`
125+
- **Storage**: AES-256-GCM encrypted in OS keychain (existing provider)
126+
- **Comparison**: Constant-time to prevent timing attacks
127+
- **Uniqueness**: Independent key per workload
128+
- **Lifecycle**: Keys deleted with workload
129+
130+
## Usage Examples
131+
132+
### Basic Usage
133+
134+
```bash
135+
# Enable shared key authentication
136+
thv run my-server --shared-key-auth
137+
138+
# ToolHive automatically:
139+
# 1. Generates secure key
140+
# 2. Stores in OS keychain
141+
# 3. Configures middleware
142+
# 4. Updates client config
143+
```
144+
145+
### With Other Security Features
146+
147+
```bash
148+
# Combine with authorization and audit
149+
thv run my-server --shared-key-auth \
150+
--authz-config authz.yaml \
151+
--audit-config audit.yaml
152+
```
153+
154+
### Debugging
155+
156+
```bash
157+
# Verbose mode
158+
thv proxy stdio my-server --verbose
159+
160+
# Inspect key
161+
thv secret get workload:my-server:shared-key
162+
```
163+
164+
## Operational Considerations
165+
166+
### Key Lifecycle
167+
168+
**Generation**: Automatic on deployment with `--shared-key-auth` flag
169+
**Storage**: OS keychain via encrypted secrets provider
170+
**Deletion**: Automatic on `thv rm my-server`
171+
172+
### Backward Compatibility
173+
174+
- Opt-in feature via flag or RunConfig
175+
- Existing workloads continue working without changes
176+
- No breaking changes to configurations
177+
- Gradual migration: enable per-workload incrementally
178+
179+
### Multi-Workload Scenarios
180+
181+
- Each workload has unique key
182+
- Keys identified by workload name
183+
- Stdio bridge automatically selects correct key
184+
- Group operations work transparently
185+
186+
## Security Considerations
187+
188+
### Threat Model
189+
190+
**Protected Against**:
191+
- Unauthorized local process connections
192+
- Port scanning attacks
193+
- Accidental credential exposure in config files
194+
- Replay attacks (keys are workload-scoped)
195+
196+
**Not Protected Against**:
197+
- Root/administrator access to keychain
198+
- Process memory inspection
199+
- Physical machine access
200+
- Compromised OS keychain
201+
202+
### Defense in Depth
203+
204+
Shared key authentication adds a layer to existing security:
205+
206+
1. Network isolation (localhost binding)
207+
2. Container isolation
208+
3. **Shared key authentication** ← New layer
209+
4. Authorization (Cedar policies)
210+
5. Audit logging
211+
212+
Each layer provides independent protection.
213+
214+
## Alternatives Considered
215+
216+
### Certificate-Based Authentication (mTLS)
217+
218+
**Pros**: Industry-standard, strong crypto
219+
**Cons**: Complex (certificate generation, validation, expiry), TLS infrastructure overhead, overkill for localhost
220+
**Decision**: Rejected due to complexity
221+
222+
### Unix Domain Sockets
223+
224+
**Pros**: Filesystem-based permissions, OS-level access control
225+
**Cons**: Not cross-platform (Windows), breaks HTTP architecture, requires significant changes
226+
**Decision**: Rejected due to platform limitations
227+
228+
### API Keys in Config Files
229+
230+
**Pros**: Simple implementation, easy debugging
231+
**Cons**: Plaintext storage, git commit risk, no leverage of existing secrets infrastructure
232+
**Decision**: Rejected due to poor security properties
233+
234+
### Time-Based One-Time Passwords (TOTP)
235+
236+
**Pros**: Strong authentication, industry-standard
237+
**Cons**: Time synchronization complexity, poor UX for automated clients, overkill for localhost
238+
**Decision**: Rejected due to complexity and poor UX
239+
240+
## Implementation Plan
241+
242+
### Phase 1: Core Infrastructure
243+
- Create `pkg/auth/sharedkey/` package
244+
- Implement key generation and storage
245+
- Implement shared key authentication middleware
246+
- Add middleware factory registration
247+
- Extend RunConfig schema
248+
249+
### Phase 2: Workload Integration
250+
- Modify workloads manager for key lifecycle
251+
- Add CLI flag `--shared-key-auth`
252+
- Configure middleware chain automatically
253+
- Implement key cleanup on deletion
254+
255+
### Phase 3: Stdio Bridge Enhancement
256+
- Enhance `thv proxy stdio` to retrieve keys
257+
- Implement header injection in HTTP transport
258+
- Add error handling for auth failures
259+
- Update client configuration generation
260+
261+
### Phase 4: Testing and Documentation
262+
- Unit tests for middleware and key management
263+
- Integration tests for end-to-end flows
264+
- Security testing (timing attacks, etc.)
265+
- Update architecture documentation
266+
- Create user guide
267+

0 commit comments

Comments
 (0)