No description
Find a file
Désiré Werner Menrath 58584f437e
All checks were successful
Push to ilunix Registry / build-and-push (push) Successful in 7s
fix version number
2026-02-22 10:47:28 +01:00
.forgejo/workflows fix version number 2026-02-22 10:47:28 +01:00
locale/de - Batch notification logic corrected (buffer was cleared but not sent) 2026-02-22 10:42:30 +01:00
.dockerignore replace push script 2026-02-11 00:05:37 +01:00
.gitignore replace push script 2026-02-11 00:05:37 +01:00
authelia-watcher-cleanup all files tranlate, add health check, watcher-config check 2026-02-18 20:27:53 +01:00
CHANGELOG.md - Batch notification logic corrected (buffer was cleared but not sent) 2026-02-22 10:44:56 +01:00
cleanup.sh - edit readme replace domain 2026-02-19 16:37:16 +01:00
docker-compose.yml - Documentation: Replaced specific domains with example.com in README rules. 2026-02-18 16:33:22 +01:00
Dockerfile fix view log 2026-02-19 17:04:08 +01:00
entrypoint.sh - edit readme replace domain 2026-02-19 16:37:16 +01:00
healthcheck.sh all files tranlate, add health check, watcher-config check 2026-02-18 20:27:53 +01:00
LICENSE performance fix1 2026-02-04 10:49:01 +01:00
README.md remove .gitognore on caddy example and rules 2026-02-20 12:34:02 +01:00
VERSION - Batch notification logic corrected (buffer was cleared but not sent) 2026-02-22 10:42:30 +01:00
watch.sh - Batch notification logic corrected (buffer was cleared but not sent) 2026-02-22 10:42:30 +01:00

🛡️ 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.json and provides clear error messages.
  • Automatic Ban Expiration: Old bans are automatically removed from the blacklist and iptables after 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.sh for 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

  1. 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.
  1. 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
    }
  ]
}

  1. (Optional) Create config/.env file 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=""
  1. 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.
    
  1. 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