Core Feature: Firewall Blocking via Ubiquiti
Relevant source files
The following files were used as context for generating this wiki page:The Ubiquiti integration is a core component of the Overlord Network Kill Switch project, providing robust network control by interacting directly with a Ubiquiti Network Controller. This feature allows for enabling or disabling specific firewall rules and blocking or unblocking client devices based on their MAC addresses. These actions are exposed via a FastAPI web server, which in turn is typically controlled by MQTT messages orchestrated through Node-RED, allowing for easy integration with home automation systems like HomeKit.
This system is designed to simplify network management for non-technical users, offering capabilities like parental controls or creating "focus time" by restricting internet access for specific devices or services. The integration relies on the Ubiquiti Network API, using an API key for authentication.
Sources: README.md, AGENTS.md, lib/ubiquity/ubiquity.py
Architecture and Initialization
The Ubiquiti functionality is encapsulated within the UbiquitiOverlord class. The main application, defined in controller.py, initializes this module during its startup lifespan event if ubiquiti_control_enabled is set to true in the configuration.
The init_config function reads the config.ini file, populates an app_config dictionary with Ubiquiti-specific settings, and then the lifespan manager calls ubiquity.init(app_config). This creates a global instance of the UbiquitiOverlord class. The FastAPI application then includes the API routes defined in ubiquity.py, making the control endpoints available.
Sources: cgi-bin/controller.py:38-40, cgi--bin/controller.py:78-98, cgi-bin/controller.py:183-185, lib/ubiquity/ubiquity.py:539-542
This diagram illustrates the initialization process.
Sources: cgi-bin/controller.py:78-98, 183-185
Configuration
Configuration for the Ubiquiti integration is managed through etc/config.ini and can be overridden by environment variables.
| Section | Key | Environment Variable | Description |
|---|---|---|---|
[general] | ubiquiti_control_enabled | - | Boolean (true/false) to enable or disable the entire Ubiquiti module. |
[ubiquiti] | remote_ubiquiti_device | REMOTE_UBIQUITI_DEVICE | The IP address or hostname of the UniFi controller. |
[ubiquiti] | remote_ubiquiti_api_key | REMOTE_UBIQUITI_API_KEY | The API key for authenticating with the UniFi controller. |
[ubiquiti_targets] | (dynamic) | - | Defines groups of MAC addresses. The key is the group name (e.g., kids_devices), and the value is a newline-separated list of MAC addresses. |
[ubiquiti_rules] | (dynamic) | - | Defines firewall rules to be managed. The key is an arbitrary name for the rule group, and the value is a newline-separated list of firewall rule names as they appear in the UniFi controller. |
Sources: cgi-bin/controller.py:78-98, lib/ubiquity/ubiquity.py:68-70, 92-93, AGENTS.md
Core Class: UbiquitiOverlord
This class, located in lib/ubiquity/ubiquity.py, handles all interactions with the Ubiquiti Network Controller API.
This diagram shows the key properties and methods of the UbiquitiOverlord class.
Sources: lib/ubiquity/ubiquity.py:59-96
Connection Management
Connection to the Ubiquiti controller is managed using a requests.Session object. Authentication is performed using an API key, which is a more modern approach than the deprecated username/password method.
first_connect(): This method is called to establish the initial connection. It creates arequests.Sessionand sets theX-API-KEYheader with the provided API key. It marks the session aslogged_in.check_logged_in(): Before any API call, this method verifies that an API key is configured and that thelogged_inflag is true. If not, it triggersfirst_connect().shutdown(): Closes therequests.Sessionto gracefully terminate the connection.
Sources: lib/ubiquity/ubiquity.py:202-211, 303-311, 195-199
Firewall Rule Management
The system can enable or disable existing firewall rules. It maintains a cache of the rules to avoid excessive API calls.
parse_firewall_rules(): Fetches all firewall policies from the controller via the/proxy/network/v2/api/site/default/firewall-policiesendpoint. It then updates its internalfirewall_rulesdictionary with the full rule object from the API, keyed by the rule name. This is crucial for obtaining the_idneeded for state changes.check_rules_freshness(): Compares the current time withlast_rules_check. If the time elapsed is greater thanrule_cache_ttl(default 60 seconds), it callsparse_firewall_rules()to refresh the cache.change_rule(requested_status, rule): Modifies a firewall rule's state. It sends aPUTrequest to the/proxy/network/v2/api/site/default/firewall-policies/batchendpoint with a payload containing the rule's_idand the newenabledstate (trueorfalse). After the change, it refreshes the rule cache.
Sources: lib/ubiquity/ubiquity.py:157-188, 332-345, 401-443
The sequence diagram below shows the process of disabling a firewall rule.
Device Blocking
The system can block and unblock client devices by their MAC address.
change_device(requested_status, target): Manages the block status for a group of MAC addresses defined in the configuration under[ubiquiti_targets]. It iterates through the MACs in the specifiedtargetgroup and sends aPOSTrequest to the/proxy/network/api/s/default/cmd/stamgrendpoint.- To block, it uses
{"cmd": "block-sta", "mac": "..."}. - To unblock, it uses
{"cmd": "unblock-sta", "mac": "..."}.
- To block, it uses
status_device(target): Returns the last known state (onlineoroffline) for a device group. Note that this state is maintained within theUbiquitiOverlordinstance and may not reflect the actual state on the controller if changes were made externally.
Sources: lib/ubiquity/ubiquity.py:445-493, 381-399
API Endpoints
The Ubiquiti functionality is exposed through a set of RESTful API endpoints, all prefixed with /ubiquiti.
| Method | Endpoint | Description | Handler Function |
|---|---|---|---|
GET | /status_rule/{target} | Gets the current status (true/false) of a named firewall rule. | get_status_rule |
GET | /enable_rule/{target} | Enables a named firewall rule. | set_enable_rule |
GET | /disable_rule/{target} | Disables a named firewall rule. | set_disable_rule |
GET | /status_device/{target} | Gets the last known status (online/offline) of a device group. | get_status_target |
GET | /enable_device/{target} | Unblocks all devices in a target group. | set_enable_target |
GET | /disable_device/{target} | Blocks all devices in a target group. | set_disable_target |
GET | /refresh | Forces a refresh of the internal firewall rule cache. | refresh_rules |
Sources: lib/ubiquity/ubiquity.py:545-579, AGENTS.md
Integration with Node-RED
Node-RED acts as the bridge between MQTT messages (from HomeKit) and the FastAPI server. The provided dns.json flow listens for MQTT messages on specific topics and translates them into HTTP requests to the /ubiquiti endpoints.
For example, a message on cmnd/router_controller/change_rule/{rule_name} with a payload of true or false triggers a function node that constructs the appropriate URL (/ubiquiti/enable_rule/{rule_name} or /ubiquiti/disable_rule/{rule_name}) and sends the request. The response is then published back to a status topic.
This diagram shows the flow for a request to change a firewall rule.
Sources: node_red_flows/dns.json
Conclusion
The Firewall Blocking via Ubiquiti feature is a powerful and flexible component of the Overlord Network Kill Switch. By exposing control over Ubiquiti firewall rules and device access through a simple API, it allows for sophisticated network automation. The UbiquitiOverlord class provides a clean abstraction over the Ubiquiti API, handling authentication, state management, and caching, while the integration with Node-RED and MQTT makes it easily accessible to home automation platforms.
Sources: lib/ubiquity/ubiquity.py, README.md