Update .gitignore to exclude fail2ban data directory, clean up docker-compose.yaml by removing unused volume mappings, and add new Fail2Ban filter configurations for enhanced security against various attack vectors.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
|||||||
database
|
database
|
||||||
assets
|
assets
|
||||||
fail2ban
|
fail2ban/data
|
||||||
https_portal
|
https_portal
|
||||||
logs
|
logs
|
||||||
nginx
|
nginx
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ services:
|
|||||||
start_period: 60s # Grace period before health checks start
|
start_period: 60s # Grace period before health checks start
|
||||||
volumes:
|
volumes:
|
||||||
- "./assets:/usr/src/app/packages/dev-server/assets"
|
- "./assets:/usr/src/app/packages/dev-server/assets"
|
||||||
- "./server_custom:/usr/src/app/packages/dev-server/custom"
|
|
||||||
# - "./logs:/usr/src/app/packages/dev-server/logs"
|
# - "./logs:/usr/src/app/packages/dev-server/logs"
|
||||||
phoenix-worker:
|
phoenix-worker:
|
||||||
restart: always
|
restart: always
|
||||||
@@ -306,7 +306,7 @@ services:
|
|||||||
start_period: 30s # Grace period before health checks start
|
start_period: 30s # Grace period before health checks start
|
||||||
volumes:
|
volumes:
|
||||||
- "./assets:/usr/src/app/packages/dev-server/assets"
|
- "./assets:/usr/src/app/packages/dev-server/assets"
|
||||||
- "./server_custom:/usr/src/app/packages/dev-server/custom"
|
|
||||||
# - "./logs:/usr/src/app/packages/dev-server/logs"
|
# - "./logs:/usr/src/app/packages/dev-server/logs"
|
||||||
phoenix-redis:
|
phoenix-redis:
|
||||||
image: 'bitnami/redis:latest'
|
image: 'bitnami/redis:latest'
|
||||||
|
|||||||
8
fail2ban/filter.d/http-get-dos-compressed.conf
Normal file
8
fail2ban/filter.d/http-get-dos-compressed.conf
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Definition]
|
||||||
|
# Count any GET/POST from the same client IP. We prefer X-Forwarded-For
|
||||||
|
# (real client IP) when present; otherwise fall back to remote_addr.
|
||||||
|
failregex = ^.*"x_forwarded_for":"<HOST>(?:, [^"]+)?".*"request_method":"(?:GET|POST)".*$
|
||||||
|
^.*"remote_addr":"<HOST>".*"request_method":"(?:GET|POST)".*$
|
||||||
|
|
||||||
|
# Ignore safe/health endpoints (adjust to your env)
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
8
fail2ban/filter.d/http-get-dos.conf
Normal file
8
fail2ban/filter.d/http-get-dos.conf
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Definition]
|
||||||
|
# Count lots of requests from same IP regardless of status code.
|
||||||
|
# Prefer X-Forwarded-For (client IP behind proxy), fallback to remote_addr.
|
||||||
|
failregex = ^.*"x_forwarded_for":"<HOST>".*"(GET|POST|HEAD)".*$
|
||||||
|
^.*"remote_addr":"<HOST>".*"(GET|POST|HEAD)".*$
|
||||||
|
|
||||||
|
# Ignore health and stub_status hits
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
6
fail2ban/filter.d/nginx-429.conf
Normal file
6
fail2ban/filter.d/nginx-429.conf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Definition]
|
||||||
|
# JSON access log with x_forwarded_for preferred, else remote_addr
|
||||||
|
failregex = ^.*"x_forwarded_for":"<HOST>(?:, [^"]+)?".*"status":429.*$
|
||||||
|
^.*"remote_addr":"<HOST>".*"status":429.*$
|
||||||
|
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
6
fail2ban/filter.d/nginx-4xx.conf
Normal file
6
fail2ban/filter.d/nginx-4xx.conf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Definition]
|
||||||
|
# Match either x_forwarded_for (preferred) or remote_addr
|
||||||
|
failregex = ^.*"x_forwarded_for":"<HOST>".*"status":4\d\d.*$
|
||||||
|
^.*"remote_addr":"<HOST>".*"status":4\d\d.*$
|
||||||
|
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
12
fail2ban/filter.d/nginx-badbots.conf
Normal file
12
fail2ban/filter.d/nginx-badbots.conf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Definition]
|
||||||
|
# JSON logs with ISO-8601 timestamps
|
||||||
|
datepattern = {^LN-BEG}%%Y-%%m-%%dT%%H:%%M:%%S(?:[.,]\\d+)?(?:Z|[+\\-]\\d{2}:\\d{2})?
|
||||||
|
|
||||||
|
# Catch typical scanners and CLI/automation libraries (case-insensitive via (?i))
|
||||||
|
# Prefer x_forwarded_for (real client IP) if present; fall back to remote_addr.
|
||||||
|
# NOTE: One "failregex =" key, multiple indented lines. No backslashes for wrapping.
|
||||||
|
failregex = (?i)^.*"x_forwarded_for":"<HOST>(?:, [^"]+)?".*"http_user_agent":"[^"]*(?:sqlmap|nikto|acunetix|wpscan|dirbuster|gobuster|masscan|zgrab|ZmEu|nessus|openvas|libwww-perl|mechanize|lwp-trivial|python-requests|python-urllib|urllib|aiohttp|httpx|scrapy|curl|wget|Go-http-client|okhttp|httpclient|jakarta|java)[^"]*".*$
|
||||||
|
(?i)^.*"remote_addr":"<HOST>".*"http_user_agent":"[^"]*(?:sqlmap|nikto|acunetix|wpscan|dirbuster|gobuster|masscan|zgrab|ZmEu|nessus|openvas|libwww-perl|mechanize|lwp-trivial|python-requests|python-urllib|urllib|aiohttp|httpx|scrapy|curl|wget|Go-http-client|okhttp|httpclient|jakarta|java)[^"]*".*$
|
||||||
|
|
||||||
|
# Ignore your health/status endpoints
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
6
fail2ban/filter.d/nginx-botsearch.conf
Normal file
6
fail2ban/filter.d/nginx-botsearch.conf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Definition]
|
||||||
|
datepattern = {^LN-BEG}%%Y-%%m-%%dT%%H:%%M:%%S(?:[.,]\\d+)?(?:Z|[+\\-]\\d{2}:\\d{2})?
|
||||||
|
failregex = ^.*"x_forwarded_for":"<HOST>(?:, [^"]+)?".*"request_uri":"/(?:\\.env|\\.git/|wp-login\\.php|xmlrpc\\.php|wp-admin(?:/|$)|phpinfo\\.php|vendor/phpunit|setup\\.php|manager/html|id\\.php|shell\\.php|\\.DS_Store)[^"]*".*"status":(?:40[0-4]|403|404).*$
|
||||||
|
^.*"remote_addr":"<HOST>".*"request_uri":"/(?:\\.env|\\.git/|wp-login\\.php|xmlrpc\\.php|wp-admin(?:/|$)|phpinfo\\.php|vendor/phpunit|setup\\.php|manager/html|id\\.php|shell\\.php|\\.DS_Store)[^"]*".*"status":(?:40[0-4]|403|404).*$
|
||||||
|
|
||||||
|
ignoreregex = ^.*"request_uri":"\/(?:stub_status|health\/system|health\/worker|pgadmin4(?:\/|$)|\.well-known\/acme-challenge\/|.*\.(?:css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?))".*$
|
||||||
7
fail2ban/filter.d/sshd-pubkey.conf
Normal file
7
fail2ban/filter.d/sshd-pubkey.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[Definition]
|
||||||
|
failregex = ^%(__prefix_line)s(?:error: )?Received disconnect from <HOST> port \d+: .* \[preauth\]$
|
||||||
|
^%(__prefix_line)sInvalid user .* from <HOST> port \d+ \[preauth\]$
|
||||||
|
^%(__prefix_line)sFailed publickey for .* from <HOST> port \d+ ssh2$
|
||||||
|
^%(__prefix_line)sConnection closed by (invalid user )?.* <HOST> port \d+ \[preauth\]$
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
7
fail2ban/jail.d/00-defaults.local
Normal file
7
fail2ban/jail.d/00-defaults.local
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
banaction = iptables-multiport
|
||||||
|
# Push the rule into DOCKER-USER and cover your ports
|
||||||
|
action = iptables-multiport[name=nginx, port="80,443,3000", protocol=tcp, chain=DOCKER-USER]
|
||||||
|
ignoreip = 127.0.0.1/8 ::1 \
|
||||||
|
172.19.0.0/16 172.20.0.0/16 172.22.0.0/16 \
|
||||||
|
5.75.153.161 167.235.254.4
|
||||||
88
fail2ban/jail.d/nginx-phoenix.local
Normal file
88
fail2ban/jail.d/nginx-phoenix.local
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# /etc/fail2ban/jail.d/nginx-phoenix.local
|
||||||
|
[DEFAULT]
|
||||||
|
backend = polling
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
# ban timing: start short, escalate for repeat offenders, add jitter to avoid stampedes
|
||||||
|
bantime = 5m
|
||||||
|
bantime.increment = true
|
||||||
|
bantime.factor = 1.5
|
||||||
|
bantime.overalljails = true
|
||||||
|
bantime.rndtime = 60s
|
||||||
|
|
||||||
|
# honor your global ignore list from 00-defaults.local (don’t repeat here)
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Common scanners / bad bots
|
||||||
|
# -----------------------------
|
||||||
|
[nginx-badbots]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-badbots
|
||||||
|
logpath = /data/nginx-logs/access_json.log
|
||||||
|
port = 80,443,3000
|
||||||
|
findtime = 2m
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 30m
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Bot search / generic probing
|
||||||
|
# (odd paths, wp-admin, phpinfo, etc.)
|
||||||
|
# -----------------------------
|
||||||
|
[nginx-botsearch]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-botsearch
|
||||||
|
logpath = /data/nginx-logs/access_json.log
|
||||||
|
port = 80,443,3000
|
||||||
|
findtime = 5m
|
||||||
|
maxretry = 6
|
||||||
|
bantime = 30m
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Many 4xx in a short window
|
||||||
|
# (likely brute/scan or broken client)
|
||||||
|
# -----------------------------
|
||||||
|
[nginx-4xx]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-4xx
|
||||||
|
logpath = /data/nginx-logs/access_json.log
|
||||||
|
port = 80,443,3000
|
||||||
|
findtime = 5m
|
||||||
|
maxretry = 20
|
||||||
|
bantime = 15m
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Simple HTTP GET/POST flood
|
||||||
|
# (rate-based; pairs well with nginx rate limit) http-get-dos
|
||||||
|
# Not in use anymore, because to avoid bloking phx url paths, i would have to manually add all of them to avoi be banned. Instead we use
|
||||||
|
# nginx-429 witch is managed by nginx and once it hits the rate limit, it send back a 429 status code.
|
||||||
|
# -----------------------------
|
||||||
|
# [http-get-dos]
|
||||||
|
# enabled = true
|
||||||
|
# filter = http-get-dos-compressed
|
||||||
|
# logpath = /data/nginx-logs/access_json.log
|
||||||
|
# port = 80,443,3000
|
||||||
|
# findtime = 60s
|
||||||
|
# maxretry = 20
|
||||||
|
# bantime = 10m
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# (Optional) recidive: longer ban for repeat offenders across jails
|
||||||
|
# Requires fail2ban.log inside the container (already present by default)
|
||||||
|
# -----------------------------
|
||||||
|
# [recidive]
|
||||||
|
# enabled = true
|
||||||
|
# logpath = /data/fail2ban.log /var/log/fail2ban.log
|
||||||
|
# backend = auto
|
||||||
|
# banaction = nftables-allports
|
||||||
|
# findtime = 12h
|
||||||
|
# maxretry = 4
|
||||||
|
# bantime = 24h
|
||||||
|
|
||||||
|
[nginx-429]
|
||||||
|
enabled = true
|
||||||
|
filter = nginx-429
|
||||||
|
logpath = /data/nginx-logs/access_json.log
|
||||||
|
port = 80,443,3000
|
||||||
|
findtime = 60s
|
||||||
|
maxretry = 10
|
||||||
|
bantime = 10m
|
||||||
17
fail2ban/jail.d/sshd.local
Normal file
17
fail2ban/jail.d/sshd.local
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
banaction = nftables-multiport
|
||||||
|
backend = auto
|
||||||
|
findtime = 10m
|
||||||
|
bantime = 30m
|
||||||
|
maxretry = 3
|
||||||
|
bantime.increment = false
|
||||||
|
bantime.factor = 1
|
||||||
|
bantime.maxtime = 24h
|
||||||
|
mode = aggressive
|
||||||
|
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = 2522
|
||||||
|
filter = sshd
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
action = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="tcp"]
|
||||||
Reference in New Issue
Block a user