## New Security Documentation Structure
/security/
├── README.md # Security overview & contacts
├── forensics-checklist.md # Incident investigation guide
├── persistence-scan.sh # Quick persistence detector
├── runtime-detector.sh # Mining/suspicious process detector
└── hardening/
├── docker.md # Docker security baseline
├── kubernetes.md # K8s policies (future reference)
└── cloud.md # Hetzner-specific hardening
## Key Components
### Forensics Checklist
- Process analysis commands
- Persistence mechanism detection
- Network connection analysis
- File system inspection
- Authentication audit
- Decision matrix for threat response
### Scripts
- persistence-scan.sh: Cron, systemd, executables, SSH keys
- runtime-detector.sh: Mining process detection with --kill option
### Hardening Guides
- Docker: Secure compose template, Dockerfile best practices
- Kubernetes: NetworkPolicy, PodSecurityStandard, Falco rules
- Cloud: Egress firewall, SSH hardening, fail2ban, monitoring
## Post-Incident Documentation
Based on lessons learned from Incidents #1 and #2 (Jan 2026)
Co-authored-by: Cursor Agent <agent@cursor.sh>
311 lines
6.9 KiB
Markdown
311 lines
6.9 KiB
Markdown
# ☁️ Cloud Security Hardening — Hetzner
|
||
|
||
**Версія:** 1.0.0
|
||
**Provider:** Hetzner Dedicated (GEX44)
|
||
**Server:** NODE1 (144.76.224.179)
|
||
|
||
---
|
||
|
||
## 🎯 Критичні налаштування
|
||
|
||
### 1. Egress Firewall (№1 Priority)
|
||
|
||
**Блокування внутрішніх мереж Hetzner:**
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# /root/firewall-egress.sh
|
||
|
||
# Block Hetzner internal networks (prevent scanning)
|
||
iptables -I OUTPUT -d 10.0.0.0/8 -j DROP
|
||
iptables -I OUTPUT -d 172.16.0.0/12 -j DROP
|
||
iptables -I OUTPUT -d 192.168.0.0/16 -j DROP
|
||
|
||
# Allow necessary internal traffic
|
||
iptables -I OUTPUT -d 10.0.0.0/8 -p tcp --dport 443 -j ACCEPT
|
||
iptables -I OUTPUT -d 10.0.0.0/8 -p tcp --dport 80 -j ACCEPT
|
||
|
||
# Log blocked attempts
|
||
iptables -I OUTPUT -d 10.0.0.0/8 -j LOG --log-prefix "BLOCKED_INTERNAL: "
|
||
|
||
# Block known mining pool ports
|
||
MINING_PORTS="3333 5555 7777 14433 45700 45560 14444 9999"
|
||
for port in $MINING_PORTS; do
|
||
iptables -A OUTPUT -p tcp --dport $port -j DROP
|
||
iptables -A OUTPUT -p tcp --dport $port -j LOG --log-prefix "BLOCKED_MINING: "
|
||
done
|
||
|
||
# Save rules
|
||
iptables-save > /etc/iptables/rules.v4
|
||
```
|
||
|
||
### 2. SSH Hardening
|
||
|
||
```bash
|
||
# /etc/ssh/sshd_config
|
||
|
||
# Disable root login with password
|
||
PermitRootLogin prohibit-password
|
||
|
||
# Use only SSH keys
|
||
PasswordAuthentication no
|
||
PubkeyAuthentication yes
|
||
|
||
# Limit authentication attempts
|
||
MaxAuthTries 3
|
||
MaxSessions 5
|
||
|
||
# Idle timeout
|
||
ClientAliveInterval 300
|
||
ClientAliveCountMax 2
|
||
|
||
# Disable unused features
|
||
X11Forwarding no
|
||
AllowAgentForwarding no
|
||
AllowTcpForwarding no
|
||
|
||
# Allow only specific users
|
||
AllowUsers root
|
||
|
||
# Use strong ciphers
|
||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
|
||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||
```
|
||
|
||
### 3. Fail2ban Configuration
|
||
|
||
```ini
|
||
# /etc/fail2ban/jail.local
|
||
|
||
[DEFAULT]
|
||
bantime = 3600
|
||
findtime = 600
|
||
maxretry = 3
|
||
|
||
[sshd]
|
||
enabled = true
|
||
port = ssh
|
||
filter = sshd
|
||
logpath = /var/log/auth.log
|
||
maxretry = 3
|
||
bantime = 86400
|
||
|
||
[docker-abuse]
|
||
enabled = true
|
||
filter = docker-abuse
|
||
logpath = /var/log/syslog
|
||
maxretry = 5
|
||
bantime = 3600
|
||
```
|
||
|
||
---
|
||
|
||
## 🔒 Network Security
|
||
|
||
### UFW Configuration
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# /root/setup-ufw.sh
|
||
|
||
# Reset UFW
|
||
ufw --force reset
|
||
|
||
# Default policies
|
||
ufw default deny incoming
|
||
ufw default deny outgoing
|
||
|
||
# Allow SSH (from specific IPs if possible)
|
||
ufw allow in 22/tcp
|
||
|
||
# Allow HTTP/HTTPS
|
||
ufw allow in 80/tcp
|
||
ufw allow in 443/tcp
|
||
|
||
# Allow outbound DNS
|
||
ufw allow out 53/tcp
|
||
ufw allow out 53/udp
|
||
|
||
# Allow outbound HTTP/HTTPS
|
||
ufw allow out 80/tcp
|
||
ufw allow out 443/tcp
|
||
|
||
# Allow outbound to Docker registry
|
||
ufw allow out to any port 443 proto tcp
|
||
|
||
# Block mining pools (additional layer)
|
||
# Add rules from firewall-egress.sh
|
||
|
||
# Enable UFW
|
||
ufw --force enable
|
||
```
|
||
|
||
### Port Exposure Rules
|
||
|
||
| Port | Service | Binding | Notes |
|
||
|------|---------|---------|-------|
|
||
| 22 | SSH | 0.0.0.0 | With fail2ban |
|
||
| 80 | Nginx | 0.0.0.0 | Redirect to 443 |
|
||
| 443 | Nginx | 0.0.0.0 | HTTPS only |
|
||
| 3000 | daarion-web | 127.0.0.1 | Via Nginx |
|
||
| 9102 | Router | 127.0.0.1 | Internal |
|
||
| 9300 | Gateway | 127.0.0.1 | Via Nginx |
|
||
| * | Other services | 127.0.0.1 | Never public |
|
||
|
||
---
|
||
|
||
## 📊 Monitoring
|
||
|
||
### System Monitoring Script
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# /root/security-monitor.sh
|
||
# Run via cron every 5 minutes
|
||
|
||
LOG_FILE="/var/log/security-monitor.log"
|
||
ALERT_THRESHOLD_CPU=80
|
||
ALERT_THRESHOLD_LOAD=10
|
||
|
||
timestamp() {
|
||
date '+%Y-%m-%d %H:%M:%S'
|
||
}
|
||
|
||
# Check load average
|
||
LOAD=$(cat /proc/loadavg | awk '{print $1}')
|
||
LOAD_INT=${LOAD%.*}
|
||
|
||
if [ "$LOAD_INT" -gt "$ALERT_THRESHOLD_LOAD" ]; then
|
||
echo "[$(timestamp)] ALERT: High load average: $LOAD" >> $LOG_FILE
|
||
# Send alert (implement your notification)
|
||
fi
|
||
|
||
# Check for mining processes
|
||
MINING=$(ps aux | grep -iE "xmrig|catcal|softirq|vrarhpb|miner" | grep -v grep)
|
||
if [ -n "$MINING" ]; then
|
||
echo "[$(timestamp)] CRITICAL: Mining process detected!" >> $LOG_FILE
|
||
echo "$MINING" >> $LOG_FILE
|
||
# Kill the process
|
||
echo "$MINING" | awk '{print $2}' | xargs -r kill -9
|
||
fi
|
||
|
||
# Check outbound connections to mining ports
|
||
MINING_CONN=$(ss -antp | grep -E ":(3333|5555|7777|14433)")
|
||
if [ -n "$MINING_CONN" ]; then
|
||
echo "[$(timestamp)] CRITICAL: Mining pool connection!" >> $LOG_FILE
|
||
echo "$MINING_CONN" >> $LOG_FILE
|
||
fi
|
||
|
||
# Check Docker containers CPU
|
||
docker stats --no-stream --format "{{.Name}}: {{.CPUPerc}}" | while read line; do
|
||
CPU=$(echo "$line" | grep -oE '[0-9]+' | head -1)
|
||
if [ -n "$CPU" ] && [ "$CPU" -gt "$ALERT_THRESHOLD_CPU" ]; then
|
||
echo "[$(timestamp)] WARNING: High CPU container: $line" >> $LOG_FILE
|
||
fi
|
||
done
|
||
```
|
||
|
||
### Cron Setup
|
||
|
||
```bash
|
||
# /etc/cron.d/security-monitor
|
||
|
||
# Run security monitor every 5 minutes
|
||
*/5 * * * * root /root/security-monitor.sh
|
||
|
||
# Run persistence scan daily
|
||
0 3 * * * root /opt/microdao-daarion/security/persistence-scan.sh >> /var/log/persistence-scan.log 2>&1
|
||
|
||
# Log rotation
|
||
0 0 * * * root find /var/log -name "*.log" -mtime +30 -delete
|
||
```
|
||
|
||
---
|
||
|
||
## 🚨 Incident Response (Hetzner)
|
||
|
||
### При отриманні Abuse Report:
|
||
|
||
1. **Негайно:**
|
||
```bash
|
||
# Заблокувати весь вихідний трафік
|
||
iptables -I OUTPUT -j DROP
|
||
|
||
# Зберегти стан для аналізу
|
||
ps auxf > /root/incident/ps_$(date +%s).txt
|
||
ss -antp > /root/incident/ss_$(date +%s).txt
|
||
docker ps -a > /root/incident/docker_$(date +%s).txt
|
||
```
|
||
|
||
2. **Знайти джерело:**
|
||
```bash
|
||
# Запустити forensics
|
||
/opt/microdao-daarion/security/persistence-scan.sh
|
||
/opt/microdao-daarion/security/runtime-detector.sh
|
||
```
|
||
|
||
3. **Усунути загрозу:**
|
||
```bash
|
||
# Kill процеси
|
||
killall -9 <process_name>
|
||
|
||
# Видалити контейнер ТА образ
|
||
docker stop <container>
|
||
docker rm <container>
|
||
docker rmi <image> # КРИТИЧНО!
|
||
```
|
||
|
||
4. **Відповісти Hetzner:**
|
||
- URL: https://statement-abuse.hetzner.com/statements/?token=<token>
|
||
- Описати причину та вжиті заходи
|
||
- Зареєструвати retry test
|
||
|
||
5. **Задокументувати:**
|
||
- Оновити INFRASTRUCTURE.md
|
||
- Створити incident report
|
||
|
||
---
|
||
|
||
## 🔐 Secrets Management
|
||
|
||
### Environment Variables
|
||
|
||
```bash
|
||
# /opt/microdao-daarion/.env
|
||
|
||
# NEVER commit to git!
|
||
# Use .env.example as template
|
||
|
||
# Generate secure passwords
|
||
openssl rand -base64 32
|
||
|
||
# Store secrets securely
|
||
chmod 600 .env
|
||
chown root:root .env
|
||
```
|
||
|
||
### SSH Keys
|
||
|
||
```bash
|
||
# Generate strong SSH key
|
||
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519
|
||
|
||
# Rotate keys periodically
|
||
# Keep backup of old keys until rotation complete
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Hetzner Security Checklist
|
||
|
||
- [ ] Egress firewall blocking internal networks
|
||
- [ ] Mining pool ports blocked
|
||
- [ ] SSH hardened (key-only, fail2ban)
|
||
- [ ] UFW configured (deny by default)
|
||
- [ ] All services bound to 127.0.0.1 except Nginx
|
||
- [ ] Security monitoring cron active
|
||
- [ ] Log rotation configured
|
||
- [ ] .env file secured (chmod 600)
|
||
- [ ] Rescue mode access documented
|
||
- [ ] Hetzner Robot access secured
|