# Security
This guide covers security features and best practices for mcp-server-db2i.
## Security Features
- **Read-only access**: Only SELECT statements are permitted
- **No credentials in code**: All sensitive data via environment variables or file-based secrets
- **Query validation**: AST-based SQL parsing plus regex validation blocks dangerous operations
- **Result limiting**: Default limit of 1000 rows, configurable max limit (default: 10000)
- **Rate limiting**: Configurable request throttling to prevent abuse (100 req/15 min default)
- **Structured logging**: Automatic redaction of sensitive fields like passwords
- **Token-based HTTP auth**: Per-request credentials for HTTP transport
## Credential Management
The server supports multiple methods for providing credentials, listed from most to least secure.
### Option 1: Docker Secrets (Recommended for Production)
Docker secrets provide the most secure credential management. Secrets are mounted as files and never exposed in environment variables or process listings.
1. Create secret files:
```bash
mkdir -p ./secrets
echo "your-username" > ./secrets/db2i_username.txt
echo "your-password" > ./secrets/db2i_password.txt
chmod 600 ./secrets/*.txt
```
2. Configure docker-compose.yml to use secrets:
```yaml
services:
mcp-server-db2i:
# ... other config ...
environment:
- DB2I_HOSTNAME=${DB2I_HOSTNAME}
- DB2I_USERNAME_FILE=/run/secrets/db2i_username
- DB2I_PASSWORD_FILE=/run/secrets/db2i_password
secrets:
- db2i_username
- db2i_password
secrets:
db2i_username:
file: ./secrets/db2i_username.txt
db2i_password:
file: ./secrets/db2i_password.txt
```
For Docker Swarm or Kubernetes, use their native secret management instead of file-based secrets.
### Option 2: External Secret Management
For enterprise deployments, integrate with secret management systems:
- **HashiCorp Vault**: Inject secrets at runtime
- **AWS Secrets Manager**: Use IAM roles for access
- **Azure Key Vault**: Integrate with managed identities
- **Google Secret Manager**: Use service account authentication
These systems can populate the `*_FILE` environment variables or inject secrets directly.
### Option 3: Environment Variables (Development Only)
Plain environment variables are convenient for development but expose credentials through:
- `docker inspect` output
- Process listings (`ps aux`)
- Shell history
- Log files
```bash
# .env file (ensure it's in .gitignore)
DB2I_USERNAME=your-username
DB2I_PASSWORD=your-password
```
**Warning**: Never commit `.env` files or credentials to version control.
### File-Based Secret Variables
| Variable | Description |
|----------|-------------|
| `DB2I_USERNAME_FILE` | Path to file containing username (takes priority over `DB2I_USERNAME`) |
| `DB2I_PASSWORD_FILE` | Path to file containing password (takes priority over `DB2I_PASSWORD`) |
## Rate Limiting
The server includes built-in rate limiting to protect the IBM i database from excessive queries.
### Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `RATE_LIMIT_WINDOW_MS` | `900000` | Time window in milliseconds (15 min) |
| `RATE_LIMIT_MAX_REQUESTS` | `100` | Maximum requests per window |
| `RATE_LIMIT_ENABLED` | `true` | Set to `false` to disable |
### Behavior
- **Default**: 100 requests per 15-minute window
- **Scope**: Per server instance (for stdio transport, this means per-client since each MCP client spawns its own server process)
- **HTTP transport**: Rate limiting applies per authenticated token
When the rate limit is exceeded, queries return an error with `waitTimeSeconds` indicating when to retry:
```json
{
"success": false,
"error": "Rate limit exceeded",
"waitTimeSeconds": 120,
"limit": 100,
"windowMs": 900000
}
```
## Query Validation
The server validates all SQL queries before execution using multiple layers:
```mermaid
flowchart LR
query["SQL Query"] --> ast["AST Parser"]
ast -->|"SELECT only"| regex["Regex Patterns"]
ast -->|"DDL/DML/DCL"| reject1[["REJECTED"]]
regex -->|"Safe"| limit["Result Limiter"]
regex -->|"Dangerous patterns"| reject2[["REJECTED"]]
limit --> db[("DB2 for i")]
```
### AST-based Validation
Queries are parsed into an Abstract Syntax Tree (AST) to verify:
- Only SELECT statements are allowed
- No DDL (CREATE, ALTER, DROP)
- No DML (INSERT, UPDATE, DELETE)
- No DCL (GRANT, REVOKE)
### Regex Validation
Additional regex patterns block:
- Command execution attempts
- System procedure calls
- Dangerous functions
### Result Limiting
Query results are automatically limited to prevent memory exhaustion:
| Variable | Default | Description |
|----------|---------|-------------|
| `QUERY_DEFAULT_LIMIT` | `1000` | Applied when no limit specified |
| `QUERY_MAX_LIMIT` | `10000` | Maximum allowed (caps user limits) |
## HTTP Transport Security
When using HTTP transport, additional security measures apply:
### Token-based Authentication
- Credentials must be provided per-request to `/auth`
- Environment variable credentials are **not** used for HTTP
- Tokens expire after 1 hour by default (`MCP_TOKEN_EXPIRY`)
### Auth Endpoint Rate Limiting
The `/auth` endpoint has additional rate limiting to prevent brute-force attacks:
| Setting | Value | Description |
|---------|-------|-------------|
| Max attempts | 5 | Maximum failed attempts before lockout |
| Window | 60 seconds | Time window for tracking attempts |
**Behavior:**
- Failed authentication attempts are tracked per IP address
- After 5 failed attempts within 60 seconds, the IP is temporarily locked out
- Successful authentication clears the failure count for that IP
- Lockout automatically expires after the window period
> **Note:** These values are currently hardcoded. Environment variable configuration may be added in a future release.
### TLS/HTTPS
For production HTTP deployments:
```bash
# Enable built-in TLS
MCP_TLS_ENABLED=true
MCP_TLS_CERT_PATH=/path/to/cert.pem
MCP_TLS_KEY_PATH=/path/to/key.pem
```
Or run behind a reverse proxy (nginx, Caddy, cloud load balancer) that handles TLS termination.
### Session Limits
Control concurrent sessions to prevent resource exhaustion:
```bash
MCP_MAX_SESSIONS=100 # Maximum concurrent sessions
```
## Logging Security
The structured logger automatically redacts sensitive fields:
- Passwords are never logged
- Connection strings are sanitized
- Query parameters with sensitive names are masked
### Log Levels
| Level | When to Use |
|-------|-------------|
| `error` | Production (errors only) |
| `warn` | Production (errors + warnings) |
| `info` | Default (normal operations) |
| `debug` | Development/troubleshooting |
In production, use JSON logging for better parsing:
```bash
NODE_ENV=production
LOG_LEVEL=info
```
## Security Checklist
### Production Deployment
- [ ] Use Docker secrets or external secret management
- [ ] Enable TLS for HTTP transport
- [ ] Set appropriate rate limits
- [ ] Configure query limits
- [ ] Use `info` or higher log level
- [ ] Run as non-root user (Docker image does this by default)
- [ ] Restrict network access to IBM i system
- [ ] Monitor logs for suspicious activity
### Development
- [ ] Use `.env` file (add to `.gitignore`)
- [ ] Enable debug logging if needed
- [ ] Test with production-like rate limits
- [ ] Verify query validation works as expected
## Reporting Security Issues
If you discover a security vulnerability, please report it responsibly:
1. **Do not** open a public GitHub issue
2. Use GitHub's [private vulnerability reporting](https://github.com/Strom-Capital/mcp-server-db2i/security/advisories/new) to submit your report
3. Include steps to reproduce the issue
4. Allow time for a fix before public disclosure