# Fail2Ban filter: DokuWiki media-manager parameter-space crawling. # # Install as: /etc/fail2ban/filter.d/apache-vhost-dokuwiki-crawl.conf # # Matches Apache "vhost_combined" (other_vhosts_access.log), same format as # apache-vhost-wp-probe.conf. # # DEPLOY ORDER MATTERS. This traffic arrives as HTTP 200 - DokuWiki is # serving it successfully - so it cannot be told apart from a legitimate # editor by status code alone. Add the Apache rule from # dokuwiki-block.md FIRST so anonymous do=media requests become 403, then # enable this filter, which counts only the 403s. A logged-in editor still # gets 200 and is never counted, so there is no way for this to ban your # own people. [Definition] datepattern = ^\[%%d/%%b/%%Y:%%H:%%M:%%S %%z\] # do=media is DokuWiki's fullscreen media manager - an editor UI surface, # never something an anonymous reader or a well-behaved crawler needs. Its # tab_details/tab_files/ns/image parameters multiply combinatorially, which # is exactly what was being enumerated. Paired with the Apache rule, a 4xx # here means "a cookieless client asked for the media manager" - # unambiguous. failregex = ^\s*\S+ \S+ \S+ "(?:GET|POST|HEAD) [^"]*[?&]do=media(?:&[^"]*)? HTTP/[^"]+" (?:4\d\d|5\d\d) \d+ # ALTERNATIVE - status-agnostic, for immediate relief BEFORE the Apache rule # is in place. This matches successful (200) requests too, so it CAN ban a # logged-in editor who opens the media manager more than maxretry times. # Use it only as a stopgap, and swap back to the line above afterwards: # ^\s*\S+ \S+ \S+ "(?:GET|POST|HEAD) [^"]*[?&]do=media(?:&[^"]*)? HTTP/[^"]+" \d+ \d+ # Optionally also catch the other expensive dynamic actions crawlers explode # on. Commented out because real anonymous readers legitimately use # diff/revisions/export_pdf on a public wiki - enable only if yours is # read-restricted: # ^\s*\S+ \S+ \S+ "(?:GET|POST|HEAD) [^"]*[?&]do=(?:diff|revisions|recent|index|backlink|export_[a-z]+)(?:&[^"]*)? HTTP/[^"]+" (?:4\d\d|5\d\d) \d+ # Never count the real search-engine crawlers. They identify honestly and # obey robots.txt; this incident forged Chrome UAs instead. ignoreregex = "(?:[^"]*(?:Googlebot|bingbot|DuckDuckBot|Applebot|YandexBot)[^"]*)"$