Skip to content

Repository files navigation

IoT Test Services

Docker-based testbed for IoT firmware connectivity. Each service mirrors or echoes incoming traffic and records every exchange to a shared SQLite store.

Built to exercise the TLS / DTLS stacks of NB-IoT and other cellular IoT modems against a controlled, instrumented echo / mirror server.

Overview

A .NET 10 host supervises an OpenVPN client and a set of echo / mirror services. Devices reach the echo and mirror services through the VPN tunnel; only the HTTP management API on port 8664 is published to the host.

  • OpenVPN client: joins an APN tunnel so test devices reach the services without going through the public internet.
  • Echo and mirror services: UDP, TCP, TLS, DTLS, HTTP, HTTPS. Each binds 0.0.0.0 inside the container and is reachable only via the tunnel.
  • HTTP management API: overview, configuration, status, per-service dashboards, CSV export, CA downloads (port 8664).
  • Run analytics: handshakes (TLS / DTLS / HTTPS), per-packet timing, sequence numbers, server-side UDP probes, per-run environment snapshots, exportable as JSON via /runs/{id}/export.json.
  • Legacy cipher support: OpenSSL SECLEVEL=1 and a ClientHello-aware cert selector let constrained modems (e.g. Quectel BC660K-GL, static-RSA only) actually complete a handshake.
Internet → Docker container → OpenVPN client → VPN server
                    ↓
         Echo / mirror services (UDP / TCP / TLS / DTLS / HTTP / HTTPS)
         only reachable via the VPN tunnel
                    ↓
         HTTP management API (8664), exposed externally

Service ports

Service Port Notes
UDP echo 5001 Datagram reflector
TCP echo 5002 Stream reflector
TLS echo 5003 Stream reflector, RSA + ECC dual-stack
DTLS echo 5004 Datagram reflector, RSA + ECC dual-stack
HTTP mirror 80 Plain-text mirror of the request
HTTPS mirror 443 Same, TLS-protected
Management 8664 Dashboards, status, config, CSV / JSON export

Setup

1. Configure VPN credentials

Copy DockerSetup/vpn_config.ovpn.example to DockerSetup/vpn_config.ovpn and fill in the <key>, <cert>, <ca>, and <tls-crypt> blocks supplied by your VPN provider. The real config is gitignored.

Tested against 1NCE managed IoT connectivity (<user>.openvpn.1nce.com); any OpenVPN-compatible provider should work as long as the resulting tunnel reaches the device-side network.

2. Generate TLS material

A CA and per-service certificates must exist in DockerSetup/certs. They are issued by the bundled CertificateProvisioner tool.

# Windows PowerShell (5.1+)
powershell -NoLogo -NoProfile -File .\DockerSetup\scripts\ensure-certs.ps1

# PowerShell 7+
pwsh ./DockerSetup/scripts/ensure-certs.ps1
# macOS / Linux
bash ./DockerSetup/scripts/ensure-certs.sh

Pass --force to re-issue the certificates.

3. Build and run

Docker Compose (recommended):

docker-compose up -d

Or Docker directly:

docker build -t iot-test-services -f DockerSetup/Dockerfile .

docker run -d \
  --name iot-test-services \
  --cap-add=NET_ADMIN \
  --device /dev/net/tun \
  -p 8664:8664 \
  -v $(pwd)/DockerSetup/vpn_config.ovpn:/app/vpn_config.ovpn:ro \
  iot-test-services

4. Verify

curl http://localhost:8664/status          # service status (JSON)
docker logs -f iot-test-services           # container log
docker exec iot-test-services ip addr show tun0

Configuration

All runtime options live in DockerSetup/appsettings.json. The tables below highlight the keys most commonly overridden.

OpenVPN supervisor

Setting Default Notes
ConfigPath /app/vpn_config.ovpn OpenVPN configuration file
LogPath /var/log/openvpn.log OpenVPN log file
InterfaceName tun0 VPN tunnel interface to monitor
ConnectionAttempts 30 Polls of the tunnel before giving up
ConnectionAttemptDelaySeconds 1 Delay between polls
InitialDelaySeconds 1 Delay before the first poll
StopOnConnectionFailure false Stop the host if the VPN never comes up
DumpLogOnFailure true Dump the OpenVPN log on failure or timeout

Echo / mirror services

Each service block uses the same shape: Port, BindAddress, Enabled, plus TLS / DTLS / HTTPS variants additionally take RSA + ECC certificate paths and a PreferEcc toggle. DTLS adds Mtu (default 256). TLS, DTLS, and HTTPS expose EnableLegacyCiphers (default true) for static-RSA modems.

Section Default port Cert paths required
EchoServices:Udp 5001 no
EchoServices:Tcp 5002 no
EchoServices:Tls 5003 yes (RSA + ECC)
EchoServices:Dtls 5004 yes (RSA + ECC)
EchoServices:Http 80 no
EchoServices:Https 443 yes (RSA + ECC)

Storage and host

Setting Default Notes
Storage:DatabasePath /app/data/echo.sqlite SQLite store for runs / packets
Host:BindAddress 0.0.0.0 Management API bind address
Host:Port 8664 Management API port

Certificate authority

Setting Default
Certificates:Authority:Name IoT Test Services Test CA
Certificates:Authority:RsaPublicCertPath /app/certs/authority/ca.crt
Certificates:Authority:EccPublicCertPath /app/certs/authority/ca-ecc.crt

Testing the services

From a device on the same VPN:

VPN_IP=$(docker exec iot-test-services ip -4 addr show tun0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')

# UDP / TCP echo
echo "Hello UDP" | nc -u $VPN_IP 5001
echo "Hello TCP" | nc    $VPN_IP 5002

# TLS / DTLS echo (OpenSSL)
echo "Hello TLS"  | openssl s_client -connect $VPN_IP:5003 -quiet
echo "Hello DTLS" | openssl s_client -dtls1_2 -connect $VPN_IP:5004 -quiet

# HTTP / HTTPS mirror
curl    http://$VPN_IP:80/test
curl -k https://$VPN_IP:443/test

The /config page lists the CA in PEM form. Clients that verify the server should import it from /certificates/authority/rsa or /certificates/authority/ecc.

Cellular modem compatibility

Many cellular IoT modems ship TLS / DTLS stacks limited to legacy cipher suites, often static-RSA only and without forward secrecy. The TLS and DTLS handlers peek the ClientHello and select an RSA or ECC certificate accordingly; EnableLegacyCiphers: true is on by default so constrained devices (e.g. Quectel BC660K-GL) can negotiate at all.

Companion firmware

The Mbed CE projects below talk to this testbed. The shared test library drives the echo and mirror endpoints exposed here; the per-modem EVKIT apps adapt it to a specific cellular driver and target board.

  • mbed-ce-cellular-test — reusable test library: exercises UDP / TCP / TLS / DTLS / HTTP / HTTPS against the endpoints exposed here and reports run metadata via /runs/{id} so dashboards line up across modems.
  • mbed-ce-bc660kgl-app — example app for the Quectel BC660K-GL (NB-IoT, static-RSA only).
  • mbed-ce-bg770agl-app — example app for the Quectel BG770A-GL (LTE Cat-M1 / NB-IoT / NB2).
  • mbed-ce-st87m01-app — example app for the ST87M01 (LTE Cat-M1, ECC-capable).

Security notes

  • Echo and mirror services must only be reached over the VPN tunnel. Do not publish ports 80, 443, or 50015004 directly in production.
  • Certificates for TLS, DTLS, and HTTPS are issued from the local IoT Test Services Test CA. Clients that validate the server need the CA PEM from /config or the /certificates/authority/* endpoints.
  • Re-run ensure-certs --force if certificate material is compromised.
  • The bundled VPN config example contains no credentials; the real vpn_config.ovpn is gitignored.

Troubleshooting

docker exec iot-test-services cat /var/log/openvpn.log   # OpenVPN log
docker exec iot-test-services ip link show tun0          # Tunnel interface
docker exec iot-test-services ip route                   # Routing

Development

Run locally without Docker:

dotnet restore
dotnet run --project IoTTestServices

OpenVPN must be configured separately when running outside Docker.

Layout

iot-test-service/
├── IoTTestServices.sln
├── IoTTestServices/                 # .NET 10 web host (entry point)
├── IoTTestServices.Core/            # storage, packets, runs, dashboards, TLS helpers
├── IoTTestServices.Echo.Udp/        # UDP echo service
├── IoTTestServices.Echo.Tcp/        # TCP echo service
├── IoTTestServices.Echo.Tls/        # TLS echo service (RSA + ECC)
├── IoTTestServices.Echo.Dtls/       # DTLS echo service (RSA + ECC)
├── IoTTestServices.Mirror.Http/     # HTTP request mirror
├── IoTTestServices.Mirror.Https/    # HTTPS request mirror
├── IoTTestServices.Tests/           # xUnit test suite
├── Tools/
│   └── CertificateProvisioner/      # generates CA + per-service certs
└── DockerSetup/
    ├── Dockerfile
    ├── docker-compose.yml
    ├── appsettings.json
    ├── openssl-legacy.cnf
    ├── vpn_config.ovpn.example
    ├── scripts/                     # ensure-certs.{ps1,sh}, dtls-selftest.sh
    └── certs/                       # generated TLS material (gitignored)

License

MIT License. See LICENSE.

About

Lightweight C# / .NET backend service for validating IoT device connectivity end-to-end. Ships as a Docker container and exposes the test endpoints used by the mbed-ce-cellular-test library.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages