170 lines
6.2 KiB
Markdown
170 lines
6.2 KiB
Markdown
# ZeroTier Kill Switch
|
|
|
|
## Overview
|
|
|
|
The kill-switch mechanism provides emergency deactivation of ZeroTier network access at three granularities: a single device membership, all memberships for a user, or all memberships on a network. All kill operations are **reversible** — they set `active=False` and (in most cases) `status=SUSPENDED` but do not delete records, so affected users can re-activate or re-authenticate.
|
|
|
|
## Three Kill Operations
|
|
|
|
| Granularity | Endpoint | Admin-only | Behavior |
|
|
|---|---|---|---|
|
|
| **Device X on network Y** | `POST /orgs/<id>/memberships/<id>/deactivate` | No (owner can self-deactivate) | Sets `active=False`. Status stays APPROVED. |
|
|
| **All devices for a user** | `POST /orgs/<id>/kill-switch` | Yes | Sets `active=False`, `status=SUSPENDED` for every active membership in the org (optionally filtered to specific networks). |
|
|
| **All devices on a network** | `POST /orgs/<id>/networks/<id>/kill-switch` | Yes | Sets `active=False`, `status=SUSPENDED` for every active membership on the network, across all users. |
|
|
|
|
## Detailed Endpoint Reference
|
|
|
|
### 1. Kill a Single Membership (Device + Network)
|
|
|
|
```
|
|
POST /api/v1/organizations/<org_id>/memberships/<membership_id>/deactivate
|
|
```
|
|
|
|
**Auth:** `@login_required`, `@full_access_required`
|
|
**Admin override:** Admins can deactivate any membership; non-admins can only deactivate their own.
|
|
|
|
**Request body:** None
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"request": {
|
|
"id": "...",
|
|
"active": false,
|
|
"status": "approved",
|
|
...
|
|
}
|
|
},
|
|
"message": "Request deactivated successfully"
|
|
}
|
|
```
|
|
|
|
**Behavior:**
|
|
- Ends the active `ActivationSession` with reason `manual_revoke`
|
|
- De-authorizes the device node in the ZeroTier controller
|
|
- Sets `request.active = False` (status **unchanged** — stays `approved`)
|
|
- Logs `zt.membership.deactivated` audit event
|
|
|
|
**Re-activation:** The user can re-activate via `POST /memberships/<id>/activate` or `POST /memberships/activate-all`.
|
|
|
|
---
|
|
|
|
### 2. Kill All Devices for a User
|
|
|
|
```
|
|
POST /api/v1/organizations/<org_id>/kill-switch
|
|
```
|
|
|
|
**Auth:** `@login_required`, `@require_admin`, `@full_access_required`
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"target_user_id": "uuid-of-user-to-kill",
|
|
"scope": "organization",
|
|
"network_ids": ["uuid-of-network-1", "uuid-of-network-2"],
|
|
"reason": "Security incident — force deactivation"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|---|---|---|---|---|
|
|
| `target_user_id` | string (UUID) | yes | — | The user to deactivate |
|
|
| `scope` | string | no | `"organization"` | `"organization"` (all networks) or `"selected_networks"` |
|
|
| `network_ids` | array of UUIDs | no | `null` | Required when scope is `selected_networks` |
|
|
| `reason` | string | no | `null` | Max 500 chars |
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"affected_count": 3
|
|
},
|
|
"message": "Kill switch triggered successfully"
|
|
}
|
|
```
|
|
|
|
**Behavior:**
|
|
- Queries all active, non-deleted `NetworkAccessRequest` rows for the target user
|
|
- For each: ends session (reason `kill_switch`), de-authorizes in ZT
|
|
- Sets `active = False`, **and** sets `status = SUSPENDED` if currently `APPROVED`
|
|
- Logs `zt.kill_switch.activated` audit event with `affected_count` and `scope`
|
|
|
|
**Re-activation:** The user's memberships are in `SUSPENDED` state. An admin must explicitly re-approve (change status back to `APPROVED`) before the user can re-activate.
|
|
|
|
---
|
|
|
|
### 3. Kill All Devices on a Network
|
|
|
|
```
|
|
POST /api/v1/organizations/<org_id>/networks/<network_id>/kill-switch
|
|
```
|
|
|
|
**Auth:** `@login_required`, `@require_admin`, `@full_access_required`
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"reason": "Network compromised — emergency deactivation"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|---|---|---|---|---|
|
|
| `reason` | string | no | `null` | Max 500 chars |
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"affected_count": 12
|
|
},
|
|
"message": "Network kill switch triggered successfully"
|
|
}
|
|
```
|
|
|
|
**Behavior:**
|
|
- Queries all active, non-deleted `NetworkAccessRequest` rows for the network, **regardless of user**
|
|
- For each: ends session (reason `kill_switch`), de-authorizes in ZT
|
|
- Sets `active = False`, and `status = SUSPENDED` if currently `APPROVED`
|
|
- Logs `zt.network_kill_switch.activated` audit event with `affected_count`
|
|
|
|
**Re-activation:** Same as user kill switch — each affected membership is `SUSPENDED` and needs admin re-approval.
|
|
|
|
## Comparison: Deactivation vs. Deletion
|
|
|
|
| Operation | active | status | deleted_at | DB row | Reversible? |
|
|
|---|---|---|---|---|---|
|
|
| `POST /memberships/<id>/deactivate` | `false` | unchanged | `null` | preserved | Yes — re-activate |
|
|
| `POST /kill-switch` (user) | `false` | `suspended` | `null` | preserved | Yes — admin re-approve |
|
|
| `POST /networks/<id>/kill-switch` | `false` | `suspended` | `null` | preserved | Yes — admin re-approve |
|
|
| `DELETE /memberships/<id>` (soft) | `false` | unchanged | set | preserved | Partial — depends on join logic |
|
|
| `DELETE /admin/memberships/<id>` | `false` | — | — | **hard-deleted** | No |
|
|
|
|
## Audit Events
|
|
|
|
| Event | Trigger |
|
|
|---|---|
|
|
| `zt.membership.deactivated` | Single membership deactivated (endpoint #1) |
|
|
| `zt.kill_switch.activated` | User kill switch triggered (endpoint #2) |
|
|
| `zt.network_kill_switch.activated` | Network kill switch triggered (endpoint #3) |
|
|
|
|
All audit entries include `organization_id`, `user_id` (the actor), `resource_type`, `resource_id`, and `metadata` (affected count, scope, network IDs).
|
|
|
|
## Key Source Files
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `gatehouse_app/api/v1/zerotier.py` | Route handlers for all three endpoints |
|
|
| `gatehouse_app/services/network_access_service.py` | `deactivate_request()`, `kill_switch()`, `kill_switch_network()` |
|
|
| `gatehouse_app/services/zerotier_api_service.py` | `deauthorize_member()` — ZT controller call |
|
|
| `gatehouse_app/utils/constants.py` | `AuditAction` and `KillSwitchScope` enums |
|
|
| `gatehouse_app/models/zerotier/network_access_request.py` | `NetworkAccessRequest` model |
|
|
| `gatehouse_app/models/zerotier/activation_session.py` | `ActivationSession` model |
|
|
| `gatehouse_app/models/zerotier/kill_switch_event.py` | `KillSwitchEvent` model |
|
|
| `tests/integration/test_zerotier.py` | Integration tests in `TestZeroTierMembership` |
|