130 lines
2.9 KiB
Markdown
130 lines
2.9 KiB
Markdown
```docker-compose.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
nginx:
|
||
image: owasp/modsecurity:nginx
|
||
container_name: secure_nginx
|
||
ports:
|
||
- "80:80"
|
||
volumes:
|
||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||
- ./nginx/html:/usr/share/nginx/html:ro
|
||
- ./nginx/modsecurity/modsecurity.conf:/etc/modsecurity/modsecurity.conf:ro
|
||
- ./nginx/log:/var/log/nginx
|
||
restart: always
|
||
|
||
fail2ban:
|
||
image: crazymax/fail2ban:latest
|
||
container_name: fail2ban
|
||
volumes:
|
||
- ./fail2ban/jail.local:/data/jail.local:ro
|
||
- ./fail2ban/filter.d:/data/filter.d:ro
|
||
- ./nginx/log:/var/log/nginx:ro
|
||
- fail2ban-data:/data
|
||
restart: always
|
||
cap_add:
|
||
- NET_ADMIN
|
||
- NET_RAW
|
||
|
||
volumes:
|
||
fail2ban-data:
|
||
```
|
||
|
||
```conf
|
||
worker_processes 1;
|
||
|
||
events {
|
||
worker_connections 1024;
|
||
}
|
||
|
||
http {
|
||
include mime.types;
|
||
default_type application/octet-stream;
|
||
|
||
modsecurity on;
|
||
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
|
||
|
||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||
'$status $body_bytes_sent "$http_referer" '
|
||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||
|
||
access_log /var/log/nginx/access.log main;
|
||
|
||
limit_req_zone $binary_remote_addr zone=limit1:10m rate=5r/s;
|
||
|
||
server {
|
||
listen 80;
|
||
server_name localhost;
|
||
|
||
root /usr/share/nginx/html;
|
||
index index.html;
|
||
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||
|
||
location / {
|
||
limit_req zone=limit1 burst=10 nodelay;
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
nginx/html/index.html
|
||
|
||
```
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>ModSecurity + Fail2Ban</title>
|
||
</head>
|
||
<body>
|
||
<h1>Güvenlik Duvarı Aktif!</h1>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
nginx/modsecurity/modsecurity.conf
|
||
|
||
```
|
||
SecRuleEngine On
|
||
SecRequestBodyAccess On
|
||
SecResponseBodyAccess Off
|
||
|
||
SecRule REQUEST_HEADERS:User-Agent "curl" "id:10001,phase:1,deny,status:403,msg:'Curl client blocked'"
|
||
|
||
# Basit rate limit sayacı
|
||
SecAction "id:10010,phase:1,initcol:ip=%{REMOTE_ADDR},pass,nolog"
|
||
SecRule IP:REQCOUNT "@gt 20" "id:10011,phase:2,deny,status:429,msg:'Too many requests'"
|
||
SecAction "id:10012,phase:2,pass,nolog,setvar:ip.reqcount=+1"
|
||
```
|
||
|
||
fail2ban/jail.local
|
||
|
||
```
|
||
[nginx-req-limit]
|
||
enabled = true
|
||
filter = nginx-req-limit
|
||
action = iptables[name=HTTP, port=http, protocol=tcp]
|
||
logpath = /var/log/nginx/access.log
|
||
maxretry = 5
|
||
findtime = 60
|
||
bantime = 3600
|
||
```
|
||
|
||
fail2ban/filter.d/nginx-req-limit.conf
|
||
|
||
```
|
||
[Definition]
|
||
failregex = <HOST> -.* "(GET|POST).*HTTP.*" 429
|
||
ignoreregex =
|
||
|
||
```
|
||
|
||
mkdir -p nginx/html nginx/modsecurity fail2ban/filter.d
|
||
docker-compose up -d
|