This guide covers security best practices for deploying crawlkit in production.
# Generate a strong JWT secret (256-bit minimum)
export JWT_SECRET=$(openssl rand -hex 32)
# Use environment variable, never hardcode
crawlkit-api --port 8080- Minimum 12 characters
- Require uppercase, lowercase, numbers, symbols
- Use argon2id for hashing (default)
- Implement account lockout after 5 failed attempts
# Create API key with limited permissions
curl -X POST http://localhost:4000/api/v1/keys \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{"name": "ci-runner", "requests_per_minute": 60}'
# Rotate keys periodically
curl -X DELETE http://localhost:4000/api/v1/keys/<key-id> \
-H "Authorization: Bearer <jwt>"# Generate encryption key (256-bit for AES-256-GCM)
export CRAWLKIT_ENCRYPTION_KEY=$(openssl rand -hex 32)
# Crawl with encryption
crawlkit crawl https://example.com --encrypt- Store keys in environment variables or secret managers
- Never commit keys to version control
- Rotate keys periodically
- Use different keys per environment (dev/staging/prod)
# Always use HTTPS for API endpoints
crawlkit-api --port 443 --tls-cert cert.pem --tls-key key.pem// Restrict CORS to known origins
let cors = CorsLayer::new()
.allow_origin(["https://crawlkit.example.com".parse().unwrap()])
.allow_methods([Method::GET, Method::POST])
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE]);# Configure per-IP rate limiting
[api]
rate_limit_per_minute = 60
rate_limit_burst = 10// Validate URLs before crawling
let url = Url::parse(&input)
.map_err(|_| "Invalid URL")?;
// Check against allowlist
if !is_allowed_domain(&url, &allowed_domains) {
return Err("Domain not allowed");
}// Sanitize HTML before analysis
let sanitized = ammonia::clean(&html);[features]
audit_trail = true[audit]
retention_days = 90
export_format = "json"# Run security audit
cargo audit
# Check for known vulnerabilities
cargo deny check# Update dependencies
cargo update
# Check for security advisories
cargo audit# Use non-root user
RUN adduser --disabled-password --gecos "" appuser
USER appuser
# Read-only filesystem
RUN chmod -R 555 /app
# No new privileges
--security-opt=no-new-privilegessecurityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL# Monitor failed authentication attempts
curl http://localhost:4000/metrics | grep auth_failures
# Monitor rate limit hits
curl http://localhost:4000/metrics | grep rate_limit_hits# Alert on security events
- alert: HighAuthFailures
expr: rate(auth_failures_total[5m]) > 10
for: 5m
labels:
severity: critical- Enable audit logging
- Implement role-based access control
- Encrypt data at rest and in transit
- Regular security assessments
- Document security policies
- Implement access controls
- Monitor and log access
- Regular vulnerability assessments
- Implement data minimization
- Provide data subject rights
- Document data processing
- Appoint data protection officer
- Containment: Isolate affected systems
- Eradication: Remove threat
- Recovery: Restore from backups
- Lessons Learned: Document and improve