pfSense REST API

repository·master·Indexed 21 days ago

https://github.com/pfrest/pfsense-pkg-restapi

An unofficial, open-source REST and GraphQL interface for pfSense CE and pfSense Plus firewalls. It provides over 200 endpoints for programmatic management of firewall services, featuring HATEOAS-driven discovery, built-in Swagger documentation, and support for Basic, API Key, and JWT authentication. The package includes a modular framework for extending functionality via custom PHP components and a build system using make_package.py for FreeBSD package generation.

Tokens
29.5K
Snippets
69
Records
127
Agent score
68%

What's inside pfrest-pfsense-pkg-restapi

  1. Overview of the pfSense REST API Package

    master

    The pfSense REST API package provides an unofficial, open-source REST and GraphQL API for pfSense CE and pfSense Plus firewalls. It is designed for lightweight and fast management of firewall services.

    Key Capabilities:

    • REST API: Over 200 endpoints for managing firewall and associated services.
    • GraphQL API: Supports flexible data retrieval and mutations.
    • Querying: Built-in support for querying, filtering, and sorting.
    • Security: Configurable security settings and customizable authentication options.
    • Discovery: Supports HATEOAS-driven development and includes built-in Swagger documentation.
  2. Explore pfSense REST API integrations

    master

    The pfSense REST API can be used with several third-party tools to extend its functionality:

    • pfSense Prometheus Exporter: Monitor firewall performance and health in real-time via REST API endpoints. It includes Helm charts and Grafana dashboards.
    • pfSense Ansible Collection: Automate pfSense deployments and configurations using Ansible playbooks. It provides over 400 Ansible modules that utilize the REST API for management tasks.
  3. Understand the Native Schema structure

    master

    The Native Schema is organized into top-level objects for endpoints and models. The structure is derived directly from the codebase's Endpoint, Model, and Field classes.

    Key structural components include:

    • version: The schema version.
    • endpoints: A mapping of URL paths to their respective endpoint definitions.
    • models: A mapping of model names to their constituent fields and their attributes.
    {
      "version": "v0.0.0",
      "endpoints": {
        "/endpoint/url/path": {
          "..."
        }
      },
      "models": {
        "ModelName": {
          "fields": {
            "field_name": {
              "..."
            }
          }
        }
      }
    }
  4. Concurrency and scalability limitations

    master

    When using the pfSense REST API, be aware of the following architectural constraints:

    • Concurrency: pfSense's XML configuration was not designed for large-scale concurrency. To prevent unexpected behavior like configuration overwrites, you may need to introduce delays between API calls.
    • Large Datasets: While the REST API provides better resource management than the standard webConfigurator, it is still recommended to exercise caution when performing operations on very large datasets.
  5. Configure Model data source via config_path or internal_callable

    master

    When initializing a Model class in __construct(), you must define how the Model retrieves its data. You must choose exactly one of the following:

    1. config_path: Defines the XML configuration path in pfSense used to read and write objects. This is required for configuration-based models.
      • If a parent_model_class is defined, config_path is appended to the parent's path.
    2. internal_callable: Defines a public or protected method within the Model class used to fetch data that is not stored in the XML configuration (e.g., real-time metrics).
      • For many = true models, the method must return an array of objects.
      • For many = false models, the method must return a single object.
    // Using config_path
    $this->config_path = 'unbound/hosts';
    
    // OR using internal_callable
    $this->internal_callable = 'get_internal_data';
  6. How HATEOAS works in the pfSense REST API

    master

    HATEOAS (Hypermedia As The Engine Of Application State) allows clients to navigate the API by following links provided directly in the server's response data. The pfSense REST API uses a combination of HAL (Hypertext Application Language) and custom link types to help clients discover available actions and related resources without hardcoding URLs.

    Links are returned in a _links object. This object can appear:

    1. At the root of the API response (typically for collection-level navigation like pagination).
    2. Nested under specific objects within the data section (providing links specific to that individual resource).

    Warning: Enabling HATEOAS increases the size of API responses and can impact performance on large datasets. It is recommended to use pagination when HATEOAS is enabled.

  7. Map HTTP methods to Model methods

    master

    The request_method_options property determines which Model methods are invoked based on the HTTP request:

    HTTP MethodEndpoint TypeModel Method Called
    GETmany enabledread_all()
    GETnon-manyread()
    POSTnon-manycreate()
    PATCHnon-manyupdate()
    DELETEnon-manydelete()
    PUTmany enabledreplace_all()

    Note: OPTIONS is automatically supported and does not need to be defined.

  8. How ContentHandler classes work

    master

    ContentHandler classes manage the translation between raw API request/response content and PHP arrays based on the Content-Type and Accept headers.

    The Lifecycle of a Request:

    1. Decoding: When an Endpoint receives a request, the package finds a ContentHandler matching the Content-Type header. The handler's _decode() method converts the raw request content into a PHP array, which is then passed to a Model class.
    2. Processing: The Model processes the array and returns a representation as a PHP array.
    3. Encoding: The Endpoint finds a ContentHandler matching the Accept header. The handler's _encode() method converts the Model's PHP array back into the requested MIME type format for the client response.

    Note: You do not need to manually set HTTP response headers; the REST API package handles this automatically based on the handler's MIME type.

  9. Understand the difference between Singular and Plural endpoints

    master

    The pfSense REST API categorizes endpoints into two types based on how they handle data and objects:

    Singular Endpoints

    • Purpose: Interact with and return a single object (e.g., a specific user or a single firewall rule).
    • Data Format: The data field in the response is an associative array representing that single object.
    • Requirement: Often require an object ID to identify the target.
    • Examples:
      • /api/v2/firewall/rule
      • /api/v2/user
      • /api/v2/interface

    Plural (Many) Endpoints

    • Purpose: Interact with and return multiple objects (e.g., a list of all users).
    • Data Format: The data field in the response is an array of associative arrays.
    • Examples:
      • /api/v2/firewall/rules
      • /api/v2/users
      • /api/v2/interfaces

    Note: Always check the official API documentation for a specific endpoint to confirm its type and supported HTTP methods, as behavior can vary.

  10. Understand supported package versions

    master

    The project maintains two distinct versions of the API package. Users should prioritize the v2 package for active development and full maintenance.

    • v2 package (pfSense-pkg-RESTAPI): The latest version. It is actively developed and fully maintained.
    • v1 package (pfSense-pkg-API): The legacy version. It is no longer actively developed and only receives compatibility fixes and critical security updates when necessary.

    It is highly recommended to regularly update to the latest version to ensure you receive important bug fixes and security updates.

  11. Manage synchronous vs asynchronous execution with async

    master

    Controls whether the API applies changes in the background.

    • async (Boolean, default: true): The API processes changes in the background.
    • async: false: Forces the API to wait for changes to be completed on the backend before responding.

    Warning: Setting async to false can cause the API to hang or timeout if the operation is long-running. It is recommended to keep async: true and periodically poll the status using a GET request to the applicable apply endpoint.

  12. Create non-configuration based Models

    master

    If you need a Model that does not interact with the pfSense XML configuration (e.g., for fetching real-time data, interacting with services, or executing commands), you can create a non-configuration based Model.

    To do this, omit the config_path property and instead define the internal_callable property. The internal_callable must point to a method within your class that fetches the data.

    Return Value Requirements:

    • For Models with $this->many = true;: The callable must return an array of objects.
    • For Models with $this->many = false; (default): The callable must return a single object.

    Pagination Support: To handle large datasets efficiently, your internal_callable can implement pagination by accepting three specific parameters: limit, offset, and reverse. If these are present, the responsibility for returning the correct subset of data shifts to your callable.

    namespace RESTAPI\
    Models;
    
    use RESTAPI\Core\Model;
    
    class MyCustomModel extends Model {
        public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], mixed ...$options) {
            # Use internal_callable instead of config_path
            $this->internal_callable = 'fetch_data';
            $this->many = true;
            
            # Define fields as usual
            $this->name = new StringField(required: true);
            
            parent::__construct($id, $parent_id, $data, ...$options);
        }
    
        /**
        * Fetches data for the Model object.
        * @return array The data for the Model object.
        */
        public function fetch_data(): array {
            return [
                ['name' => 'EXAMPLE_1', 'enabled' => true],
                ['name' => 'EXAMPLE_2', 'enabled' => false],
            ];
        }
    }