Skip to content

Add shared jail feature#258

Open
xavernitsch wants to merge 4 commits intotomMoulard:mainfrom
xavernitsch:main
Open

Add shared jail feature#258
xavernitsch wants to merge 4 commits intotomMoulard:mainfrom
xavernitsch:main

Conversation

@xavernitsch
Copy link

@xavernitsch xavernitsch commented Mar 5, 2026

With the help of AI I was able to implement the shared jail feature #256

I hope you and some others like it

Summary by CodeRabbit

  • New Features

    • Option to share a jail across multiple routers so bans propagate across associated instances.
    • Configuration now supports file-based IP lists in addition to inline IP entries.
  • Documentation

    • Added a "Shared Jail" subsection with YAML example and behavior notes.
  • Tests

    • Added tests validating shared-jail behavior across configurations, including successive-request scenarios.
  • Chores

    • Improved logging around creation and reuse of jail instances.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 17e4157d-22df-47bd-a28f-cdd8422d895d

📥 Commits

Reviewing files that changed from the base of the PR and between 6e1923b and 5993ff4.

📒 Files selected for processing (2)
  • fail2ban.go
  • fail2ban_test.go

📝 Walkthrough

Walkthrough

Adds optional shared-jail support to the fail2ban middleware: new global jail map and mutex, Config.SharedJail flag, List.Files field, and New now accepts a name to key shared jails; README and tests updated to exercise shared vs. per-instance jail behavior.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added "Shared Jail" subsection with YAML example and note that bans propagate across subdomains when sharedJail: true.
Core Implementation
fail2ban.go
Added globalJails map and globalMu mutex; added Config.SharedJail and List.Files; changed New signature to accept name and implemented getOrCreateSharedJail to create/reuse shared jails keyed by name+config hash; logs updated.
Tests
fail2ban_test.go
Added testRemoteAddr constant, imported crypto/sha256, and added TestFail2Ban_SuccessiveRequests_SharedJail to validate shared vs non-shared jail behavior across two handler instances; test cleans global state per case.
Manifest
go.mod
Module manifest updated (lines changed noted in summary).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant H1 as Handler A
    participant Global as globalJails (map + globalMu)
    participant Jail as Fail2Ban Jail
    participant H2 as Handler B

    Client->>H1: HTTP request
    H1->>Global: lock globalMu / lookup or create jail (key: name+config hash)
    Global-->>H1: return Jail
    H1->>Jail: check ban / record failure
    Jail-->>H1: banned? / allow or block
    H1->>Client: response

    Note over Client,H2: Subsequent request via another middleware instance
    Client->>H2: HTTP request
    H2->>Global: lock globalMu / lookup jail
    Global-->>H2: return same Jail (if SharedJail)
    H2->>Jail: check ban
    Jail-->>H2: banned? / allow or block
    H2->>Client: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I found a tiny global key,
A map and mutex under a tree,
Two routers share one tiny jail,
Bans hop borders on the trail,
Hooray — one lock, one shared melody.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add shared jail feature' directly and accurately describes the main change in the changeset, which is the implementation of a shared jail feature across multiple files (README, fail2ban.go, and fail2ban_test.go).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fail2ban_test.go`:
- Around line 519-537: Two table-driven test cases share the same "name" value
which makes test failures ambiguous; update the second test case's name field to
a unique, descriptive string (e.g., differentiate by "shared jail" vs "separate
jail" or include an index) so subtest names are distinct. Locate the duplicate
"name" fields in the table of test cases (entries that include cfg: &Config{...}
and rules.Rules) and rename the second occurrence to a different string, keeping
the rest of the test data (cfg, handlerStatus, expectStatus, expectStatusSecond)
unchanged.
- Around line 554-565: The subtest must use a unique jail key per run and
fail-fast on setup errors: replace the fixed string "fail2ban_test" passed to
New(...) with a deterministic per-subtest key (e.g., include test.name or
t.Name()) to avoid shared-jail state leakage, and check the returned error from
both calls to New (handler, err := New(...)) and (handler2, err := New(...)) and
call t.Fatalf (or t.Fatal) if err != nil so the test stops immediately on setup
failure.

In `@fail2ban.go`:
- Around line 148-157: The current SharedJail logic reuses an existing f2b from
globalJails keyed only by `name`, which silently ignores new
`rules`/`allowNetIPs`; modify the logic in the block that references
`SharedJail`, `jailKey`, `globalMu`, `globalJails`, and `fail2ban.New` to detect
config drift or include the config in the key: either compute a stable
fingerprint (hash) of `rules` and `allowNetIPs` and use `name + ":" +
fingerprint` as the `jailKey`, or load the existing `f2b` and compare its stored
config to the incoming `rules`/`allowNetIPs` and return an error if they differ;
ensure access is still protected by `globalMu` and that creation still calls
`fail2ban.New(rules, allowNetIPs)` when no matching keyed jail exists.

In `@README.md`:
- Around line 55-56: The markdown heading "### Shared Jail" is not followed by a
blank line which triggers markdownlint rule MD022; fix it by inserting a single
empty line immediately after the "### Shared Jail" heading in README.md so the
heading is separated from the following paragraph content and the lint rule
passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 931bb141-7ec2-44ba-a0b0-a1ac529e51ce

📥 Commits

Reviewing files that changed from the base of the PR and between de2a436 and 07bc3b3.

📒 Files selected for processing (3)
  • README.md
  • fail2ban.go
  • fail2ban_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fail2ban.go`:
- Around line 152-153: The json.Marshal error is ignored when creating jailKey
(keyBytes, _ := json.Marshal(config)); instead capture the error and handle it:
call json.Marshal into (keyBytes, err), and if err != nil either return the
error up from the function or log it and fail fast so you don't compute md5.Sum
on nil bytes; then proceed to compute jailKey := fmt.Sprintf("%s-%x", name,
md5.Sum(keyBytes)) only when keyBytes is valid. Ensure you reference the
existing names json.Marshal, keyBytes, err, jailKey, md5.Sum, name, and config
when implementing the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: db6e6350-b6b9-4e20-951b-af811c0788b8

📥 Commits

Reviewing files that changed from the base of the PR and between 07bc3b3 and 4a4b223.

📒 Files selected for processing (3)
  • README.md
  • fail2ban.go
  • fail2ban_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fail2ban_test.go`:
- Around line 560-562: The cleanup is deleting the wrong key: tests currently
call delete(globalJails, jailKey) but shared jails are stored under a
"name-hash" key. While holding globalMu, replace the deletion to remove the
actual shared-jail key (e.g., compute and delete fmt.Sprintf("%s-%s", name,
hash) or call the existing helper used to build the shared key) instead of
jailKey so globalJails is truly cleaned up; ensure you use the same name and
hash values that were used to create the shared jail.

In `@fail2ban.go`:
- Around line 150-163: The shared-jail map (globalJails) grows unbounded because
every distinct config hash creates a new entry in the block inside the
config.SharedJail branch; change this to use a bounded-retention strategy: when
inserting a new jailKey in the else branch (where fail2ban.New is called and
globalJails[jailKey] = f2b) maintain eviction metadata (e.g., lastUsed timestamp
or an LRU list) and evict old/stale entries when the map exceeds a configured
max size or when entries age out (implement eviction under globalMu to preserve
concurrency safety); ensure accesses (lookup, update of lastUsed, insert, and
eviction) are all protected by globalMu and consider a background cleanup
goroutine to remove expired entries if using TTLs so stale jails are reclaimed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6121fed7-e7ce-43c6-90ce-c9dc88875b90

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4b223 and 6e1923b.

📒 Files selected for processing (2)
  • fail2ban.go
  • fail2ban_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant