Allow local CLI maintenance across direct connections
This commit is contained in:
1 parent
001b4985f7
commit
06c51fdef5
39 files changed
+3338
-2312
No files matched your search
@@ -75,8 +75,9 @@ def requirements(platform, defines, target):
|
||||
parts["mqtt_connections_buffers"] = 24576
|
||||
if "ENABLE_OTA" in defines:
|
||||
parts["ota_source_scratch"] = 8192
|
||||
if (companion and "WIFI_SSID" in defines
|
||||
and "WEBCONFIG_DISABLED" not in defines):
|
||||
if ("WEBCONFIG_DISABLED" not in defines
|
||||
and ((companion and "WIFI_SSID" in defines)
|
||||
or (not companion and "ADMIN_PASSWORD" in defines))):
|
||||
parts["browser_terminal_session"] = 2048
|
||||
if "BOARD_HAS_PSRAM" not in defines:
|
||||
# Larger replies grow on demand only while preserving 32 KiB
|
||||
|
||||
@@ -41,7 +41,8 @@ EXPECTED_FAILURES = {
|
||||
|
||||
# Commands that change the node out from under the audit.
|
||||
SKIP = {"reboot", "clkreboot", "poweroff", "shutdown", "erase", "start ota",
|
||||
"stop webconfig", "ota update", "start webconfig", "start webconfig ap"}
|
||||
"stop webconfig", "ota update", "start webconfig", "start webconfig ap",
|
||||
"log", "get acl"} # Listings use /api/terminal, not this bounded API audit.
|
||||
|
||||
|
||||
def table():
|
||||
@@ -80,8 +81,6 @@ NOT_OFFERED = {
|
||||
"tls.bundletest", # TLS debugging, not an operator command
|
||||
"start ota", # binds port 80, which the portal is already using
|
||||
"clock sync", # takes its time from the caller; a web request has none
|
||||
"log", # streams to Serial and stalls the radio ("log start" is offered)
|
||||
"get acl", # streams to Serial, returns nothing
|
||||
}
|
||||
|
||||
|
||||
@@ -121,10 +120,9 @@ ROUND_TRIPS = [
|
||||
("set path.hash.mode 2", "get path.hash.mode", "2"),
|
||||
("set mqtt.iata den", "get mqtt.iata", "DEN"),
|
||||
("set radio.rxps 70000 60000", "get radio.rxps", "on,70000,60000"),
|
||||
# Secret reads are masked back down for an HTTP caller, in CommonCLI's own
|
||||
# words for a non-serial one (wcIsSecretReadCommand).
|
||||
("set guest.password hunter2", "get guest.password", "******** (serial only)"),
|
||||
("set wifi.pwd hunter2", "get wifi.pwd", "******** (serial only)"),
|
||||
# Explicit authenticated LAN getters have local-connection privileges.
|
||||
("set guest.password hunter2", "get guest.password", "hunter2"),
|
||||
("set wifi.pwd hunter2", "get wifi.pwd", "hunter2"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -358,9 +358,9 @@ def apply_set(cfg, key, val):
|
||||
return True, "OK"
|
||||
|
||||
if key == "prv.key":
|
||||
# write-only by design: the identity goes in, nothing reads it back
|
||||
if not _hex64(val):
|
||||
return False, "Error: private key must be 64 hex characters"
|
||||
if len(val) != 128 or not all(c in "0123456789abcdefABCDEF" for c in val):
|
||||
return False, "Error: private key must be 128 hex characters"
|
||||
cfg["identity_private_key"] = val
|
||||
return True, "OK - identity restored, reboot to apply"
|
||||
|
||||
if key == "mqtt.owner":
|
||||
@@ -518,9 +518,8 @@ CLI_UNAVAILABLE = [
|
||||
"Run it from the serial console, or use `ota update`."),
|
||||
("clock sync", True, "clock sync takes its time from the caller, which a web request "
|
||||
"has no way to supply. Use `time <epoch-seconds>` instead."),
|
||||
("log", False, "log writes the packet log to the serial console, not here, and "
|
||||
"blocks the radio while it does. Use `log start` / `log stop`."),
|
||||
("get acl", False, "get acl writes to the serial console, not here."),
|
||||
("log", False, "Use /api/terminal for streamed listings."),
|
||||
("get acl", False, "Use /api/terminal for streamed listings."),
|
||||
]
|
||||
|
||||
|
||||
@@ -534,17 +533,6 @@ def cli_unavailable(cmd):
|
||||
# Failure replies CommonCLI emits that do NOT start with "Err" -- the shapes that
|
||||
# made a naive prefix test call them success. Mirrors
|
||||
# WebConfigBatch::cliReplyIsFailure.
|
||||
def cli_reads_secret(cmd):
|
||||
"""Commands that READ a secret. CommonCLI gates these on the caller being
|
||||
the serial console; the portal is not, so the value is masked here the way
|
||||
CommonCLI masks it for remote callers. Mirrors wcCliReadsSecret()."""
|
||||
if not cmd.startswith("get "):
|
||||
return False
|
||||
key = cmd[4:].strip()
|
||||
return key in ("prv.key", "guest.password", "alert.psk", "bridge.secret") \
|
||||
or is_secret_key(key)
|
||||
|
||||
|
||||
def cli_reply_is_failure(reply):
|
||||
if not reply:
|
||||
return False
|
||||
@@ -560,6 +548,7 @@ GETTERS = {
|
||||
"sf": lambda c: str(c["radio"]["sf"]),
|
||||
"cr": lambda c: str(c["radio"]["cr"]),
|
||||
"public.key": lambda c: "a1b2c3d4" * 8,
|
||||
"prv.key": lambda c: c.get("identity_private_key", "a5" * 64),
|
||||
"wifi.status": lambda c: (
|
||||
"SSID: %s\nIP: 192.168.1.42\nRSSI: -58 dBm\nUptime: %dm"
|
||||
% (c["wifi"]["ssid"] or "(not set)", int(time.time() - ST.start) // 60)),
|
||||
@@ -613,10 +602,11 @@ def _cli_get_value(cfg, key):
|
||||
if key in GETTERS:
|
||||
val = GETTERS[key](cfg)
|
||||
return (True, val) if val is not None else (False, "Error: unsupported")
|
||||
if key == "password":
|
||||
return True, ADMIN_PASSWORD
|
||||
if is_secret_key(key):
|
||||
# The serial console prints these; the portal is reachable over the LAN,
|
||||
# so it masks them the same way /api/config does.
|
||||
return True, SENTINEL if cli_read_key(cfg, key) else "(not set)"
|
||||
# Explicit LAN CLI getters return values; /api/config remains masked.
|
||||
return True, cli_read_key(cfg, key) or "(not set)"
|
||||
val = cli_read_key(cfg, key)
|
||||
if val is None:
|
||||
return False, "??: %s" % key # CommonCLI::handleGetCmd fallthrough
|
||||
@@ -1035,10 +1025,6 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if cmd.startswith("password "):
|
||||
reply = "OK" # never echo the new password back
|
||||
ST.admin_pwd_set = True
|
||||
elif cli_reads_secret(cmd):
|
||||
val = reply[2:] if reply.startswith("> ") else reply
|
||||
reply = ("> (not set)" if val in ("", "(not set)")
|
||||
else "> ******** (serial only)")
|
||||
ok = not cli_reply_is_failure(reply)
|
||||
# Only writes gate the reboot, and only on the "OK" convention every
|
||||
# setter keeps (WebConfigBatch::cliReplyGatesReboot).
|
||||
|
||||
Reference in new issue
Block a user