feature/ignore-self-toggle #1
Reference in New Issue
Block a user
Delete Branch "feature/ignore-self-toggle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Backend (tasks 2.1–2.6, 2.10): - settings_repo: get/set/delete/get_all CRUD for the key-value settings table - session_repo: create/get/delete/delete_expired for session rows - setup_service: bcrypt password hashing, one-time-only enforcement, run_setup() / is_setup_complete() / get_password_hash() - auth_service: login() with bcrypt verify + token creation, validate_session() with expiry check, logout() - setup router: GET /api/setup (status), POST /api/setup (201 / 409) - auth router: POST /api/auth/login (token + HttpOnly cookie), POST /api/auth/logout (clears cookie, idempotent) - SetupRedirectMiddleware: 307 → /api/setup for all API paths until setup done - require_auth dependency: cookie or Bearer token → Session or 401 - conftest.py: manually bootstraps app.state.db for router tests (ASGITransport does not trigger ASGI lifespan) - 85 tests pass; ruff 0 errors; mypy --strict 0 errors Frontend (tasks 2.7–2.9): - types/auth.ts, types/setup.ts, api/auth.ts, api/setup.ts - AuthProvider: sessionStorage-backed context (isAuthenticated, login, logout) - RequireAuth: guard component → /login?next=<path> when unauthenticated - SetupPage: Fluent UI form, client-side validation, inline errors - LoginPage: single password input, ?next= redirect after success - DashboardPage: placeholder (full impl Stage 5) - App.tsx: full route tree (/setup, /login, /, *)- history.py models: HistoryBanItem, HistoryListResponse, IpTimelineEvent, IpDetailResponse - history_service.py: list_history() with dynamic WHERE clauses (range/jail/ip prefix/all-time), get_ip_detail() with timeline aggregation - history.py router: GET /api/history + GET /api/history/{ip} (404 for unknown) - Fixed latent bug in ban_service._parse_data_json: json.loads('null') -> None -> AttributeError; now checks isinstance(parsed, dict) before assigning obj - 317 tests pass (27 new), ruff + mypy clean (46 files) - types/history.ts, api/history.ts, hooks/useHistory.ts created - HistoryPage.tsx: filter bar (time range/jail/IP), DataGrid table, high-ban-count row highlighting, per-IP IpDetailView with timeline, pagination - Frontend tsc + ESLint clean (0 errors/warnings) - Tasks.md Stage 9 marked doneTask 1 — fix Stop/Reload Jail returning 404 Root cause: reload_jail and reload_all sent an empty config stream (["reload", name, [], []]). In fail2ban's reload protocol the end-of- reload phase deletes every jail still in reload_state — i.e. every jail that received no configuration commands. An empty stream means *all* affected jails are silently removed from the daemon's runtime, causing everything touching those jails afterwards (including stop) to receive UnknownJailException → HTTP 404. Fixes: - reload_jail: send ["start", name] in the config stream; startJail() removes the jail from reload_state so the end phase commits instead of deletes, and un-idles the jail. - reload_all: fetch current jail list first, build a ["start", name] entry for every active jail, then send reload --all with that stream. - stop_jail: made idempotent — if the jail is already gone (not-found error) the operation silently succeeds (200 OK) rather than returning 404, matching the user expectation that stop = ensure-stopped. - Router: removed dead JailNotFoundError handler from stop endpoint. 391 tests pass (2 new), ruff clean, mypy clean (pre-existing config.py error unchanged). Task 2 — access list simulator - Docker/simulate_accesses.sh: writes fake HTTP-scan log lines in custom format (bangui-access: http scan from <IP> ...) to Docker/logs/access.log so the bangui-access jail detects them. - fail2ban/filter.d/bangui-access.conf: failregex matching the above. - fail2ban/jail.d/bangui-access.conf: polling jail on access.log, same settings as bangui-sim (maxretry=3, bantime=60s). - .gitignore: whitelist new bangui-access.conf files. - Docker/fail2ban-dev-config/README.md: added "Testing the Access List Feature" section with step-by-step instructions and updated Configuration Reference + Troubleshooting.- Task 1: Mark imported blocklist IP addresses - Add BanOrigin type and _derive_origin() to ban.py model - Populate origin field in ban_service list_bans() and bans_by_country() - BanTable and MapPage companion table show origin badge column - Tests: origin derivation in test_ban_service.py and test_dashboard.py - Task 2: Add origin filter to dashboard and world map - ban_service: _origin_sql_filter() helper; origin param on list_bans() and bans_by_country() - dashboard router: optional origin query param forwarded to service - Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts - fetchBans / fetchBansByCountry forward origin to API - useBans / useMapData accept and pass origin; page resets on change - BanTable accepts origin prop; DashboardPage adds segmented filter - MapPage adds origin Select next to time-range picker - Tests: origin filter assertions in test_ban_service and test_dashboardTask 4 (Better Jail Configuration) implementation: - Add fail2ban_config_dir setting to app/config.py - New file_config_service: list/view/edit/create jail.d, filter.d, action.d files with path-traversal prevention and 512 KB content size limit - New file_config router: GET/PUT/POST endpoints for jail files, filter files, and action files; PUT .../enabled for toggle on/off - Extend config_service with delete_log_path() and add_log_path() - Add DELETE /api/config/jails/{name}/logpath and POST /api/config/jails/{name}/logpath - Extend geo router with re-resolve endpoint; add geo_re_resolve background task - Update blocklist_service with revised scheduling helpers - Update Docker compose files with BANGUI_FAIL2BAN_CONFIG_DIR env var and rw volume mount for the fail2ban config directory - Frontend: new Jail Files, Filters, Actions tabs in ConfigPage; file editor with accordion-per-file, editable textarea, save/create; add/delete log paths - Frontend: types in types/config.ts; API calls in api/config.ts and api/endpoints.ts - 63 new backend tests (test_file_config_service, test_file_config, test_geo_re_resolve) - 6 new frontend tests in ConfigPageLogPath.test.tsx - ruff, mypy --strict, tsc --noEmit, eslint: all clean; 617 backend tests pass- Backend: config_file_service.py parses jail.conf/jail.local/jail.d/* following fail2ban merge order; discovers jails not running in fail2ban - Backend: 3 new API endpoints (GET /jails/inactive, POST /jails/{name}/activate, POST /jails/{name}/deactivate); moved /jails/inactive before /jails/{name} to fix route-ordering conflict - Frontend: ActivateJailDialog component with optional parameter overrides - Frontend: JailsTab extended with inactive jail list and InactiveJailDetail pane - Frontend: JailsPage JailOverviewSection shows inactive jails with toggle - Tests: 57 service tests + 16 router tests for all new endpoints (all pass) - Docs: Features.md, Architekture.md, Tasks.md updated; Tasks 1.1-1.5 marked done- 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- PUT /api/config/filters/{name}: updates failregex/ignoreregex/datepattern/ journalmatch in filter.d/{name}.local; validates regex via re.compile(); supports ?reload=true - POST /api/config/filters: creates filter.d/{name}.local from FilterCreateRequest; returns 409 if file already exists - DELETE /api/config/filters/{name}: deletes .local only; returns 409 for conf-only (readonly) filters - POST /api/config/jails/{name}/filter: assigns filter to jail by writing 'filter = {name}' to jail.d/{jail}.local; supports ?reload=true - New models: FilterUpdateRequest, FilterCreateRequest, AssignFilterRequest - New service helpers: _safe_filter_name, _validate_regex_patterns, _write_filter_local_sync, _set_jail_local_key_sync - Fixed .local-only filter discovery in _parse_filters_sync (5-tuple return) - Fixed get_filter extension stripping (.local is 6 chars not 5) - Renamed file_config.py raw-write routes to /raw suffix (PUT /filters/{name}/raw, POST /filters/raw) to avoid routing conflicts - Full service + router tests; all 930 tests pass- Rewrite FiltersTab: use fetchFilters() for FilterConfig[] with embedded active status; show 'Active — sshd, apache-auth' badge labels; FilterDetail sub- component with source_file/override badges, FilterForm, Assign button, raw config section - New AssignFilterDialog: selects jail from enabled-jails list, calls POST /config/jails/{name}/filter with optional fail2ban reload - New CreateFilterDialog: name+failregex+ignoreregex form, calls POST /config/filters, closes and selects new filter on success - Extend ConfigListDetail: add listHeader (for Create button) and itemBadgeLabel (for custom badge text) optional props - Fix updateFilterFile bug: was PUT /config/filters/{name} (structured endpoint), now correctly PUT /config/filters/{name}/raw - Fix createFilterFile bug: was POST /config/filters, now POST /config/filters/raw - Add updateFilter, createFilter, deleteFilter, assignFilterToJail to api/config.ts - Add FilterUpdateRequest, FilterCreateRequest, AssignFilterRequest to types/config.ts - Add configFiltersRaw, configJailFilter endpoints - Tests: 24 new tests across FiltersTab, AssignFilterDialog, CreateFilterDialog (all 89 frontend tests passing)- ActionConfig extended with active, used_by_jails, source_file, has_local_override - New models: ActionListResponse, ActionUpdateRequest, ActionCreateRequest, AssignActionRequest - New service functions: list_actions, get_action, update_action, create_action, delete_action, assign_action_to_jail, remove_action_from_jail - New error classes: ActionNotFoundError, ActionAlreadyExistsError, ActionReadonlyError, ActionNameError - New router endpoints: GET/PUT/POST/DELETE /api/config/actions, POST/DELETE /api/config/jails/{name}/action - Service + router tests: 290 tests passing, mypy strict clean, ruff cleanTask 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 fileTask 2: adds a new Log tab to the Configuration page. Backend: - New Pydantic models: Fail2BanLogResponse, ServiceStatusResponse (backend/app/models/config.py) - New service methods in config_service.py: read_fail2ban_log() — queries socket for log target/level, validates the resolved path against a safe-prefix allowlist (/var/log) to prevent path traversal, then reads the tail of the file via the existing _read_tail_lines() helper; optional substring filter applied server-side. get_service_status() — delegates to health_service.probe() and appends log level/target from the socket. - New endpoints in routers/config.py: GET /api/config/fail2ban-log?lines=200&filter=... GET /api/config/service-status Both require authentication; log endpoint returns 400 for non-file log targets or path-traversal attempts, 502 when fail2ban is unreachable. Frontend: - New LogTab.tsx component: Service Health panel (Running/Offline badge, version, jail count, bans, failures, log level/target, offline warning banner). Log viewer with color-coded lines (error=red, warning=yellow, debug=grey), toolbar (filter input + debounce, lines selector, manual refresh, auto-refresh with interval selector), truncation notice, and auto-scroll to bottom on data updates. fetchData uses Promise.allSettled so a log-read failure never hides the service-health panel. - Types: Fail2BanLogResponse, ServiceStatusResponse (types/config.ts) - API functions: fetchFail2BanLog, fetchServiceStatus (api/config.ts) - Endpoint constants (api/endpoints.ts) - ConfigPage.tsx: Log tab added after existing tabs Tests: - Backend service tests: TestReadFail2BanLog (6), TestGetServiceStatus (2) - Backend router tests: TestGetFail2BanLog (8), TestGetServiceStatus (3) - Frontend: LogTab.test.tsx (8 tests) Docs: - Features.md: Log section added under Configuration View - Architekture.md: config.py router and config_service.py descriptions updated - Tasks.md: Task 2 marked done- Backend: extend activate_jail() with pre-validation and 4-attempt post-reload health probe; add validate_jail_config() and rollback_jail() service functions - Backend: new endpoints POST /api/config/jails/{name}/validate, GET /api/config/pending-recovery, POST /api/config/jails/{name}/rollback - Backend: extend JailActivationResponse with fail2ban_running + validation_warnings; add JailValidationIssue, JailValidationResult, PendingRecovery, RollbackResponse models - Backend: health_check task tracks last_activation and creates PendingRecovery record when fail2ban goes offline within 60 s of an activation - Backend: add fail2ban_start_command setting (configurable start cmd for rollback) - Frontend: ActivateJailDialog — pre-validation on open, crash-detected callback, extended spinner text during activation+verify - Frontend: JailsTab — Validate Config button for inactive jails, validation result panels (blocking errors + advisory warnings) - Frontend: RecoveryBanner component — polls pending-recovery, shows full-width alert with Disable & Restart / View Logs buttons - Frontend: MainLayout — mount RecoveryBanner at layout level - Tests: 19 new backend service tests (validate, rollback, filter/action parsing) + 6 health_check crash-detection tests + 11 router tests; 5 RecoveryBanner frontend tests; fix mock setup in existing activate_jail testsBackend: - Add JailBannedIpsResponse Pydantic model (ban.py) - Add get_jail_banned_ips() service: server-side pagination, optional IP substring search, geo enrichment on page slice only (jail_service.py) - Add GET /api/jails/{name}/banned endpoint with page/page_size/search query params, 400/404/502 error handling (routers/jails.py) - 23 new tests: 13 service tests + 10 router tests (all passing) Frontend: - Add JailBannedIpsResponse TS interface (types/jail.ts) - Add jailBanned endpoint helper (api/endpoints.ts) - Add fetchJailBannedIps() API function (api/jails.ts) - Add BannedIpsSection component: Fluent UI DataGrid, debounced search (300 ms), prev/next pagination, page-size dropdown, per-row unban button, loading spinner, empty state, error MessageBar (BannedIpsSection.tsx) - Mount BannedIpsSection in JailDetailPage between stats and patterns - 12 new Vitest tests for BannedIpsSection (all passing)backend/tests/test_routers/test_file_config.py: - TestListActionFiles.test_200_returns_files: GET /api/config/actions is handled by config.router (registered before file_config.router), so mock config_file_service.list_actions and assert on ActionListResponse.actions - TestCreateActionFile.test_201_creates_file: same route conflict; mock config_file_service.create_action and use ActionCreateRequest body format frontend/src/components/__tests__/ConfigPageLogPath.test.tsx: - Log paths are rendered as <Input value={path}>, not text nodes; replace getByText() with getByDisplayValue() for both test assertionsTask 1: Remove ActiveBansSection from JailsPage - Delete buildBanColumns, fmtTimestamp, ActiveBansSection - Remove Dialog/Delete/Dismiss imports, ActiveBan type - Update JSDoc to reflect three sections Task 2: Remove JailDistributionChart from Dashboard - Delete import and JSX block from DashboardPage.tsx Task 3: Fix transparent pie chart (TopCountriesPieChart) - Add Cell import and per-slice <Cell fill={slice.fill}> children inside <Pie> - Suppress @typescript-eslint/no-deprecated (recharts v3 types) Task 4: Allow /config/log as safe log prefix - Add '/config/log' to _SAFE_LOG_PREFIXES in config_service.py - Update error message to list both allowed directories Task 5: Block jail activation on missing filter/logpath - activate_jail refuses to proceed when filter/logpath issues found - ActivateJailDialog treats all validation issues as blocking - Trigger immediate _run_probe after activation in config router - /api/health now reports fail2ban online/offline from cached probe - Add TestActivateJailBlocking tests; fix existing tests to mock validationImplements the missing UI control for POST /api/jails/{name}/ignoreself: - Add jailIgnoreSelf endpoint constant to endpoints.ts - Add toggleIgnoreSelf(name, on) API function to jails.ts - Expose toggleIgnoreSelf action from useJailDetail hook - Replace read-only 'ignore self' badge with a Fluent Switch in IgnoreListSection to allow enabling/disabling the flag per jail - Add 5 vitest tests for checked/unchecked state and toggle behaviourTask 1: Move Configuration to last position in sidebar NAV_ITEMS Task 2: Add automatic rollback when jail activation fails - Back up .local override file before writing - Restore original file (or delete) on reload failure, health-check failure, or jail not appearing post-reload - Return recovered=True/False in JailActivationResponse - Show warning/critical banner in ActivateJailDialog based on recovery - Add _restore_local_file_sync and _rollback_activation_async helpers - Add 3 new tests: rollback on reload failure, health-check failure, and double failure (recovered=False) Task 3: Color pie chart legend labels to match their slice color - legendFormatter now returns ReactNode with span style={{ color }} - Import LegendPayload from recharts/types/component/DefaultLegendContent