Restrict api_key query parameter to /mcp/tools, /mcp/tool/call, and /mcp/sse only. Updated documentation to reflect query param usage for ChatGPT UI without header support.
438 lines
10 KiB
Markdown
438 lines
10 KiB
Markdown
# Authentication Setup Guide
|
|
|
|
This guide walks you through setting up API key authentication for AegisGitea MCP to ensure only your ChatGPT workspace can access your self-hosted Gitea instance.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
AegisGitea MCP uses **Bearer Token authentication** to secure access:
|
|
|
|
- **API Keys**: Cryptographically secure 64-character tokens
|
|
- **Header-based**: Keys sent via `Authorization: Bearer <key>` header
|
|
- **Multi-key support**: Multiple keys for rotation grace periods
|
|
- **Rate limiting**: Failed auth attempts trigger IP-based rate limits
|
|
- **Audit logging**: All auth attempts logged for security monitoring
|
|
|
|
---
|
|
|
|
## Quick Start (5 minutes)
|
|
|
|
### 1. Generate API Key
|
|
|
|
```bash
|
|
# Using Make
|
|
make generate-key
|
|
|
|
# Or directly
|
|
python3 scripts/generate_api_key.py
|
|
```
|
|
|
|
This will:
|
|
- Generate a secure 64-character API key
|
|
- Show you the key (save it immediately!)
|
|
- Provide `.env` configuration snippet
|
|
- Optionally save metadata (not the key itself) for tracking
|
|
|
|
**Example output:**
|
|
```
|
|
✓ API Key Generated Successfully!
|
|
|
|
API KEY:
|
|
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
|
|
|
|
📋 Next Steps:
|
|
1. Add this key to your .env file
|
|
2. Restart the MCP server
|
|
3. Configure ChatGPT Business
|
|
```
|
|
|
|
### 2. Add Key to .env
|
|
|
|
```bash
|
|
# Edit .env
|
|
nano .env
|
|
|
|
# Add/update this line:
|
|
MCP_API_KEYS=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
|
|
```
|
|
|
|
### 3. Restart MCP Server
|
|
|
|
```bash
|
|
docker-compose restart aegis-mcp
|
|
|
|
# Verify authentication is enabled
|
|
docker-compose logs aegis-mcp | grep "authentication"
|
|
```
|
|
|
|
Expected log output:
|
|
```
|
|
API key authentication ENABLED (1 key(s) configured)
|
|
```
|
|
|
|
### 4. Test Authentication
|
|
|
|
```bash
|
|
# Without key - should fail with 401
|
|
curl https://mcp.yourdomain.com/mcp/tools
|
|
|
|
# With valid key (header) - should succeed
|
|
curl -H "Authorization: Bearer YOUR_KEY_HERE" \
|
|
https://mcp.yourdomain.com/mcp/tools
|
|
|
|
# With valid key (query param) - should succeed
|
|
curl "https://mcp.yourdomain.com/mcp/tools?api_key=YOUR_KEY_HERE"
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Options
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# Enable/disable authentication
|
|
AUTH_ENABLED=true # Set to false ONLY for testing
|
|
|
|
# API keys (comma-separated for multiple)
|
|
MCP_API_KEYS=key1 # Single key
|
|
MCP_API_KEYS=key1,key2,key3 # Multiple keys (rotation grace period)
|
|
|
|
# Rate limiting
|
|
MAX_AUTH_FAILURES=5 # Max failed attempts before blocking
|
|
AUTH_FAILURE_WINDOW=300 # Time window in seconds (5 min)
|
|
```
|
|
|
|
### Multiple Keys (Rotation Grace Period)
|
|
|
|
During key rotation, you can temporarily allow multiple keys:
|
|
|
|
```bash
|
|
# Old key
|
|
MCP_API_KEYS=old-key-here
|
|
|
|
# Add new key (both work)
|
|
MCP_API_KEYS=old-key-here,new-key-here
|
|
|
|
# After updating ChatGPT, remove old key
|
|
MCP_API_KEYS=new-key-here
|
|
```
|
|
|
|
---
|
|
|
|
## Security Features
|
|
|
|
### 1. Constant-Time Comparison
|
|
|
|
Keys are compared using `hmac.compare_digest()` to prevent timing attacks.
|
|
|
|
### 2. Rate Limiting
|
|
|
|
- **Threshold**: 5 failed attempts per IP
|
|
- **Window**: 5 minutes (configurable)
|
|
- **Action**: Reject all requests from that IP until window expires
|
|
- **Logging**: High-severity security event logged
|
|
|
|
### 3. Audit Logging
|
|
|
|
Every authentication attempt logs:
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2026-01-29T12:34:56.789Z",
|
|
"event": "api_authentication",
|
|
"status": "success",
|
|
"client_ip": "203.0.113.42",
|
|
"user_agent": "ChatGPT-User/1.0",
|
|
"key_hint": "a1b2c3d4...e1f2"
|
|
}
|
|
```
|
|
|
|
Failed attempts:
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2026-01-29T12:34:56.789Z",
|
|
"event": "access_denied",
|
|
"reason": "invalid_api_key",
|
|
"client_ip": "203.0.113.42"
|
|
}
|
|
```
|
|
|
|
### 4. Key Format Validation
|
|
|
|
- Minimum length: 32 characters
|
|
- Recommended length: 64 characters
|
|
- Format: Hexadecimal string (0-9, a-f)
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: "No API keys configured" error
|
|
|
|
**Symptoms:**
|
|
```
|
|
docker-compose logs: "No API keys configured in environment"
|
|
```
|
|
|
|
**Solution:**
|
|
1. Check `.env` file exists and contains `MCP_API_KEYS`
|
|
2. Ensure no typos (`MCP_API_KEYS` not `MCP_API_KEY`)
|
|
3. Verify key is at least 32 characters
|
|
4. Restart container after updating `.env`
|
|
|
|
### Issue: "Invalid API key" on valid key
|
|
|
|
**Possible causes:**
|
|
|
|
1. **Whitespace in .env**: Remove spaces around `=`
|
|
```bash
|
|
# Wrong
|
|
MCP_API_KEYS = key-here
|
|
|
|
# Correct
|
|
MCP_API_KEYS=key-here
|
|
```
|
|
|
|
2. **Key truncated**: Ensure entire 64-char key is copied
|
|
```bash
|
|
# Check key length
|
|
echo -n "your-key-here" | wc -c
|
|
# Should output: 64
|
|
```
|
|
|
|
3. **Container not restarted**: Always restart after changing `.env`
|
|
```bash
|
|
docker-compose restart aegis-mcp
|
|
```
|
|
|
|
### Issue: Rate limit blocking legitimate requests
|
|
|
|
**Symptoms:**
|
|
```
|
|
"Too many failed authentication attempts"
|
|
```
|
|
|
|
**Solution:**
|
|
1. Check audit logs for failed attempts:
|
|
```bash
|
|
docker-compose exec aegis-mcp cat /var/log/aegis-mcp/audit.log | grep "invalid_api_key"
|
|
```
|
|
|
|
2. Wait 5 minutes for rate limit to reset
|
|
|
|
3. If accidentally blocked yourself:
|
|
```bash
|
|
# Restart container (clears in-memory rate limits)
|
|
docker-compose restart aegis-mcp
|
|
```
|
|
|
|
4. Adjust rate limit settings in `.env` if needed:
|
|
```bash
|
|
MAX_AUTH_FAILURES=10
|
|
AUTH_FAILURE_WINDOW=600
|
|
```
|
|
|
|
### Issue: ChatGPT can't connect after adding auth
|
|
|
|
See [CHATGPT_SETUP.md](CHATGPT_SETUP.md) for detailed ChatGPT configuration.
|
|
|
|
**Quick check:**
|
|
1. Verify key in ChatGPT settings matches `.env`
|
|
2. Check Authorization header format: `Bearer <key>` (with space)
|
|
3. Test manually with curl first
|
|
|
|
---
|
|
|
|
## Monitoring Authentication
|
|
|
|
### View All Auth Attempts
|
|
|
|
```bash
|
|
# All auth events
|
|
docker-compose exec aegis-mcp grep "api_authentication" /var/log/aegis-mcp/audit.log
|
|
|
|
# Failed attempts only
|
|
docker-compose exec aegis-mcp grep "access_denied" /var/log/aegis-mcp/audit.log
|
|
|
|
# Rate limit triggers
|
|
docker-compose exec aegis-mcp grep "auth_rate_limit_exceeded" /var/log/aegis-mcp/audit.log
|
|
```
|
|
|
|
### Real-Time Monitoring
|
|
|
|
```bash
|
|
# Follow auth events
|
|
docker-compose exec aegis-mcp tail -f /var/log/aegis-mcp/audit.log | grep "auth"
|
|
```
|
|
|
|
### Weekly Summary Script
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Save as scripts/auth_summary.sh
|
|
|
|
CONTAINER="aegis-gitea-mcp"
|
|
LOG_PATH="/var/log/aegis-mcp/audit.log"
|
|
|
|
echo "=== Weekly Auth Summary ==="
|
|
echo ""
|
|
echo "Total auth attempts:"
|
|
docker exec $CONTAINER grep "api_authentication" $LOG_PATH | wc -l
|
|
|
|
echo ""
|
|
echo "Successful:"
|
|
docker exec $CONTAINER grep "api_authentication.*success" $LOG_PATH | wc -l
|
|
|
|
echo ""
|
|
echo "Failed:"
|
|
docker exec $CONTAINER grep "access_denied.*invalid_api_key" $LOG_PATH | wc -l
|
|
|
|
echo ""
|
|
echo "Rate limited IPs:"
|
|
docker exec $CONTAINER grep "auth_rate_limit_exceeded" $LOG_PATH | wc -l
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. Key Storage
|
|
|
|
- ✅ Store in `.env` file (never commit to git)
|
|
- ✅ Use password manager for backup
|
|
- ✅ Save key metadata (not key itself) for tracking
|
|
- ❌ Never hardcode in application code
|
|
- ❌ Never share via unencrypted channels
|
|
|
|
### 2. Key Rotation
|
|
|
|
- **Schedule**: Every 90 days (automated check available)
|
|
- **Method**: Use `make rotate-key` for guided rotation
|
|
- **Grace period**: Temporarily allow both old and new keys
|
|
- **Verification**: Test new key before removing old key
|
|
|
|
See [KEY_ROTATION.md](KEY_ROTATION.md) for detailed rotation procedures.
|
|
|
|
### 3. Access Control
|
|
|
|
- **Single workspace**: One key for your ChatGPT Business account
|
|
- **Multiple users**: One key per user (track with metadata)
|
|
- **Revocation**: Remove key from `.env` and restart
|
|
|
|
### 4. Monitoring
|
|
|
|
- **Weekly**: Review auth logs for anomalies
|
|
- **Monthly**: Check key age with `make check-key-age`
|
|
- **Quarterly**: Rotate keys
|
|
- **Alerts**: Set up notifications for rate limit events
|
|
|
|
---
|
|
|
|
## Advanced Configuration
|
|
|
|
### Disable Authentication (Testing Only)
|
|
|
|
**⚠️ WARNING: Only for isolated test environments**
|
|
|
|
```bash
|
|
# .env
|
|
AUTH_ENABLED=false
|
|
```
|
|
|
|
Server will log critical warning:
|
|
```
|
|
API key authentication DISABLED - server is open to all requests!
|
|
```
|
|
|
|
### Custom Rate Limiting
|
|
|
|
```bash
|
|
# More lenient (for high-traffic)
|
|
MAX_AUTH_FAILURES=20
|
|
AUTH_FAILURE_WINDOW=600 # 10 minutes
|
|
|
|
# More strict (for high-security)
|
|
MAX_AUTH_FAILURES=3
|
|
AUTH_FAILURE_WINDOW=1800 # 30 minutes
|
|
```
|
|
|
|
### Key Hashing (Future Enhancement)
|
|
|
|
Currently keys are stored in plaintext in `.env`. Future versions will support hashed keys in database.
|
|
|
|
**Current security model:**
|
|
- ✅ Keys never logged in full (only first 8 + last 4 chars)
|
|
- ✅ `.env` file protected by filesystem permissions
|
|
- ✅ Container runs as non-root user
|
|
- ⚠️ Ensure `.env` has restrictive permissions: `chmod 600 .env`
|
|
|
|
---
|
|
|
|
## Security Checklist
|
|
|
|
Before going to production:
|
|
|
|
- [ ] API key generated with `make generate-key` (not manually typed)
|
|
- [ ] Key is exactly 64 characters
|
|
- [ ] `.env` file has restrictive permissions (`chmod 600 .env`)
|
|
- [ ] `.env` is in `.gitignore` (verify: `git status` shows no .env)
|
|
- [ ] Container restarted after adding key
|
|
- [ ] Authentication enabled (check logs for "ENABLED" message)
|
|
- [ ] Test curl command succeeds with key, fails without
|
|
- [ ] ChatGPT configured with Authorization header
|
|
- [ ] Rate limiting tested (try 6 failed attempts)
|
|
- [ ] Audit logs capturing auth events
|
|
- [ ] Key rotation reminder set (90 days)
|
|
- [ ] Backup key stored securely (password manager)
|
|
|
|
---
|
|
|
|
## FAQ
|
|
|
|
**Q: Can I use the same key for multiple ChatGPT workspaces?**
|
|
|
|
A: Yes, but not recommended. Generate separate keys for auditability:
|
|
```bash
|
|
MCP_API_KEYS=workspace1-key,workspace2-key
|
|
```
|
|
|
|
**Q: How do I revoke access immediately?**
|
|
|
|
A: Remove key from `.env` and restart:
|
|
```bash
|
|
# Edit .env (remove old key)
|
|
docker-compose restart aegis-mcp
|
|
```
|
|
|
|
Access is revoked instantly after restart.
|
|
|
|
**Q: Does rate limiting affect valid keys?**
|
|
|
|
A: No. Rate limiting only applies to IPs that fail authentication. Valid keys are never rate limited.
|
|
|
|
**Q: Can I see which key was used for each request?**
|
|
|
|
A: Yes. Audit logs include `key_hint` (first 8 + last 4 characters):
|
|
```json
|
|
{"key_hint": "a1b2c3d4...e1f2"}
|
|
```
|
|
|
|
**Q: What happens if I lose my API key?**
|
|
|
|
A: Generate a new one with `make generate-key`, update `.env`, restart server, update ChatGPT config. The old key stops working immediately after restart.
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
- [ChatGPT Setup Guide](CHATGPT_SETUP.md) - Configure ChatGPT Business
|
|
- [Key Rotation Guide](KEY_ROTATION.md) - Automated rotation procedures
|
|
- [Security Policy](SECURITY.md) - Overall security best practices
|
|
|
|
---
|
|
|
|
**Need help?** Open an issue in the Gitea repository.
|