Management REST API binds all interfaces with no authentication; app load path runs unverified bytes
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
Direzione di ricerca
Read the REST router and bind setup in src/rest_api_lib/src/lib.rs, then trace POST /app and DELETE /app/{id} through src/controller/jrtc_int.c and the Python loader paths. Run the supplied curl check against a controller instance and verify the completed behavior includes loopback-safe defaults, deliberate authenticated remote access, protected app loading, and rejected out-of-range IDs.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
The management REST API defaults to 0.0.0.0:3001 with no authentication on any route, and POST /app feeds caller-supplied bytes straight into a dlopen inside the controller process. For the documented single-host usage (jrtc-ctl against localhost:3001) nothing is exposed; but the defaults an integrator inherits before wiring a management framework are bind-all-interfaces with anonymous app loading. Two asks: safest-by-default binding plus an optional auth token — and one small memory-safety fix found next to it.
1. Unauthenticated management API on all interfaces, feeding an unsigned native-code load path
In src/rest_api_lib/src/lib.rs the router has no authentication or authorization middleware on any route, and the server binds every interface:
let app = Router::new()
.route("/app", get(get_apps).post(load_app))
.route("/app/:id", get(get_app).delete(unload_app))
.with_state(state);
let addr = SocketAddr::from(([0, 0, 0, 0], port));
load_app takes the base64 app bytes from POST /app and passes them to the C load_app callback (src/controller/jrtc_int.c L300-335), which writes them to a memfd and calls dlopen("/proc/self/fd/N", RTLD_LAZY) (_jrtc_load_app_from_memory, src/controller/jrtc_int.c L149-176). There is no signature, hash, or allowlist check anywhere on this path.
Consequence: any host that can reach port 3001 can load arbitrary native code into the controller process — the process holding the jbpf shared-memory telemetry and control channels to the instrumented network functions. On a testbed where the controller and the RAN workloads share a bridge network, that is every peer on the bridge. The app_type: "python" route reaches a similar outcome: REST-supplied paths are appended to sys.path and REST-supplied modules are imported (src/pythonapp_loader/jrtc_pythonapp_loader.c L161-169, L357-367).
The README positions the management plane as integrator territory — jrtc-ctl "can be optionally integrated with other management frameworks (e.g. nRT-RIC or SMO in O-RAN terminology)". That is exactly why the built-in defaults should be safe without such a framework: a deployment that has not integrated one inherits today's bind-everywhere, no-auth defaults, and startup prints listening on 0.0.0.0:3001 with no indication that the port loads native code.
Minimal check (observed from source; runs in seconds): start the controller on host A, then from host B on the same network — no credential required, the shared library is loaded into the controller process:
curl -s -X POST http://A:3001/app -H 'content-type: application/json' \
-d '{"app":"<base64 of a .so exporting the jrtc app entry points>","app_name":"probe","runtime_us":1000,"deadline_us":0,"period_us":1000,"ioq_size":16,"app_path":"/tmp/probe","app_type":"binary","app_params":{},"device_mapping":{},"app_modules":[]}'
Expected: loopback by default, and remote management possible only after the integrator turns it on deliberately.
Suggested fixes:
- Default the REST bind to
127.0.0.1, with a configurable bind address for integrators who want remote management. - Add optional shared-token authentication (e.g. a bearer token from an environment variable or the config file) enforced as axum middleware, so a remote bind never means anonymous app loading.
- Optionally verify app payloads against an integrator-configured hash allowlist before
dlopen, so only artifacts the integrator has blessed can be loaded.
2. unload_app indexes app_envs[app_id] with no bounds check
DELETE /app/{id} passes the raw i32 path segment to the C unload_app (src/controller/jrtc_int.c L396-400), which indexes the fixed-size global array struct jrtc_app_env* app_envs[MAX_NUM_JRTC_APPS] (declared at L95) with no range check — a negative or large id reads out of bounds. A one-line guard closes it:
if (app_id < 0 || app_id >= MAX_NUM_JRTC_APPS) return -1;
and the REST layer could map out-of-range ids to a 400 response.
Environment
jrt-controller @ f18345e0fb23eb5bebdfead10b1335678c9dcaf2 (main, 2026-09-15). Findings from source review; the curl one-liner above is the minimal runtime check.
Happy to send PRs for any of these if the approach sounds right.
- Lingua principale
- C
- Stelle
- 18
- Fork
- 13
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/jrt-controller
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 55/100
microsoft/jrt-controller#86 ·
-
Flakey: jrtc_controller_testAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
microsoft/jrt-controller#83 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
microsoft/jrt-controller#82 ·
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 25/100
microsoft/jrt-controller#70 ·
-
Implement jrtc_router_stop()Forse di nuovo libera @xfoukas l’ha presa 484 giorni fa e non c’è nessuna pull request aperta. Aperta
microsoft/jrt-controller#62 · 1 assegnatario ·
Tutte le issue di microsoft/jrt-controller
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
BasedHardware/omi#20271 ·
I maintainer di solito rispondono entro 1 giorno
-
area/ysql kind/bug priority/medium
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
yugabyte/yugabyte-db#34552 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
flux-framework/flux-coral2#509 ·
-
documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
github/copilot-sdk#2804 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
MixinNetwork/flutter-plugins#507 ·