Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Management REST API binds all interfaces with no authentication; app load path runs unverified bytes

Aperta
#96 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
c, rust
Ambito
api, backend, security

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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di microsoft/jrt-controller

Tutte le issue di microsoft/jrt-controller

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.