Task 1: Backend/LogEncoding/DatePattern dropdowns in JailConfigDetail
- Added BACKENDS, LOG_ENCODINGS, DATE_PATTERN_PRESETS constants
- Backend and Log Encoding: <Input readOnly> → <Select> (editable, auto-saves)
- Date Pattern: <Input> → <Combobox freeform> with presets
- Extended JailConfigUpdate model (backend, log_encoding) and service
- Added readOnly prop to JailConfigDetail (all fields, toggles, buttons)
- Extended RegexList with readOnly prop
Task 2: Fix raw action/filter config always blank
- Added key={selectedAction.name} to ActionDetail in ActionsTab
- Added key={selectedFilter.name} to FilterDetail in FiltersTab
Task 3: Inactive jail full GUI same as active jails
- Extended InactiveJail Pydantic model with all config fields
- Added _parse_time_to_seconds helper to config_file_service
- Updated _build_inactive_jail to populate all extended fields
- Extended InactiveJail TypeScript type to match
- Rewrote InactiveJailDetail to reuse JailConfigDetail (readOnly=true)
Task 4: Fix banaction interpolation error when activating jails
- _write_local_override_sync now includes banaction=iptables-multiport
and banaction_allports=iptables-allports in every .local file
14 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.
✅ Task 1 — Convert Backend, Log Encoding, and Date Pattern to Dropdowns in Jail Config
Completed. Backend and Log Encoding are now <Select> dropdowns; Date Pattern is a <Combobox> with presets. Both are wired to auto-save. The JailConfigDetail component also gained a readOnly prop (wired throughout all fields, toggles, and buttons) in preparation for Task 3. Backend updated JailConfigUpdate model to accept backend and log_encoding, and update_jail_config sends them to the daemon.
✅ Task 2 — Fix Raw Action Configuration Always Blank in Actions Tab
Completed. Added key={selectedAction.name} to <ActionDetail> in ActionsTab.tsx and key={selectedFilter.name} to <FilterDetail> in FiltersTab.tsx. This forces a full remount on item switch, resetting RawConfigSection's loadedRef so it re-fetches content for every selected item.
✅ Task 3 — Give Inactive Jail Configs the Same GUI as Active Ones
Completed. Extended InactiveJail backend Pydantic model with all config fields (ban_time_seconds, find_time_seconds, log_encoding, backend, date_pattern, use_dns, prefregex, fail_regex, ignore_regex, bantime_escalation). Added _parse_time_to_seconds helper to config_file_service.py and updated _build_inactive_jail to populate all new fields. Extended the InactiveJail TypeScript type to match. Rewrote InactiveJailDetail to map fields into a JailConfig-compatible object and render JailConfigDetail with readOnly=true, showing filter/port/source_file as informational fields above the shared form.
✅ Task 4 — Fix banaction Interpolation Error When Activating Jails
Completed. _write_local_override_sync now always writes banaction = iptables-multiport and banaction_allports = iptables-allports into the per-jail .local file (Option A), ensuring fail2ban can resolve the %(banaction)s interpolation used in action_.
Problem: In the Config → Jails page, the Backend, Log Encoding, and Date Pattern fields for active jails are currently plain text <Input> fields (Backend and Log Encoding are read-only, Date Pattern is a free-text input). These should be <Select> dropdowns so users pick from valid options instead of typing arbitrary values.
Where to change:
frontend/src/components/config/JailsTab.tsx— theJailConfigDetailcomponent, around lines 250–270. This is the right-pane form for active jails. Replace the three<Input>fields with<Select>dropdowns.
Dropdown options to use:
Backend
Reference: fail2ban-master/config/jail.conf lines 115–132. The JailFileForm.tsx already defines a BACKENDS constant at line 163 — reuse or mirror it.
| Value | Label |
|---|---|
auto |
auto — pyinotify, then polling |
polling |
polling — standard polling algorithm |
pyinotify |
pyinotify — requires pyinotify library |
systemd |
systemd — uses systemd journal |
gamin |
gamin — legacy file alteration monitor |
Log Encoding
Reference: fail2ban-master/config/jail.conf lines 145–150. The option auto uses the system locale; the rest are standard Python codec names.
| Value | Label |
|---|---|
auto |
auto — use system locale |
ascii |
ascii |
utf-8 |
utf-8 |
latin-1 |
latin-1 (ISO 8859-1) |
Date Pattern
Date Pattern should remain a combobox (editable dropdown) since users may enter custom patterns, but offer common presets as suggestions.
| Value | Label |
|---|---|
| (empty) | auto-detect (leave blank) |
{^LN-BEG} |
{^LN-BEG} — line beginning |
%%Y-%%m-%%d %%H:%%M:%%S |
YYYY-MM-DD HH:MM:SS |
%%d/%%b/%%Y:%%H:%%M:%%S |
DD/Mon/YYYY:HH:MM:SS (Apache) |
%%b %%d %%H:%%M:%%S |
Mon DD HH:MM:SS (syslog) |
EPOCH |
EPOCH — Unix timestamp |
%%Y-%%m-%%dT%%H:%%M:%%S |
ISO 8601 |
TAI64N |
TAI64N |
Implementation notes:
- Backend and Log Encoding are currently
readOnly. They must become editable<Select>fields. Wire theironChangeto local state and include them in the auto-save payload sent to the backend. - The backend model at
backend/app/models/config.pyline 67 already acceptsbackend: strandlog_encoding: str, so no backend changes are needed unless you want to add server-side validation. - Date Pattern should use a Fluent UI
Combobox(editable dropdown) instead of aSelect, so users can still type a custom pattern. - Follow the existing DNS Mode dropdown pattern at lines 271–280 of
JailsTab.tsxas a reference for styling and onChange handling.
Task 2 — Fix Raw Action Configuration Always Blank in Actions Tab
Problem: In the Config → Actions page, the "Raw Action Configuration" accordion section is always blank when expanded.
Where to look:
frontend/src/components/config/ActionsTab.tsx— theActionDetailcomponent (lines 65–185). ThefetchRawcallback (line 82) callsfetchActionFile(action.name)and returnsresult.content.frontend/src/components/config/RawConfigSection.tsx— the collapsible raw editor. It uses aloadedRef(line 62) to fetch content only on first accordion expansion, and never resets.frontend/src/api/config.tsline 257 —fetchActionFile()callsGET /config/actions/{name}.backend/app/services/file_config_service.pyline 734 —get_action_file()reads fromaction.d/using_read_conf_file(), which tries.conffirst, then.local.
Root cause investigation — check these two possibilities:
Possibility A: Stale loadedRef across action switches
ActionDetail at line 301 of ActionsTab.tsx is rendered without a key prop:
<ActionDetail
action={selectedAction}
onAssignClick={() => { setAssignOpen(true); }}
onRemovedFromJail={handleRemovedFromJail}
/>
Without key={selectedAction.name}, React reuses the same component instance when switching between actions. The RawConfigSection inside ActionDetail keeps its loadedRef.current = true from a previous expansion, so it never re-fetches for the new action.
Fix: Add key={selectedAction.name} to ActionDetail so it fully unmounts/remounts on action switch, resetting all internal state including loadedRef.
Possibility B: Backend returning empty content
The _read_conf_file function at file_config_service.py line 492 tries .conf first then .local. If the action only has a .conf file and it's readable, content should be returned. Verify by:
- Checking browser DevTools Network tab for the
GET /api/config/actions/{name}response body. - Confirming the response has a non-empty
contentfield.
Implementation:
- Add
key={selectedAction.name}to the<ActionDetail>component inActionsTab.tsxline 301. - Test by selecting different actions and expanding the raw config accordion — each should show the correct file content.
- If the backend is returning empty content, debug
_read_conf_fileto check which file path it resolves and whether it can read the file.
Task 3 — Give Inactive Jail Configs the Same GUI as Active Ones
Problem: In the Config → Jails page, inactive jails show a minimal read-only preview (InactiveJailDetail component) with only basic fields (filter, port, ban time, find time, max retry, log paths, actions, source file). Active jails like bangui-sim show a full editable form (JailConfigDetail component) with all fields including Backend, Log Encoding, Date Pattern, DNS Mode, Prefix Regex, editable log paths, Fail/Ignore Regex lists, Ban-time Escalation, and a Raw Configuration editor.
The inactive jail detail should display the same rich GUI as the active one — same layout, same fields — but in read-only mode (since the jail is not running on fail2ban).
Where to change:
-
frontend/src/components/config/JailsTab.tsx:InactiveJailDetailcomponent (lines 493–580): the current minimal read-only view.JailConfigDetailcomponent (lines ~200–490): the full editable form for active jails.- Selection logic at lines 721–752 determines which component renders based on jail kind.
-
frontend/src/types/config.ts—InactiveJailtype (around line 484) defines the data shape for inactive jails. Compare withJailConfigto see which fields are missing. -
Backend — check that the inactive jail endpoint returns enough data. The backend model
InactiveJailmay need additional fields (backend, log_encoding, date_pattern, dns_mode, prefix_regex, failregex, ignoreregex, ban-time escalation settings) to match the activeJailConfigmodel.
Implementation approach:
- Extend the
InactiveJailmodel in the backend (backend/app/models/config.py) to include all fields thatJailConfighas:backend,log_encoding,date_pattern,usedns,prefixregex,failregex,ignoreregex,bantime_increment(escalation settings), etc. - Update the backend service that parses inactive jail configs to extract these additional fields from the
.conf/.localfiles. - Rewrite
InactiveJailDetailto mirrorJailConfigDetail's layout but with all fields in read-only mode (all<Input>havereadOnly, all<Select>are disabled). Keep the "Activate Jail" button at the bottom. - Alternatively, refactor
JailConfigDetailto accept areadOnlyprop and reuse it for both active and inactive jails, avoiding code duplication. - Include the Raw Configuration accordion (using
RawConfigSection) for inactive jails as well, pointed at the jail's config file.
Testing: Select an inactive jail and verify it shows the same fields and layout as an active jail like bangui-sim, with all values populated from the config file but not editable.
Task 4 — Fix banaction Interpolation Error When Activating Jails
Problem: Activating certain jails (e.g. airsonic-auth) causes fail2ban to crash with:
ERROR Failed during configuration: Bad value substitution: option 'action'
in section 'airsonic-auth' contains an interpolation key 'banaction' which
is not a valid option name. Raw value: '%(action_)s'
Root cause — the interpolation chain breaks:
- Most jails inherit
action = %(action_)sfrom[DEFAULT]. action_is defined infail2ban-master/config/jail.confline 212 as:action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]banactionis commented out in the[DEFAULT]section ofjail.conf(line 208):#banaction = iptables-multiport #banaction_allports = iptables-allports- When BanGUI activates a jail,
_write_local_override_sync()inbackend/app/services/config_file_service.py(line 478) writes a.localfile with onlyenabled,bantime,findtime,maxretry,port, andlogpath— it does not includebanactionoraction. - fail2ban merges the configs, hits
%(banaction)s, finds no value, and crashes.
Where to change:
backend/app/services/config_file_service.py— function_write_local_override_sync()around line 478. This writes the per-jail.localoverride file.backend/app/models/config.py— theActivateJailRequestor equivalent model that defines which fields are accepted when activating a jail.
Fix options (pick one):
Option A: Write banaction in every jail .local file (recommended)
When BanGUI writes a jail .local file, include a banaction field with a sensible default (e.g. iptables-multiport). This ensures the interpolation chain always resolves.
- Add a
banactionfield (defaulting toiptables-multiport) to the jail activation model/request. - In
_write_local_override_sync(), always writebanaction = <value>into the[jailname]section of the.localfile. - Also include
banaction_allports = iptables-allportsas a fallback for jails that reference it.
Option B: Write a global jail.local with DEFAULT banaction
Create or update a jail.local file in the fail2ban config directory with:
[DEFAULT]
banaction = iptables-multiport
banaction_allports = iptables-allports
This provides the missing defaults for all jails at once.
Option C: Expose banaction in the jail config UI
Add banaction as a configurable field in the jail config form so users can choose the ban action (iptables-multiport, nftables-multiport, firewallcmd-rich-rules, etc.). Write the selected value to the .local file on save/activate.
Testing:
- Activate
airsonic-auth(or any jail that inheritsaction = %(action_)swithout defining its ownbanaction). - Verify fail2ban starts without the interpolation error.
- Verify
fail2ban-client status airsonic-authshows the jail running with the correct ban action.