Repository navigation
Security and Roles
Since 0.2.0 the gateway tells an SMO-internal caller from an rApp:
-
internal: an SMO module (or the GUI backend). It proves it by presenting the enrollment secret (
X-SMO-Enrollment, the compose secretsecrets/enrollment_secret) when it registers with SME. It is mounted into SME and every SMO module, and never into an rApp. Internal invokers may hold thesmo-internalandsmo-guiscopes. -
rApp: everything else. It may only ask for
smo-rapp.
SME records the role per invoker and returns it on token introspection; R1 Termination forwards it as X-R1-Role (any caller-supplied value is dropped).
Two lists in smo/shared/smo_shared/roles.py decide it at the gateway, before any module is called:
-
Deny-list (
INTERNAL_ONLY): routes an rApp may not call in any method: per-rApp limits, KPI definitions, config-history purge, the kill switch, safeguard subscriptions and refusals, and the KPI standard-set and publish routes. -
Allow-list for changes (
RAPP_MAY_CHANGE, since 0.3.0): an rApp may make a change (POST, PUT, PATCH, DELETE) only on what the AI Runtime SDK uses, the consumer-facing request routes of the AI/ML services, config jobs and their rollback (where its safeguards apply), and the rApp-facing modules. Everywhere else a change is refused with403 ROLE_NOT_PERMITTED: onboarding, instance management, orchestration, other invokers' registrations, and administration of the services.
Reads are still open to every valid token apart from the deny-list. An SMO module is never refused by either list.
When an operator stops an rApp (see rApp Safeguards) the gateway refuses every change it makes, and every change an SMO module makes on its behalf, with 403 RAPP_KILLED. It can still read, delete what it made, get a token and roll back its own config job.
Every authenticated change through the gateway (including the ones it refuses) is written to a tamper-evident hash chain: who (invoker id and role), what (method and path, never the query or body), the result and the correlation id. python -m smo_shared.audit verify finds an edited, removed or inserted row; export writes the rows as JSON lines or syslog with a closing head line to keep elsewhere. See Operations.
| Variable | Meaning |
|---|---|
SMO_ROLE_ENFORCEMENT |
enforce (default) or audit (count and log, then allow; for rolling upgrades and for moving an rApp that calls a route outside the allow-list) |
R1_KILL_SWITCH |
on (default) or off: whether the gateway refuses changes by a stopped rApp |
R1_AUDIT |
on (default) or off: whether the gateway records changes in the audit chain |
SME_ALLOW_OPEN_ENROLLMENT |
tests and development only |
rApp Management registers an SME invoker for every instance; its id is the instance's oauthClientId. POST /instances/{id}/credentials issues the client id and secret once (Cache-Control: no-store) and rotates them on a repeat while the instance is DEPLOYING. Terminating the instance removes the invoker. On Kubernetes the credentials can be delivered straight into a Secret for the workload (see Helm on Kubernetes).
- No default credentials:
scripts/init_secrets.shcreates the secrets. Rotation runbooks for each secret:smo/docs/SECRETS.md(scripts/rotate_db_password.shfor the database password). - TLS edge profile with HSTS; the GUI session cookie is
Secureby default. - Containers run non-root with capabilities dropped; request body size is capped and R1 rate-limits callers.
- Reporting a vulnerability: see
SECURITY.md(do not open a public issue). Supported:mainand the newest0.MINORline (currently 0.3.x).
AI-RAN SMO Reference Implementation · Copyright 2026 Radisys · Licensed under the Apache License 2.0
This is a reference implementation for learning, integration testing and demos. It is not hardened or audited for production; do not expose it to an untrusted network or a live RAN.