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
207 lines
14 KiB
Markdown
207 lines
14 KiB
Markdown
# 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`** — the `JailConfigDetail` component, 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:**
|
||
|
||
1. Backend and Log Encoding are currently `readOnly`. They must become editable `<Select>` fields. Wire their `onChange` to local state and include them in the auto-save payload sent to the backend.
|
||
2. The backend model at `backend/app/models/config.py` line 67 already accepts `backend: str` and `log_encoding: str`, so no backend changes are needed unless you want to add server-side validation.
|
||
3. Date Pattern should use a Fluent UI `Combobox` (editable dropdown) instead of a `Select`, so users can still type a custom pattern.
|
||
4. Follow the existing DNS Mode dropdown pattern at lines 271–280 of `JailsTab.tsx` as 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`** — the `ActionDetail` component (lines 65–185). The `fetchRaw` callback (line 82) calls `fetchActionFile(action.name)` and returns `result.content`.
|
||
- **`frontend/src/components/config/RawConfigSection.tsx`** — the collapsible raw editor. It uses a `loadedRef` (line 62) to fetch content only on first accordion expansion, and never resets.
|
||
- **`frontend/src/api/config.ts`** line 257 — `fetchActionFile()` calls `GET /config/actions/{name}`.
|
||
- **`backend/app/services/file_config_service.py`** line 734 — `get_action_file()` reads from `action.d/` using `_read_conf_file()`, which tries `.conf` first, 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**:
|
||
```tsx
|
||
<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:
|
||
1. Checking browser DevTools Network tab for the `GET /api/config/actions/{name}` response body.
|
||
2. Confirming the response has a non-empty `content` field.
|
||
|
||
**Implementation:**
|
||
|
||
1. Add `key={selectedAction.name}` to the `<ActionDetail>` component in `ActionsTab.tsx` line 301.
|
||
2. Test by selecting different actions and expanding the raw config accordion — each should show the correct file content.
|
||
3. If the backend is returning empty content, debug `_read_conf_file` to 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`**:
|
||
- `InactiveJailDetail` component (lines 493–580): the current minimal read-only view.
|
||
- `JailConfigDetail` component (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`** — `InactiveJail` type (around line 484) defines the data shape for inactive jails. Compare with `JailConfig` to see which fields are missing.
|
||
|
||
- **Backend** — check that the inactive jail endpoint returns enough data. The backend model `InactiveJail` may need additional fields (backend, log_encoding, date_pattern, dns_mode, prefix_regex, failregex, ignoreregex, ban-time escalation settings) to match the active `JailConfig` model.
|
||
|
||
**Implementation approach:**
|
||
|
||
1. **Extend the `InactiveJail` model** in the backend (`backend/app/models/config.py`) to include all fields that `JailConfig` has: `backend`, `log_encoding`, `date_pattern`, `usedns`, `prefixregex`, `failregex`, `ignoreregex`, `bantime_increment` (escalation settings), etc.
|
||
2. **Update the backend service** that parses inactive jail configs to extract these additional fields from the `.conf`/`.local` files.
|
||
3. **Rewrite `InactiveJailDetail`** to mirror `JailConfigDetail`'s layout but with all fields in **read-only** mode (all `<Input>` have `readOnly`, all `<Select>` are disabled). Keep the "Activate Jail" button at the bottom.
|
||
4. Alternatively, refactor `JailConfigDetail` to accept a `readOnly` prop and reuse it for both active and inactive jails, avoiding code duplication.
|
||
5. 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:**
|
||
|
||
1. Most jails inherit `action = %(action_)s` from `[DEFAULT]`.
|
||
2. `action_` is defined in `fail2ban-master/config/jail.conf` line 212 as:
|
||
```ini
|
||
action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||
```
|
||
3. `banaction` is **commented out** in the `[DEFAULT]` section of `jail.conf` (line 208):
|
||
```ini
|
||
#banaction = iptables-multiport
|
||
#banaction_allports = iptables-allports
|
||
```
|
||
4. When BanGUI activates a jail, `_write_local_override_sync()` in `backend/app/services/config_file_service.py` (line 478) writes a `.local` file with only `enabled`, `bantime`, `findtime`, `maxretry`, `port`, and `logpath` — it does **not** include `banaction` or `action`.
|
||
5. 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 `.local` override file.
|
||
- **`backend/app/models/config.py`** — the `ActivateJailRequest` or 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.
|
||
|
||
1. Add a `banaction` field (defaulting to `iptables-multiport`) to the jail activation model/request.
|
||
2. In `_write_local_override_sync()`, always write `banaction = <value>` into the `[jailname]` section of the `.local` file.
|
||
3. Also include `banaction_allports = iptables-allports` as 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:
|
||
```ini
|
||
[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:**
|
||
1. Activate `airsonic-auth` (or any jail that inherits `action = %(action_)s` without defining its own `banaction`).
|
||
2. Verify fail2ban starts without the interpolation error.
|
||
3. Verify `fail2ban-client status airsonic-auth` shows the jail running with the correct ban action.
|
||
|