diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec4eee..db2ea43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,36 +4,29 @@ All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.1.0] - 2026-07-10 - -### Added -- **Settings page** (standalone) with a top-right profile dropdown (Settings / - Logout). Configure language, notifications, allowlist, and login security at - runtime — no redeploy needed. Preferences persist to `data/prefs.json`. -- **ntfy notifications** on a ban spike, with a configurable threshold/window, - a background poller, and a one-click *Send test* button. -- **Allowlist** management with two modes: - - fail2ban `ignoreip` (default) — works with any firewall backend. - - nftables set — with a first-run setup guide and *Create set* button. -- **Login rate limiting** per client IP (brute-force protection). -- **JSON stats endpoint** `GET /api/stats` and **HTTP Basic auth** for the API, - for dashboard widgets (e.g. Homepage `customapi`). -- Shield **favicon** and static asset serving. -- **Responsive layout** for mobile (sidebar, header, tables, settings, login). - -### Changed -- Settings moved out of the sidebar into the profile dropdown. - -## [1.0.0] - 2026-07-08 - -### Added -- Read-only dashboard: banned IPs, top attackers, open ports, firewall rules. +## [1.0.0] - 2026-07-10 + +Initial public release. + +### Observability +- Dashboard for banned IPs, top attackers, open ports, and firewall rules. - Sortable/filterable detail pages and a home overview with live refresh (HTMX). - Real GeoIP country flags via the free DB-IP database. -- English / Korean i18n. -- Single-password login gate (HMAC session cookie). -- Ban / unban actions via `fail2ban-client`, including bulk ban, with - SweetAlert2 confirmations. -[1.1.0]: https://github.com/Jamkris/Bastion/releases/tag/v1.1.0 +### Management +- Ban / unban via `fail2ban-client`, including bulk ban, with SweetAlert2 confirms. +- Allowlist with two modes: fail2ban `ignoreip` (default, any firewall) and a + dedicated nftables set (with a first-run setup guide + *Create set* button). + +### Alerting +- ntfy push notifications on a ban spike (configurable threshold/window), + a background poller, and a *Send test* button. + +### Security & UX +- Single-password login gate (HMAC session cookie) with per-IP rate limiting. +- Standalone Settings page + profile dropdown to configure language, + notifications, allowlist, and login security at runtime. +- English / Korean i18n; responsive layout for mobile. +- JSON stats endpoint `GET /api/stats` and HTTP Basic auth for dashboard widgets. + [1.0.0]: https://github.com/Jamkris/Bastion/releases/tag/v1.0.0 diff --git a/bastion/__init__.py b/bastion/__init__.py index e7313e5..8d9f32c 100644 --- a/bastion/__init__.py +++ b/bastion/__init__.py @@ -1,3 +1,3 @@ """Bastion — self-hosted fail2ban + nftables observability dashboard.""" -__version__ = "1.1.0" +__version__ = "1.0.0" diff --git a/pyproject.toml b/pyproject.toml index 1b72544..7f70ccf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "bastion" -version = "1.1.0" +version = "1.0.0" description = "Self-hosted fail2ban + nftables observability dashboard" requires-python = ">=3.10" dependencies = [ diff --git a/tests/test_web.py b/tests/test_web.py index a0fb3f2..0631645 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -122,11 +122,17 @@ def test_allowlist_ignoreip_mode_default(): def test_allowlist_nftset_mode_shows_setup(monkeypatch): - # In nftset mode with nft absent, the page shows the create-set setup guide. + # In nftset mode, a "set not found" error must render the create-set setup + # guide. Force both prefs and the lookup so the test does not depend on + # whether/how `nft` fails in the CI environment. monkeypatch.setattr( webapp.prefs, "get", lambda section: {"mode": "nftset", "family": "inet", "table": "filter", "set": "bastion_allow"}, ) + monkeypatch.setattr( + webapp.allowlist, "list_entries", + lambda: ([], "Error: No such file or directory"), + ) c = TestClient(app) r = c.get("/view/allowlist") assert r.status_code == 200