Files
BanGUI/Docs/Tasks.md
Lukas 4c138424a5 Add filter discovery endpoints with active/inactive status (Task 2.1)
- Add list_filters() and get_filter() to config_file_service.py:
  scans filter.d/, parses [Definition] + [Init] sections, merges .local
  overrides, and cross-references running jails to set active/used_by_jails
- Add FilterConfig.active, used_by_jails, source_file, has_local_override
  fields to the Pydantic model; add FilterListResponse and FilterNotFoundError
- Add GET /api/config/filters and GET /api/config/filters/{name} to config.py
- Remove the shadowed GET /api/config/filters list route from file_config.py;
  rename GET /api/config/filters/{name} raw variant to /filters/{name}/raw
- Update frontend: fetchFilterFiles() adapts FilterListResponse -> ConfFilesResponse;
  add fetchFilters() and fetchFilter() to api/config.ts; remove unused
  fetchFilterFiles/fetchActionFiles calls from useConfigActiveStatus
- Fix ConfigPageLogPath test mock to include fetchInactiveJails and related
  exports introduced by Stage 1
- Backend: 169 tests pass, mypy --strict clean, ruff clean
- Frontend: 63 tests pass, tsc --noEmit clean, eslint clean
2026-03-13 16:48:27 +01:00

24 KiB

BanGUI — Task List

This document breaks the entire BanGUI project into development stages, ordered so that each stage builds on the previous one. Every task is described in prose with enough detail for a developer to begin work. References point to the relevant documentation.


Stage 1 — Inactive Jail Discovery and Activation

Currently BanGUI only shows jails that are actively running in fail2ban. fail2ban ships with dozens of pre-defined jails in jail.conf (sshd, apache-auth, nginx-http-auth, postfix, etc.) that are disabled by default. Users must be able to see these inactive jails and activate them from the web interface.

Task 1.1 — Backend: Parse Inactive Jails from Config Files DONE

Goal: Read all jail definitions from the fail2ban configuration files and identify which ones are not currently enabled.

Details:

  • Create a new service config_file_service.py in app/services/ responsible for reading and writing fail2ban config files on disk.
  • Parse jail.conf and any jail.local / jail.d/*.conf / jail.d/*.local override files. Use Python's configparser (INI format) since fail2ban configs follow that format. Local files override .conf files (fail2ban merge order: jail.confjail.localjail.d/*.confjail.d/*.local).
  • For each jail section found, extract at minimum: jail name, enabled flag (defaults to false if absent), filter name (defaults to the jail name if absent), action references, port, logpath, backend, bantime, findtime, maxretry.
  • Build a list of InactiveJail objects for jails where enabled = false or that are defined in config but not reported by fail2ban-client status.
  • The fail2ban config base path should come from the application settings (default: /etc/fail2ban/). The path is already available via config.py settings or can be derived from the fail2ban socket connection.
  • Handle the [DEFAULT] section properly — its values provide defaults inherited by every jail section.
  • Handle [INCLUDES] sections (before / after directives) so that path includes like paths-debian.conf are resolved, providing variables like %(sshd_log)s.

Files to create/modify:

  • app/services/config_file_service.py (new)
  • app/models/config.py (add InactiveJail, InactiveJailListResponse models)

References: Features.md §6, Architekture.md §2, Backend-Development.md


Task 1.2 — Backend: API Endpoints for Inactive Jails DONE

Goal: Expose inactive jail data and an activation endpoint via the REST API.

Details:

  • Add a GET /api/config/jails/inactive endpoint to the config router that returns all inactive jails found in the config files. Each entry should include: name, filter, actions (list), port, logpath, bantime, findtime, maxretry, and the source file where the jail is defined.
  • Add a POST /api/config/jails/{name}/activate endpoint that enables an inactive jail. This endpoint should:
    1. Validate the jail name exists in the parsed config.
    2. Write enabled = true into the appropriate .local override file (jail.local or jail.d/{name}.local). Never modify .conf files directly — always use .local overrides following fail2ban convention.
    3. Optionally accept a request body with override values (bantime, findtime, maxretry, port, logpath) that get written alongside the enabled = true line.
    4. Trigger a fail2ban reload so the jail starts immediately.
    5. Return the jail's new status after activation.
  • Add a POST /api/config/jails/{name}/deactivate endpoint that sets enabled = false in the .local file and reloads fail2ban to stop the jail.
  • All endpoints require authentication. Return 404 if the jail name is not found in any config file. Return 409 if the jail is already active/inactive.

Files to create/modify:

  • app/routers/config.py (add endpoints)
  • app/services/config_file_service.py (add activate/deactivate logic)
  • app/models/config.py (add request/response models: ActivateJailRequest, JailActivationResponse)

References: Features.md §6, Backend-Development.md §3


Task 1.3 — Frontend: Inactive Jails in Configuration View DONE

Goal: Display inactive jails in the Configuration page and let users activate them.

Details:

  • In the Jails tab of the Configuration page, extend the existing master/detail list layout. The left pane currently shows active jails — add a section or toggle that also shows inactive jails. Use the Active/Inactive badge system described in Features.md §6: active jails sorted to the top with an "Active" badge, inactive jails below with an "Inactive" badge, alphabetical within each group.
  • Clicking an inactive jail in the list loads its config details in the right detail pane. Show all parsed fields (filter, actions, port, logpath, bantime, findtime, maxretry) as read-only or editable fields.
  • Add an "Activate Jail" button (prominent, primary style) in the detail pane for inactive jails. Clicking it opens a confirmation dialog that allows the user to override default values (bantime, findtime, maxretry, port, logpath) before activation. On confirm, call POST /api/config/jails/{name}/activate with the overrides.
  • After successful activation, refresh the jail list and show the jail under the active group with a success toast notification.
  • For active jails, add a "Deactivate Jail" button (secondary/danger style) that calls the deactivate endpoint after confirmation.
  • Add the API functions fetchInactiveJails(), activateJail(name, overrides), deactivateJail(name) to frontend/src/api/config.ts.
  • Add corresponding TypeScript types (InactiveJail, ActivateJailRequest) to frontend/src/types/config.ts.

Files to create/modify:

  • frontend/src/api/config.ts (add API calls)
  • frontend/src/types/config.ts (add types)
  • frontend/src/components/config/JailsTab.tsx (add inactive jail list + activation UI)
  • New component: frontend/src/components/config/ActivateJailDialog.tsx

References: Features.md §6, Web-Design.md, Web-Development.md


Task 1.4 — Frontend: Inactive Jails on Jails Page DONE

Goal: Show inactive jails alongside active jails in the Jail Management overview.

Details:

  • On the Jails Page (JailsPage.tsx), extend the jail overview table to include inactive jails. Add a column or badge showing each jail's activation state.
  • Inactive jails should appear in the table with dimmed/muted styling and show "Inactive" as status. They should not have start/stop/idle/reload controls — only an "Activate" action button.
  • Add a toggle or filter above the table: "Show inactive jails" (default: on). When off, only active jails are displayed.
  • Clicking an inactive jail's name navigates to the Configuration page's jail detail for that jail, pre-selecting it in the list.

Files to create/modify:

  • frontend/src/pages/JailsPage.tsx
  • frontend/src/hooks/useJails.ts (extend to optionally fetch inactive jails)

References: Features.md §5, Web-Design.md


Task 1.5 — Tests: Inactive Jail Parsing and Activation DONE

Goal: Full test coverage for the new inactive-jail functionality.

Details:

  • Service tests (tests/test_services/test_config_file_service.py): Test config file parsing with mock jail.conf content containing multiple jails (enabled and disabled). Test the merge order (.conf.localjail.d/). Test DEFAULT section inheritance. Test variable interpolation (%(sshd_log)s). Test activation writes to .local files. Test deactivation.
  • Router tests (tests/test_routers/test_config.py): Add tests for the three new endpoints. Test 404 for unknown jail names. Test 409 for already-active/inactive jails. Test that override values are passed through correctly.
  • Frontend tests: Add unit tests for the new API functions. Add component tests for the activation dialog and the inactive jail display.

Files to create/modify:

  • backend/tests/test_services/test_config_file_service.py (new)
  • backend/tests/test_routers/test_config.py (extend)
  • frontend/src/components/config/__tests__/ (new tests)

References: Backend-Development.md, Web-Development.md


Stage 2 — Filter Configuration Discovery and Activation

fail2ban ships with a large collection of filter definitions in filter.d/ (over 80 files). Users need to see all available filters — both those currently in use by active jails and those available but unused — and assign them to jails.

Task 2.1 — Backend: List All Available Filters with Active/Inactive Status DONE

Goal: Enumerate all filter config files and mark each as active or inactive.

Details:

  • Add a method list_filters() to config_file_service.py that scans the filter.d/ directory within the fail2ban config path.
  • For each .conf file found, parse its [Definition] section to extract: failregex (list of patterns), ignoreregex (list), datepattern, journalmatch, and any other fields present. Also parse [Init] sections for default variable bindings.
  • Handle .local overrides: if sshd.local exists alongside sshd.conf, merge the local values on top of the conf values (local wins).
  • Determine active/inactive status: a filter is "active" if its name matches the filter field of any currently enabled jail (cross-reference with the active jail list from fail2ban and the inactive jail configs). Mark it accordingly.
  • Return a list of FilterConfig objects with: name, active (bool), used_by_jails (list of jail names using this filter), failregex (list), ignoreregex (list), datepattern, source_file (path to the .conf file), has_local_override (bool).
  • Add a GET /api/config/filters endpoint to the config router returning all filters.
  • Add a GET /api/config/filters/{name} endpoint returning the full parsed detail of a single filter.

Files to create/modify:

  • app/services/config_file_service.py (add list_filters, get_filter)
  • app/models/config.py (add FilterConfig, FilterListResponse, FilterDetailResponse)
  • app/routers/config.py (add endpoints)

References: Features.md §6, Architekture.md §2


Task 2.2 — Backend: Activate and Edit Filters

Goal: Allow users to assign a filter to a jail and edit filter regex patterns.

Details:

  • Add a PUT /api/config/filters/{name} endpoint that writes changes to a filter's .local override file. Accepts updated failregex, ignoreregex, datepattern, and journalmatch values. Never write to the .conf file directly.
  • Add a POST /api/config/jails/{jail_name}/filter endpoint that changes which filter a jail uses. This writes filter = {filter_name} to the jail's .local config. Requires a reload for the change to take effect.
  • Add a POST /api/config/filters endpoint to create a brand-new filter. Accepts a name and the filter definition fields. Creates a new file at filter.d/{name}.local.
  • Add a DELETE /api/config/filters/{name} endpoint that deletes a custom filter's .local file. Refuse to delete files that are .conf (shipped defaults) — only user-created .local files without a corresponding .conf can be fully removed.
  • Validate all regex patterns using Python's re module before writing them to disk. Return 422 with specific error details if any pattern is invalid.
  • After any write operation, optionally trigger a fail2ban reload if the user requests it (query param ?reload=true).

Files to create/modify:

  • app/services/config_file_service.py (add filter write/create/delete methods)
  • app/routers/config.py (add endpoints)
  • app/models/config.py (add FilterUpdateRequest, FilterCreateRequest)

References: Features.md §6, Backend-Development.md


Task 2.3 — Frontend: Filters Tab with Active/Inactive Display and Activation

Goal: Enhance the Filters tab in the Configuration page to show all filters with their active/inactive status and allow editing.

Details:

  • Redesign the Filters tab to use the master/detail list layout described in Features.md §6.
  • The left pane lists all filter names with an Active or Inactive badge. Active filters (those used by at least one enabled jail) are sorted to the top. Within each group, sort alphabetically.
  • The badge should also show which jails use the filter (e.g., "Active — used by sshd, apache-auth").
  • Clicking a filter loads its detail in the right pane: failregex patterns (editable list), ignoreregex patterns (editable list), datepattern (editable), source file info, and whether a .local override exists.
  • Add a "Save Changes" button that calls PUT /api/config/filters/{name} to persist edits to the .local override. Show save-state feedback (idle → saving → saved → error).
  • Add an "Assign to Jail" button that opens a dialog where the user selects a jail and assigns this filter to it. This calls the assign-filter endpoint.
  • Add a "Create Filter" button at the top of the list pane that opens a dialog for entering a new filter name and regex patterns.
  • On narrow screens (< 900 px), collapse the list pane into a dropdown as specified in Features.md §6.
  • Include the existing Raw Configuration collapsible section at the bottom of the detail pane for direct file editing.

Files to create/modify:

  • frontend/src/components/config/FiltersTab.tsx (rewrite with master/detail layout)
  • frontend/src/components/config/FilterForm.tsx (update for editable fields)
  • frontend/src/api/config.ts (add fetchFilters, fetchFilter, updateFilter, createFilter, deleteFilter, assignFilterToJail)
  • frontend/src/types/config.ts (add types)
  • New component: frontend/src/components/config/AssignFilterDialog.tsx

References: Features.md §6, Web-Design.md, Web-Development.md


Task 2.4 — Tests: Filter Discovery and Management

Goal: Test coverage for filter listing, editing, creation, and assignment.

Details:

  • Service tests: Mock the filter.d/ directory with sample .conf and .local files. Test parsing of [Definition] sections. Test merge of .local over .conf. Test active/inactive detection by cross-referencing with mock jail data. Test write operations create correct .local content. Test regex validation rejects bad patterns.
  • Router tests: Test all new filter endpoints (list, detail, update, create, delete, assign). Test auth required. Test 404/409/422 responses.
  • Frontend tests: Test the filter list rendering with mixed active/inactive items. Test the form submission in the detail pane. Test the assign dialog.

Files to create/modify:

  • backend/tests/test_services/test_config_file_service.py (extend)
  • backend/tests/test_routers/test_config.py (extend)
  • frontend/src/components/config/__tests__/ (add filter tests)

Stage 3 — Action Configuration Discovery and Activation

fail2ban ships with many action definitions in action.d/ (iptables, firewalld, cloudflare, sendmail, etc.). Users need to see all available actions, understand which are in use, and assign them to jails.

Task 3.1 — Backend: List All Available Actions with Active/Inactive Status

Goal: Enumerate all action config files and mark each as active or inactive based on jail usage.

Details:

  • Add a method list_actions() to config_file_service.py that scans the action.d/ directory.
  • For each .conf file, parse the [Definition] section to extract: actionstart, actionstop, actioncheck, actionban, actionunban commands. Also parse [Init] for default variable bindings (port, protocol, chain, etc.).
  • Handle .local overrides the same way as filters.
  • Determine active/inactive status: an action is "active" if its name appears in the action field of any currently enabled jail. Cross-reference against both running jails (from fail2ban) and the config files.
  • Return ActionConfig objects with: name, active (bool), used_by_jails (list), actionban (command template), actionunban (command template), actionstart, actionstop, init_params (dict of Init variables), source_file, has_local_override.
  • Add GET /api/config/actions endpoint returning all actions.
  • Add GET /api/config/actions/{name} endpoint returning the full parsed detail of one action.

Files to create/modify:

  • app/services/config_file_service.py (add list_actions, get_action)
  • app/models/config.py (add ActionConfig, ActionListResponse, ActionDetailResponse)
  • app/routers/config.py (add endpoints)

References: Features.md §6, Architekture.md §2


Task 3.2 — Backend: Activate and Edit Actions

Goal: Allow users to assign actions to jails, edit action definitions, and create new actions.

Details:

  • Add a PUT /api/config/actions/{name} endpoint that writes changes to an action's .local override file. Accepts updated actionban, actionunban, actionstart, actionstop, actioncheck values and any [Init] parameters.
  • Add a POST /api/config/jails/{jail_name}/action endpoint that adds an action to a jail's action list. This writes the action reference into the jail's .local config file. Multiple actions per jail are supported (fail2ban allows comma-separated action lists). Include optional parameters for the action (e.g., port, protocol).
  • Add a DELETE /api/config/jails/{jail_name}/action/{action_name} endpoint that removes an action from a jail's configuration.
  • Add a POST /api/config/actions endpoint to create a brand-new action definition (.local file).
  • Add a DELETE /api/config/actions/{name} endpoint to delete a custom action's .local file. Same safety rules as filters — refuse to delete shipped .conf files.
  • After any write, optionally reload fail2ban (?reload=true).

Files to create/modify:

  • app/services/config_file_service.py (add action write/create/delete/assign methods)
  • app/routers/config.py (add endpoints)
  • app/models/config.py (add ActionUpdateRequest, ActionCreateRequest, AssignActionRequest)

References: Features.md §6, Backend-Development.md


Task 3.3 — Frontend: Actions Tab with Active/Inactive Display and Activation

Goal: Enhance the Actions tab in the Configuration page to show all actions with active/inactive status and allow editing and assignment.

Details:

  • Redesign the Actions tab to use the same master/detail list layout as Filters.
  • The left pane lists all action names with Active/Inactive badges. Active actions (used by at least one enabled jail) sorted to the top.
  • Badge shows which jails reference the action (e.g., "Active — used by sshd, postfix").
  • Clicking an action loads its detail in the right pane: actionban, actionunban, actionstart, actionstop, actioncheck (each in a code/textarea editor), [Init] parameters as key-value fields.
  • Add a "Save Changes" button for persisting edits to the .local override.
  • Add an "Assign to Jail" button that opens a dialog for selecting a jail and providing action parameters (port, protocol, chain). Calls the assign-action endpoint.
  • Add a "Remove from Jail" option in the detail pane — shows a list of jails currently using the action with a remove button next to each.
  • Add a "Create Action" button at the top of the list pane.
  • Raw Configuration collapsible section at the bottom.
  • Responsive collapse on narrow screens.

Files to create/modify:

  • frontend/src/components/config/ActionsTab.tsx (rewrite with master/detail layout)
  • frontend/src/components/config/ActionForm.tsx (update for editable fields)
  • frontend/src/api/config.ts (add fetchActions, fetchAction, updateAction, createAction, deleteAction, assignActionToJail, removeActionFromJail)
  • frontend/src/types/config.ts (add types)
  • New component: frontend/src/components/config/AssignActionDialog.tsx

References: Features.md §6, Web-Design.md, Web-Development.md


Task 3.4 — Tests: Action Discovery and Management

Goal: Test coverage for action listing, editing, creation, and assignment.

Details:

  • Service tests: Mock action.d/ with sample configs. Test parsing of [Definition] and [Init] sections. Test active/inactive detection. Test write and create operations. Test delete safety (refuse .conf deletion).
  • Router tests: Test all new action endpoints. Test auth, 404, 409, 422 responses.
  • Frontend tests: Test action list rendering, form editing, assign dialog, remove-from-jail flow.

Files to create/modify:

  • backend/tests/test_services/test_config_file_service.py (extend)
  • backend/tests/test_routers/test_config.py (extend)
  • frontend/src/components/config/__tests__/ (add action tests)

Stage 4 — Unified Config File Service and Shared Utilities

Task 4.1 — Config File Parser Utility

Goal: Build a robust, reusable parser for fail2ban INI-style config files that all config-related features share.

Details:

  • Create app/utils/config_parser.py with a Fail2BanConfigParser class that wraps Python's configparser.ConfigParser with fail2ban-specific behaviour:
    • Merge order: .conf file first, then .local overlay, then *.d/ directory overrides.
    • Variable interpolation: Support %(variable)s syntax, resolving against [DEFAULT] and [Init] sections.
    • Include directives: Process before = filename and after = filename in [INCLUDES] sections, resolving paths relative to the config directory.
    • Multi-line values: Handle backslash-continuation and multi-line regex lists.
    • Comments: Strip # and ; line/inline comments correctly.
  • The parser should return structured dict data that the config_file_service methods consume.
  • This is a pure utility — no I/O aside from reading files. Everything is synchronous, callable from async context via run_in_executor.
  • Write comprehensive unit tests in tests/test_utils/test_config_parser.py covering all edge cases: empty sections, missing files, circular includes, invalid interpolation, multi-line regex, override merging.

Files to create/modify:

  • app/utils/config_parser.py (new)
  • tests/test_utils/test_config_parser.py (new)

References: Backend-Development.md


Task 4.2 — Config File Writer Utility

Goal: Build a safe writer utility for creating and updating .local override files.

Details:

  • Create app/utils/config_writer.py with functions for:
    • write_local_override(base_path, section, key_values) — Writes or updates a .local file. If the file exists, update only the specified keys under the given section. If it does not exist, create it with a header comment explaining it's a BanGUI-managed override.
    • remove_local_key(base_path, section, key) — Removes a specific key from a .local file.
    • delete_local_file(path) — Deletes a .local file, but only if no corresponding .conf exists or the caller explicitly allows orphan deletion.
  • All write operations must be atomic: write to a temporary file first, then rename into place (os.replace). This prevents corruption on crash.
  • Add a file-level lock (per config file) to prevent concurrent writes from racing.
  • Never write to .conf files — assert this in every write function.

Files to create/modify:

  • app/utils/config_writer.py (new)
  • tests/test_utils/test_config_writer.py (new)

References: Backend-Development.md