the LAN rule moves from nginx into the app, as a setting for keys only

api_keys_from is a typed setting, lan (default, the historical rule) or
anywhere, checked in admin_api._auth against the first X-Forwarded-For hop
that NPM sets. It governs API keys only: a session is never restricted, the
console is a session from anywhere; and the break-glass token is ALWAYS
LAN-only, which is not a choice and so is not a setting. LAN ranges are
facts about the network and live in code; the docker range is included
because NPM, the routines and sibling containers reach the app from
arrstack_arr_net. An unparseable address is not LAN - the rule fails closed.

With this in place the location /api/admin/ block on NPM host 51 can come
out; the app enforces what it enforced, and the toggle never touches NPM.

tests/smoke_admin.py 58 -> 76. Proven on a throwaway site: key LAN 200,
key WAN 403 naming the setting, session WAN 200, token LAN 200, token WAN
403; set anywhere, key WAN 200 and token WAN still 403; bogus value 422.
This commit is contained in:
2026-09-04 17:45:17 -04:00
parent 4b5b2a3667
commit 69b177d519
3 changed files with 97 additions and 3 deletions
+14
View File
@@ -867,10 +867,24 @@ def _setting_https_url(v):
return v
def _setting_choice(*allowed):
def check(v):
v = _setting_text(v).lower()
if v not in allowed:
raise IdentityError(422, "value must be one of %s" % ", ".join(allowed))
return v
return check
# key -> (code default, checker)
SETTINGS_KEYS = {
"nearby_source_name": ("Continental District unit list", _setting_text),
"nearby_source_url": ("https://tinyurl.com/ContinentalScouts", _setting_https_url),
# Where API keys may be used from. "lan" is the historical rule, kept as
# the default. Sessions are never restricted (the console IS a session
# from anywhere) and the break-glass token is ALWAYS LAN-only - that one
# is not a choice, so it is not a setting. See admin_api.client_is_lan.
"api_keys_from": ("lan", _setting_choice("lan", "anywhere")),
}