device-provisioning.md 4.2 KB

Device Provisioning — Kiến trúc bảo mật

Luồng provisioning

┌─ Device Pi (DSLR agent) ──────────────────────┐
│                                               │
│  [1] BOOT                                     │
│      - Generate UUID (machine-id hoặc random)   │
│      - Generate claimCode = random 6-char      │
│      - POST /v1/devices/claim                  │
│        { uuid, name, serialNo, claimCode }    │
│      - Display claimCode + UUID on terminal     │
│      - Poll /v1/devices/claim/:code/status    │
│                                               │
│  [2] APPROVED?                                │
│      - /claim/:code/status → { status, apiKey }│
│      - Save apiKey to /boot/timelapse/agent.key│
│      - chmod 600 (owner read/write only)       │
│                                               │
│  [3] HEARTBEAT LOOP                          │
│      - POST /v1/devices/:id/heartbeat         │
│        X-API-Key: <key from agent.key>       │
│      - gphoto2 capture loop                    │
│      - Poll /v1/devices/:id/commands          │
└───────────────────────────────────────────────┘
         HTTPS (TLS)                    HTTPS (TLS)
              ↓                              ↑
┌─ Server (NestJS) ────────────────────────────────────────────┐
│                                                              │
│  POST /v1/devices/claim         → claimCode + device info    │
│  GET  /v1/devices/claim/:code  → polling status            │
│  GET  /v1/devices/pending      → dashboard list            │
│  POST /v1/devices/pending/:id/approve                       │
│  POST /v1/devices/pending/:id/reject                         │
│  POST /v1/devices/:id/heartbeat    ← X-API-Key auth        │
│  GET  /v1/devices/:id/commands  → pending commands queue   │
└────────────────────────────────────────────────────────────┘

Bảng device_claims (schema)

CREATE TABLE device_claims (
  id          TEXT PRIMARY KEY,        -- nanoid
  claim_code  TEXT UNIQUE NOT NULL,     -- 6-char, uppercase
  device_uuid TEXT UNIQUE NOT NULL,     -- device unique id
  device_name TEXT NOT NULL,
  serial_no   TEXT,
  status      TEXT DEFAULT 'pending',   -- pending | approved | rejected | expired
  api_key_hash TEXT,                   -- bcrypt hash of API key
  expires_at  TIMESTAMP NOT NULL,      -- 24h from creation
  approved_by TEXT,
  approved_at TIMESTAMP,
  created_at  TIMESTAMP DEFAULT NOW()
);

Security Checklist

  • Claim code: 6-char alphanumeric, uppercase, expires 24h
  • API key: 32-char random, stored bcrypt-hashed on server
  • API key: stored in /boot/timelapse/agent.key (chmod 600) on device
  • TLS: server phải có valid HTTPS (production)
  • Rate limit: /claim endpoint — max 10 requests/IP/giờ
  • Device không lưu password — chỉ API key
  • Audit log: mọi provisioning event được ghi

API Endpoints

POST /v1/devices/claim

Device gửi thông tin để đăng ký.

Request:

{
  "deviceUuid": "pi-abc123",
  "deviceName": "Pi-Camera-01",
  "serialNo": "RPI-0001"
}

Response (201):

{
  "claimCode": "XK7M2P",
  "status": "pending",
  "expiresAt": "2025-01-01T12:00:00Z"
}

GET /v1/devices/claim/:code/status

Device polling để kiểm tra approval.

Response:

{
  "status": "pending" | "approved" | "rejected" | "expired",
  "apiKey": "..."        // chỉ khi approved
  "deviceId": "DEMO-001" // chỉ khi approved
}

GET /v1/devices/pending

Dashboard list pending devices (admin only).

POST /v1/devices/pending/:id/approve

Admin approve → sinh API key → lưu hash → trả cho device qua polling.

POST /v1/devices/pending/:id/reject

Admin reject → xóa claim record.