Files
gatehouse-api/docs/zerotier-kill-switch.md
T

6.2 KiB

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):

{
  "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:

{
  "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):

{
  "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:

{
  "reason": "Network compromised — emergency deactivation"
}
Field Type Required Default Description
reason string no null Max 500 chars

Response (200):

{
  "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