GeoIP alias: Kosovo (XK) is downloaded from MaxMind but cannot be selected or saved ("not a valid country code")
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Domain
- backend, networking
Research direction
Start with the two places that build the country list: AliasContentField::getCountryCodes() in src/opnsense/mvc/app/models/OPNsense/Firewall/FieldTypes/AliasContentField.php, which validates entries, and AliasController::listCountriesAction() in src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/AliasController.php, which feeds the dialog. Both special-case EU; add XK the same way and confirm XK is accepted and listed. Done when an alias with content XK validates and Kosovo appears under Europe in the GeoIP dialog.
Written by the indexing model from the issue text.
Description
Important notices
- I have read the contributing guide lines at https://github.com/opnsense/core/blob/master/CONTRIBUTING.md
- I am convinced that my issue is new after having checked both open and closed issues at https://github.com/opnsense/core/issues?q=is%3Aissue
Describe the bug
The GeoIP downloader (scripts/filter/lib/alias/geoip.py) writes per-country files for every country code found in the MaxMind GeoLite2 Country CSV. Kosovo uses the user-assigned code XK, so after a normal download the files /usr/local/share/GeoIP/alias/XK-IPv4 (51 networks) and XK-IPv6 (58 networks) exist on the system.
Kosovo can't be used, though:
- The country list in the alias dialog (
AliasController::listCountriesAction()) is built fromcontrib/iana/tzdata-iso3166.tab, which does not containXK. - Validation (
AliasContentField::getCountryCodes()) uses the same file and rejects it:Entry "XK" is not a valid country code.
EU (MaxMind "Unclassified") is already special-cased in both places. XK is the only code present in the MaxMind data and missing in OPNsense:
OPNsense selectable countries: 249 | MaxMind countries in /usr/local/share/GeoIP/alias: 250
present in MaxMind data but not selectable: XK (IPv4 51, IPv6 58)
This was reported before in #9562 (Kosovo missing from the list). It was closed by the bot after 180 days without a fix. Back then the reporter noted that MaxMind had the same gap, but the current GeoLite2 data does contain Kosovo.
To Reproduce
- Configure a MaxMind GeoLite2 license and download the GeoIP data.
ls /usr/local/share/GeoIP/alias/XK-*shows that the files exist.- Firewall → Aliases → + → type GeoIP: Kosovo is not in the Europe list.
- Create the alias via API/model with content
XK:Entry "XK" is not a valid country code.
Expected behavior
Kosovo can be selected in GeoIP aliases, like EU.
Proposed fix
Handle XK the same way as EU (patch below). The alias resolver (geoip.py iter_addresses()) already reads <country>-<proto> files generically, so no other change is needed.
--- a/src/opnsense/mvc/app/models/OPNsense/Firewall/FieldTypes/AliasContentField.php
+++ b/src/opnsense/mvc/app/models/OPNsense/Firewall/FieldTypes/AliasContentField.php
@@ -105,6 +105,8 @@
if (empty(self::$internalCountryCodes)) {
// Maxmind's country code 6255148 (EU Unclassified)
self::$internalCountryCodes[] = 'EU';
+ // Maxmind's user-assigned code for Kosovo (not part of ISO 3166 / tzdata)
+ self::$internalCountryCodes[] = 'XK';
$contribDir = (new AppConfig())->application->contribDir;
--- a/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/AliasController.php
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/AliasController.php
@@ -248,6 +248,10 @@
$result = [
'EU' => [
'name' => gettext('Unclassified'),
+ 'region' => 'Europe'
+ ],
+ 'XK' => [
+ 'name' => gettext('Kosovo'),
'region' => 'Europe'
]
];
Related observation: the EU special case seems to be obsolete. The current GeoLite2 Country CSV (GeoLite2-Country-CSV_20261113) has no location with country code EU any more (0 entries in GeoLite2-Country-Locations-en.csv), and no EU-IPv4/EU-IPv6 files are created. So "Unclassified (EU)" is still offered in the dialog, but an alias using it silently resolves to nothing.
php -l passes for both files. Current workaround: a static network alias filled from XK-IPv4/XK-IPv6, but that one does not update together with the GeoIP data.
Environment
OPNsense 26.7.9_1 (amd64), MaxMind GeoLite2 Country CSV (GeoLite2-Country-CSV_20261113, downloaded 2026-11-14)
AI disclosure
This report and the proposed fix were prepared with the help of an AI assistant (Claude). The analysis was done on my own OPNsense system: the file comparison, the CSV check, the validation error and the php -l check come from my firewall. I reviewed the report before submitting.
- Dominant language
- PHP
- Stars
- 4.8k
- Forks
- 1k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 36
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from opnsense/core
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
opnsense/core#11171 · 1 reaction ·
Maintainers usually reply within 1 day
-
support
Difficulty 1/5 1-3 hours Newbie friendliness 85/100
opnsense/core#11153 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
opnsense/core#11148 · 1 reaction ·
Maintainers usually reply within 1 day
Similar issues
-
Old /download.php attachment links: redirect drops the subfolder, nginx.example.conf returns 404OpenBug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 Under an hour Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
0. Needs triage 35-feedback bug
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 72/100
wpgenius/stackfolio#22 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Automattic/static-site-importer#3024 ·
Maintainers usually reply within 1 day