Add core infrastructure: config, Binance adapter, docs, and auto-setup
- Add Pydantic settings with trading mode validation (paper/testnet/live) - Implement Binance USDⓈ-M Futures adapter with hedge mode, isolated margin - Add type definitions for orders, positions, and market data - Create documentation (PLAN.md, ARCHITECTURE.md, SECURITY.md) - Add setup.sh with uv/pip auto-detection for consistent dev environments - Configure Docker multi-stage build and docker-compose services - Add pyproject.toml with all dependencies and tool configs
This commit is contained in:
270
docs/SECURITY.md
Normal file
270
docs/SECURITY.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# TradeFinder Security Guidelines
|
||||
|
||||
## API Key Management
|
||||
|
||||
### Key Permissions
|
||||
|
||||
**Binance Futures API Keys**:
|
||||
| Permission | Required | Notes |
|
||||
|------------|----------|-------|
|
||||
| Enable Futures | Yes | Required for USDⓈ-M trading |
|
||||
| Enable Reading | Yes | Account info, positions, orders |
|
||||
| Enable Spot & Margin Trading | No | Not needed for futures |
|
||||
| Enable Withdrawals | **NEVER** | Do not enable under any circumstances |
|
||||
| Enable Internal Transfer | No | Not needed |
|
||||
|
||||
### Key Separation
|
||||
|
||||
Maintain separate API keys for:
|
||||
1. **Testnet** (paper trading): Lower security concern, can be more permissive
|
||||
2. **Production** (live trading): Maximum security, IP whitelist required
|
||||
|
||||
```bash
|
||||
# .env structure
|
||||
BINANCE_TESTNET_API_KEY=xxx # Testnet keys
|
||||
BINANCE_TESTNET_SECRET=xxx
|
||||
|
||||
BINANCE_API_KEY=xxx # Production keys (empty until ready)
|
||||
BINANCE_SECRET=xxx
|
||||
```
|
||||
|
||||
### IP Whitelisting
|
||||
|
||||
For production keys:
|
||||
1. Enable IP restriction in Binance API settings
|
||||
2. Whitelist only the server IP running the bot
|
||||
3. If using Docker, whitelist the host machine's public IP
|
||||
|
||||
---
|
||||
|
||||
## Secrets Storage
|
||||
|
||||
### Environment Variables (Required)
|
||||
|
||||
All secrets must be stored in environment variables:
|
||||
|
||||
```bash
|
||||
# Local development
|
||||
cp .env.example .env
|
||||
chmod 600 .env # Restrict file permissions
|
||||
|
||||
# Docker deployment
|
||||
docker run --env-file .env tradefinder
|
||||
```
|
||||
|
||||
### What NOT to Do
|
||||
|
||||
| Violation | Risk |
|
||||
|-----------|------|
|
||||
| Commit `.env` to git | Keys exposed in repo history forever |
|
||||
| Hardcode keys in source | Keys exposed to anyone with code access |
|
||||
| Log API keys | Keys visible in log files |
|
||||
| Share testnet keys | Still bad practice, builds bad habits |
|
||||
|
||||
### .gitignore
|
||||
|
||||
Ensure these patterns are in `.gitignore`:
|
||||
```gitignore
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.pem
|
||||
*.key
|
||||
secrets/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging Security
|
||||
|
||||
### Sensitive Data Handling
|
||||
|
||||
```python
|
||||
# BAD - Never log secrets
|
||||
logger.info(f"API Key: {api_key}")
|
||||
|
||||
# GOOD - Mask sensitive data
|
||||
logger.info(f"API Key: {api_key[:4]}...{api_key[-4:]}")
|
||||
|
||||
# BEST - Don't log at all
|
||||
logger.info("API credentials loaded successfully")
|
||||
```
|
||||
|
||||
### Structlog Configuration
|
||||
|
||||
```python
|
||||
# Filter sensitive fields from logs
|
||||
def mask_sensitive(_, __, event_dict):
|
||||
sensitive_keys = ['api_key', 'secret', 'password', 'token']
|
||||
for key in sensitive_keys:
|
||||
if key in event_dict:
|
||||
event_dict[key] = "***MASKED***"
|
||||
return event_dict
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Security
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Environment | Base URL | Notes |
|
||||
|-------------|----------|-------|
|
||||
| Testnet | `https://testnet.binancefuture.com` | Safe for testing |
|
||||
| Production | `https://fapi.binance.com` | Real money |
|
||||
|
||||
### Request Signing
|
||||
|
||||
All authenticated requests must be signed:
|
||||
```python
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
def sign_request(params: dict, secret: str) -> str:
|
||||
query_string = urlencode(params)
|
||||
signature = hmac.new(
|
||||
secret.encode('utf-8'),
|
||||
query_string.encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
return signature
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Respect Binance rate limits to avoid IP bans:
|
||||
| Limit Type | Value |
|
||||
|------------|-------|
|
||||
| Request weight | 2400/minute |
|
||||
| Order rate | 300/minute |
|
||||
| WebSocket connections | 5/second |
|
||||
|
||||
---
|
||||
|
||||
## Docker Security
|
||||
|
||||
### Container Isolation
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml security settings
|
||||
services:
|
||||
engine:
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
user: "1000:1000" # Non-root user
|
||||
```
|
||||
|
||||
### Secrets in Docker
|
||||
|
||||
```yaml
|
||||
# Use Docker secrets (production)
|
||||
secrets:
|
||||
binance_api_key:
|
||||
file: ./secrets/binance_api_key.txt
|
||||
binance_secret:
|
||||
file: ./secrets/binance_secret.txt
|
||||
|
||||
services:
|
||||
engine:
|
||||
secrets:
|
||||
- binance_api_key
|
||||
- binance_secret
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operational Security
|
||||
|
||||
### Pre-Deployment Checklist
|
||||
|
||||
- [ ] API keys have NO withdrawal permission
|
||||
- [ ] Production keys are IP-whitelisted
|
||||
- [ ] `.env` is not in git history
|
||||
- [ ] Logs do not contain secrets
|
||||
- [ ] Container runs as non-root
|
||||
- [ ] Redis is not exposed to public network
|
||||
- [ ] DuckDB file has restricted permissions
|
||||
|
||||
### Monitoring for Compromise
|
||||
|
||||
Watch for:
|
||||
- Unexpected positions or orders
|
||||
- API calls from unknown IPs (check Binance API logs)
|
||||
- Unusual account balance changes
|
||||
- Failed authentication attempts
|
||||
|
||||
### Incident Response
|
||||
|
||||
If keys are compromised:
|
||||
1. **Immediately** delete API key in Binance console
|
||||
2. Check for unauthorized trades/withdrawals
|
||||
3. Generate new API key with fresh permissions
|
||||
4. Review how compromise occurred
|
||||
5. Update security practices
|
||||
|
||||
---
|
||||
|
||||
## Testing Security
|
||||
|
||||
### Testnet Isolation
|
||||
|
||||
- Always start with testnet credentials
|
||||
- Verify `TRADING_MODE=testnet` before any trade logic
|
||||
- Production code should fail-safe if mode is unclear
|
||||
|
||||
```python
|
||||
def validate_mode(config: Config) -> None:
|
||||
if config.trading_mode == TradingMode.LIVE:
|
||||
if not config.binance_api_key:
|
||||
raise ValueError("Production API key required for live trading")
|
||||
if config.binance_testnet_api_key:
|
||||
logger.warning("Testnet keys present in live mode - ignoring")
|
||||
```
|
||||
|
||||
### Paper Trading
|
||||
|
||||
For in-house simulation (no API needed):
|
||||
- Use simulated order fills
|
||||
- No actual API calls
|
||||
- Safe for strategy development
|
||||
|
||||
---
|
||||
|
||||
## Dependency Security
|
||||
|
||||
### Regular Updates
|
||||
|
||||
```bash
|
||||
# Check for security vulnerabilities
|
||||
pip audit
|
||||
|
||||
# Update dependencies
|
||||
pip install --upgrade -r requirements.txt
|
||||
```
|
||||
|
||||
### Pinned Versions
|
||||
|
||||
Use exact versions in production:
|
||||
```toml
|
||||
# pyproject.toml
|
||||
dependencies = [
|
||||
"ccxt>=4.2.0,<5.0.0", # Pin major version
|
||||
"pydantic>=2.5.0,<3.0.0",
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Requirement |
|
||||
|----------|-------------|
|
||||
| API Keys | No withdrawal permission, IP whitelisted |
|
||||
| Secrets | Environment variables only, never committed |
|
||||
| Logging | Mask all sensitive data |
|
||||
| Network | HTTPS only, signed requests |
|
||||
| Container | Non-root, read-only where possible |
|
||||
| Monitoring | Watch for unauthorized activity |
|
||||
Reference in New Issue
Block a user