Skip to content

17 Development and Testing

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

Development & Testing

The project is deliberately small, dependency-free and testable. This page describes the layout, the test suite, the CI gates and the release procedure that keeps the self-updater trustworthy.


Repository layout

Fail2Ban_AbuseIPDB/
├─ install.sh                  production installer (backups, key file, state, fail2ban gate)
├─ uninstall.sh                compatibility shim → install.sh --uninstall
├─ bin/
│  ├─ abuseipdb-enqueue        fail2ban action helper (fast, no network)
│  ├─ abuseipdb-report         worker / CLI (--stats, --dry-run, --setup, --check-key)
│  └─ abuseipdb-upgrade        self-update CLI
├─ src/abuseipdb_reporter/     stdlib-only Python package
│  ├─ config.py  db.py  enqueue.py  reporter.py
│  ├─ evidence.py  comments.py  api.py  iputil.py  log.py
│  ├─ keys.py                  key resolution, key file, prompt, migration
│  ├─ updater.py               manifest, SHA-256, safe extraction, installer call
│  └─ cli.py                   entry points
├─ config/reporter.conf.example
├─ fail2ban/action.d/abuseipdb-async.conf
├─ fail2ban/jail.d/50-...example
├─ systemd/  cron/  logrotate/
├─ tests/                      88 unit tests + integration against a mock API
├─ tools/mock_abuseipdb.py     local AbuseIPDB stand-in
├─ docs/                       INSTALL, CONFIGURATION, ARCHITECTURE, UPDATE, TROUBLESHOOTING
├─ updater.json                release manifest (version, tarball URL, SHA-256)
└─ .github/workflows/ci.yml   CI matrix, integration, shellcheck, staged install

Running the tests

# unit + integration-ish tests (Linux or Windows, Python 3.9+)
python3 -m unittest discover -s tests -t .

# one file
python3 -m unittest tests.test_updater -v

# integration against the local mock AbuseIPDB (Linux)
bash tests/integration/run_integration.sh

# shell
shellcheck -x install.sh uninstall.sh tests/integration/run_integration.sh

The suite covers:

File What it proves
test_config.py config parsing, defaults, legacy api_key, ignore ranges, categories
test_db.py schema, dedupe, state transitions, retention
test_evidence.py fail2ban DB reading, evidence classification, PII rules
test_comments.py comment format, truncation, attribution footer
test_reporter.py worker loop, budgets, throttling, error handling
test_api.py HTTP client behaviour, timeouts, error mapping
test_enqueue.py validation, ignore rules, skip_jails, local IP refusal
test_cli.py entry points, --setup, --migrate-key, --key-status, non-interactive exit 2
test_keys.py resolution order, atomic save/mode, prompt, migration, no-touch on resolve
test_updater.py version compare, manifest, SHA-256, traversal rejection, key snapshot/restore

The integration script starts tools/mock_abuseipdb.py, queues events, runs the worker, asserts the API call, the report journal, the failure modes (429 / 422 / 500-then-200), the key file, and an updater check against a local manifest.

CI gates

.github/workflows/ci.yml runs on every push and PR:

Job What it does
tests (3.9 … 3.13) unit suite on all supported Pythons
integration (mock AbuseIPDB) end-to-end queue → report → retry
shellcheck install.sh, uninstall.sh, integration script
staged install / uninstall fresh install into /tmp/stage, key file mode 600, config has no key, action attached, re-install without --api-key/--jails keeps the key and jail list, uninstall keeps config/key, purge removes them

A release is only published after all gates are green.

Invariants that must not be broken

  1. stdlib only — no third-party imports in src/; Python 3.9 compatible syntax.
  2. No secrets in the repository — *.key is git-ignored; examples use placeholders.
  3. Key file is never overwritten by upgrades — the installer skips an existing key file; the updater snapshots/restores it.
  4. No attacker-controlled text in shell commands — the action passes only fail2ban-generated fields; evidence is read from SQLite and sent in a form-encoded body.
  5. Updater trusts only a checksummed archive — missing SHA-256 is a refusal, not a warning.
  6. Fail2ban config is validated before restart — fail2ban-client -t is a gate.
  7. Backups before replacement — pristine copies + install state.
  8. No background auto-update — the check timer must never install anything.

Changing fail2ban integration

  • action template: fail2ban/action.d/abuseipdb-async.conf;
  • installer generates jail.d/50-abuseipdb-reporter.local from the jail list;
  • filters are not shipped by the project — they are host-specific and documented in Web Protection and SSH, Mail, FTP;
  • when changing the action, verify on a staging root: sudo ./install.sh --root /tmp/stage --api-key ... --jails "sshd" --cron --skip-api-check --no-restart.

Release procedure

  1. Update __version__ in src/abuseipdb_reporter/__init__.py, cli.py, pyproject.toml, install.sh (VERSION=) and the README badge.
  2. Add a CHANGELOG.md section and docs/release-notes-<version>.md.
  3. Run the full suite, integration, shellcheck and the staged install locally/on Linux.
  4. Build the release archive (Fail2Ban_AbuseIPDB-<version>.tar.gz, excluding .git/__pycache__) and compute its SHA-256.
  5. Write version, tarball URL and checksum into updater.json.
  6. Commit, push, tag v<version>, create the GitHub release, upload the archive as an asset.
  7. Verify: download the asset and compare its SHA-256 with updater.json; run python3 bin/abuseipdb-upgrade --check from a checkout of the new version (expect exit 0).
  8. Optionally verify the previous version can self-update to the new one on a staging host (this was done in production for v1.1.0 → v1.1.1).

Because the tarball's own copy of updater.json is built before the checksum is known, the manifest inside the archive may differ from the repository manifest; the updater always reads the repository manifest, so this is harmless.

Wiki maintenance

This wiki is a separate git repository:

git clone https://github.com/paulmann/Fail2Ban_AbuseIPDB.wiki.git
# edit pages, then
git add -A && git commit -m "docs: ..." && git push

Pages are plain Markdown; _Sidebar.md is the navigation, _Footer.md the footer. Keep commands in sync with the published release and mark anything unverified.

Contributing

  • small, focused pull requests; one behaviour per PR;
  • a test for every change (especially key handling and updater safety);
  • no secrets, no PII, no host-specific addresses in examples (use documentation ranges);
  • update the docs and the wiki when the CLI or configuration changes;
  • report security issues privately per SECURITY.md.

Next: Home · FAQ · Repository

Clone this wiki locally