chore: update

This commit is contained in:
2026-06-10 17:50:44 +02:00
parent 9b4e2c4811
commit 78bd98e72f
40 changed files with 3661 additions and 380 deletions
+9
View File
@@ -0,0 +1,9 @@
RUSTFS_ACCESS_KEY=iBacJPHUHSf6SMTnxrr
RUSTFS_SECRET_KEY=4jNzZhstv1WkwTwtvR4gHSbrx01m4sJl4LBBleeJ
XAVIA_ADMIN_PASSWORD=2VYfheOweyMiP510GGfjNYmq3vJGJ
XAVIA_UPLOAD_KEY=dNW2tiNXRR6w1cLwlmCOyKFIKvoTz8xQMSyd
XAVIA_PRIVATE_KEY_BASE_64=MlRvcGlyNHpFRjhYOTlvUjMxbHFCVHRQdjYyelh4Wnc0MDM4TkZaVQ==
XAVIA_POSTGRES_USER=xavia
XAVIA_POSTGRES_PASSWORD=6DzLcsb5rgWukuGdrHtRBQ222f
XAVIA_POSTGRES_DB=releases_db
+99
View File
@@ -0,0 +1,99 @@
services:
nginx:
image: nginx:alpine
container_name: s3_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
- nginx_logs:/var/log/nginx
depends_on:
- rustfs
- xavia
networks:
- s3_net
rustfs_perm:
image: alpine:latest
container_name: rustfs_perm
volumes:
- /mnt/data:/data
command: chown -R 10001:10001 /data
rustfs:
image: rustfs/rustfs:latest
depends_on:
rustfs_perm:
condition: service_completed_successfully
hostname: rustfs
container_name: rustfs
restart: unless-stopped
volumes:
- /mnt/data:/data
- /mnt/logs:/logs
environment:
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY:-iBacJPHUHSf6SMTnxrr}
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY:-4jNzZhstv1WkwTwtvR4gHSbrx01m4sJl4LBBleeJ}
- RUSTFS_SERVER_DOMAINS=rustfs.uber-stup.club
- RUSTFS_CONSOLE_ENABLE=true
- RUSTFS_ADDRESS=:9000
- RUSTFS_CONSOLE_ADDRESS=:9001
networks:
- s3_net
xavia_db:
image: postgres:16-alpine
container_name: xavia_db
restart: unless-stopped
environment:
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
volumes:
- xavia_db_data:/var/lib/postgresql/data
networks:
- s3_net
xavia:
image: xaviaio/xavia-ota:latest
container_name: xavia
restart: unless-stopped
environment:
- HOST=https://ota.uber-stup.club
- BLOB_STORAGE_TYPE=local
- DB_TYPE=postgres
- ADMIN_PASSWORD=${XAVIA_ADMIN_PASSWORD}
- UPLOAD_KEY=${XAVIA_UPLOAD_KEY}
- PRIVATE_KEY_BASE_64=${XAVIA_PRIVATE_KEY_BASE_64}
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
- POSTGRES_HOST=xavia_db
- POSTGRES_PORT=5432
volumes:
- xavia_blobs:/app/blobs
depends_on:
- xavia_db
networks:
- s3_net
dozzle-agent:
image: amir20/dozzle:latest
command: agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "7007:7007"
restart: unless-stopped
networks:
s3_net:
driver: bridge
volumes:
nginx_logs:
xavia_db_data:
xavia_blobs:
+98
View File
@@ -0,0 +1,98 @@
worker_processes auto;
events {
worker_connections 1024;
}
http {
resolver 127.0.0.11 valid=10s ipv6=off;
# Redirect HTTP -> HTTPS
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
# ── RustFS Console ──────────────────────────────────────
server {
listen 443 ssl;
server_name rustfs.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/rustfs_access.log;
error_log /var/log/nginx/rustfs_error.log;
location / {
set $upstream http://rustfs:9001;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
client_max_body_size 500m;
}
}
# ── RustFS S3 API ───────────────────────────────────────
server {
listen 443 ssl;
server_name s3.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/rustfs_s3_access.log;
error_log /var/log/nginx/rustfs_s3_error.log;
location / {
set $upstream http://rustfs:9000;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 2g;
}
}
# ── Xavia OTA ───────────────────────────────────────────
server {
listen 443 ssl;
server_name ota.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/xavia_access.log;
error_log /var/log/nginx/xavia_error.log;
location / {
set $upstream http://xavia:3000;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
client_max_body_size 500m;
}
}
}
+283
View File
@@ -0,0 +1,283 @@
# ClamAV Integration with Wazuh
Integration guide for deploying ClamAV antivirus with Wazuh monitoring stack.
## Overview
ClamAV scans files and containers for malware, while Wazuh collects and analyzes ClamAV logs for:
- Real-time malware detection alerts
- Ransomware detection
- Signature database update status
- Scan statistics and quarantine actions
---
## Architecture
```
┌─────────────────────────────────────┐
│ ClamAV Container (clamav) │
│ • Scans Docker containers/files │
│ • Generates logs: clamav.log │
│ • Freshclam updates signatures │
└──────────────┬──────────────────────┘
│ (logs volume)
┌─────────────────────────────────────┐
│ Wazuh Manager (wazuh.manager) │
│ • Collects ClamAV logs │
│ • Parses with decoders │
│ • Matches detection rules │
│ • Sends alerts to indexer │
└──────────────┬──────────────────────┘
│ (syslog format)
┌─────────────────────────────────────┐
│ Wazuh Dashboard (UI) │
│ • Display malware detections │
│ • Show scan results │
│ • Alert severity levels │
└─────────────────────────────────────┘
```
---
## Deployment
### 1. Add ClamAV to docker-compose
The following service has been added to `docker-compose-security.yml`:
```yaml
clamav:
image: clamav/clamav:stable
container_name: clamav
restart: unless-stopped
environment:
- FRESHCLAM_CHECKS=24
- CLAMD_SCAN_MAX_FILESIZE=100M
volumes:
- clamav_data:/var/lib/clamav # Signature database
- clamav_logs:/var/log/clamav # Log output
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- monitoring_net
```
### 2. Deploy ClamAV
```bash
cd /home/ubuntu/docker
docker compose -f docker-compose-security.yml up -d clamav
# Verify deployment
docker ps | grep clamav
docker logs clamav
```
### 3. Wazuh Configuration
#### Log Collection
File: `wazuh/config/wazuh_manager/localfile_clamav.conf`
Configures Wazuh to collect:
- `clamav.log` — Main scanner logs
- `freshclam.log` — Signature update logs
- `alert.log` — High-priority detections
#### Detection Rules
File: `wazuh/config/wazuh_manager/rules/clamav_rules.xml`
Key rules (level/priority):
| Rule ID | Level | Trigger | Example |
|---------|-------|---------|---------|
| 100501 | 15 | FOUND/Infected | Malware detected |
| 100502 | 12 | Trojan/PUA | Potentially unwanted app |
| 100503 | 15 | .Virus/Worm | Classic virus detection |
| 100504 | 15 | Ransomware | Ransomware families |
| 100505 | 3 | Update successful | Signature DB refreshed |
| 100506 | 10 | Update failed | Download/connection error |
| 100507 | 8 | Daemon error | ClamAV process error |
| 100515 | 16 | Multiple hits in 1h | Possible outbreak |
---
## Log Format
### ClamAV Log Example
```
Jun 10 14:35:22 clamav clamd[1234]: /var/data/suspect.exe: Trojan.Win32.Generic!c FOUND
Jun 10 14:35:25 clamav freshclam[5678]: ClamAV update completed. 1 databases updated.
Jun 10 14:36:01 clamav clamd[1234]: Scanning started
Jun 10 14:36:45 clamav clamd[1234]: Scanning finished. 2 files scanned, 0 threats found
```
### Wazuh Alert Example
```json
{
"timestamp": "2026-06-10T14:35:22.000Z",
"rule": {
"id": "100501",
"level": 15,
"description": "ClamAV: Malware detected"
},
"data": {
"srcfile": "/var/data/suspect.exe",
"alert": "Trojan.Win32.Generic!c FOUND"
},
"groups": ["malware", "clamav", "infection"]
}
```
---
## Operations
### Check ClamAV Status
```bash
# Container status
docker ps | grep clamav
# View logs
docker logs clamav
# Manual scan
docker exec clamav clamscan -r /var/data
```
### Update Signatures
ClamAV automatically updates signatures (configured via `FRESHCLAM_CHECKS=24`).
Manual update:
```bash
docker exec clamav freshclam
```
### View Wazuh Alerts
1. Access Wazuh Dashboard: `https://10.0.0.2`
2. Go to: **Security Events****Search**
3. Filter by:
- `rule.id: 100501` — Malware detections
- `rule.id: 100504` — Ransomware alerts
- `rule.groups: malware` — All malware-related events
### Query via API
```bash
# Get recent malware detections
curl -k -H "Authorization: Bearer YOUR_TOKEN" \
"https://monitoring-uber:55000/api/v0/search?q=rule.id:100501&pretty"
# Get ClamAV scan statistics
curl -k -H "Authorization: Bearer YOUR_TOKEN" \
"https://monitoring-uber:55000/api/v0/search?q=group:clamav&pretty"
```
---
## Tuning & Optimization
### Scan Performance
Adjust scan parameters in docker-compose environment:
```yaml
environment:
- CLAMD_SCAN_MAX_FILESIZE=100M # Max file size to scan
- CLAMD_MAX_SCAN_SIZE=200M # Max total scan size
- CLAMD_SCAN_PE_PLUS=yes # Enhanced PE detection
- CLAMD_SCAN_ARCHIVE=yes # Scan inside archives
- CLAMD_MAX_FILES=10000 # Max files to scan
```
### Update Frequency
Default: 24 checks per day (every hour)
Change via:
```yaml
environment:
- FRESHCLAM_CHECKS=48 # 2-hourly updates
```
### Alert Severity
Adjust rule levels in `clamav_rules.xml` based on your risk tolerance:
- Level 3-6: Info/Low
- Level 8-10: Medium
- Level 12-15: High
- Level 16+: Critical/Outbreak
---
## Troubleshooting
### ClamAV Not Scanning
```bash
# Check daemon is running
docker exec clamav ps aux | grep clamd
# Check signatures are loaded
docker exec clamav clamscan --version
# Manually scan
docker exec clamav clamscan /var/data
```
### Signature Updates Failing
```bash
# Check freshclam logs
docker logs clamav | grep freshclam
# Manual update with verbose output
docker exec clamav freshclam -v
# Check internet connectivity
docker exec clamav wget https://cvd.clamav.net
```
### Wazuh Not Receiving Logs
```bash
# Check logs are being generated
docker exec clamav tail -f /var/log/clamav/clamav.log
# Check Wazuh log collection
docker exec wazuh_manager tail -f /var/ossec/logs/ossec.log | grep clamav
# Verify rules loaded
docker exec wazuh_manager cat /var/ossec/etc/rules/clamav_rules.xml | head -10
```
---
## Files Modified/Created
| File | Purpose |
|------|---------|
| `docker-compose-security.yml` | Added ClamAV service + volumes |
| `wazuh/config/wazuh_manager/localfile_clamav.conf` | Log collection config |
| `wazuh/config/wazuh_manager/rules/clamav_rules.xml` | Malware detection rules |
| `CLAMAV_INTEGRATION.md` | This documentation |
---
## Next Steps
1. Deploy: `docker compose up -d clamav`
2. Wait for initial database download (5-10 minutes)
3. Monitor logs: `docker logs -f clamav`
4. Check Wazuh dashboard for alerts
5. Configure scan schedules/locations as needed
6. Set up active response (optional) for automatic quarantine
+165
View File
@@ -0,0 +1,165 @@
# Firewall Configuration Rules
Updated: 2026-06-10
## Overview
Firewall rules for monitoring stack with Wazuh, Dozzle, Beszel, and VPN infrastructure.
---
## monitoring-uber (185.103.167.138)
### Inbound Rules (INPUT)
| Port | Protocol | Source | Purpose | Status |
|------|----------|--------|---------|--------|
| 22 | TCP | Admin IPs | SSH administration | ALLOW |
| 22 | TCP | 185.103.166.119 (prod) | SSH — BLOCKED | **DROP** |
| 22 | TCP | 185.103.166.112 (pre-prod) | SSH — BLOCKED | **DROP** |
| 22 | TCP | 80.96.58.164 (s3) | SSH — BLOCKED | **DROP** |
| 1514 | TCP | 185.103.166.119 | Wazuh agents (prod) | ALLOW |
| 1514 | TCP | 185.103.166.112 | Wazuh agents (pre-prod) | ALLOW |
| 51820 | UDP | Any | WireGuard VPN | ALLOW |
| 443 | TCP | 10.0.0.0/24 | Wazuh Dashboard (VPN) | ALLOW |
| 8080 | TCP | 10.0.0.0/24 | Dozzle logs (VPN) | ALLOW |
| 8090 | TCP | 10.0.0.0/24 | Beszel monitoring (VPN) | ALLOW |
| 9000-9001 | TCP | 10.0.0.0/24 | S3/RustFS (VPN) | ALLOW |
### Outbound Rules (OUTPUT)
- **Default:** ACCEPT (all traffic allowed)
- Allows DNS, updates, internet access
### Forward Rules (FORWARD)
- **Default:** DROP (no transit traffic)
### Persistence
- Rules saved to `/etc/iptables/rules.v4`
- Auto-loaded on boot via `iptables-persistent`
---
## vpn-uber (45.150.111.158)
### Inbound Rules (INPUT)
| Port | Protocol | Source | Purpose | Status |
|------|----------|--------|---------|--------|
| 22 | TCP | Any | SSH administration | ALLOW |
| 51820 | UDP | Any | WireGuard VPN | ALLOW |
| 10001 | TCP | 10.0.0.0/24 | Beszel agent (from VPN only) | ALLOW |
### Outbound Rules (OUTPUT)
- **Default:** ACCEPT (all traffic allowed)
### Forward Rules (FORWARD)
- **Default:** ACCEPT
- Enables routing: wg0 ↔ eth0 (VPN ↔ Internet)
- ESTABLISHED/RELATED always allowed
### Network Features
- NAT masquerade: `eth0` (POSTROUTING)
- IP forwarding: enabled (`net.ipv4.ip_forward=1`)
- Allows VPN clients to reach internet through VPN server
### Persistence
- Rules saved to `/etc/iptables/rules.v4`
- IP forwarding saved to `/etc/sysctl.conf`
---
## Security Hardening
### Principle: Least Privilege
1. **SSH restrictions**: Production servers (prod, pre-prod, s3) **cannot** SSH into monitoring-uber
- Prevents lateral movement if a prod server is compromised
- Admins must SSH directly to monitoring-uber or via VPN
2. **Service isolation**: All user-facing services (Wazuh, Dozzle, Beszel) accessible **only via VPN**
- Not exposed to public internet
- Network: 10.0.0.0/24 (private VPN)
3. **Port whitelist**: Only required ports open
- Wazuh agent ingest: 1514/tcp (prod/pre-prod only)
- VPN: 51820/udp (all)
- SSH: 22/tcp (admin access, blocked from prod servers)
- Monitoring agents: 10001/tcp (VPN only)
### Attack Surface Reduction
- Production agents cannot access monitoring infrastructure
- Monitoring dashboard only accessible via VPN
- No exposed dashboards or logs to public internet
- SSH brute-force: limited by rate (ESTABLISHED/RELATED state tracking)
---
## Updating Rules
### monitoring-uber
```bash
ssh root@185.103.167.138
./firewall-monitoring-uber.sh
```
### vpn-uber
```bash
ssh root@45.150.111.158
./firewall-vpn-server.sh
```
### Verify Rules Applied
```bash
# Check current rules
iptables -L INPUT -n
iptables -L FORWARD -n
# Check saved rules
cat /etc/iptables/rules.v4
```
### Restore Rules on Boot
Rules are automatically restored via `/etc/iptables/rules.v4` if `iptables-persistent` is installed:
```bash
apt-get install iptables-persistent
```
---
## Beszel Agent Configuration
Beszel agents installed on all infrastructure servers:
| Server | Type | Port | Access |
|--------|------|------|--------|
| prod-uber | Docker | 10001 | Public IP (185.103.166.119:10001) |
| pre-prod-uber | Docker | 10001 | Public IP (185.103.166.112:10001) |
| s3-uber | Docker | 10001 | Public IP (80.96.58.164:10001) |
| vpn-uber | Binarie (systemd) | 10001 | VPN IP (10.0.0.1:10001) |
**Note:** vpn-uber agent is accessible **only from VPN** due to firewall rules.
---
## Troubleshooting
### Service not reachable
```bash
# Check if service is listening
ss -tlnp | grep <port>
# Check firewall allows traffic
iptables -L INPUT -n | grep <port>
# Test connectivity
nc -zv <ip> <port>
```
### SSH access denied from prod
- Expected behavior (security hardening)
- Use admin SSH keys or SSH via VPN instead
### Beszel agent shows offline
- Check agent is listening: `ss -tlnp | grep 10001`
- Check firewall allows Beszel dashboard to reach agent
- Verify network routing between monitoring-uber and agent server
@@ -0,0 +1,246 @@
# OWASP Top 10 ModSecurity Rules — Wazuh Active Response
## Vue d'ensemble
Ce système détecte et répond aux attaques OWASP Top 10 via ModSecurity + Wazuh.
**Deux couches de détection :**
| Type | Trigger | Règle | Level | AR |
|------|---------|-------|-------|-----|
| **Bloqué (403)** | HTTP 403 | 100220-100281 | 12-15 | Fermer 4h-24h |
| **Remontée (Warning)** | Anomaly scoring | 100320-100381 | 6-11 | Fermer 30min-6h |
---
## Correspondances CRS ModSecurity
```
941xxx — XSS (Cross-Site Scripting)
942xxx — SQL Injection, LDAP Injection, etc.
943xxx — XXE (XML External Entity)
930xxx — Path Traversal, RFI, RCE, LFI
932xxx — Remote Command Execution
933xxx — PHP Injection
934xxx — Java Injection
950xxx — Exploit attempts
951xxx — Regex DoS, Scanner detection
952xxx — Restricted File Access
953xxx — Insecure File Upload
954xxx — Proxy abuse, Scanner detection
955xxx — HTTP Response Splitting, Header Injection
970xxx — SSRF (Server-Side Request Forgery)
971xxx — SSRF (alternative patterns)
```
---
## Règles par OWASP Category
### OWASP #1 : Broken Access Control
**Détecte :** Path Traversal, File Inclusion, Unauthorized Access
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100250 | Bloqué | 930xx (path\|traversal\|lfi) | 12 | LFI/Path Traversal bloquée |
| 100251 | Bloqué | 930xx (rfi\|remote\|include) | 13 | RFI bloquée |
| 100252 | Bloqué | 952xx | 11 | File Access bloquée |
| 100293 | Bloqué | Freq(100250)×4/180s | 12 | Path Traversal brute-force |
| 100350 | Remontée | 930xx (path\|traversal\|lfi) | 8 | LFI attempt (warning) |
| 100351 | Remontée | 930xx (rfi\|remote\|include) | 9 | RFI attempt (warning) |
| 100352 | Remontée | 952xx | 8 | File Access attempt (warning) |
**Active Response :**
- Bloqué : `firewall-drop 6h`
- Brute-force : `firewall-drop 24h`
- Remontée : `firewall-drop 2h-4h`
---
### OWASP #3 : Injection
**Détecte :** SQLi, LDAP, Command Injection, PHP/Java Injection
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100220 | Bloqué | 942xx | 13 | SQL Injection bloquée |
| 100221 | Bloqué | 942xx (LDAP pattern) | 12 | LDAP Injection bloquée |
| 100222 | Bloqué | 932xx | 14 | Command Injection bloquée |
| 100223 | Bloqué | 933xx | 13 | PHP Injection bloquée |
| 100224 | Bloqué | 934xx | 13 | Java Injection bloquée |
| 100290 | Bloqué | Freq(100220)×3/60s | 14 | SQLi brute-force |
| 100292 | Bloqué | Freq(100222)×2/60s | 15 | Command Injection CRITICAL |
| 100320 | Remontée | 942xx | 9 | SQLi attempt (warning) |
| 100321 | Remontée | 932xx | 10 | Command Injection attempt |
| 100322 | Remontée | 942xx (LDAP) | 9 | LDAP Injection attempt |
| 100323 | Remontée | 933xx\|934xx | 9 | PHP/Java Injection attempt |
| 100394 | Remontée | Freq(100320)×5/300s | 10 | SQLi anomaly scoring |
| 100396 | Remontée | Freq(100321)×2/60s | 12 | Command Injection CRITICAL |
**Active Response :**
- SQLi Bloquée : `firewall-drop 6h`
- SQLi Brute-force : `firewall-drop 24h + host-deny permanent`
- Command Injection : `firewall-drop 24h + host-deny`
- Command Injection CRITICAL : `firewall-drop permanent + host-deny permanent`
- SQLi Remontée : `firewall-drop 2h`
- SQLi Anomaly : `firewall-drop 4h`
- Command Injection Remontée : `firewall-drop 4h`
---
### OWASP #6 : Vulnerable & Outdated Components
**Détecte :** RCE, Exploit attempts, known vulnerabilities
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100230 | Bloqué | 930xx\|950xx | 14 | RCE/Exploit bloquée |
| 100330 | Remontée | 930xx\|950xx | 11 | RCE/Exploit attempt (warning) |
**Active Response :**
- Bloqué : `firewall-drop 24h`
- Remontée : `firewall-drop 6h`
---
### OWASP #7 : Authentication & Session Management
**Détecte :** XSS, Header Injection, Session hijacking attempts
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100240 | Bloqué | 941xx | 12 | XSS bloquée |
| 100241 | Bloqué | 955xx | 12 | Header Injection bloquée |
| 100291 | Bloqué | Freq(100240)×5/120s | 13 | XSS anomaly scoring |
| 100340 | Remontée | 941xx | 8 | XSS attempt (warning) |
| 100341 | Remontée | 955xx | 8 | Header Injection attempt |
| 100395 | Remontée | Freq(100340)×8/300s | 10 | XSS anomaly scoring |
**Active Response :**
- XSS Bloquée : `firewall-drop 4h`
- XSS Anomaly : `firewall-drop 6h`
- Header Injection : `firewall-drop 6h`
- XSS Remontée : `firewall-drop 1h`
- Header Remontée : `firewall-drop 2h`
---
### OWASP #8 : Software & Data Integrity Failures
**Détecte :** XXE, Deserialization attacks
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100260 | Bloqué | 943xx | 13 | XXE bloquée |
| 100360 | Remontée | 943xx | 9 | XXE attempt (warning) |
**Active Response :**
- Bloqué : `firewall-drop 12h`
- Remontée : `firewall-drop 4h`
---
### OWASP #9 : Logging & Monitoring Failures
**Détecte :** Web scanners, bot activity, reconnaissance
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100270 | Bloqué | 951xx\|954xx | 10 | Scanner detection (bloquée) |
| 100370 | Remontée | 951xx\|954xx | 6 | Scanner detection (warning) |
**Active Response :**
- Bloqué : `firewall-drop 2h`
- Remontée : `firewall-drop 30min`
---
### Autres : SSRF & File Upload
| Rule ID | Type | Patterns | Level | Description |
|---------|------|----------|-------|-------------|
| 100280 | Bloqué | 970xx\|971xx | 13 | SSRF bloquée |
| 100281 | Bloqué | 953xx | 12 | File Upload bloquée |
| 100380 | Remontée | 970xx\|971xx | 9 | SSRF attempt |
| 100381 | Remontée | 953xx | 8 | File Upload attempt |
**Active Response :**
- SSRF Bloquée : `firewall-drop 12h`
- File Upload Bloquée : `firewall-drop 6h`
- SSRF Remontée : `firewall-drop 4h`
- File Upload Remontée : `firewall-drop 2h`
---
## Timeline des Active Responses
### Bloquées (HTTP 403)
| Durée | Règles | Motif |
|-------|--------|-------|
| Permanent | 100292 | Command Injection CRITICAL (2+ tentatives / 60s) |
| 24h | 100290 | SQLi brute-force (3+ / 60s) |
| 24h | 100230 | RCE/Exploit |
| 12h | 100251, 100260, 100280 | RFI, XXE, SSRF |
| 6h | 100220, 100221, 100250, 100252, 100281, 100330 | SQLi, LDAP, Path Traversal, File Access, Upload, RCE |
| 4h | 100240 | XSS |
| 2h | 100270 | Scanner |
### Remontées (Anomaly Scoring)
| Durée | Règles | Motif |
|-------|--------|-------|
| Permanent | 100396 | Command Injection (2+ / 60s) |
| 6h | 100395, 100330, 100351 | XSS anomaly, RCE, RFI |
| 4h | 100394, 100321, 100323, 100360, 100380 | SQLi anomaly, Cmd Inj, PHP/Java, XXE, SSRF |
| 2h | 100320, 100322, 100341, 100350, 100352, 100381 | SQLi, LDAP, Header Inj, Path Traversal, File Access, Upload |
| 30min | 100370 | Scanner |
---
## Files
- **`modsecurity_owasp_rules.xml`** — Définition des règles (100220-100396)
- **`ossec.conf`** — Active-response configs (firewall-drop, durées)
- **`firewall-monitoring-uber.sh`** — iptables pour autoriser trafic ModSec
---
## Monitoring
**Dashboard Wazuh :**
1. **Alerts → Security Events → OWASP**
2. **Visualization:**
- Blocking rate (HTTP 403)
- Anomaly scoring trends
- Top attackers (srcip)
- Attack distribution by category
**Logs:**
```bash
# On monitoring-uber
tail -f /var/ossec/logs/alerts/alerts.json | grep -i "owasp\|100[23][0-9][0-9]"
```
---
## Tuning
Les seuils de fréquence peuvent être ajustés :
- **SQLi anomaly:** `frequency="5" timeframe="300"` → Réduire pour être plus agressif
- **XSS anomaly:** `frequency="8" timeframe="300"` → Augmenter pour réduire false positives
- **Command Injection:** `frequency="2" timeframe="60"` → Critique, ne pas modifier
---
## Notes
- **First-match semantics:** Wazuh évalue les règles par ID croissant → les règles bloquées (100220+) ont priorité sur les remontées (100320+)
- **Field matching:** Les patterns utilisent `<field name="transaction.messages.message">` pour matcher les CRS rule IDs dans les logs ModSecurity
- **SCA suppressed:** Les alertes SCA (CIS benchmark) sont à level 0 (voir `local_sca_noise.xml`)
- **Persistence:** Tous les AR sont sauvegardés dans `iptables-save` → survient les reboots
@@ -0,0 +1,258 @@
# VPN Deployment Summary
## Infrastructure
| Role | Hostname | IP Public | IP VPN | Status |
|------|----------|-----------|--------|--------|
| **VPN Server** | vpn-uber | 45.150.111.158 | 10.0.0.1/24 | ✓ Active |
| **Monitoring** | monitoring-uber | 185.103.167.138 | 10.0.0.2/24 | ✓ Connected |
| **Admins** | (clients) | — | 10.0.0.3+ | Ready |
---
## Server Credentials
### VPN Server (vpn-uber)
- **IP Public:** 45.150.111.158
- **Port:** 51820/udp
- **Server Private Key:** (secured at `/etc/wireguard/privatekey`)
- **Server Public Key:** `VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg=`
- **Network:** 10.0.0.0/24
- **Interface IP:** 10.0.0.1
**SSH Access:**
```bash
ssh root@45.150.111.158
# Password: yqZ98EJL1h3ISlfT6l (from host.ini)
```
### Monitoring Server (monitoring-uber)
- **IP Public:** 185.103.167.138
- **VPN IP:** 10.0.0.2/24
- **Status:** Connected to VPN ✓
- **Firewall:** Configured (iptables)
- **Interface:** wg0 UP
**SSH Access:**
```bash
ssh root@185.103.167.138
# Password: rL9lY6YkcDQmfRuZ3Z (from host.ini)
```
---
## VPN Configuration
### Firewall Rules
**VPN Server (vpn-uber):**
```
INPUT:
✓ 22/tcp (SSH)
✓ 51820/udp (WireGuard)
✗ Everything else → REJECT
OUTPUT:
✓ All
FORWARD:
✓ wg0 interface (VPN traffic)
✓ NAT masquerade (10.0.0.0/24 → Internet)
```
**Monitoring Server (monitoring-uber):**
```
INPUT:
✓ 1514/tcp FROM 185.103.166.119 (Prod agents)
✓ 1514/tcp FROM 185.103.166.112 (Pre-prod agents)
✓ 51820/udp (VPN)
✓ 443/tcp FROM 10.0.0.0/24 (Wazuh Dashboard)
✓ 8080/tcp FROM 10.0.0.0/24 (Dozzle)
✓ 9090/tcp FROM 10.0.0.0/24 (Beszel)
✓ 9000-9001/tcp FROM 10.0.0.0/24 (S3/RustFS)
✗ Everything else → DROP
OUTPUT:
✓ All (Internet)
```
### WireGuard Routing
```
VPN Server (10.0.0.1)
Monitoring (10.0.0.2) — Connected ✓
Admin Clients (10.0.0.3+) — Ready to connect
```
**Current Peers:**
```
peer: 4WLn2BR9ZGhM195mIvLbZR7tP/mb7ks4+mZ5ppZ21xw= (monitoring-uber)
allowed ips: 10.0.0.2/32
status: Connected ✓ (45ms latency, 0% loss)
```
---
## Services Accessible via VPN
Once connected to VPN from admin client:
| Service | URL | Port | Status |
|---------|-----|------|--------|
| **Wazuh Dashboard** | https://10.0.0.2 | 443 | ✓ (VPN only) |
| **Dozzle** | http://10.0.0.2:8080 | 8080 | ✓ (VPN only) |
| **Beszel** | http://10.0.0.2:9090 | 9090 | ✓ (VPN only) |
| **S3/RustFS Console** | http://10.0.0.2:9001 | 9001 | ✓ (VPN only) |
| **S3/RustFS API** | http://10.0.0.2:9000 | 9000 | ✓ (VPN only) |
**Mode:** VPN + Internet normal (no kill switch)
- Admins can access services via VPN
- Admins keep normal Internet access
- If VPN drops → automatic fallback to Internet
---
## Next Steps for Admins
### 1. Generate Client Configs
```bash
./wireguard-admin-client.sh admin1 45.150.111.158 "VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg="
```
Creates: `admin1.conf`
### 2. Add Client to VPN Server
SSH to VPN server:
```bash
ssh root@45.150.111.158
```
Then:
```bash
# Get client's public key from admin1.conf (PrivateKey → PublicKey)
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.3/32
wg show
```
### 3. Admin Setup
- Install WireGuard app (wireguard.com/install)
- Import admin1.conf
- Connect to VPN
- Test access to services
---
## Testing
### From Monitoring Server
```bash
# Test VPN connection
ping 10.0.0.1
# Response: 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=45.0 ms
# Check VPN status
ip addr show wg0
ip route show | grep wg0
# VPN traffic
tcpdump -i wg0
```
### From VPN Server
```bash
# Monitor active connections
wg show
# Check interface
ip addr show wg0
# Verify firewall rules
iptables -L
iptables -L -t nat
```
---
## Troubleshooting
### Monitoring not connecting to VPN
```bash
# Check WireGuard daemon
ssh root@185.103.167.138
systemctl status wg-quick@wg0
journalctl -u wg-quick@wg0 -n 20
# Restart
systemctl restart wg-quick@wg0
```
### VPN Server not accepting peers
```bash
# On VPN server, verify WireGuard is running
wg show
# Check if peer was added correctly
wg show wg0
# If not showing, re-add:
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.X/32
```
### Admin client can't connect
- Verify VPN server is listening: `netstat -ulnp | grep 51820`
- Verify firewall allows 51820/udp: `ufw status` or `iptables -L`
- Check client config has correct server IP and pubkey
- Try reconnecting after 2-3 seconds
---
## Files & Scripts Used
- `firewall-vpn-server.sh` — VPN server firewall (iptables)
- `wireguard-server-setup.sh` — WireGuard server install + config
- `firewall-monitoring-uber.sh` — Monitoring firewall (iptables)
- `wireguard-client-setup.sh` — WireGuard client install
- `wireguard-admin-client.sh` — Generate admin client configs
- `VPN_SETUP.md` — Full setup guide (phases 1-7)
---
## Persistence
All configurations survive reboot:
- **Firewall:** `/etc/iptables/rules.v4` (loaded via iptables-restore)
- **WireGuard:** Enabled at boot via `systemctl enable wg-quick@wg0`
- **Routes:** Persistent in wg0 config
---
## Security Notes
- Firewall rules block unauthorized access
- WireGuard handshake: 45ms latency (acceptable)
- No kill switch enabled (VPN + Internet normal)
- Services (Wazuh, Dozzle, Beszel, S3) accessible ONLY via VPN
- Agents (prod/pre-prod) still have direct access to Wazuh manager (1514/tcp)
---
## Status
✓ VPN Server deployed on vpn-uber (45.150.111.158)
✓ Monitoring-uber connected to VPN (10.0.0.2)
✓ Firewall configured on both servers
✓ VPN connection tested (45ms, 0% loss)
✓ Ready for admin client setup
+255
View File
@@ -0,0 +1,255 @@
# VPN Setup for Monitoring Stack
## Architecture
```
Internet (public)
├─ [Prod/Pre-prod] (accessible)
│ └─→(1514/tcp)→ monitoring-uber (port ouvert)
├─ [VPN Server] (nouveau VPS, 1 CPU 2GB RAM)
│ └─ WireGuard 0.0.0.0:51820/udp
└─ [monitoring-uber] (185.103.167.138)
├─ Client VPN (10.0.0.2)
└─ Services: Wazuh, Dozzle, Beszel, S3 (VPN only)
VPN Network: 10.0.0.0/24
├─ VPN Server: 10.0.0.1
├─ monitoring-uber: 10.0.0.2
└─ Admins: 10.0.0.3+
```
## Configuration
**Mode:** VPN + Internet normal (pas de kill switch)
- Admins connectés au VPN → accès à services VPN (10.0.0.0/24)
- Admins gardent aussi accès à Internet normal (pas de restriction)
- Si VPN tombe → retrouvent Internet automatiquement
---
## Implementation Steps
### Phase 1: VPN Server Setup (nouveau VPS)
```bash
# 1. Login to VPS
ssh root@<VPN_SERVER_IP>
# 2. Run firewall setup
chmod +x /path/to/firewall-vpn-server.sh
./firewall-vpn-server.sh
# 3. Setup WireGuard server
chmod +x /path/to/wireguard-server-setup.sh
./wireguard-server-setup.sh
# Output will show:
# - Server Public Key (note this)
# - Example: aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE=
```
Save the **Server Public Key** — you'll need it for clients.
### Phase 2: monitoring-uber VPN Client
```bash
# 1. Login to monitoring-uber
ssh root@185.103.167.138
# 2. Run firewall setup
chmod +x /path/to/firewall-monitoring-uber.sh
./firewall-monitoring-uber.sh
# 3. Setup WireGuard client
# Syntax: wireguard-client-setup.sh <VPN_SERVER_IP> <VPN_SERVER_PUBKEY>
chmod +x /path/to/wireguard-client-setup.sh
./wireguard-client-setup.sh <VPN_SERVER_IP> "<SERVER_PUBKEY>"
# Example:
# ./wireguard-client-setup.sh 123.45.67.89 "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE="
# Output will show:
# - Client Public Key (note this)
# - Example: XyZ9876543210AbCdEfGhIjKlMnOpQrStUvWxYz=
```
### Phase 3: Add monitoring-uber to VPN Server
```bash
# Back on VPS, add monitoring-uber as a peer
ssh root@<VPN_SERVER_IP>
# Use the Client Public Key from Phase 2
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.2/32
# Verify
wg show
# Example output:
# interface: wg0
# public key: aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE=
# private key: (hidden)
# listening port: 51820
#
# peer: XyZ9876543210AbCdEfGhIjKlMnOpQrStUvWxYz=
# endpoint: <monitoring-uber-ip>:xxxxx
# allowed ips: 10.0.0.2/32
# latest handshake: X seconds ago
# transfer: X B received, X B sent
```
### Phase 4: Verify VPN Connection
```bash
# On monitoring-uber
ping 10.0.0.1
# Should respond
# PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
# 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=XX.X ms
# Check interface
ip addr show wg0
```
### Phase 5: Rebind Services to VPN IP
```bash
# On monitoring-uber
chmod +x /path/to/rebind-services-to-vpn.sh
./rebind-services-to-vpn.sh
# This will update docker-compose-security.yml:
# - Wazuh Dashboard: 0.0.0.0:443 → 10.0.0.2:443
# - Dozzle: 0.0.0.0:8080 → 10.0.0.2:8080
# - Beszel: 0.0.0.0:9090 → 10.0.0.2:9090
# - S3/RustFS: 0.0.0.0:9000/9001 → 10.0.0.2:9000/9001
# - Wazuh Manager: stays on 0.0.0.0:1514 (for agents)
# Restart services
cd /home/ubuntu/docker
docker compose -f docker-compose-security.yml down
docker compose -f docker-compose-security.yml up -d
# Verify
docker ps
```
### Phase 6: Create Admin VPN Clients
```bash
# Generate config for each admin
chmod +x /path/to/wireguard-admin-client.sh
# Syntax: wireguard-admin-client.sh <name> <vps_ip> <vps_pubkey>
./wireguard-admin-client.sh admin1 123.45.67.89 "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE="
./wireguard-admin-client.sh admin2 123.45.67.89 "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE="
# Output: admin1.conf, admin2.conf
# For each admin, add to VPN server:
ssh root@<VPN_SERVER_IP>
wg set wg0 peer <ADMIN1_PUBKEY> allowed-ips 10.0.0.3/32
wg set wg0 peer <ADMIN2_PUBKEY> allowed-ips 10.0.0.4/32
# Verify
wg show
```
### Phase 7: Admin Connection
Each admin:
1. Download WireGuard app: https://www.wireguard.com/install/
2. Import config file (admin1.conf, admin2.conf, etc.)
3. Connect to VPN
4. Access services:
- **Wazuh Dashboard**: https://10.0.0.2
- **Dozzle**: http://10.0.0.2:8080
- **Beszel**: http://10.0.0.2:9090
- **S3/RustFS Console**: http://10.0.0.2:9001
## Firewall Rules Summary
### VPS VPN Server
| Protocol | Port | Source | Action |
|----------|------|--------|--------|
| UDP | 51820 | Any | ACCEPT (WireGuard) |
| TCP | 22 | Any | ACCEPT (SSH) |
| ICMP | echo-request | Any | ACCEPT |
| Any | Any | Any | REJECT |
NAT masquerade enabled for VPN → Internet routing.
### monitoring-uber (185.103.167.138)
| Protocol | Port | Source | Action |
|----------|------|--------|--------|
| TCP | 1514 | 185.103.166.119 | ACCEPT (Prod Wazuh Agent) |
| TCP | 1514 | 185.103.166.112 | ACCEPT (Pre-prod Wazuh Agent) |
| UDP | 51820 | Any | ACCEPT (VPN) |
| TCP | 443 | 10.0.0.0/24 | ACCEPT (Wazuh Dashboard — VPN) |
| TCP | 8080 | 10.0.0.0/24 | ACCEPT (Dozzle — VPN) |
| TCP | 9090 | 10.0.0.0/24 | ACCEPT (Beszel — VPN) |
| TCP | 9000-9001 | 10.0.0.0/24 | ACCEPT (S3 — VPN) |
| TCP | 22 | Any | ACCEPT (SSH) |
| Any | Any | Any | DROP (Deny all) |
## Troubleshooting
### VPN connection not establishing
```bash
# On monitoring-uber
systemctl status wg-quick@wg0
journalctl -u wg-quick@wg0 -n 20
# Restart
systemctl restart wg-quick@wg0
```
### Can't access services over VPN
```bash
# On monitoring-uber, check bindings
netstat -tlnp | grep -E "(443|8080|9090|9000)"
# Should show 10.0.0.2 (not 0.0.0.0)
```
### Wazuh agents can't connect
Make sure firewall allows 1514/tcp from prod/pre-prod:
```bash
# On monitoring-uber
iptables -L INPUT -v | grep 1514
```
Should show rules for prod (185.103.166.119) and pre-prod (185.103.166.112).
## Persistence & Boot
All rules are saved with:
- `iptables-save``/etc/iptables/rules.v4`
- WireGuard: `systemctl enable wg-quick@wg0`
Both survive reboots.
## Next Steps
- [ ] Deploy VPS VPN Server
- [ ] Run firewall + WireGuard setup on VPS
- [ ] Get Server Public Key
- [ ] Deploy firewall + WireGuard client on monitoring-uber
- [ ] Add monitoring-uber peer on VPS
- [ ] Verify VPN connection (ping 10.0.0.1)
- [ ] Rebind services to VPN IP
- [ ] Restart docker containers
- [ ] Generate admin client configs
- [ ] Add admin peers on VPS
- [ ] Test admin VPN connection
- [ ] Test service access (Wazuh, Dozzle, Beszel, S3)
+10
View File
@@ -0,0 +1,10 @@
[Interface]
Address = 10.0.0.3/32
PrivateKey = ePPTC7QXatWQd9qZWmAqlgd+IskVQ9/7rvwsfUtosEs=
DNS = 8.8.8.8
[Peer]
PublicKey = VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg=
AllowedIPs = 10.0.0.0/24
Endpoint = 45.150.111.158:51820
PersistentKeepalive = 25
+30 -94
View File
@@ -1,5 +1,4 @@
services:
# ─── Dozzle ───────────────────────────────────────────────
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
@@ -10,11 +9,10 @@ services:
environment:
DOZZLE_AUTH_PROVIDER: simple
DOZZLE_AUTH_TTL: 48h
DOZZLE_REMOTE_AGENT: "5.181.0.112:7007|demo-uber|VPS,185.234.9.102:7007|mln-uber|VPS"
DOZZLE_REMOTE_AGENT: "185.103.166.112:7007|pre-prod-uber|VPS,185.103.166.119:7007|prod-uber|VPS"
networks:
- monitoring_net
# ─── Nginx ────────────────────────────────────────────────
nginx:
image: nginx:alpine
container_name: monitoring_nginx
@@ -29,10 +27,35 @@ services:
depends_on:
- dozzle
- wazuh.dashboard
- beszel
networks:
- monitoring_net
# ─── ClamAV Antivirus ────────────────────────────────────
clamav:
image: clamav/clamav:stable
container_name: clamav
restart: unless-stopped
environment:
- FRESHCLAM_CHECKS=24
- CLAMD_SCAN_MAX_FILESIZE=100M
volumes:
- clamav_data:/var/lib/clamav
- clamav_logs:/var/log/clamav
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- monitoring_net
# ─── Beszel Hub ───────────────────────────────────────────
beszel:
image: henrygd/beszel:latest
container_name: beszel
restart: unless-stopped
volumes:
- beszel_data:/beszel_data
networks:
- monitoring_net
# ─── Wazuh Manager ────────────────────────────────────────
wazuh.manager:
image: wazuh/wazuh-manager:4.14.5
hostname: wazuh.manager
@@ -131,99 +154,11 @@ services:
- wazuh.manager
networks:
- monitoring_net
rustfs_perm:
image: alpine:latest
container_name: rustfs_perm
volumes:
- /mnt/data:/data
command: chown -R 1000:1000 /data
rustfs:
image: rustfs/rustfs:latest
depends_on:
rustfs_perm:
condition: service_completed_successfully
hostname: rustfs
container_name: rustfs
restart: unless-stopped
volumes:
- /mnt/data:/data
environment:
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY:-rustfsadmin}
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY:-rustfsadmin}
- RUSTFS_SERVER_DOMAINS=rustfs.uber-stup.club
- RUSTFS_CONSOLE_ENABLE=true
- RUSTFS_ADDRESS=:9000
- RUSTFS_CONSOLE_ADDRESS=:9001
networks:
- monitoring_net
# ─── Xavia OTA — PostgreSQL ───────────────────────────────
xavia_db:
image: postgres:16-alpine
container_name: xavia_db
restart: unless-stopped
environment:
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
volumes:
- xavia_db_data:/var/lib/postgresql/data
networks:
- monitoring_net
# ─── Xavia OTA ────────────────────────────────────────────
xavia:
image: xaviaio/xavia-ota:latest
container_name: xavia
restart: unless-stopped
environment:
- HOST=https://ota.uber-stup.club
- BLOB_STORAGE_TYPE=local
- DB_TYPE=postgres
- ADMIN_PASSWORD=${XAVIA_ADMIN_PASSWORD}
- UPLOAD_KEY=${XAVIA_UPLOAD_KEY}
- PRIVATE_KEY_BASE_64=${XAVIA_PRIVATE_KEY_BASE_64}
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
- POSTGRES_HOST=xavia_db
- POSTGRES_PORT=5432
volumes:
- xavia_blobs:/app/blobs
depends_on:
- xavia_db
networks:
- monitoring_net
# ─── Gitea ────────────────────────────────────────────────
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__server__DOMAIN=${GITEA_DOMAIN:-gitea.uber-stup.club}
- GITEA__server__ROOT_URL=https://${GITEA_DOMAIN:-gitea.uber-stup.club}
- GITEA__server__HTTP_PORT=3000
- GITEA__service__DISABLE_REGISTRATION=${GITEA_DISABLE_REGISTRATION:-true}
- GITEA__service__REQUIRE_SIGNIN_VIEW=true
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY}
- GITEA__security__INTERNAL_TOKEN=${GITEA_INTERNAL_TOKEN}
volumes:
- gitea_data:/data
networks:
- monitoring_net
networks:
monitoring_net:
driver: bridge
volumes:
# Xavia
xavia_db_data:
xavia_blobs:
# Wazuh
wazuh_api_configuration:
wazuh_etc:
wazuh_logs:
@@ -237,5 +172,6 @@ volumes:
filebeat_var:
wazuh_indexer_data:
nginx_logs:
# Gitea
gitea_data:
beszel_data:
clamav_data:
clamav_logs:
+1 -1
View File
@@ -2,5 +2,5 @@ users:
admin:
name: Admin
email: admin@uber-stup.club
password: $2a$11$/JnHCr3JYFZxuoY5COpjM.2MMJUYyKsFmgu/5qphIrbDLVz1qTCOC
password: $2b$11$AOUC5QG2l8Ee0gUKIyZGKuHdL0iuvDa3C0bVAl2Gyt6Axx7.E/hHm
roles: download
@@ -0,0 +1,106 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# Firewall monitoring-uber (185.103.167.138)
# ─────────────────────────────────────────────────────────────────
# INBOUND:
# • 1514/tcp FROM prod (185.103.166.119) — agents Wazuh
# • 1514/tcp FROM pre-prod (185.103.166.112) — agents Wazuh
# • 51820/udp — VPN WireGuard
# • 10.0.0.0/24 (VPN admins) → 443, 8080, 8090, 9000, 9001
# • SSH: BLOCKED from prod/pre-prod/s3 (security hardening)
# ─────────────────────────────────────────────────────────────────
# OUTBOUND:
# • Tout (0.0.0.0/0) — Internet, updates, DNS
# • VPN vers VPS
# ═══════════════════════════════════════════════════════════════════
set -e
echo "[*] Configurant firewall monitoring-uber..."
# Flush des règles existantes
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# Politique par défaut
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# ─── INPUT ────────────────────────────────────────────────────────
# Loopback (services internes)
iptables -A INPUT -i lo -j ACCEPT
# Established/Related
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# SSH (administration locale, bloquer depuis prod/pre-prod/s3)
iptables -A INPUT -p tcp --dport 22 -s 185.103.166.119 -j DROP # prod
iptables -A INPUT -p tcp --dport 22 -s 185.103.166.112 -j DROP # pre-prod
iptables -A INPUT -p tcp --dport 22 -s 80.96.58.164 -j DROP # s3
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # allow from other sources
# ─── Agents Wazuh (prod + pre-prod) ────────────────────────────
# Prod: 185.103.166.119
iptables -A INPUT -p tcp --dport 1514 -s 185.103.166.119 -j ACCEPT
# Pre-prod: 185.103.166.112
iptables -A INPUT -p tcp --dport 1514 -s 185.103.166.112 -j ACCEPT
# ─── VPN (WireGuard) ──────────────────────────────────────────
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# ─── Services (accessibles via VPN seulement) ─────────────────
# Admins via VPN: 10.0.0.0/24
# Wazuh Dashboard (443/https)
iptables -A INPUT -p tcp --dport 443 -s 10.0.0.0/24 -j ACCEPT
# Dozzle (8080/http)
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/24 -j ACCEPT
# Beszel (8090/http)
iptables -A INPUT -p tcp --dport 8090 -s 10.0.0.0/24 -j ACCEPT
# S3/RustFS (9000/9001)
iptables -A INPUT -p tcp --dport 9000 -s 10.0.0.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 9001 -s 10.0.0.0/24 -j ACCEPT
# ─── ICMP ─────────────────────────────────────────────────────
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
# Reject le reste
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
# ─── FORWARD ──────────────────────────────────────────────────
# Blocker forward par défaut (services locaux, pas de transit)
iptables -A FORWARD -j REJECT --reject-with icmp-host-prohibited
# ─── Sauvegarder ──────────────────────────────────────────────
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4
echo "[✓] Firewall monitoring-uber configuré"
echo ""
echo "Règles appliquées (ALLOW) :"
echo " • SSH 22/tcp — from admin IPs only (BLOCKED from prod/pre-prod/s3)"
echo " • Wazuh 1514/tcp FROM prod (185.103.166.119) — agents Wazuh"
echo " • Wazuh 1514/tcp FROM pre-prod (185.103.166.112) — agents Wazuh"
echo " • VPN 51820/udp — WireGuard"
echo " • Wazuh Dashboard 443/tcp FROM 10.0.0.0/24 (VPN)"
echo " • Dozzle 8080/tcp FROM 10.0.0.0/24 (VPN)"
echo " • Beszel 8090/tcp FROM 10.0.0.0/24 (VPN)"
echo " • S3/RustFS 9000-9001/tcp FROM 10.0.0.0/24 (VPN)"
echo " • OUTPUT (Internet/DNS)"
echo ""
echo "Règles appliquées (DENY) :"
echo " • SSH 22/tcp FROM prod (185.103.166.119) — BLOCKED"
echo " • SSH 22/tcp FROM pre-prod (185.103.166.112) — BLOCKED"
echo " • SSH 22/tcp FROM s3 (80.96.58.164) — BLOCKED"
echo " • All other INPUT/FORWARD — default DROP"
@@ -0,0 +1,80 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# Firewall VPS VPN Server (WireGuard) — vpn-uber (45.150.111.158)
# Allow: WireGuard (51820/udp), SSH (22/tcp), Beszel agent (10001/tcp from VPN)
# Reject: tout le reste
# NAT: masquerade pour routage VPN
# ═══════════════════════════════════════════════════════════════════
set -e
echo "[*] Configurant firewall VPS VPN Server..."
# Flush des règles existantes
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# Politique par défaut
iptables -P INPUT DROP
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# ─── INPUT ────────────────────────────────────────────────────────
# Loopback
iptables -A INPUT -i lo -j ACCEPT
# Established/Related
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# SSH (administration)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# WireGuard
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# Beszel Agent (10001/tcp) — accessible depuis VPN uniquement
iptables -A INPUT -p tcp --dport 10001 -s 10.0.0.0/24 -j ACCEPT
# ICMP (ping, MTU discovery)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
# Reject le reste
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
# ─── FORWARD ──────────────────────────────────────────────────────
# VPN ↔ Internet
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
# ─── NAT ──────────────────────────────────────────────────────────
# Masquerade pour routage VPN
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# ─── Sauvegarder ──────────────────────────────────────────────────
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4
# ─── IP Forwarding ────────────────────────────────────────────────
sysctl -w net.ipv4.ip_forward=1
grep -q "net.ipv4.ip_forward" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "[✓] Firewall VPS VPN Server configuré"
echo ""
echo "Règles appliquées (ALLOW) :"
echo " • SSH 22/tcp — administration"
echo " • WireGuard 51820/udp — VPN peers"
echo " • Beszel Agent 10001/tcp FROM 10.0.0.0/24 — monitoring dashboard"
echo " • ICMP (ping, MTU discovery)"
echo ""
echo "Configuration :"
echo " • FORWARD ACCEPT (VPN ↔ Internet routing)"
echo " • NAT masquerade activé (eth0)"
echo " • IP forwarding activé (net.ipv4.ip_forward=1)"
echo " • Default INPUT policy: DROP"
@@ -0,0 +1,74 @@
worker_processes auto;
events {
worker_connections 1024;
}
http {
# Main HTTPS server
server {
listen 0.0.0.0:80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
server {
listen 0.0.0.0:443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Wazuh (root)
location / {
proxy_pass https://wazuh.dashboard:5601/;
proxy_ssl_verify off;
proxy_http_version 1.1;
proxy_set_header Host wazuh.dashboard:5601;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_hide_header Content-Security-Policy;
}
# Dozzle
location /dozzle {
proxy_pass http://dozzle:8080;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect ~^http://dozzle:8080/(.*)$ /dozzle/$1;
proxy_redirect ~^/$/ /dozzle/;
proxy_read_timeout 3600s;
}
# Beszel
location /beszel {
proxy_pass http://beszel:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect ~^http://beszel:8090/(.*)$ /beszel/$1;
proxy_redirect ~^/$/ /beszel/;
sub_filter 'href="/' 'href="/beszel/';
sub_filter 'src="/' 'src="/beszel/';
sub_filter 'url(/' 'url(/beszel/';
sub_filter_once off;
proxy_read_timeout 3600s;
}
}
}
@@ -0,0 +1,83 @@
worker_processes auto;
events {
worker_connections 1024;
}
http {
resolver 127.0.0.11 valid=10s ipv6=off;
upstream wazuh_backend {
server wazuh.dashboard:5601;
}
upstream dozzle_backend {
server dozzle:8080;
}
upstream beszel_backend {
server beszel:8090;
}
# Redirect HTTP to HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
# Main HTTPS server
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Root redirects to /wazuh
location = / {
return 301 https://$host/wazuh;
}
# Wazuh
location /wazuh/ {
proxy_pass https://wazuh_backend/;
proxy_ssl_verify off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
# Dozzle
location /dozzle/ {
proxy_pass http://dozzle_backend/;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
# Beszel
location /beszel/ {
proxy_pass http://beszel_backend/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}
}
+95 -107
View File
@@ -5,6 +5,8 @@ events {
}
http {
resolver 127.0.0.11 valid=10s ipv6=off;
# Redirect HTTP -> HTTPS
server {
listen 80;
@@ -12,10 +14,70 @@ http {
return 301 https://$host$request_uri;
}
# ── Default server (by IP + path) ────────────────────────
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Root Wazuh Dashboard
location = / {
return 301 https://$host/wazuh;
}
# /wazuh Wazuh Dashboard
location /wazuh/ {
set $upstream https://wazuh.dashboard:5601;
proxy_pass $upstream;
proxy_ssl_verify off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_hide_header Content-Security-Policy;
}
# /dozzle Dozzle
location /dozzle/ {
set $upstream http://dozzle:8080;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
# /beszel Beszel
location /beszel/ {
set $upstream http://beszel:8090;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}
# ── Dozzle ──────────────────────────────────────────────
server {
listen 443 ssl;
server_name dozzle.uber-stup.club;
server_name dozzle.demo-uber.xyz;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
@@ -26,7 +88,8 @@ http {
error_log /var/log/nginx/dozzle_error.log;
location / {
proxy_pass http://dozzle:8080;
set $upstream http://dozzle:8080;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
@@ -39,113 +102,10 @@ http {
}
}
# ── RustFS Console ──────────────────────────────────────
server {
listen 443 ssl;
server_name rustfs.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/rustfs_access.log;
error_log /var/log/nginx/rustfs_error.log;
# Console web (port 9001)
location / {
proxy_pass http://rustfs:9001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
client_max_body_size 500m;
}
}
# ── RustFS S3 API ───────────────────────────────────────
server {
listen 443 ssl;
server_name s3.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/rustfs_s3_access.log;
error_log /var/log/nginx/rustfs_s3_error.log;
location / {
proxy_pass http://rustfs:9000;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 2g;
}
}
# ── Xavia OTA ───────────────────────────────────────────
server {
listen 443 ssl;
server_name ota.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/xavia_access.log;
error_log /var/log/nginx/xavia_error.log;
location / {
proxy_pass http://xavia:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 500m;
}
}
# ── Gitea ───────────────────────────────────────────────
server {
listen 443 ssl;
server_name gitea.uber-stup.club;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/gitea_access.log;
error_log /var/log/nginx/gitea_error.log;
location / {
proxy_pass http://gitea:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 100m;
}
}
# ── Wazuh Dashboard ─────────────────────────────────────
server {
listen 443 ssl;
server_name wazuh.uber-stup.club;
server_name wazuh.demo-uber.xyz;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
@@ -156,7 +116,8 @@ http {
error_log /var/log/nginx/wazuh_error.log;
location / {
proxy_pass https://wazuh.dashboard:5601;
set $upstream https://wazuh.dashboard:5601;
proxy_pass $upstream;
proxy_ssl_verify off;
@@ -177,4 +138,31 @@ http {
proxy_hide_header X-XSS-Protection;
}
}
# ── Beszel ──────────────────────────────────────────────
server {
listen 443 ssl;
server_name beszel.demo-uber.xyz;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/beszel_access.log;
error_log /var/log/nginx/beszel_error.log;
location / {
set $upstream http://beszel:8090;
proxy_pass $upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}
}
@@ -0,0 +1,61 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# Rebind services to private VPN IP (10.0.0.2)
# Services: Wazuh Dashboard, Dozzle, Beszel, S3/RustFS
#
# IMPORTANT:
# - Wazuh Manager stays on 0.0.0.0:1514 (agents need access)
# - Other services bind to 10.0.0.2 (VPN only)
# ═══════════════════════════════════════════════════════════════════
set -e
VPN_IP="10.0.0.2"
COMPOSE_FILE="/home/ubuntu/docker/docker-compose-security.yml"
if [ ! -f "$COMPOSE_FILE" ]; then
echo "[!] File not found: $COMPOSE_FILE"
exit 1
fi
echo "[*] Rebinding services to VPN IP ($VPN_IP)..."
echo " Config: $COMPOSE_FILE"
# ─── Wazuh Dashboard (443) ────────────────────────────────────
echo "[*] Updating Wazuh Dashboard (443)..."
sed -i 's|0\.0\.0\.0:443:|'"$VPN_IP"':443:|g' "$COMPOSE_FILE"
# ─── Dozzle (8080) ────────────────────────────────────────────
echo "[*] Updating Dozzle (8080)..."
sed -i 's|0\.0\.0\.0:8080:|'"$VPN_IP"':8080:|g' "$COMPOSE_FILE"
# ─── Beszel (9090) ────────────────────────────────────────────
echo "[*] Updating Beszel (9090)..."
sed -i 's|0\.0\.0\.0:9090:|'"$VPN_IP"':9090:|g' "$COMPOSE_FILE"
# ─── RustFS / S3 (9000, 9001) ─────────────────────────────────
echo "[*] Updating RustFS/S3 (9000, 9001)..."
sed -i 's|0\.0\.0\.0:9000:|'"$VPN_IP"':9000:|g' "$COMPOSE_FILE"
sed -i 's|0\.0\.0\.0:9001:|'"$VPN_IP"':9001:|g' "$COMPOSE_FILE"
# ─── Keep Wazuh Manager on 0.0.0.0:1514 ───────────────────────
# (agents need public access)
echo ""
echo "[✓] Services rebound to $VPN_IP"
echo ""
echo "Verify changes:"
grep -n "ports:" -A 2 "$COMPOSE_FILE" | grep -E "(443|8080|9090|9000|9001|1514)" || true
echo ""
echo "Services now listening on:"
echo " • 0.0.0.0:1514 — Wazuh Manager (agents)"
echo "$VPN_IP:443 — Wazuh Dashboard (VPN only)"
echo "$VPN_IP:8080 — Dozzle (VPN only)"
echo "$VPN_IP:9090 — Beszel (VPN only)"
echo "$VPN_IP:9000/9001 — RustFS (VPN only)"
echo ""
echo "Next: Restart services"
echo " cd /home/ubuntu/docker"
echo " docker compose -f docker-compose-security.yml down"
echo " docker compose -f docker-compose-security.yml up -d"
@@ -0,0 +1,45 @@
---
_meta:
type: "internalusers"
config_version: 2
admin:
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
reserved: true
backend_roles:
- "admin"
description: "Admin user"
kibanaserver:
hash: "$2b$12$czi/zi7uht/K5.vcCvsXueFi6ot7GFwFfVBax7rCaNqBq1jhTOZ7u"
reserved: true
description: "Kibanaserver user"
kibanaro:
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
reserved: false
backend_roles:
- "kibanauser"
- "readall"
description: "Kibana read-only user"
logstash:
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
reserved: false
backend_roles:
- "logstash"
description: "Logstash user"
readall:
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
reserved: false
backend_roles:
- "readall"
description: "Readall user"
snapshotrestore:
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
reserved: false
backend_roles:
- "snapshotrestore"
description: "Snapshotrestore user"
@@ -0,0 +1,4 @@
<!-- ModSecurity decoder — non utilisé, remplacé par règles json+location -->
<decoder name="waf-modsec-unused">
<prematch>UNUSED_MODSEC_PLACEHOLDER_NEVER_MATCHES</prematch>
</decoder>
@@ -0,0 +1,23 @@
<!-- ClamAV Log Monitoring -->
<!-- Collect ClamAV logs for antivirus detection -->
<!-- ClamAV Main Log -->
<localfile>
<log_format>syslog</log_format>
<location>/var/log/clamav/clamav.log</location>
<source_type>log</source_type>
</localfile>
<!-- ClamAV Freshclam (Signature Updates) Log -->
<localfile>
<log_format>syslog</log_format>
<location>/var/log/clamav/freshclam.log</location>
<source_type>log</source_type>
</localfile>
<!-- ClamAV Alert Log (High Priority Detections) -->
<localfile>
<log_format>syslog</log_format>
<location>/var/log/clamav/alert.log</location>
<source_type>log</source_type>
</localfile>
@@ -317,6 +317,338 @@
<timeout>86400</timeout>
</active-response>
<!-- SSH brute-force (3 tentatives / 60s) → firewall-drop 1 heure -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100300</rules_id>
<timeout>3600</timeout>
</active-response>
<!-- SSH scan de comptes (3 users inexistants / 60s) → firewall-drop 24 heures -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100301</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- Sudo brute-force (3 échecs / 5 min) → disable-account 2h + firewall-drop 6h -->
<active-response>
<command>disable-account</command>
<location>local</location>
<rules_id>100310</rules_id>
<timeout>7200</timeout>
</active-response>
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100310</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- ═══════════════════════════════════════════════════════════
OWASP Top 10 — Active Response (ModSecurity)
═══════════════════════════════════════════════════════════ -->
<!-- OWASP#3 SQL Injection (100220) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100220</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#3 SQL Injection brute-force (100290) → firewall-drop 24h + host-deny -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100290</rules_id>
<timeout>86400</timeout>
</active-response>
<active-response>
<command>host-deny</command>
<location>local</location>
<rules_id>100290</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 LDAP Injection (100221) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100221</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#3 Command Injection (100222) → firewall-drop 24h + host-deny permanent -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100222</rules_id>
<timeout>86400</timeout>
</active-response>
<active-response>
<command>host-deny</command>
<location>local</location>
<rules_id>100222</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 Command Injection brute-force CRITICAL (100292) → permanent ban -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100292</rules_id>
<timeout>0</timeout>
</active-response>
<active-response>
<command>host-deny</command>
<location>local</location>
<rules_id>100292</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 PHP/Java Injection (100223, 100224) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100223,100224</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#6 RCE / Exploit attempts (100230) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100230</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#7 XSS attacks (100240) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100240</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#7 XSS brute-force (100291) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100291</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#7 HTTP Header Injection (100241) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100241</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#1 Path Traversal / LFI (100250) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100250</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#1 Path Traversal brute-force (100293) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100293</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#1 Remote File Inclusion (100251) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100251</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#1 Restricted File Access (100252) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100252</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#8 XXE (XML External Entity) (100260) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100260</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#9 Scanner Detection (100270) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100270</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP SSRF (100280) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100280</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP File Upload (100281) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100281</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- ═══════════════════════════════════════════════════════════
OWASP — Remontées (non-bloquées, anomaly scoring)
═══════════════════════════════════════════════════════════ -->
<!-- OWASP#3 SQLi remontée (100320) → firewall-drop 2h (soft) -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100320</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#3 SQLi anomaly (100394) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100394</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#3 Command Injection remontée (100321) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100321</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#3 Command Injection anomaly CRITICAL (100396) → permanent ban -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100396</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 LDAP/PHP/Java remontées (100322, 100323) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100322,100323</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#6 RCE/Exploit remontée (100330) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100330</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#7 XSS remontée (100340) → firewall-drop 1h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100340</rules_id>
<timeout>3600</timeout>
</active-response>
<!-- OWASP#7 XSS anomaly (100395) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100395</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#7 Header Injection remontée (100341) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100341</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#1 Path Traversal remontée (100350) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100350</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#1 RFI remontée (100351) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100351</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#1 File Access remontée (100352) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100352</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#8 XXE remontée (100360) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100360</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#9 Scanner remontée (100370) → firewall-drop 30min -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100370</rules_id>
<timeout>1800</timeout>
</active-response>
<!-- OWASP SSRF remontée (100380) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100380</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP File Upload remontée (100381) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100381</rules_id>
<timeout>7200</timeout>
</active-response>
<active-response>
<disabled>no</disabled>
<command>host-deny</command>
@@ -396,6 +728,302 @@
<timeout>21600</timeout>
</active-response>
<!-- SSH brute-force (3 tentatives / 60s) → firewall-drop 1 heure sur tous les agents -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100300</rules_id>
<timeout>3600</timeout>
</active-response>
<!-- SSH scan de comptes → firewall-drop 24 heures sur tous les agents -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100301</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- ═══════════════════════════════════════════════════════════
OWASP Top 10 — Active Response (ModSecurity - location=all)
═══════════════════════════════════════════════════════════ -->
<!-- OWASP#3 SQLi (100220) → firewall-drop 6h (tous agents) -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100220</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#3 SQLi brute-force (100290) → firewall-drop 24h (tous agents) -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100290</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#3 LDAP (100221) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100221</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#3 Command Injection (100222) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100222</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#3 Command Injection CRITICAL (100292) → permanent ban -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100292</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 PHP/Java (100223, 100224) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100223,100224</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#6 RCE/Exploit (100230) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100230</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#7 XSS (100240) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100240</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#7 XSS brute-force (100291) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100291</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#7 Header Injection (100241) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100241</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#1 Path Traversal/LFI (100250) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100250</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#1 Path Traversal brute-force (100293) → firewall-drop 24h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100293</rules_id>
<timeout>86400</timeout>
</active-response>
<!-- OWASP#1 RFI (100251) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100251</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#1 File Access (100252) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100252</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#8 XXE (100260) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100260</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP#9 Scanner (100270) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100270</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP SSRF (100280) → firewall-drop 12h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100280</rules_id>
<timeout>43200</timeout>
</active-response>
<!-- OWASP File Upload (100281) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100281</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- ═══════════════════════════════════════════════════════════
OWASP — Remontées (location=all)
═══════════════════════════════════════════════════════════ -->
<!-- OWASP#3 SQLi remontée (100320) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100320</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#3 SQLi anomaly (100394) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100394</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#3 Command Injection remontée (100321) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100321</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#3 Command Injection CRITICAL (100396) → permanent -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100396</rules_id>
<timeout>0</timeout>
</active-response>
<!-- OWASP#3 LDAP/PHP/Java (100322, 100323) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100322,100323</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#6 RCE/Exploit (100330) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100330</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#7 XSS remontée (100340) → firewall-drop 1h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100340</rules_id>
<timeout>3600</timeout>
</active-response>
<!-- OWASP#7 XSS anomaly (100395) → firewall-drop 6h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100395</rules_id>
<timeout>21600</timeout>
</active-response>
<!-- OWASP#7 Header Injection (100341) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100341</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#1 Path Traversal (100350) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100350</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#1 RFI (100351) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100351</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#1 File Access (100352) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100352</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- OWASP#8 XXE (100360) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100360</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP#9 Scanner (100370) → firewall-drop 30min -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100370</rules_id>
<timeout>1800</timeout>
</active-response>
<!-- OWASP SSRF (100380) → firewall-drop 4h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100380</rules_id>
<timeout>14400</timeout>
</active-response>
<!-- OWASP File Upload (100381) → firewall-drop 2h -->
<active-response>
<command>firewall-drop</command>
<location>all</location>
<rules_id>100381</rules_id>
<timeout>7200</timeout>
</active-response>
<!-- ═══════════════════════════════════════════════════════════
LOG ANALYSIS (commandes système)
═══════════════════════════════════════════════════════════ -->
@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<group name="clamav">
<rule id="100500" level="0">
<decoded_as>syslog</decoded_as>
<program_name>clam</program_name>
<description>ClamAV message grouping</description>
</rule>
<!-- ClamAV Malware Detections (Critical) -->
<rule id="100501" level="15">
<parent>100500</parent>
<regex>FOUND|Infected|detected</regex>
<description>ClamAV: Malware detected - $(var malware_name)</description>
<group>malware,clamav,infection</group>
</rule>
<!-- ClamAV Trojan/PUA Detection -->
<rule id="100502" level="12">
<parent>100500</parent>
<regex>Trojan|PUA|unwanted|grayware</regex>
<description>ClamAV: Potentially Unwanted Application detected</description>
<group>malware,clamav,pua</group>
</rule>
<!-- ClamAV Virus Detection -->
<rule id="100503" level="15">
<parent>100500</parent>
<regex>\.Virus\.|\.Win\.|\.Linux\.|\.Worm\.|\.Backdoor</regex>
<description>ClamAV: Virus detected - High severity</description>
<group>malware,clamav,virus</group>
</rule>
<!-- ClamAV Ransomware Detection -->
<rule id="100504" level="15">
<parent>100500</parent>
<regex>Ransomware|Encrypted|Cryptolocker|BadRabbit|WannaCry</regex>
<description>ClamAV: Ransomware detected</description>
<group>malware,clamav,ransomware</group>
</rule>
<!-- ClamAV Signature Update Success -->
<rule id="100505" level="3">
<parent>100500</parent>
<program_name>freshclam</program_name>
<regex>ClamAV update completed successfully|updated</regex>
<description>ClamAV: Signature database updated successfully</description>
<group>clamav,updates</group>
</rule>
<!-- ClamAV Signature Update Failed -->
<rule id="100506" level="10">
<parent>100500</parent>
<program_name>freshclam</program_name>
<regex>ERROR|Failed|error downloading|connection failed|timeout</regex>
<description>ClamAV: Signature update failed</description>
<group>clamav,updates,error</group>
</rule>
<!-- ClamAV Daemon Error -->
<rule id="100507" level="8">
<parent>100500</parent>
<program_name>clamd</program_name>
<regex>ERROR|error|CRITICAL</regex>
<description>ClamAV: Daemon error</description>
<group>clamav,error</group>
</rule>
<!-- ClamAV Scanner Started -->
<rule id="100508" level="3">
<parent>100500</parent>
<regex>Scanning started|initializing scanner</regex>
<description>ClamAV: Scanning started</description>
<group>clamav,scanning</group>
</rule>
<!-- ClamAV Scanner Completed -->
<rule id="100509" level="3">
<parent>100500</parent>
<regex>Scanning finished|scan completed</regex>
<description>ClamAV: Scanning completed</description>
<group>clamav,scanning</group>
</rule>
<!-- ClamAV Corrupted Files Alert -->
<rule id="100510" level="8">
<parent>100500</parent>
<regex>corrupted|corrupt file|damaged|unreadable</regex>
<description>ClamAV: Corrupted file detected</description>
<group>clamav,corruption</group>
</rule>
<!-- ClamAV Quarantine Actions -->
<rule id="100511" level="10">
<parent>100500</parent>
<regex>quarantine|moved|removed|deleted|archived</regex>
<description>ClamAV: File quarantined/removed</description>
<group>clamav,quarantine,action</group>
</rule>
<!-- ClamAV Scan Statistics -->
<rule id="100512" level="3">
<parent>100500</parent>
<regex>infects|infected files|detected</regex>
<description>ClamAV: Scan statistics</description>
<group>clamav,statistics</group>
</rule>
<!-- ClamAV Permission Denied -->
<rule id="100513" level="6">
<parent>100500</parent>
<regex>Permission denied|access denied|unable to scan</regex>
<description>ClamAV: Permission denied when scanning file</description>
<group>clamav,access</group>
</rule>
<!-- ClamAV Database Problem -->
<rule id="100514" level="10">
<parent>100500</parent>
<regex>database error|bad database|corrupt database|outdated database</regex>
<description>ClamAV: Database problem detected</description>
<group>clamav,database,error</group>
</rule>
<!-- Aggregate: Multiple Malware Detections in Short Time -->
<rule id="100515" level="16">
<parent>100501</parent>
<frequency>3</timeframe>3600</frequency>
<same_source_ip />
<description>ClamAV: Multiple malware detections in short time - possible outbreak</description>
<group>malware,clamav,outbreak</group>
</rule>
<!-- Aggregate: Ransomware Alert with Infection -->
<rule id="100516" level="16">
<if_matched_sid>100501</if_matched_sid>
<if_matched_sid>100504</if_matched_sid>
<same_source_ip />
<timeframe>300</timeframe>
<description>ClamAV: Ransomware and virus detected together - critical threat</description>
<group>malware,clamav,ransomware,critical</group>
</rule>
</group>
@@ -0,0 +1,45 @@
<!-- ═══════════════════════════════════════════════════════════════════
Active-Response — règles de détection précoce pour l'AR graduée
═══════════════════════════════════════════════════════════════════
Ces règles viennent EN PLUS des bans existants sur les règles natives
(5710, 5716, etc.) pour produire des alertes clairement labellisées
et déclencher des AR spécifiques par scénario d'attaque.
-->
<!-- ─── SSH ─────────────────────────────────────────────────────────── -->
<group name="syslog,sshd,authentication_failures,">
<!-- 3 mots de passe incorrects en 60s depuis la même IP
Note : le ban PERMANENT sur 5716 (1ère tentative) reste actif en parallèle.
Cette règle produit une alerte "brute-force" explicite + AR temporaire graduée. -->
<rule id="100300" level="10" frequency="3" timeframe="60">
<if_matched_sid>5716</if_matched_sid>
<same_source_ip />
<description>SSH: brute-force — 3 mots de passe faux en 60s depuis $(srcip)</description>
<group>authentication_failures,ssh_brute,</group>
</rule>
<!-- 3 users inexistants en 60s depuis la même IP → scan de comptes -->
<rule id="100301" level="12" frequency="3" timeframe="60">
<if_matched_sid>5710</if_matched_sid>
<same_source_ip />
<description>SSH: scan de comptes — 3 users inexistants en 60s depuis $(srcip)</description>
<group>authentication_failures,ssh_scan,</group>
</rule>
</group>
<!-- ─── SUDO ─────────────────────────────────────────────────────────── -->
<group name="syslog,sudo,authentication_failures,">
<!-- 3 échecs sudo en 5 min par le même utilisateur.
Couverture non présente dans les règles natives → AR disable-account. -->
<rule id="100310" level="10" frequency="3" timeframe="300">
<if_matched_sid>5401</if_matched_sid>
<same_user />
<description>Sudo: 3 échecs d'auth en 5 min — tentative d'escalade de privilèges</description>
<group>authentication_failures,sudo_brute,</group>
</rule>
</group>
@@ -0,0 +1,39 @@
<!-- ═══════════════════════════════════════════════════════════════
Règles ModSecurity — WAF container (gestion-waf)
Le decoder json intégré parse les events — on utilise <field> + <location>.
═══════════════════════════════════════════════════════════════ -->
<group name="web,modsecurity,">
<!-- Base : tout event JSON provenant du log ModSecurity -->
<rule id="100100" level="3">
<decoded_as>json</decoded_as>
<location>modsec_audit.log</location>
<field name="transaction.client_ip">\.+</field>
<description>ModSecurity: WAF event capturé</description>
<group>web,modsecurity,</group>
</rule>
<!-- Accès bloqué (HTTP 403) -->
<rule id="100102" level="10">
<if_sid>100100</if_sid>
<field name="transaction.response.http_code">^403$</field>
<description>ModSecurity: accès bloqué (403) — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>web,modsecurity,attack,blocked,</group>
</rule>
<!-- Trafic passant avec règle déclenchée (warning, anomaly scoring) -->
<rule id="100101" level="6">
<if_sid>100100</if_sid>
<description>ModSecurity: règle WAF déclenchée (non bloqué) — $(transaction.client_ip)</description>
<group>web,modsecurity,attack,</group>
</rule>
<!-- Blocages répétés depuis la même IP -->
<rule id="100103" level="12" frequency="5" timeframe="60">
<if_matched_sid>100102</if_matched_sid>
<description>ModSecurity: blocages répétés — possible scan ou attaque soutenue</description>
<group>web,modsecurity,attack,blocked,</group>
</rule>
</group>
@@ -0,0 +1,113 @@
<!-- ═══════════════════════════════════════════════════════════════════
Suppression des alertes SCA (Security Configuration Assessment)
═══════════════════════════════════════════════════════════════════
Les scans CIS benchmark génèrent des centaines d'alertes par cycle.
Toutes passées à level 0 — les résultats restent consultables dans
l'onglet SCA du dashboard sans polluer le flux d'alertes.
-->
<group name="sca,">
<!-- ─── Résumés de scan (score < 90%, <80%, <50%, <30%) ─────────── -->
<rule id="19001" level="0" overwrite="yes">
<if_sid>19000</if_sid>
<field name="sca.type">^summary</field>
<options>no_full_log</options>
<description>SCA summary — supprimé (voir onglet SCA du dashboard).</description>
</rule>
<rule id="19002" level="0" overwrite="yes">
<if_sid>19001</if_sid>
<field name="sca.score">^8\d</field>
<options>no_full_log</options>
<description>SCA summary: score &lt; 90% — supprimé.</description>
</rule>
<rule id="19003" level="0" overwrite="yes">
<if_sid>19001</if_sid>
<field name="sca.score">^7\d|^6\d|^5\d</field>
<options>no_full_log</options>
<description>SCA summary: score &lt; 80% — supprimé.</description>
</rule>
<rule id="19004" level="0" overwrite="yes">
<if_sid>19001</if_sid>
<field name="sca.score">^4\d|^3\d</field>
<options>no_full_log</options>
<description>SCA summary: score &lt; 50% — supprimé.</description>
</rule>
<rule id="19005" level="0" overwrite="yes">
<if_sid>19001</if_sid>
<field name="sca.score">^2\d|^1\d$|^\d$</field>
<options>no_full_log</options>
<description>SCA summary: score &lt; 30% — supprimé.</description>
</rule>
<!-- ─── Checks individuels (passed / failed / not applicable) ────── -->
<rule id="19007" level="0" overwrite="yes">
<if_sid>19006</if_sid>
<field name="sca.check.result">^failed</field>
<options>no_full_log</options>
<description>SCA check failed — supprimé.</description>
</rule>
<rule id="19008" level="0" overwrite="yes">
<if_sid>19006</if_sid>
<field name="sca.check.result">^passed</field>
<options>no_full_log</options>
<description>SCA check passed — supprimé.</description>
</rule>
<rule id="19009" level="0" overwrite="yes">
<if_sid>19006</if_sid>
<field name="sca.check.result">^not applicable</field>
<options>no_full_log</options>
<description>SCA check not applicable — supprimé.</description>
</rule>
<!-- ─── Changements de statut entre scans ───────────────────────── -->
<rule id="19010" level="0" overwrite="yes">
<if_sid>19008</if_sid>
<field name="sca.check.previous_result">^failed</field>
<options>no_full_log</options>
<description>SCA: failed → passed — supprimé.</description>
</rule>
<rule id="19011" level="0" overwrite="yes">
<if_sid>19007</if_sid>
<field name="sca.check.previous_result">^passed</field>
<options>no_full_log</options>
<description>SCA: passed → failed — supprimé.</description>
</rule>
<rule id="19012" level="0" overwrite="yes">
<if_sid>19009</if_sid>
<field name="sca.check.previous_result">^passed</field>
<options>no_full_log</options>
<description>SCA: passed → not applicable — supprimé.</description>
</rule>
<rule id="19013" level="0" overwrite="yes">
<if_sid>19009</if_sid>
<field name="sca.check.previous_result">^failed</field>
<options>no_full_log</options>
<description>SCA: failed → not applicable — supprimé.</description>
</rule>
<rule id="19014" level="0" overwrite="yes">
<if_sid>19007</if_sid>
<field name="sca.check.previous_result">^not applicable</field>
<options>no_full_log</options>
<description>SCA: not applicable → failed — supprimé.</description>
</rule>
<rule id="19015" level="0" overwrite="yes">
<if_sid>19008</if_sid>
<field name="sca.check.previous_result">^not applicable</field>
<options>no_full_log</options>
<description>SCA: not applicable → passed — supprimé.</description>
</rule>
</group>
@@ -107,28 +107,27 @@
</group>
<!-- ─── IOC Suricata/Windows — parents exclus (0475, 0580, 0840) ──────────
Ces règles dans 0999-malicious-ioc-rules.xml ont des if_sid qui
pointent vers des fichiers exclus. Sans overwrite, analysisd log des
warnings (7617)/(7619) au démarrage. On les remplace par des règles
level 0 pour silence les warnings sans affecter la détection IOC SSH. -->
IMPORTANT: <match>PLACEHOLDER_NEVER_MATCHES</match> au lieu de <match>.</match>
pour éviter que ces rules catch-all bloquent l'évaluation des rules avec
un ID plus élevé (first-match semantics dans Wazuh). -->
<group name="ids,">
<rule id="99917" level="0" overwrite="yes">
<match>.</match>
<description>Suricata: malicious domain — non applicable (pas d'agent Suricata).</description>
<match>WAZUH_PLACEHOLDER_SURICATA_NEVER_MATCHES</match>
<description>Suricata: malicious domain — overwrite placeholder (non applicable).</description>
</rule>
<rule id="99918" level="0" overwrite="yes">
<match>.</match>
<description>Suricata: malicious domain DNS — non applicable.</description>
<match>WAZUH_PLACEHOLDER_SURICATA_DNS_NEVER_MATCHES</match>
<description>Suricata: malicious domain DNS — overwrite placeholder (non applicable).</description>
</rule>
</group>
<group name="windows,windows_security,">
<rule id="99919" level="0" overwrite="yes">
<match>.</match>
<description>Windows: failed logon malicious IP — non applicable (pas d'agent Windows).</description>
<match>WAZUH_PLACEHOLDER_WINDOWS_FAILED_NEVER_MATCHES</match>
<description>Windows: failed logon malicious IP — overwrite placeholder (non applicable).</description>
</rule>
<rule id="99920" level="0" overwrite="yes">
<match>.</match>
<description>Windows: successful logon malicious IP — non applicable.</description>
<match>WAZUH_PLACEHOLDER_WINDOWS_SUCCESS_NEVER_MATCHES</match>
<description>Windows: successful logon malicious IP — overwrite placeholder (non applicable).</description>
</rule>
</group>
@@ -0,0 +1,352 @@
<!-- ═══════════════════════════════════════════════════════════════════
ModSecurity OWASP Top 10 — Règles granulaires
═══════════════════════════════════════════════════════════════════
Détection des attaques OWASP Top 10 via ModSecurity + active-response
Correspondances CRS (OWASP ModSec Core Rule Set) :
• 941xxx : XSS (Cross-Site Scripting)
• 942xxx : SQL Injection, LDAP Injection
• 943xxx : XXE (XML External Entity)
• 930xxx : Path Traversal, RFI (Remote File Inclusion)
• 932xxx : Remote Command Execution
• 933xxx : PHP Injection
• 934xxx : Java Injection
• 950xxx : Exploit attempts
• 951xxx : Regex DoS
• 952xxx : Restricted File Access
• 953xxx : Insecure File Upload
• 954xxx : Proxy Abuse
• 955xxx : Malicious HTTP Headers
• 970xxx : SSRF / Open Redirect
-->
<group name="web,modsecurity,owasp,">
<!-- ═══════════════════════════════════════════════════════════
RÈGLES BLOQUÉES (100102 - HTTP 403)
═══════════════════════════════════════════════════════════ -->
<!-- ═══════════════════════════════════════════════════════════
OWASP #3 : INJECTION (SQLi, Command Injection, LDAP, etc.)
═══════════════════════════════════════════════════════════ -->
<!-- SQL Injection BLOQUÉE (942xx) -->
<rule id="100220" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">942[0-9]</field>
<description>OWASP#3 SQL Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,sql_injection,</group>
</rule>
<!-- LDAP Injection (942xx - LDAP patterns) -->
<rule id="100221" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">942[0-9].*LDAP\|ldap_filter</field>
<description>OWASP#3 LDAP Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,ldap_injection,</group>
</rule>
<!-- Command Injection / OS Command Execution (932xx) -->
<rule id="100222" level="14">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">932[0-9]</field>
<description>OWASP#3 Remote Command Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,command_injection,rce,</group>
</rule>
<!-- PHP Injection (933xx) -->
<rule id="100223" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">933[0-9]</field>
<description>OWASP#3 PHP Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,php_injection,</group>
</rule>
<!-- Java Injection (934xx) -->
<rule id="100224" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">934[0-9]</field>
<description>OWASP#3 Java Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,java_injection,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
OWASP #6 : VULNERABLE & OUTDATED COMPONENTS (Exploit attempts)
═══════════════════════════════════════════════════════════ -->
<!-- Remote Code Execution attempts (930xx, 950xx) -->
<rule id="100230" level="14">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">930[0-9]\|950[0-9]</field>
<description>OWASP#6 RCE / Exploit attempt bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_vulnerable_components,rce,exploit,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
OWASP #7 : AUTHENTICATION & SESSION (XSS/CSRF/Session Theft)
═══════════════════════════════════════════════════════════ -->
<!-- Cross-Site Scripting (941xx) -->
<rule id="100240" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">941[0-9]</field>
<description>OWASP#7 XSS attack bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_auth,xss,session_theft,</group>
</rule>
<!-- HTTP Response Splitting / Header Injection (955xx) -->
<rule id="100241" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">955[0-9]</field>
<description>OWASP#7 HTTP Header Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_auth,header_injection,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
OWASP #1 : BROKEN ACCESS CONTROL (Path Traversal, Bypass)
═══════════════════════════════════════════════════════════ -->
<!-- Path Traversal / Local File Inclusion (930xx - LFI patterns) -->
<rule id="100250" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">930[0-9].*\(path\|traversal\|lfi\)</field>
<description>OWASP#1 Path Traversal / LFI bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,lfi,path_traversal,</group>
</rule>
<!-- Remote File Inclusion (930xx - RFI patterns) -->
<rule id="100251" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">930[0-9].*\(rfi\|remote\|include\)</field>
<description>OWASP#1 Remote File Inclusion bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,rfi,</group>
</rule>
<!-- Restricted File Access (952xx) -->
<rule id="100252" level="11">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">952[0-9]</field>
<description>OWASP#1 Unauthorized File Access bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,file_access,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
OWASP #8 : SOFTWARE & DATA INTEGRITY (XXE, Deserialization)
═══════════════════════════════════════════════════════════ -->
<!-- XML External Entity (XXE) (943xx) -->
<rule id="100260" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">943[0-9]</field>
<description>OWASP#8 XML External Entity (XXE) bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_data_integrity,xxe,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
OWASP #9 : LOGGING & MONITORING FAILURES (Scanner Detection)
═══════════════════════════════════════════════════════════ -->
<!-- Web Application Scanner Detection (951xx, 954xx) -->
<rule id="100270" level="10">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">951[0-9]\|954[0-9]</field>
<description>OWASP#9 Web Scanner / Bot detected — $(transaction.client_ip)</description>
<group>owasp_monitoring,scanner_detection,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
AUTRES : SSRF, Open Redirect, Malicious File Upload
═══════════════════════════════════════════════════════════ -->
<!-- Server-Side Request Forgery (970xx, 971xx) -->
<rule id="100280" level="13">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">970[0-9]\|971[0-9]</field>
<description>OWASP Server-Side Request Forgery (SSRF) bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_misc,ssrf,</group>
</rule>
<!-- Insecure File Upload (953xx) -->
<rule id="100281" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">953[0-9]</field>
<description>OWASP Insecure File Upload bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_misc,file_upload,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
FRÉQUENCE & PATTERNS RÉPÉTÉS
═══════════════════════════════════════════════════════════ -->
<!-- SQLi répétées (brute-force injection) -->
<rule id="100290" level="14" frequency="3" timeframe="60">
<if_matched_sid>100220</if_matched_sid>
<description>OWASP#3 SQL Injection brute-force — $(transaction.client_ip) tentatives répétées</description>
<group>owasp_injection,sql_injection,brute_force,</group>
</rule>
<!-- XSS répétées (scanner ou attaque soutenue) -->
<rule id="100291" level="13" frequency="5" timeframe="120">
<if_matched_sid>100240</if_matched_sid>
<description>OWASP#7 XSS attack pattern — $(transaction.client_ip) attaque soutenue</description>
<group>owasp_auth,xss,</group>
</rule>
<!-- RCE attempts répétées (critiques) -->
<rule id="100292" level="15" frequency="2" timeframe="60">
<if_matched_sid>100222</if_matched_sid>
<description>OWASP#3 Command Injection attempts — $(transaction.client_ip) CRITICAL</description>
<group>owasp_injection,command_injection,critical,</group>
</rule>
<!-- Path Traversal brute-force -->
<rule id="100293" level="12" frequency="4" timeframe="180">
<if_matched_sid>100250</if_matched_sid>
<description>OWASP#1 Path Traversal brute-force — $(transaction.client_ip)</description>
<group>owasp_access_control,lfi,brute_force,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
RÈGLES REMONTÉES (100100 - tous les events, bloqués ou non)
Patterns OWASP détectés sans être bloqués
═══════════════════════════════════════════════════════════ -->
<!-- OWASP#3 SQLi remontée (942xx - non bloquée, anomaly scoring) -->
<rule id="100320" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">942[0-9]</field>
<description>OWASP#3 SQL Injection attempt (anomaly scoring) — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,sql_injection,warning,</group>
</rule>
<!-- OWASP#3 Command Injection remontée (932xx - non bloquée) -->
<rule id="100321" level="10">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">932[0-9]</field>
<description>OWASP#3 Command Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,command_injection,warning,</group>
</rule>
<!-- OWASP#3 LDAP Injection remontée (942xx LDAP) -->
<rule id="100322" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">942[0-9].*LDAP\|ldap_filter</field>
<description>OWASP#3 LDAP Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,ldap_injection,warning,</group>
</rule>
<!-- OWASP#3 PHP/Java Injection remontée (933xx, 934xx) -->
<rule id="100323" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">933[0-9]\|934[0-9]</field>
<description>OWASP#3 Code Injection attempt (PHP/Java) — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_injection,code_injection,warning,</group>
</rule>
<!-- OWASP#6 RCE/Exploit remontée (930xx, 950xx - non bloquée) -->
<rule id="100330" level="11">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">930[0-9]\|950[0-9]</field>
<description>OWASP#6 RCE / Exploit attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_vulnerable_components,rce,warning,</group>
</rule>
<!-- OWASP#7 XSS remontée (941xx - non bloquée) -->
<rule id="100340" level="8">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">941[0-9]</field>
<description>OWASP#7 XSS attempt (anomaly scoring) — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_auth,xss,warning,</group>
</rule>
<!-- OWASP#7 Header Injection remontée (955xx) -->
<rule id="100341" level="8">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">955[0-9]</field>
<description>OWASP#7 HTTP Header Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_auth,header_injection,warning,</group>
</rule>
<!-- OWASP#1 Path Traversal remontée (930xx LFI patterns) -->
<rule id="100350" level="8">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">930[0-9].*\(path\|traversal\|lfi\)</field>
<description>OWASP#1 Path Traversal attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,lfi,warning,</group>
</rule>
<!-- OWASP#1 RFI remontée (930xx RFI patterns) -->
<rule id="100351" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">930[0-9].*\(rfi\|remote\|include\)</field>
<description>OWASP#1 Remote File Inclusion attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,rfi,warning,</group>
</rule>
<!-- OWASP#1 File Access remontée (952xx) -->
<rule id="100352" level="8">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">952[0-9]</field>
<description>OWASP#1 Unauthorized File Access attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_access_control,file_access,warning,</group>
</rule>
<!-- OWASP#8 XXE remontée (943xx) -->
<rule id="100360" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">943[0-9]</field>
<description>OWASP#8 XML External Entity (XXE) attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_data_integrity,xxe,warning,</group>
</rule>
<!-- OWASP#9 Scanner remontée (951xx, 954xx) -->
<rule id="100370" level="6">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">951[0-9]\|954[0-9]</field>
<description>OWASP#9 Web Scanner / Bot detected — $(transaction.client_ip)</description>
<group>owasp_monitoring,scanner_detection,warning,</group>
</rule>
<!-- OWASP SSRF remontée (970xx, 971xx) -->
<rule id="100380" level="9">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">970[0-9]\|971[0-9]</field>
<description>OWASP Server-Side Request Forgery (SSRF) attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_misc,ssrf,warning,</group>
</rule>
<!-- OWASP File Upload remontée (953xx) -->
<rule id="100381" level="8">
<if_sid>100100</if_sid>
<field name="transaction.messages.message">953[0-9]</field>
<description>OWASP Insecure File Upload attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>owasp_misc,file_upload,warning,</group>
</rule>
<!-- ═══════════════════════════════════════════════════════════
FRÉQUENCE — Attaques par anomaly scoring répétées
═══════════════════════════════════════════════════════════ -->
<!-- SQLi anomaly scoring répétée -->
<rule id="100394" level="10" frequency="5" timeframe="300">
<if_matched_sid>100320</if_matched_sid>
<description>OWASP#3 SQL Injection anomaly scoring — $(transaction.client_ip) tentatives répétées</description>
<group>owasp_injection,sql_injection,anomaly,</group>
</rule>
<!-- XSS anomaly scoring répétée -->
<rule id="100395" level="10" frequency="8" timeframe="300">
<if_matched_sid>100340</if_matched_sid>
<description>OWASP#7 XSS anomaly scoring — $(transaction.client_ip) attaque soutenue</description>
<group>owasp_auth,xss,anomaly,</group>
</rule>
<!-- Command Injection tentatives répétées -->
<rule id="100396" level="12" frequency="2" timeframe="60">
<if_matched_sid>100321</if_matched_sid>
<description>OWASP#3 Command Injection attempts — $(transaction.client_ip) CRITICAL</description>
<group>owasp_injection,command_injection,anomaly,</group>
</rule>
</group>
@@ -0,0 +1,44 @@
<group name="web,modsecurity,attack,">
<!-- Règles spécialisées par type d'attaque — basées sur local_modsecurity_rules.xml (rule 100102) -->
<!-- SQL Injection bloquée -->
<rule id="100202" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">SQL Injection\|SQLi\|942[0-9]</field>
<description>ModSecurity: SQL Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>modsecurity_sqli,sql_injection,</group>
</rule>
<!-- XSS bloqué -->
<rule id="100203" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">XSS\|941[0-9]</field>
<description>ModSecurity: XSS bloqué — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>modsecurity_xss,xss,</group>
</rule>
<!-- Remote Code Execution bloqué -->
<rule id="100204" level="14">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">RCE\|930[0-9]\|932[0-9]</field>
<description>ModSecurity: tentative RCE bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>modsecurity_rce,rce,</group>
</rule>
<!-- Local File Inclusion bloqué -->
<rule id="100205" level="12">
<if_sid>100102</if_sid>
<field name="transaction.messages.message">LFI\|930[0-9]</field>
<description>ModSecurity: LFI bloqué — $(transaction.client_ip) → $(transaction.request.uri)</description>
<group>modsecurity_lfi,lfi,</group>
</rule>
<!-- Blocages répétés — attaque soutenue -->
<rule id="100210" level="14" frequency="5" timeframe="120">
<if_matched_sid>100102</if_matched_sid>
<description>ModSecurity: blocages répétés en 2 min — attaque soutenue</description>
<group>modsecurity_repeated,</group>
</rule>
</group>
+68
View File
@@ -0,0 +1,68 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# Generate WireGuard client config for admin
# Usage: wireguard-admin-client.sh <admin_name> <server_ip> <server_pubkey>
# Output: admin_name.conf (ready for import in WireGuard app)
# ═══════════════════════════════════════════════════════════════════
set -e
ADMIN_NAME="${1:-admin1}"
VPN_SERVER_IP="${2:-}"
VPN_SERVER_PUBKEY="${3:-}"
if [ -z "$VPN_SERVER_IP" ] || [ -z "$VPN_SERVER_PUBKEY" ]; then
echo "Usage: $0 <admin_name> <server_ip> <server_pubkey>"
echo ""
echo "Example:"
echo " $0 admin1 123.45.67.89 'aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE='"
exit 1
fi
echo "[*] Generating WireGuard config for admin: $ADMIN_NAME"
# ─── Generate unique keys for this admin ───────────────────────
ADMIN_PRIVATEKEY=$(wg genkey)
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVATEKEY" | wg pubkey)
# ─── Auto-assign IP from 10.0.0.3+ ─────────────────────────────
# Format: [admin_name].conf uses 10.0.0.3, 10.0.0.4, etc.
# You should track these manually or use a database
ADMIN_IP="10.0.0.3" # ← CHANGE THIS for each admin
# ─── Create config file ────────────────────────────────────────
cat > "${ADMIN_NAME}.conf" << EOF
[Interface]
Address = $ADMIN_IP/32
PrivateKey = $ADMIN_PRIVATEKEY
DNS = 8.8.8.8
[Peer]
PublicKey = $VPN_SERVER_PUBKEY
AllowedIPs = 10.0.0.0/24
Endpoint = $VPN_SERVER_IP:51820
PersistentKeepalive = 25
EOF
echo "[✓] Config created: ${ADMIN_NAME}.conf"
echo ""
echo "Configuration:"
echo " Name: $ADMIN_NAME"
echo " IP: $ADMIN_IP"
echo " Public Key: $ADMIN_PUBKEY"
echo ""
echo "Steps to add to VPN server:"
echo " 1. Copy public key above"
echo " 2. SSH to VPN server"
echo " 3. wg set wg0 peer $ADMIN_PUBKEY allowed-ips $ADMIN_IP/32"
echo " 4. wg show"
echo ""
echo "To import on your device:"
echo " 1. Install WireGuard app (Windows/Mac/Linux/iPhone/Android)"
echo " 2. Import ${ADMIN_NAME}.conf"
echo " 3. Connect to VPN"
echo " 4. Access services:"
echo " • Wazuh: https://10.0.0.2"
echo " • Dozzle: http://10.0.0.2:8080"
echo " • Beszel: http://10.0.0.2:9090"
echo " • S3: http://10.0.0.2:9000"
@@ -0,0 +1,104 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# WireGuard Client Setup (monitoring-uber)
# Install WireGuard, generate client keys, auto-connect to VPN server
# Client IP: 10.0.0.2/24
# ═══════════════════════════════════════════════════════════════════
set -e
# ─── Parameters ────────────────────────────────────────────────────
VPN_SERVER_IP="${1:-}" # IP publique du serveur VPN
VPN_SERVER_PUBKEY="${2:-}" # Clé publique du serveur VPN
if [ -z "$VPN_SERVER_IP" ] || [ -z "$VPN_SERVER_PUBKEY" ]; then
echo "Usage: $0 <VPN_SERVER_IP> <VPN_SERVER_PUBKEY>"
echo ""
echo "Example:"
echo " $0 123.45.67.89 'aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE='"
exit 1
fi
echo "[*] Setting up WireGuard Client on monitoring-uber..."
echo " Server IP: $VPN_SERVER_IP"
echo " Server Pubkey: $VPN_SERVER_PUBKEY"
# ─── Install WireGuard ─────────────────────────────────────────
echo "[*] Installing WireGuard..."
apt-get update -qq
apt-get install -y wireguard wireguard-tools
# ─── Create key directory ──────────────────────────────────────
mkdir -p /etc/wireguard
cd /etc/wireguard
umask 077
# ─── Generate client keys ──────────────────────────────────────
if [ ! -f client_privatekey ]; then
echo "[*] Generating client private key..."
wg genkey > client_privatekey
cat client_privatekey | wg pubkey > client_publickey
echo "[✓] Keys generated"
echo ""
echo "Client Public Key (for server):"
cat client_publickey
echo ""
else
echo "[!] Client keys already exist"
fi
# ─── Create wg0 configuration ──────────────────────────────────
echo "[*] Creating WireGuard client configuration..."
PRIVATE_KEY=$(cat client_privatekey)
CLIENT_PUBKEY=$(cat client_publickey)
cat > wg0.conf << EOF
[Interface]
# monitoring-uber VPN IP
Address = 10.0.0.2/24
ListenPort = 0
PrivateKey = $PRIVATE_KEY
[Peer]
# VPN Server
PublicKey = $VPN_SERVER_PUBKEY
AllowedIPs = 10.0.0.0/24
Endpoint = $VPN_SERVER_IP:51820
PersistentKeepalive = 25
EOF
chmod 600 wg0.conf
echo "[✓] Configuration created at /etc/wireguard/wg0.conf"
# ─── Enable at boot and start ──────────────────────────────────
echo "[*] Enabling WireGuard at boot..."
systemctl enable wg-quick@wg0 2>/dev/null || true
systemctl start wg-quick@wg0
sleep 2
# Vérifier connexion
if ip addr show wg0 &>/dev/null; then
echo "[✓] WireGuard interface up"
ip addr show wg0
else
echo "[!] WireGuard interface not up, check logs:"
journalctl -u wg-quick@wg0 -n 10
fi
echo ""
echo "[✓] WireGuard Client configured"
echo ""
echo "Configuration Summary:"
echo " • Interface: wg0"
echo " • Client IP: 10.0.0.2/24"
echo " • Server: $VPN_SERVER_IP:51820"
echo " • Config: /etc/wireguard/wg0.conf"
echo ""
echo "IMPORTANT: Add this client public key to VPN server:"
echo " wg set wg0 peer $(cat client_publickey) allowed-ips 10.0.0.2/32"
echo ""
echo "Verify connection:"
echo " ping 10.0.0.1"
@@ -0,0 +1,89 @@
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════
# WireGuard Server Setup (VPS VPN)
# Install WireGuard, generate server keys, configure interface
# Network: 10.0.0.0/24
# Server IP: 10.0.0.1
# Listen: 0.0.0.0:51820/udp
# ═══════════════════════════════════════════════════════════════════
set -e
echo "[*] Setting up WireGuard Server..."
# ─── Install WireGuard ─────────────────────────────────────────
echo "[*] Installing WireGuard..."
apt-get update -qq
apt-get install -y wireguard wireguard-tools
# ─── Create key directory ──────────────────────────────────────
mkdir -p /etc/wireguard
cd /etc/wireguard
umask 077
# ─── Generate server keys ──────────────────────────────────────
if [ ! -f privatekey ]; then
echo "[*] Generating server private key..."
wg genkey > privatekey
cat privatekey | wg pubkey > publickey
echo "[✓] Keys generated"
echo ""
echo "Server Public Key:"
cat publickey
echo ""
else
echo "[!] Server keys already exist"
fi
# ─── Create wg0 configuration ──────────────────────────────────
echo "[*] Creating WireGuard interface configuration..."
cat > wg0.conf << 'EOF'
[Interface]
# Server IP dans le réseau VPN
Address = 10.0.0.1/24
ListenPort = 51820
# Charger la clé privée
PrivateKey = PRIVATE_KEY_PLACEHOLDER
# Accepter VPN traffic
PostUp = iptables -I FORWARD 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -o %i -j ACCEPT; iptables -t nat -I POSTROUTING 1 -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Peers: monitoring-uber, admins (ajouté après)
EOF
# Remplacer placeholder par clé réelle
PRIVATE_KEY=$(cat privatekey)
sed -i "s|PRIVATE_KEY_PLACEHOLDER|$PRIVATE_KEY|" wg0.conf
# ─── Enable WireGuard interface ────────────────────────────────
echo "[*] Bringing up WireGuard interface..."
ip link add dev wg0 type wireguard
ip addr add 10.0.0.1/24 dev wg0
ip link set wg0 up
wg set wg0 private-key <(cat privatekey)
wg set wg0 listen-port 51820
# ─── Enable at boot ───────────────────────────────────────────
echo "[*] Enabling WireGuard at boot..."
systemctl enable wg-quick@wg0 2>/dev/null || true
systemctl start wg-quick@wg0 2>/dev/null || true
echo ""
echo "[✓] WireGuard Server configured"
echo ""
echo "Configuration Summary:"
echo " • Interface: wg0"
echo " • Server IP: 10.0.0.1/24"
echo " • Listen: 0.0.0.0:51820/udp"
echo " • Config: /etc/wireguard/wg0.conf"
echo ""
echo "Server Public Key (for clients):"
cat publickey
echo ""
echo "Next steps:"
echo " 1. Create client configs with wg-quick or manually"
echo " 2. Add peers to wg0:"
echo " wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.X/32"
@@ -1,35 +0,0 @@
<!-- ═══════════════════════════════════════════════════════════════
Decoders ModSecurity — JSON Audit Log (libmodsecurity3)
Format : {"transaction":{"client_ip":"...","request":{"uri":"..."},
"response":{"http_code":N},"messages":[{"details":{"ruleId":"N"}}]}}
Collecté via log_format:json sur les fichiers Docker container log
(Wazuh extrait le champ "log" du wrapper Docker JSON avant décodage)
═══════════════════════════════════════════════════════════════ -->
<!-- Parent : toute transaction ModSecurity JSON -->
<decoder name="docker-waf-modsec">
<prematch>{"transaction":{</prematch>
</decoder>
<!-- Accès bloqué (http_code exactement 403) avec règle déclenchée.
Listé en premier — priorité sur docker-waf-modsec-rule. -->
<decoder name="docker-waf-modsec-blocked">
<parent>docker-waf-modsec</parent>
<regex>"client_ip":"(\d+\.\d+\.\d+\.\d+)".+"uri":"([^"]+)".+"http_code":403.+"ruleId":"(\d+)"</regex>
<order>srcip,url,id</order>
</decoder>
<!-- Règle déclenchée sans blocage (warning / anomaly scoring).
Capte aussi les non-403 avec ruleId. -->
<decoder name="docker-waf-modsec-rule">
<parent>docker-waf-modsec</parent>
<regex>"client_ip":"(\d+\.\d+\.\d+\.\d+)".+"uri":"([^"]+)".+"ruleId":"(\d+)"</regex>
<order>srcip,url,id</order>
</decoder>
<!-- Trafic passant sans règle matchée (fallback) -->
<decoder name="docker-waf-modsec-pass">
<parent>docker-waf-modsec</parent>
<regex>"client_ip":"(\d+\.\d+\.\d+\.\d+)".+"uri":"([^"]+)"</regex>
<order>srcip,url</order>
</decoder>
@@ -1,37 +0,0 @@
<!-- ═══════════════════════════════════════════════════════════════
Règles ModSecurity — WAF container (gestion-waf)
Source : logs JSON audit libmodsecurity3 via docker-waf-modsec*
srcip = client_ip, url = uri, id = ruleId
═══════════════════════════════════════════════════════════════ -->
<group name="web,modsecurity,">
<!-- Trafic passant sans règle déclenchée — supprimé (niveau 0) -->
<rule id="100100" level="0">
<decoded_as>docker-waf-modsec-pass</decoded_as>
<description>ModSecurity: trafic légitime (supprimé).</description>
</rule>
<!-- Règle WAF déclenchée sans blocage (warning / anomaly scoring) -->
<rule id="100101" level="6">
<decoded_as>docker-waf-modsec-rule</decoded_as>
<description>ModSecurity: règle $(id) — $(srcip) → $(url)</description>
<group>web,modsecurity,attack,</group>
</rule>
<!-- Accès bloqué (HTTP 403) -->
<rule id="100102" level="10">
<decoded_as>docker-waf-modsec-blocked</decoded_as>
<description>ModSecurity: accès bloqué (403) — $(srcip) → $(url) [règle $(id)]</description>
<group>web,modsecurity,attack,blocked,</group>
</rule>
<!-- Blocages répétés depuis la même IP → scan ou attaque -->
<rule id="100103" level="12" frequency="5" timeframe="60">
<if_matched_sid>100102</if_matched_sid>
<same_source_ip />
<description>ModSecurity: blocages répétés depuis $(srcip) — scan ou attaque</description>
<group>web,modsecurity,attack,blocked,</group>
</rule>
</group>
@@ -1,94 +0,0 @@
<group name="web,modsecurity,attack,">
<!-- ─────────────────────────────────────────────────────────
Règles de base (décodeurs modsecurity-block / warning)
───────────────────────────────────────────────────────── -->
<!-- Bloc générique ModSecurity (403) -->
<rule id="100200" level="8">
<decoded_as>modsecurity-block</decoded_as>
<description>ModSecurity: requête bloquée ($(extra_data)) sur $(url)</description>
<group>modsecurity_block,</group>
</rule>
<!-- Warning générique ModSecurity (non-bloquant) -->
<rule id="100201" level="5">
<decoded_as>modsecurity-warning</decoded_as>
<description>ModSecurity: alerte $(extra_data) sur $(url)</description>
<group>modsecurity_warning,</group>
</rule>
<!-- ─────────────────────────────────────────────────────────
Règles spécialisées par type d'attaque
(parentes : 100200 — blocs déjà détectés)
───────────────────────────────────────────────────────── -->
<!-- SQL Injection -->
<rule id="100202" level="12">
<if_sid>100200</if_sid>
<match>SQL Injection</match>
<description>ModSecurity: SQL Injection détecté depuis $(srcip) sur $(url)</description>
<group>modsecurity_sqli,sql_injection,</group>
</rule>
<!-- XSS -->
<rule id="100203" level="12">
<if_sid>100200</if_sid>
<match>XSS</match>
<description>ModSecurity: XSS détecté depuis $(srcip) sur $(url)</description>
<group>modsecurity_xss,xss,</group>
</rule>
<!-- Remote Code Execution -->
<rule id="100204" level="14">
<if_sid>100200</if_sid>
<match>RCE</match>
<description>ModSecurity: tentative RCE depuis $(srcip) sur $(url)</description>
<group>modsecurity_rce,rce,</group>
</rule>
<!-- Local File Inclusion -->
<rule id="100205" level="12">
<if_sid>100200</if_sid>
<match>LFI</match>
<description>ModSecurity: LFI détecté depuis $(srcip) sur $(url)</description>
<group>modsecurity_lfi,lfi,</group>
</rule>
<!-- Score d'anomalie critique -->
<rule id="100206" level="13">
<if_sid>100200</if_sid>
<match>Critical anomaly score\|anomaly score</match>
<description>ModSecurity: score d'anomalie critique depuis $(srcip)</description>
<group>modsecurity_anomaly,</group>
</rule>
<!-- IP bannie par ModSecurity (règle 100000) -->
<rule id="100207" level="10">
<if_sid>100200</if_sid>
<match>IP is banned\|ip.banned</match>
<description>ModSecurity: IP bannie $(srcip) tente un accès</description>
<group>modsecurity_banned,</group>
</rule>
<!-- Trop de requêtes (rate-limit 429) -->
<rule id="100208" level="6">
<decoded_as>modsecurity-block</decoded_as>
<field name="status">429</field>
<description>ModSecurity: rate-limit déclenché depuis $(srcip)</description>
<group>modsecurity_ratelimit,</group>
</rule>
<!-- ─────────────────────────────────────────────────────────
Règle de fréquence : blocages répétés → niveau critique
Déclenche active-response firewall-drop 24h
───────────────────────────────────────────────────────── -->
<rule id="100210" level="14" frequency="5" timeframe="120">
<if_matched_sid>100200</if_matched_sid>
<same_srcip />
<description>ModSecurity: $(srcip) bloqué 5 fois en 2 min — attaque soutenue</description>
<group>modsecurity_repeated,</group>
</rule>
</group>