Enroll Your First Device¶
This page explains every step of the device enrollment flow in detail.
Tenant-Stack required
Device enrollment runs against the Tenant-Stack (IoT Bridge API + step-ca Sub-CA). Make sure Tenant-Stack Setup is complete before following this guide.
Overview¶
Device enrollment is a one-time process that:
- Generates a private key on the device — the key never leaves the device.
- Creates a Certificate Signing Request (CSR) and sends it to the Tenant IoT Bridge API.
- The API asks the Tenant step-ca Sub-CA to sign the certificate (chain: Device → Sub-CA → Root CA).
- The signed certificate and CA chain are returned to the device.
- The API also generates a WireGuard key pair and peer config for the device.
- ThingsBoard is notified via its Rule Engine webhook when the device first connects.
- The IoT Bridge API creates a corresponding target in hawkBit.
Manual Enrollment (step-cli)¶
You can perform enrollment manually to understand each step.
1. Generate a Private Key and CSR¶
# Install step-cli: https://smallstep.com/docs/step-cli/installation
step crypto key pair device.key device.pub --kty EC --curve P-256 --no-password --insecure
step certificate create device-001 device.csr device.key \
--csr --san device-001 --no-password --insecure
2. Send the CSR to the Tenant IoT Bridge API¶
Replace TENANT_API with the Tenant-Stack IoT Bridge API URL:
TENANT_API=https://tenant.example.com/api
CSR_PEM=$(cat device.csr)
curl -s -X POST "$TENANT_API/devices/device-001/enroll" \
-H "Content-Type: application/json" \
-d "{\"csr_pem\": \"$CSR_PEM\"}" | tee enroll_response.json | python3 -m json.tool
Expected response:
{
"device_id": "device-001",
"certificate_pem": "-----BEGIN CERTIFICATE-----<br>...",
"ca_chain_pem": "-----BEGIN CERTIFICATE-----<br>...",
"wireguard_config": "[Interface]<br>PrivateKey = ...<br>..."
}
3. Save the Outputs¶
python3 -c "import json,sys; d=json.load(open('enroll_response.json')); \
open('device.crt','w').write(d['certificate_pem']); \
open('ca-chain.crt','w').write(d['ca_chain_pem']); \
open('wg0.conf','w').write(d['wireguard_config'])"
4. Verify the Certificate¶
The issuer chain should read: device-001 → Tenant Sub-CA → Provider ICA → Provider Root CA.
5. Connect via MQTT (mTLS)¶
mosquitto_pub \
--host tenant.example.com --port 8883 \
--cafile ca-chain.crt \
--cert device.crt \
--key device.key \
--tls-version tlsv1.2 \
-t "v1/devices/me/telemetry" \
-m '{"test": 1}'
If the connection succeeds, the device appears in ThingsBoard (Tenant-Stack) immediately.
Automated Enrollment (docker compose)¶
The device-stack/bootstrap container automates all of the above via enroll.sh. It is
idempotent — if /certs/enrolled exists, it skips re-enrollment.
Certificate Renewal¶
Certificates issued by the Tenant step-ca have a configurable validity. To renew:
step ca renew --force device.crt device.key \
--ca-url https://tenant.example.com/pki \
--root ca-chain.crt
In production, configure the step-ca renewal daemon or a systemd timer.
Revoking a Certificate¶
If a device is decommissioned or compromised:
step ca revoke --cert device.crt --key device.key \
--ca-url https://tenant.example.com/pki --root ca-chain.crt
Then delete the device in ThingsBoard and hawkBit. For escalated revocation (compromise of the Sub-CA), see Security Incident Response.