279 lines
6.3 KiB
Markdown
279 lines
6.3 KiB
Markdown
# Security Hardening Guide
|
|
|
|
## Overview
|
|
|
|
This stack implements defense in depth with multiple security layers:
|
|
|
|
1. **Network**: Cloudflare → Traefik → Service
|
|
2. **Authentication**: Authelia (SSO + 2FA)
|
|
3. **Authorization**: Access control rules
|
|
4. **Detection**: CrowdSec (IPS/IDS)
|
|
5. **Encryption**: TLS 1.3, strong ciphers
|
|
6. **Monitoring**: Logs, metrics, alerts
|
|
|
|
## Pre-Deployment Checklist
|
|
|
|
### 1. Server Hardening
|
|
|
|
```bash
|
|
# Update system
|
|
sudo apt update && sudo apt upgrade -y
|
|
|
|
# Install fail2ban
|
|
sudo apt install fail2ban
|
|
|
|
# Configure fail2ban for SSH
|
|
cat <<EOF | sudo tee /etc/fail2ban/jail.local
|
|
[DEFAULT]
|
|
bantime = 1h
|
|
findtime = 10m
|
|
maxretry = 3
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = ssh
|
|
filter = sshd
|
|
logpath = /var/log/auth.log
|
|
maxretry = 3
|
|
EOF
|
|
|
|
sudo systemctl restart fail2ban
|
|
|
|
# Disable password SSH (use keys only)
|
|
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
|
|
sudo systemctl restart sshd
|
|
|
|
# Configure firewall (UFW)
|
|
sudo ufw default deny incoming
|
|
sudo ufw default allow outgoing
|
|
sudo ufw allow from 100.64.0.0/10 to any port 22 # Tailscale SSH only
|
|
sudo ufw allow 80/tcp
|
|
sudo ufw allow 443/tcp
|
|
sudo ufw enable
|
|
```
|
|
|
|
### 2. Docker Security
|
|
|
|
```bash
|
|
# Install Docker rootless (optional but recommended)
|
|
dockerd-rootless-setuptool.sh install
|
|
|
|
# Or use userns-remap
|
|
echo '{"userns-remap": "default"}' | sudo tee /etc/docker/daemon.json
|
|
sudo systemctl restart docker
|
|
|
|
# Enable Docker Content Trust
|
|
export DOCKER_CONTENT_TRUST=1
|
|
```
|
|
|
|
### 3. Secret Generation
|
|
|
|
```bash
|
|
# Generate all secrets
|
|
cd LemonSec
|
|
|
|
# JWT Secret (32+ chars)
|
|
openssl rand -hex 32 > secrets/authelia_jwt_secret.txt
|
|
|
|
# Session Secret (32+ chars)
|
|
openssl rand -hex 32 > secrets/authelia_session_secret.txt
|
|
|
|
# Storage Encryption Key (32+ chars)
|
|
openssl rand -hex 32 > secrets/authelia_storage_key.txt
|
|
|
|
# Authelia user password
|
|
docker run --rm authelia/authelia:latest \
|
|
authelia crypto hash generate argon2 \
|
|
--password 'YourStrongPassword123!'
|
|
```
|
|
|
|
### 4. Cloudflare Security
|
|
|
|
- [ ] SSL/TLS mode: Full (strict)
|
|
- [ ] Always Use HTTPS: ON
|
|
- [ ] Security Level: High
|
|
- [ ] Browser Integrity Check: ON
|
|
- [ ] Challenge Passage: 30 minutes
|
|
- [ ] Minimum TLS Version: 1.2
|
|
|
|
## Post-Deployment Security
|
|
|
|
### 1. Verify CrowdSec is Working
|
|
|
|
```bash
|
|
# Check CrowdSec is detecting attacks
|
|
docker-compose exec crowdsec cscli metrics
|
|
|
|
# View active decisions (bans)
|
|
docker-compose exec crowdsec cscli decisions list
|
|
|
|
# Simulate an attack to test
|
|
curl -I http://your-ip/.env
|
|
docker-compose exec crowdsec cscli decisions list
|
|
# Should show your IP banned
|
|
```
|
|
|
|
### 2. Configure Authelia Notifications
|
|
|
|
Edit `authelia/configuration.yml`:
|
|
|
|
```yaml
|
|
notifier:
|
|
smtp:
|
|
address: smtp.gmail.com:587
|
|
username: your-email@gmail.com
|
|
password: ${SMTP_PASSWORD}
|
|
sender: "Authelia <security@lemonlink.eu>"
|
|
```
|
|
|
|
Test: Try logging in with wrong password 3 times → should get email.
|
|
|
|
### 3. Enable CrowdSec Notifications
|
|
|
|
```bash
|
|
# Install Discord/Slack plugin
|
|
docker-compose exec crowdsec cscli notifications add slack
|
|
|
|
# Configure in CrowdSec container
|
|
docker-compose exec crowdsec sh -c "cat > /etc/crowdsec/notifications/slack.yaml" << 'EOF'
|
|
type: slack
|
|
name: slack_default
|
|
log_level: info
|
|
format: |
|
|
{{range . -}}
|
|
{{$alert := . -}}
|
|
{{range .Decisions -}}
|
|
:warning: *CrowdSec Alert*
|
|
*IP:* {{.Value}}
|
|
*Reason:* {{.Scenario}}
|
|
*Duration:* {{.Duration}}
|
|
{{end -}}
|
|
{{end -}}
|
|
webhook: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
|
|
EOF
|
|
```
|
|
|
|
### 4. Regular Security Tasks
|
|
|
|
**Weekly:**
|
|
```bash
|
|
# Check for new decisions
|
|
docker-compose exec crowdsec cscli decisions list
|
|
|
|
# Review failed logins
|
|
docker-compose logs authelia | grep "unsuccessful"
|
|
|
|
# Update images
|
|
docker-compose pull
|
|
docker-compose up -d
|
|
```
|
|
|
|
**Monthly:**
|
|
```bash
|
|
# Rotate secrets
|
|
./scripts/rotate-secrets.sh # You need to create this
|
|
|
|
# Review access logs
|
|
docker-compose logs traefik | grep -E "(401|403|429)"
|
|
|
|
# Backup
|
|
tar czf backup-$(date +%Y%m%d).tar.gz traefik/ authelia/ crowdsec/ .env
|
|
```
|
|
|
|
## Threat Model
|
|
|
|
### What This Stack Protects Against
|
|
|
|
| Threat | Mitigation |
|
|
|--------|-----------|
|
|
| Credential stuffing | Authelia rate limiting, fail2ban |
|
|
| Brute force attacks | CrowdSec detection, progressive delays |
|
|
| DDoS | Cloudflare, rate limiting |
|
|
| MITM | TLS 1.3, HSTS, certificate pinning |
|
|
| Session hijacking | Secure cookies, short expiration |
|
|
| XSS/CSRF | Security headers, SameSite cookies |
|
|
| SQL injection | Parameterized queries (app responsibility) |
|
|
|
|
### What It Doesn't Protect Against
|
|
|
|
- Zero-day vulnerabilities in applications
|
|
- Social engineering
|
|
- Compromised client devices
|
|
- Insider threats
|
|
- Application-level logic bugs
|
|
|
|
## Incident Response
|
|
|
|
### If You Suspect a Breach
|
|
|
|
1. **Isolate**
|
|
```bash
|
|
# Stop all services
|
|
docker-compose down
|
|
|
|
# Block all traffic temporarily
|
|
sudo ufw default deny incoming
|
|
```
|
|
|
|
2. **Investigate**
|
|
```bash
|
|
# Check logs
|
|
docker-compose logs > incident-$(date +%Y%m%d).log
|
|
|
|
# Check CrowdSec decisions
|
|
docker-compose exec crowdsec cscli decisions list
|
|
|
|
# Check for unknown containers
|
|
docker ps -a
|
|
```
|
|
|
|
3. **Recover**
|
|
```bash
|
|
# Rotate all secrets
|
|
# Change all passwords
|
|
# Re-deploy from clean backup
|
|
```
|
|
|
|
## Security Testing
|
|
|
|
### Automated Scans
|
|
|
|
```bash
|
|
# Test TLS configuration
|
|
docker run --rm drwetter/testssl.sh https://lemonlink.eu
|
|
|
|
# Test headers
|
|
curl -I https://lemonlink.eu | grep -E "(strict-transport-security|content-security-policy|x-frame-options)"
|
|
|
|
# Test rate limiting
|
|
for i in {1..20}; do curl -s -o /dev/null -w "%{http_code}" https://auth.lemonlink.eu/; done
|
|
```
|
|
|
|
### Manual Penetration Testing
|
|
|
|
1. Try accessing internal services from external IP
|
|
2. Attempt SQL injection in login forms
|
|
3. Test XSS payloads
|
|
4. Verify 2FA can't be bypassed
|
|
5. Check for information disclosure in headers
|
|
|
|
## Compliance Notes
|
|
|
|
This setup helps with:
|
|
- **GDPR**: Encryption, access logs, data retention
|
|
- **ISO 27001**: Defense in depth, monitoring
|
|
- **SOC 2**: Audit trails, access controls
|
|
|
|
But does NOT make you compliant by itself. You must:
|
|
- Document all data flows
|
|
- Implement data retention policies
|
|
- Conduct regular audits
|
|
- Train users on security
|
|
|
|
## Additional Resources
|
|
|
|
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
|
|
- [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/)
|
|
- [CIS Docker Benchmark](https://www.cisecurity.org/benchmark/docker)
|
|
- [Cloudflare Security Center](https://dash.cloudflare.com/?to=/:account/:zone/security)
|