287 lines
15 KiB
Markdown
287 lines
15 KiB
Markdown
# Architecture Overview
|
|
|
|
## System Components
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Network Clients │
|
|
│ (IoT Devices, Laptops, Phones with WPA-Enterprise credentials) │
|
|
└──────────────────────┬──────────────────────────────────────────┘
|
|
│ RADIUS Access-Request
|
|
│ (MAC, Username, EAP)
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ FreeRADIUS Server │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ EAP Module (Password/Certificate Verification) │ │
|
|
│ └──────────────────────────┬─────────────────────────────────┘ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ device_manager_radius.py (rlm_python) │ │
|
|
│ │ │ │
|
|
│ │ • Parse RADIUS attributes (MAC, Username, NAS, SSID) │ │
|
|
│ │ • Call Frappe API for authorization decision │ │
|
|
│ │ • Cache credentials in SQLite for offline operation │ │
|
|
│ │ • Return VLAN assignment and reply attributes │ │
|
|
│ └──────────────────────────┬─────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ SQLite Credential Cache │ │
|
|
│ │ /var/lib/freeradius/device_manager_verifier_cache.sqlite3 │ │
|
|
│ │ │ │
|
|
│ │ • Stores SSHA password hashes (no plaintext) │ │
|
|
│ │ • Device-specific VLAN assignments │ │
|
|
│ │ • Expiration timestamps │ │
|
|
│ │ • Used when Frappe unreachable │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
└──────────────────────────┬──────────────────────────────────────┘
|
|
│ HTTP POST with API token
|
|
│ /api/method/device_manager.api.radius_authorize
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Frappe Server │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ device_manager.api.radius_authorize() │ │
|
|
│ │ │ │
|
|
│ │ • Authenticate API token │ │
|
|
│ │ • Find device by MAC address │ │
|
|
│ │ • Evaluate access policy │ │
|
|
│ │ • Create audit records │ │
|
|
│ │ • Return decision with VLAN and credentials │ │
|
|
│ └──────────────────────────┬─────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ MariaDB/PostgreSQL Database │ │
|
|
│ │ │ │
|
|
│ │ • DM Device (registered devices) │ │
|
|
│ │ • DM Access Policy (authorization rules) │ │
|
|
│ │ • DM Network Segment (VLAN mappings) │ │
|
|
│ │ • DM Radius Auth Event (audit log) │ │
|
|
│ │ • DM Access Decision (decision log) │ │
|
|
│ │ • DM Device Audit Event (compliance log) │ │
|
|
│ │ • Stored Credential Verifier (password hashes) │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### 1. Normal Operation (Frappe Reachable)
|
|
|
|
```
|
|
Client → FreeRADIUS → device_manager_radius.py
|
|
│
|
|
├─→ HTTP API call to Frappe
|
|
│ POST /api/method/device_manager.api.radius_authorize
|
|
│ Authorization: token API_KEY:API_SECRET
|
|
│
|
|
│ Request payload:
|
|
│ - calling_station_id (MAC)
|
|
│ - username
|
|
│ - nas_identifier
|
|
│ - nas_ip_address
|
|
│ - ssid
|
|
│ - raw_request (full RADIUS attributes)
|
|
│
|
|
├─← Response from Frappe
|
|
│ {
|
|
│ "event": "AUTH-001",
|
|
│ "decision": "DEC-001",
|
|
│ "result": "Allow",
|
|
│ "vlan_id": 100,
|
|
│ "radius_reply_attributes": {...},
|
|
│ "cacheable_credentials": {
|
|
│ "username": "device001",
|
|
│ "control_attributes": {
|
|
│ "SSHA-Password": "base64hash"
|
|
│ }
|
|
│ }
|
|
│ }
|
|
│
|
|
├─→ Cache decision in SQLite
|
|
│
|
|
└─→ Return to FreeRADIUS
|
|
- RADIUS reply attributes (VLAN)
|
|
- Control attributes (password hash)
|
|
- Accept/Reject decision
|
|
|
|
Client ← FreeRADIUS ← Access-Accept + VLAN assignment
|
|
```
|
|
|
|
### 2. Offline Operation (Frappe Unreachable)
|
|
|
|
```
|
|
Client → FreeRADIUS → device_manager_radius.py
|
|
│
|
|
├─→ HTTP API call to Frappe (FAILS)
|
|
│ Network error / Timeout
|
|
│
|
|
├─→ Query SQLite cache
|
|
│ SELECT * FROM radius_verifier_cache
|
|
│ WHERE username = ?
|
|
│
|
|
├─← Cached decision found
|
|
│ {
|
|
│ "result": "Allow",
|
|
│ "vlan_id": 100,
|
|
│ "control_attributes": {
|
|
│ "SSHA-Password": "base64hash"
|
|
│ },
|
|
│ "from_cache": true
|
|
│ }
|
|
│
|
|
└─→ Return to FreeRADIUS
|
|
- RADIUS reply from cache
|
|
- Control attributes from cache
|
|
- Accept with cached VLAN
|
|
|
|
Client ← FreeRADIUS ← Access-Accept (from cache)
|
|
```
|
|
|
|
## Deployment Modes Comparison
|
|
|
|
### Mode 1: Standalone Client (NEW)
|
|
|
|
**Use Case:** FreeRADIUS on dedicated appliance, Frappe on app server
|
|
|
|
```
|
|
┌─────────────────┐ API over HTTPS ┌─────────────────┐
|
|
│ RADIUS Server │ ←──────────────────────→ │ Frappe Server │
|
|
│ │ │ │
|
|
│ • FreeRADIUS │ │ • Frappe │
|
|
│ • Python 3.10+ │ │ • device_mgr │
|
|
│ • device_mgr_ │ │ • MariaDB │
|
|
│ radius.py │ │ │
|
|
│ • SQLite cache │ │ │
|
|
└─────────────────┘ └─────────────────┘
|
|
|
|
Dependencies: Python stdlib only
|
|
Module: device_manager_radius
|
|
Config: DEVICE_MANAGER_FRAPPE_URL + API credentials
|
|
```
|
|
|
|
### Mode 2: Local (Integrated)
|
|
|
|
**Use Case:** Everything on one server (lab/testing)
|
|
|
|
```
|
|
┌──────────────────────────────────┐
|
|
│ Single Server │
|
|
│ │
|
|
│ • FreeRADIUS │
|
|
│ • Frappe bench │
|
|
│ • device_manager app │
|
|
│ • MariaDB │
|
|
│ │
|
|
│ In-process import: │
|
|
│ device_manager.freeradius │
|
|
│ ↓ calls ↓ │
|
|
│ device_manager.radius │
|
|
│ ↓ queries ↓ │
|
|
│ Database │
|
|
└──────────────────────────────────┘
|
|
|
|
Dependencies: Full Frappe + device_manager
|
|
Module: device_manager.freeradius
|
|
Config: DEVICE_MANAGER_BENCH_PATH + SITE
|
|
```
|
|
|
|
### Mode 3: Remote (Full App Installed)
|
|
|
|
**Use Case:** RADIUS server with device_manager installed, Frappe remote
|
|
|
|
```
|
|
┌─────────────────┐ API over HTTPS ┌─────────────────┐
|
|
│ RADIUS Server │ ←──────────────────────→ │ Frappe Server │
|
|
│ │ │ │
|
|
│ • FreeRADIUS │ │ • Frappe │
|
|
│ • Python 3.10+ │ │ • device_mgr │
|
|
│ • device_mgr │ │ • MariaDB │
|
|
│ app (full) │ │ │
|
|
│ • SQLite cache │ │ │
|
|
└─────────────────┘ └─────────────────┘
|
|
|
|
Dependencies: device_manager package
|
|
Module: device_manager.freeradius
|
|
Config: DEVICE_MANAGER_FRAPPE_URL + API credentials
|
|
```
|
|
|
|
## Security Architecture
|
|
|
|
### Authentication Flow
|
|
|
|
```
|
|
1. FreeRADIUS validates device credentials (EAP-PEAP/TLS)
|
|
└─→ If valid, call device_manager_radius
|
|
|
|
2. device_manager_radius calls Frappe API
|
|
├─→ Authorization: token API_KEY:API_SECRET
|
|
├─→ HTTPS encrypted transport
|
|
└─→ Token validated by Frappe
|
|
|
|
3. Frappe evaluates device policy
|
|
├─→ Lookup device by MAC address
|
|
├─→ Check approval status
|
|
├─→ Check lifecycle state
|
|
├─→ Evaluate access policy rules
|
|
└─→ Determine VLAN assignment
|
|
|
|
4. Response cached locally (if cacheable)
|
|
├─→ Only SSHA hashes stored (no plaintext)
|
|
├─→ Cache file owned by freerad user
|
|
├─→ Optional expiration date
|
|
└─→ Used only when Frappe unreachable
|
|
```
|
|
|
|
### Secret Management
|
|
|
|
| Secret Type | Storage Location | Access Control |
|
|
|-------------|------------------|----------------|
|
|
| Device passwords | Never stored plaintext | N/A |
|
|
| SSHA verifiers | SQLite cache + Frappe DB | freerad user, DB permissions |
|
|
| API credentials | systemd override | root:root 600 |
|
|
| Frappe session tokens | Frappe DB | System Manager only |
|
|
|
|
## Performance Characteristics
|
|
|
|
| Operation | Typical Latency | Notes |
|
|
|-----------|----------------|-------|
|
|
| Cache hit | < 5ms | SQLite query |
|
|
| API call (LAN) | 10-50ms | Network + DB query |
|
|
| API call (WAN) | 50-500ms | Depends on network |
|
|
| API timeout | 2.5s (default) | Configurable |
|
|
| Cache write | < 10ms | SQLite insert |
|
|
|
|
## Scalability
|
|
|
|
### Single RADIUS Server
|
|
- Handles 1000+ auth/sec with cache hits
|
|
- ~100 auth/sec with API calls (network bound)
|
|
- SQLite cache suitable for <100k devices
|
|
|
|
### High Availability
|
|
- Deploy multiple RADIUS servers (round-robin DNS or load balancer)
|
|
- Each server maintains independent SQLite cache
|
|
- Shared Frappe backend ensures consistent policy
|
|
- Consider Redis cache for distributed deployment (future enhancement)
|
|
|
|
## Monitoring Points
|
|
|
|
### RADIUS Server
|
|
- FreeRADIUS logs (`radiusd.L_INFO`, `radiusd.L_ERR`)
|
|
- Cache hit rate (grep logs for "using cached credentials")
|
|
- API timeout rate (grep logs for "authorization failed")
|
|
- Cache size (SQLite table row count)
|
|
|
|
### Frappe Server
|
|
- API endpoint latency (Frappe request logs)
|
|
- Authentication success/failure rate
|
|
- Policy evaluation time
|
|
- Audit log growth rate
|
|
|
|
### Network
|
|
- HTTPS latency between RADIUS and Frappe
|
|
- Packet loss between clients and RADIUS
|
|
- Certificate expiration monitoring
|