Push Tracker
ria-ran--sim-drop/README.md
A ashkan@beigi.net 4a911f02bc Split dev deps out of requirements.txt
requirements.txt is now runtime-only (Flask/cryptography/pymongo/pyyaml) so the
Pi/Termux installers no longer pull pytest into production. Test tools move to
requirements-dev.txt; README testing step installs both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 02:54:24 +00:00

206 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ria-ran--sim-drop
**Closed-loop eSIM provisioning for emergency / NTN 5G deployments.**
A self-contained appliance (Raspberry Pi or Android) that connects to a 5G gNB
for backhaul, broadcasts an open WiFi AP, and hands out eSIM credentials to
connecting devices through a captive portal. Users go from "no signal" to
"connected to the ad-hoc 5G network" in under a minute, with no operator in the
loop.
> Deep design rationale lives in [`plan/design.md`](plan/design.md). This README
> is the operational guide. Notable changes are tracked in
> [`CHANGELOG.md`](CHANGELOG.md).
## Architecture
```
┌─────────────────────────────────────────┐
│ Server Host (srsRAN gNB) │
│ - srsRAN gNB │
│ - Open5GS Core (or free5gc) │
│ - Subscriber database │
└─────────────┬───────────────────────────┘
│ 5G NR
┌─────────────────────────────────────────┐
│ UE WiFi AP Drop │
│ (Android device or Raspberry Pi) │
│ - WiFi AP (open network) │
│ - eSIM Provisioning Server (Flask) │
│ - Captive Portal (nginx + dnsmasq) │
└─────────────┬───────────────────────────┘
│ WiFi
End User Devices
```
## User flow
1. User connects to the open WiFi network `Emergency-5G`.
2. The captive portal auto-redirects to the activation page.
3. User taps **Activate eSIM**.
4. The server generates a unique IMSI / Ki / OPc / ICCID.
5. An eSIM profile is generated and offered as a one-time download.
6. User installs the profile via device Settings.
7. The device connects to the 5G network automatically.
## Quick start (local dev)
```bash
git clone https://riahub.ai/qoherent/ria-ran--sim-drop.git
cd ria-ran--sim-drop
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp config.yaml.example config.yaml # edit MCC/MNC/op_key as needed
python scripts/init_db.py
python -m server.app # → http://localhost:5000
```
Open <http://localhost:5000/activate> and click **Activate eSIM**.
## Configuration
All configuration is in `config.yaml` (start from `config.yaml.example`).
| Field | Description | Example |
|-------|-------------|---------|
| `network.mcc` | Mobile Country Code (3 digits) | `"302"` |
| `network.mnc` | Mobile Network Code (23 digits) | `"720"` |
| `network.op_key` | Operator key, 16-byte hex (32 chars) | `"63bfa50e…"` |
| `wifi.ip` | WiFi AP IP address | `"192.168.4.1"` |
| `server.host` / `server.port` | Flask bind address | `"0.0.0.0"` / `5000` |
| `server.admin_token` | Token guarding `/api/export`; empty = export disabled | `""` |
| `database.path` | SQLite database path | `"/opt/…/subscribers.db"` |
| `rate_limiting.window_hours` | One profile per MAC per window | `1` |
| `profile.apn` | APN / DNN used in the profile and core session | `"internet"` |
| `profile.smdp_address` | SM-DP+ address embedded in the LPA activation code | `"esim.local"` |
| `core.type` | Core adapter: `open5gs` / `free5gc` / `none` | `"open5gs"` |
| `core.mongodb_uri` | MongoDB connection for the core | `"mongodb://localhost:27017"` |
`op_key` is validated at startup (must be 16-byte hex) and the config file is
rejected if it is empty or missing a required section.
## API reference
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/activate` | GET | Captive portal page |
| `/api/provision` | POST | Generate credentials + profile; returns a one-time `profile_url` |
| `/api/profile/<token>` | GET | Download the profile via its unguessable capability token |
| `/api/export` | GET | Export all subscribers (JSON). Requires `X-Admin-Token`; disabled unless `server.admin_token` is set |
| `/api/status` | GET | Health check (subscriber count, uptime, core adapter) |
`POST /api/provision` accepts `{"mac_address": "aa:bb:cc:dd:ee:ff"}`; if omitted,
the server resolves the client MAC from the ARP table. Rate limiting is per MAC.
## Profile format
Two formats are selectable via `profile.format`:
- **`v2`** (default, always available) — a JSON `profile-package-v2` bundle with
the raw credentials plus **real 3GPP field encodings** (`EF_ICCID`, `EF_IMSI`,
nibble-swapped BCD per 3GPP TS 31.102) and an SGP.22 **LPA activation code**
(`LPA:1$<smdp>$<matching-id>`).
- **`saip`** — a real DER-encoded **SGP.22 SAIP `ProfileElement` sequence**:
ProfileHeader (ICCID), PE-MF personalizing EF.ICCID, PE-USIM personalizing
EF.IMSI (both against the standard "created by default" templates), and Milenage
AKA parameters (Ki/OPc). Built with `asn1tools` against
the official `PE_Definitions` ASN.1 schema. Enable by installing
`requirements-saip.txt` (see that file for the schema source) and setting
`profile.format: saip`. If prerequisites are missing the server logs a warning
and falls back to `v2`. `saip_profile.decode_saip_profile()` parses a package
back into its ProfileElements for inspection.
The SAIP package carries ICCID, IMSI, and the AKA credentials; a fully
installable profile additionally needs the remaining mandatory PEs (MF, PIN/PUK,
full EF set) and card-level validation. Consumer over-the-air install also
requires an SM-DP+ / LPA; these packages target programmable SIMs and
core-network sync. See [`CHANGELOG.md`](CHANGELOG.md).
## Deployment
### Raspberry Pi
Requirements: Pi 4 (4GB+), a compatible 5G modem (e.g. Quectel RM500Q), Raspberry
Pi OS Lite 64-bit, Python 3.11+, and a pre-provisioned SIM for gNB backhaul.
```bash
sudo bash scripts/setup_wifi_ap.sh
```
This installs hostapd/dnsmasq/nginx, configures the AP and captive-portal
redirect, deploys the server under `/opt/esim-provisioning`, creates the `esim`
service user, restricts `config.yaml` to `600`, initialises the DB, and enables
the systemd services. Then edit `/etc/esim-provisioning/config.yaml` and
`systemctl restart esim-provisioning`.
### Android / Termux
```bash
bash android/termux_setup.sh
~/esim-provisioning/start.sh
```
See [`android/README.md`](android/README.md) for details and limitations.
## Core network sync
With `core.type: none`, credentials are stored locally only. To push them to a
core network:
```bash
python scripts/sync_subscribers.py # sync unsynced subscribers to Open5GS/free5gc
python scripts/export_subscribers.py # dump all subscribers as JSON (reads the DB directly)
```
To generate or inspect a profile outside the server (dev/ops):
```bash
python scripts/make_profile.py --format saip --out profile.der # generate
python scripts/make_profile.py --inspect profile.der # decode + summarize
```
Adding a new core is a single class in `server/core_adapters/` implementing the
`CoreAdapter` interface.
## Security notes
- Profiles are served at an unguessable one-time token URL, never an enumerable
id — the profile body contains Ki/OPc.
- `/api/export` returns all key material and is **disabled by default**; set
`server.admin_token` and send it as `X-Admin-Token` to enable.
- The deployed `config.yaml` (holds `op_key`) is `chmod 600`, owned by the
service user; the systemd unit runs with `NoNewPrivileges`, `PrivateTmp`, and
`ProtectSystem=strict`.
- The WiFi network is intentionally open (emergency access); there is no
transport encryption on the local link.
## Testing
```bash
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest tests/ -q
```
The suite covers credential generation and 3GPP encodings, the database and rate
limiting, the core adapters, config/utility validation, and the Flask API
(provisioning, token download, rate-limit and export auth).
## Project layout
```
server/ Flask app, profile generator, database, core adapters
portal/ Captive-portal frontend (HTML/CSS/JS)
scripts/ init_db, sync/export subscribers, Pi AP setup
configs/ nginx / dnsmasq / hostapd reference configs
systemd/ Service units
android/ Termux deployment
tests/ Test suite
plan/ Design doc and per-unit implementation notes
```
## License
MIT (or as appropriate for the deployment context).