|
All checks were successful
Push to ilunix Registry / build-and-push (push) Successful in 7s
|
||
|---|---|---|
| .forgejo/workflows | ||
| locale/de | ||
| .dockerignore | ||
| .gitignore | ||
| authelia-watcher-cleanup | ||
| CHANGELOG.md | ||
| cleanup.sh | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| healthcheck.sh | ||
| LICENSE | ||
| README.md | ||
| VERSION | ||
| watch.sh | ||
🛡️ Authelia-Watcher (Multi-App Mode)
A lightweight Docker-based log monitor that automatically bans malicious IPs using iptables on the host. It monitors multiple applications (Authelia, Nextcloud, Vaultwarden, etc.), sends real-time notifications via Gotify, and automatically cleans up old bans.
This project is deeply integrated with the CLI Docker Manager (clidmanager), which handles configuration management and DynDNS updates.
🚀 Features
- Robust Health & Validation: A comprehensive healthcheck monitors critical components (main process, cron, config). The container will not start with an invalid
watcher-config.jsonand provides clear error messages. - Automatic Ban Expiration: Old bans are automatically removed from the blacklist and
iptablesafter a configurable period (default: 4 days) to keep the firewall ruleset clean and performant. - Multi-App Support: Monitor an unlimited number of containers via JSON configuration.
- File Monitoring: Monitor local log files (e.g. SSH
auth.log) to replace Fail2Ban. - Iptables Integration: Blocks attackers directly on the host's kernel level.
- Resilient Monitoring: Automatically reconnects to container log streams if they restart, ensuring no events are missed.
- Subnet Protection: Detects attacks from the same network. If a /24 subnet exceeds the limit, it is automatically banned.
- DynDNS Protection: Automatically whitelists your dynamic IP so you never lock yourself out.
- Gotify Notifications: Instant alerts for bans, container starts, or IP updates. Supports Batch Mode (summarized reports) or Immediate Mode.
- I18n: Fully localized (German & English supported).
🧩 CLI Docker Manager (clidmanager)
For the best experience, using the CLI Docker Manager is highly recommended. It provides a dedicated management interface for the Authelia-Watcher, including .env configuration and whitelist maintenance.
🔗 Project Link: https://git.ilunix.de/punix/CLI_Docker_Manager
IMPORTANT: The required host script
update_dyndns.shfor automatic whitelist synchronization is also located in the clidmanager repository and is automatically installed to/usr/local/bin/by its installer. jq and dig (dnsutils) are required for update_dyndns.sh crontab -e * * * * * /usr/local/bin/update_dyndns.sh > /dev/null 2>&1
🛠️ Installation & Setup
- Create project directory:
mkdir -p /opt/containers/authelia-watcher/config
cd /opt/containers/authelia-watcher
🛡️ Protection Strategy
The configuration follows a multi-layered security approach:
Instant Ban (Limit 1): Known exploit patterns and unauthorized access to honeypot paths (e.g., .env, setup.php) lead to an immediate ban.
App-Specific Protection: Tailored rules for services like Forgejo, Jellyfin, and Authelia to prevent brute-force attacks on login endpoints.
Heuristic Protection: Global rules for 404 errors and DDoS patterns to stop scanners even if they don't hit a specific honeypot.
System Health: Monitoring for 502/500 errors to get notified if a backend service crashes.
Subnet Protection: If multiple attacks originate from the same /24 subnet, the entire range is blocked to stop distributed attacks.
- Configure your rules: Create config/watcher-config.json.
{
"gotify_url": "https://gotify.example.com/message?token=YOUR_TOKEN",
"rules": [
{
"name": "Critical: Sofort-Ban (Exploits & Scans)",
"container_name": "caddy",
"description": "Ban RCE, PHPUnit, Laravel etc. (Paths the never are legal)",
"search_term": "(\\.env|\\.git|phpunit|eval-stdin|bin/sh|invokefunction|_ignition|execute-solution|setup\\.cgi|pearcmd|syscmd|/v1/chat/completions|/v1/models|/teal-info|/aws/credentials)",
"limit": 1
},
{
"name": "Globaler WordPress & PHP-Scan Protection",
"container_name": "caddy",
"description": "Ban IPs looking for PHP/WP whether status 308, 404 or 403",
"search_term": "(wp-includes|wp-content|wp-admin|xmlrpc\\.php|wlwmanifest\\.xml|\\.php)",
"limit": 3
},
{
"name": "Caddy: Globaler Honeypot & IP-Direct",
"container_name": "caddy",
"description": "Protects against direct IP scans and Honeypot thread",
"search_term": "(Honeypot|Unauthorized Path|host.:.[0-9.]+.*status.:403)",
"limit": 1
},
{
"name": "Forgejo: Aggressive Subnet Scan Protection",
"container_name": "caddy",
"description": "Blocks entire subnetworks in attempts on admin/edit path",
"search_term": "git\\.example\\.com.*(/_edit/|/_new/|/_delete/|/_upload/|/_diffpatch/|/pulls/|/issues/)",
"limit": 5,
"subnet_limit": 15
},
{
"name": "Forgejo: Massen-Abzug Protection",
"container_name": "caddy",
"description": "Allows liquid work in the browser, but stops aggressive mirror attempts",
"search_term": "git\\.example\\.com.*(\\.sh|\\.py|/raw/|/archive/|/src/commit/).*status\":200",
"limit": 150,
"subnet_limit": 300
},
{
"name": "Forgejo: API & 403 Buffer",
"container_name": "caddy",
"description": "Protects from API scans but leaves the admin buffer for errors",
"search_term": "git\\.example\\.com.*status\":(401|403|404)",
"limit": 15,
"subnet_limit": 30
},
{
"name": "Filebrowser: Login & Upload Protection",
"container_name": "caddy",
"description": "Enough buffer for many API requests at large uploads",
"search_term": "filebrowser\\.example\\.com.*(/api/login|/api/renew|/api/settings|/api/resources).*status.:(401|403)",
"limit": 20
},
{
"name": "Login-Protection: Services",
"container_name": "caddy",
"description": "Brute-Force Protection for WG, Jellyfin, Navidrome, Gotify ect.",
"search_term": "(wiregard-gui|jellyfin|navidrome|gotify)\\.example\\.com.*status.:(401|403|429)",
"limit": 5
},
{
"name": "Authelia: Login-Protection",
"container_name": "authelia",
"search_term": "(Unsuccessful 1FA|Unsuccessful 2FA|is not authorized to user|Remote-User header not found)",
"limit": 5
},
{
"name": "Globaler-404-Protection",
"container_name": "caddy",
"description": "Locks IPs that call up too many non-existent pages",
"search_term": ".*\"status\":404",
"limit": 30
},
{
"name": "System: Backend Error Monitor",
"container_name": "caddy",
"description": "Reports server errors, but does not run the user immediately",
"search_term": "status.:(502|500|503|504)",
"limit": 100
}
]
}
- (Optional) Create
config/.envfile for advanced settings.
Create a file named .env inside your config directory to customize the watcher's behavior. This file is also used by the clidmanager's update_dyndns.sh script.
# DynDNS & Whitelist Settings (for update_dyndns.sh)
# Your dynamic domain name (e.g., myhome.duckdns.org)
MY_DOMAIN=""
# Interval in seconds to check for IP changes
CHECK_INTERVAL=300
# Send a Gotify notification when the watcher container starts (1=On, 0=Off)
SEND_WATCHER_START_NOTIFICATION=1
# Send a Gotify notification when the script starts (1=On, 0=Off)
SEND_WELCOME_NOTIFICATION=1
# Send a Gotify notification on IP updates (1=On, 0=Off)
SEND_UPDATE_NOTIFICATION=1
# Authelia-Watcher Settings
# Notification mode: 'batch' (default) or 'immediate'
NOTIFICATION_MODE=batch
# Number of bans before sending a report in 'batch' mode
BATCH_SIZE=10
# Automatically unban IPs after N days. Set to 0 to disable. (Default: 4)
BLACKLIST_RETENTION_DAYS=""
- Run with Docker Compose: The watcher requires host network access and the Docker socket.
services:
authelia-watcher:
image: lxtools/authelia-watcher:latest
container_name: authelia-watcher
restart: unless-stopped
# CRITICAL: This label is used by 'clidmanager' to identify the container
labels:
- "service=authelia-watcher"
# Required for host firewall (iptables) access
network_mode: host
# Security capabilities for network management
cap_add:
- NET_ADMIN
env_file:
- ./config/.env
volumes:
# Access to Docker daemon to read logs from other containers
- /var/run/docker.sock:/var/run/docker.sock:ro
# Configuration directory (contains watcher-config.json)
- ./config:/config
# Time zone of the host (for better logtimes)
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
# For file monitoring (e.g. SSH), mount the log file:
# - /var/log/auth.log:/var/log/auth.log:ro
# Mount host hostname for correct naming in Gotify
- /etc/hostname:/etc/host_hostname:ro
# Map host log file for visibility (e.g. in Portainer console)
- type: bind
source: /var/log/dyndns_manager.log
target: /var/log/dyndns_manager.log
read_only: true
bind:
create_host_path: false
environment:
# Optional: Set timezone if different from host
# - TZ=Europe/Berlin
# Note: For other settings like BLACKLIST_RETENTION_DAYS,
# add them to your ./config/.env file.
- My Caddyfile example:
(log_access) {
log {
output stdout
}
}
(auth_check) {
forward_auth authelia:9091 {
uri /api/verify?rd=https://authelia.example.com/
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
}
(bot_grill) {
# SILENT PROTECTION LOGIC
@silent_block {
# first path for protection
path *.sql
not header_regexp Referer https?://git\.example\.com/.*
not header User-Agent *wget*
not header User-Agent *curl*
not header User-Agent *git*
not header User-Agent *F-Droid*
not header User-Agent *Android*
}
# No kein Referer -> Rewrite on Startpage
rewrite @silent_block /
# --- EXPLOIT PROTECTION (IMMEDIATE BAN) ---
# never legal paths
@bot_target_paths {
path */.env* */.config* */wp-login.php* */wp-admin*
}
handle @bot_target_paths {
respond "Access Denied - Security Block" 403
}
}
(security_bundle) {
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
import bot_grill
}
# --- GLOBAL & DOMAINS ---
:80, :443 {
import log_access
respond "Direct IP access not allowed" 403
}
# DOMAINS
heimdall.example.com {
import log_access
import security_bundle
handle {
import auth_check
reverse_proxy heimdall:80
}
}
authelia.example.com {
import log_access
import security_bundle
import block_php
handle {
reverse_proxy authelia:9091 {
header_up Host {host}
header_up X-Real-IP {remote_host}
}
}
}
🌐 DynDNS & Whitelisting
To ensure your own IP address is automatically recognized and excluded from bans:
Use the clidmanager to set your domain and check interval in the .env file.
The update_dyndns.sh script (installed via clidmanager) runs in the background on the host to update whitelist.txt.
🌍 Internationalization (i18n)
To change the language, set the LANG environment variable:
de_DE.UTF-8 (German)
en_US.UTF-8 (English - Default)
⚠️ How to Unban an IP
If you accidentally ban yourself:
Via clidmanager (Recommended): Use Option 7 (Watcher Manager) -> Option 2 (Unban IP). This cleans both the firewall and the internal blacklist.
Run both commands!
Manually via Terminal: iptables -D INPUT -s <YOUR_IP> -j DROP
iptables -D DOCKER-USER -s <YOUR_IP> -j DROP
⚖️ License
This project is licensed under the GNU General Public License v3.0 (GPL-3.0). See the LICENSE file for details.
Copyright (C) 2026 Désiré Werner Menrath