feat(access): add country and ASN rules #20

Merged
ryan.mcgee merged 44 commits from feat/geoip-rules into main 2026-10-15 18:09:07 +00:00
Owner

Summary

  • Access rules can match where the client is: country, any country but one, or AS number.
  • Data comes from MMDB files the administrator provides (MaxMind GeoLite2 or GeoIP2, DB-IP, ipinfo); nothing is bundled or downloaded.
  • Stacked on #19 (feat/domains).

Changes

  • New dependency mmdb2_decoder ~> 3.0 (Apache-2.0, pure Elixir, no processes; last release 2022 because the format is frozen, repository active, and it is what geolix builds on).
  • TraefikControl.GeoIp: loads TRAEFIK_CONTROL_GEOIP_COUNTRY_DB and TRAEFIK_CONTROL_GEOIP_ASN_DB at startup and again when a file changes (checked every TRAEFIK_CONTROL_GEOIP_RELOAD_SECONDS, default 60, 0 never). A file that cannot be read or parsed leaves the loaded database in place and is logged once. Handles Country and City databases, plain country strings and asn strings.
  • Rule matches country, country_not, asn (values normalized to DE, AS13335). Creating or changing one needs its database; switching an existing rule on or off does not.
  • A client that cannot be placed fails towards protection: no country/asn rule matches it, a deny country_not rule matches it, an allow country_not rule never does.
  • The databases are only asked when the resource has an enabled rule that needs them.
  • Access page: new match options, database status line, and a warning when a rule has lost its data. API enum, blueprints (match: country|country_not|asn, text values), README.
  • No schema change: resource_rules.match is a plain string column.

Verification

  • mix format --check-formatted, mix compile --warnings-as-errors, mix credo --strict, mix dialyzer: clean.
  • mix test: 1492 passed on SQLite and on PostgreSQL.
  • Test databases are written by a small MMDB writer in test/support, so no data files are committed; the reader was also run against MaxMind's published test files (Country, City, ASN; IPv4 and IPv6).
  • Running app with those files: added deny country_not SE in the UI; the node's real forwardAuth address answered 200 for a Swedish address and 403 for GB, IT, an unlisted public address and a private one.

Risks

  • mmdb2_decoder 3.0.1 compiles with warnings on Elixir 1.20 (unpinned size/1 variables); it works, but a future Elixir that makes this an error would need a patch or a different reader.
  • Location is only as good as the data and the client address Traefik reports (TRAEFIK_CONTROL_TRUST_PROXY); VPNs and proxies hide it.
  • Memory: each database is held in memory (country about 5 to 10 MB, a City database much more).
  • mix hex.audit reports an advisory for bandit 1.12.5 (HTTP/2 deadline reset), which is already the latest version and predates this change.
## Summary - Access rules can match where the client is: country, any country but one, or AS number. - Data comes from MMDB files the administrator provides (MaxMind GeoLite2 or GeoIP2, DB-IP, ipinfo); nothing is bundled or downloaded. - Stacked on #19 (`feat/domains`). ## Changes - New dependency `mmdb2_decoder ~> 3.0` (Apache-2.0, pure Elixir, no processes; last release 2022 because the format is frozen, repository active, and it is what `geolix` builds on). - `TraefikControl.GeoIp`: loads `TRAEFIK_CONTROL_GEOIP_COUNTRY_DB` and `TRAEFIK_CONTROL_GEOIP_ASN_DB` at startup and again when a file changes (checked every `TRAEFIK_CONTROL_GEOIP_RELOAD_SECONDS`, default 60, `0` never). A file that cannot be read or parsed leaves the loaded database in place and is logged once. Handles Country and City databases, plain `country` strings and `asn` strings. - Rule matches `country`, `country_not`, `asn` (values normalized to `DE`, `AS13335`). Creating or changing one needs its database; switching an existing rule on or off does not. - A client that cannot be placed fails towards protection: no `country`/`asn` rule matches it, a deny `country_not` rule matches it, an allow `country_not` rule never does. - The databases are only asked when the resource has an enabled rule that needs them. - Access page: new match options, database status line, and a warning when a rule has lost its data. API enum, blueprints (`match: country|country_not|asn`, text values), README. - No schema change: `resource_rules.match` is a plain string column. ## Verification - `mix format --check-formatted`, `mix compile --warnings-as-errors`, `mix credo --strict`, `mix dialyzer`: clean. - `mix test`: 1492 passed on SQLite and on PostgreSQL. - Test databases are written by a small MMDB writer in `test/support`, so no data files are committed; the reader was also run against MaxMind's published test files (Country, City, ASN; IPv4 and IPv6). - Running app with those files: added `deny country_not SE` in the UI; the node's real forwardAuth address answered 200 for a Swedish address and 403 for GB, IT, an unlisted public address and a private one. ## Risks - `mmdb2_decoder` 3.0.1 compiles with warnings on Elixir 1.20 (unpinned `size/1` variables); it works, but a future Elixir that makes this an error would need a patch or a different reader. - Location is only as good as the data and the client address Traefik reports (`TRAEFIK_CONTROL_TRUST_PROXY`); VPNs and proxies hide it. - Memory: each database is held in memory (country about 5 to 10 MB, a City database much more). - `mix hex.audit` reports an advisory for `bandit 1.12.5` (HTTP/2 deadline reset), which is already the latest version and predates this change.
- rules can match country, any country but one, or AS number, from
  MMDB files the admin provides (new dep: mmdb2_decoder)
- GeoIp reloads the files when they change on disk; an unreadable
  file keeps the loaded database
- unplaceable clients fail towards protection: deny country_not
  matches them, allow rules never do
- a geo rule needs its database to be created; access page shows
  what is loaded and warns when a rule has lost its data
- API, blueprints and tests (including real-format MMDB files)
ryan.mcgee changed target branch from feat/domains to main 2026-10-15 18:08:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
White_Ghost_Cyber_Systems/traefik-control!20
No description provided.