Skip to content

13 Agent Prompts Fail2Ban Setup

Mikhail Deynekin edited this page Oct 20, 2026 · 1 revision

Agent Prompts: Fail2Ban Setup

Prompts that make an AI agent configure fail2ban correctly: audit first, filters tested on real logs, thresholds justified, changes validated, rollback ready.


The rule that prevents most damage

Read-only audit → backup → filter test (positive and negative) → fail2ban-client -t → restart → verify → report. If any step fails, stop and report; do not "fix it creatively".

Paste the universal guardrails from Installation prompts before these prompts.


Prompt A — Read-only audit of the current fail2ban setup

Task: audit this host's fail2ban configuration and attack exposure. READ-ONLY: do not change,
restart or write anything. Produce a prioritized report.

Steps:
1. Inventory:
     fail2ban-client --version
     fail2ban-client status
     fail2ban-client -t
     ls -la /etc/fail2ban/jail.d /etc/fail2ban/filter.d | head -100
     fail2ban-client get sshd ignoreip
2. For every enabled jail: `fail2ban-client get <jail> actions`, `maxretry`, `findtime`,
   `bantime`, and the log source it reads (backend/journalmatch/logpath).
3. Attack volume for the last 24h/7d (read-only):
     grep "Failed password" /var/log/auth.log | wc -l
     journalctl -u ssh --since "-24 hours" | grep -c "Failed password"
     grep -hE '"(GET|POST).*(\.env|\.git|wp-login|phpmyadmin|\.\./)' /var/www/httpd-logs/*.access.log | wc -l
     grep -c "Primary script unknown" /var/www/httpd-logs/*.error.log
     grep -c "authenticator failed" /var/log/exim4/mainlog
     grep -c "no such user\\|failed login" /var/log/proftpd/proftpd.log 2>/dev/null
4. Check the hygiene items:
   - is /etc/fail2ban/jail.conf modified (should not be)?
   - are panic situations present: jail without `port`, `bantime` under 1h, `maxretry` 1-2,
     no `ignoreip`, recidive disabled, filter matching 0 lines on a busy log?
   - are there jails enabled that read a non-existent log path?
   - is Fail2Ban_AbuseIPDB installed? (`ls /usr/local/sbin/abuseipdb-*`,
     `/usr/local/sbin/abuseipdb-report --key-status`, `--stats`)
5. For each filter that matters, run `fail2ban-regex <log> <filter>` and record the match count.
6. Check the last 7 days of fail2ban actions: `grep -E "Ban|Unban" /var/log/fail2ban.log | tail -50`.

Constraints:
- Do not run `fail2ban-client set`, `reload`, `restart`, or edit any file.
- If a log path is missing, say so; do not guess counts.
- Mask any IP that belongs to the host or to the operator (show first octets only) in the report.

Return a table: jail | enabled | actions | thresholds | log source | 24h matches | verdict.
Then a prioritized list: P1 access-risk/false-positive issues, P2 missing high-value jails,
P3 tuning, P4 cosmetic. State what you could not verify.

Prompt B — Configure SSH protection safely

Task: configure fail2ban SSH protection on this host with thresholds that stop brute force
without locking out the administrator.

Parameters:
  MANAGEMENT_IPS = <IP1, IP2>       # must be whitelisted
  SSH_PORT = <22>                   # actual port
  BACKEND = systemd|file            # choose based on how sshd logs (check first)

Constraints:
- Never test bans with MANAGEMENT_IPS or your own address.
- Keep an existing SSH session open while changing SSH/fail2ban settings.
- Back up /etc/fail2ban and /etc/ssh before editing; use jail.d/99-ssh.local, never jail.conf.
- Run fail2ban-client -t before restart; if it fails, restore and stop.

Steps:
1. Detect the log source:
     journalctl -u ssh --since "-5 minutes" | tail -5
     ls -l /var/log/auth.log 2>/dev/null
   Use backend=systemd + journalmatch for journal-only hosts, file otherwise.
2. Check the effective sshd settings: sshd -T | grep -Ei
   '^(port|passwordauthentication|permitrootlogin|maxauthtries|kbdinteractiveauthentication)'
3. Write /etc/fail2ban/jail.d/99-ssh.local:
     [sshd]
     enabled = true
     backend = <BACKEND>
     journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd   # only for systemd backend
     maxretry = 4
     findtime = 10m
     bantime = 1d
     port = <SSH_PORT>
     ignoreip = 127.0.0.1/8 ::1 <MANAGEMENT_IPS>
   Keep an existing [sshd] block: merge, do not duplicate keys.
4. Verify the filter against real logs:
     # file backend:
     fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf | tail -5
     # systemd backend:
     journalctl -u ssh --since "-2 hours" --no-pager > /tmp/ssh-sample.log
     fail2ban-regex /tmp/ssh-sample.log /etc/fail2ban/filter.d/sshd.conf | tail -5
   Record matches. If 0, stop and report (do not enable).
5. Enable recidive in /etc/fail2ban/jail.d/99-recidive.local:
     [recidive]
     enabled = true
     logpath = /var/log/fail2ban.log
     findtime = 1d
     maxretry = 5
     bantime = 4w
     banaction = nftables[type=allports]
6. Validate and apply: fail2ban-client -t && systemctl restart fail2ban
7. Verify:
     fail2ban-client status
     fail2ban-client status sshd
     fail2ban-client get sshd ignoreip
     fail2ban-client get sshd actions
     fail2ban-client get sshd maxretry
8. Optional but recommended: if the operator approved, disable SSH password auth
   (PasswordAuthentication no, PermitRootLogin prohibit-password) in a drop-in under
   /etc/ssh/sshd_config.d/, then `sshd -t` and `systemctl reload ssh`. Verify from a second session.

Return the final jail config, filter match counts, verification commands and their output,
and anything not applied because it needed approval.

Prompt C — Add nginx web-attack jails with tested filters

Task: add three nginx jails (attack-path scanner, WordPress POST login, PHP shell probe) to
fail2ban, with filters tested against this host's real logs before enabling.

Parameters:
  ACCESS_LOGS = /var/www/httpd-logs/*.access.log
  ERROR_LOGS  = /var/www/httpd-logs/*.error.log
  PORTS       = 80,443

Constraints:
- port = 80,443 on every web jail (otherwise a ban blocks ALL ports).
- Never test bans with your own IP.
- Back up /etc/fail2ban first. Validate with fail2ban-client -t before restart.
- Do not enable a filter with 0 matches or with matches on ordinary page requests.

Steps:
1. Inspect a few log lines to confirm the combined format:
     head -3 $ACCESS_LOGS
2. Create /etc/fail2ban/filter.d/nginx-attack-scan.conf with this regex (skip the date block:
   fail2ban removes the timestamp before matching):
     failregex = ^<HOST> - - [^"]*"(?:GET|POST|HEAD|PUT|PATCH|OPTIONS|DELETE|PROPFIND|TRACE|CONNECT) [^"]*(?:\.env|\.git/|\.svn/|\.aws/|\.ssh/|/@fs/|__vite|/proc/self|/etc/passwd|/id_rsa|wp-admin/install\.php|phpmyadmin|adminer|wp-login\.php|\.\./)[^"]*"
   (write it as a single line in the [Definition] section)
3. Create /etc/fail2ban/filter.d/nginx-wp-login.conf:
     failregex = ^<HOST> - - [^"]*"POST [^"]*wp-login\.php
4. Create /etc/fail2ban/filter.d/nginx-php-probe.conf:
     failregex = ^\s*\[error\] \d+#\d+: \*\d+ FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: <HOST>,
   (leading \s* is required: the date is stripped, leaving a leading space)
5. Test each filter BEFORE writing jails:
     fail2ban-regex "$ACCESS_LOGS" /etc/fail2ban/filter.d/nginx-attack-scan.conf | tail -5
     fail2ban-regex "$ACCESS_LOGS" /etc/fail2ban/filter.d/nginx-wp-login.conf | tail -5
     fail2ban-regex "$ERROR_LOGS"  /etc/fail2ban/filter.d/nginx-php-probe.conf | tail -5
     # negative test: normal page requests must not match
     grep -h '"GET / ' $ACCESS_LOGS | head -200 > /tmp/normal.log
     fail2ban-regex /tmp/normal.log /etc/fail2ban/filter.d/nginx-attack-scan.conf
   Stop if any filter has 0 matches or matches normal traffic.
6. Write /etc/fail2ban/jail.d/99-web.local:
     [nginx-attack-scan]
     enabled = true
     filter = nginx-attack-scan
     logpath = /var/www/httpd-logs/*.access.log
     port = 80,443
     maxretry = 5
     findtime = 10m
     bantime = 1d

     [nginx-wp-login]
     enabled = true
     filter = nginx-wp-login
     logpath = /var/www/httpd-logs/*.access.log
     port = 80,443
     maxretry = 10
     findtime = 10m
     bantime = 1d

     [nginx-php-probe]
     enabled = true
     filter = nginx-php-probe
     logpath = /var/www/httpd-logs/*.error.log
     port = 80,443
     maxretry = 10
     findtime = 10m
     bantime = 1d
7. Validate and apply: fail2ban-client -t && systemctl restart fail2ban
8. Verify:
     for j in nginx-attack-scan nginx-wp-login nginx-php-probe; do
       fail2ban-client status $j | head -5
       fail2ban-client get $j actions
     done
9. Attach reporting if Fail2Ban_AbuseIPDB is installed:
     cd /path/to/Fail2Ban_AbuseIPDB
     sudo ./install.sh --jails "sshd nginx-attack-scan nginx-wp-login nginx-php-probe"
   (the installer preserves thresholds and the key file)

Return: log format evidence, filter match counts (positive and negative), final jail file,
verification output, and the list of jails now reported to AbuseIPDB.

Prompt D — Tune bantime, increments and recidive

Task: reduce repeat attacks by tuning ban duration policy without over-blocking.

Parameters:
  POLICY = conservative|standard|aggressive

Constraints:
- Back up /etc/fail2ban.
- Validate with fail2ban-client -t; restart only after it passes.
- Do not lower maxretry below 4 for SSH or 5 for web without explicit approval.
- Document the reasoning for every value changed.

Steps:
1. Read current defaults: fail2ban-client get sshd maxretry/bantime, /etc/fail2ban/jail.local
   [DEFAULT] section, any bantime.increment settings.
2. Measure repeat offenders:
     grep " Ban " /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head
     # how many IPs were banned more than 3 times in 7 days?
3. Apply the policy in /etc/fail2ban/jail.d/99-tuning.local (merge with existing keys):
     conservative: bantime 1h, increment off, recidive off
     standard:     bantime 1d, bantime.increment true, factor 2, maxtime 12w,
                   recidive 4w (maxretry 5 / findtime 1d)
     aggressive:   bantime 1d, increment true factor 2 maxtime 52w,
                   recidive 4w, SSH maxretry 3
4. Check reporting duplication: if the reporter is installed, ensure
   /etc/abuseipdb/reporter.conf has `skip_jails = recidive`.
5. Validate: fail2ban-client -t && systemctl restart fail2ban
6. Verify: fail2ban-client get sshd bantime, fail2ban-client get recidive bantime,
   fail2ban-client status recidive.

Return: before/after values table, repeat-offender statistics, changes applied, verification.

Prompt E — CDN / real-IP and whitelist review

Task: make sure fail2ban and reporting see real client IPs and never ban infrastructure.

Steps:
1. Determine whether the sites are behind a CDN/proxy:
     grep -ri "set_real_ip_from\\|real_ip_header" /etc/nginx /etc/apache2 2>/dev/null
     head -3 /var/www/httpd-logs/*.access.log     # do the client IPs match CDN ranges?
2. If behind Cloudflare, list all ranges from https://www.cloudflare.com/ips/ and:
   - add them to nginx real_ip configuration;
   - add them to /etc/abuseipdb/reporter.conf [ignore] ranges;
   - do NOT add them to fail2ban jails' ban targets; use Cloudflare firewall for edge blocking.
3. Collect infrastructure addresses that must never be banned or reported:
   management IPs, offices, monitoring, backups, partners, mail relays.
4. Show the proposed ignoreip list and the proposed reporter ignore ranges; ask for approval
   before writing.
5. After approval: merge into jail.d/99-whitelist.local ([DEFAULT] ignoreip = ...) and
   reporter.conf [ignore] ranges; fail2ban-client -t; systemctl restart fail2ban;
   /usr/local/sbin/abuseipdb-report --key-status (config still valid).
6. Verify: fail2ban-client get sshd ignoreip; grep -A5 '^\\[ignore\\]' /etc/abuseipdb/reporter.conf.

Return: whether a CDN is in use, ranges found, files changed, before/after whitelist, verification.

Prompt F — Apply a fail2ban change with verification and rollback

Task: apply this change to fail2ban: <DESCRIBE THE CHANGE>. Use the safe workflow and be ready
to roll back.

Steps:
1. Record before: fail2ban-client -t, fail2ban-client status, actions/thresholds for affected
   jails, copies of files you will edit.
2. Back up: cp -a /etc/fail2ban /root/fail2ban-backup-$(date +%Y%m%d-%H%M%S).
3. Test filters (if any) with fail2ban-regex on real logs: positive must match, a normal-traffic
   sample must not.
4. Edit only jail.d/99-*.local or filter.d/<your-new>.conf; never jail.conf.
5. Validate: fail2ban-client -t. On failure: restore the backup, restart fail2ban, report, stop.
6. Apply: systemctl restart fail2ban.
7. Verify: status, actions, thresholds, log tail (/var/log/fail2ban.log, journalctl -u fail2ban).
8. If verification fails: restore the backup, systemctl restart fail2ban, verify the old state
   is back, and report the rollback.

Return: change summary, before/after, test evidence, validation output, verification output,
and any rollback performed.

Next: Installation prompts · Operations & audit prompts · Web Protection

Clone this wiki locally