# pfSense API and Management Automation

> Configuring pfSense REST API, API key and JWT authentication, automation with Ansible and Terraform, xmlrpc, and programmatic configuration management

Source: https://opennix.org/en/docs/pfsense/development/pfsense-api-automation/


A REST API transforms pfSense from a standalone firewall into a programmable infrastructure component. Instead of manual configuration through the web interface, administrators can perform operations via HTTP requests, integrate pfSense into orchestration systems, and adopt an Infrastructure as Code approach. This becomes essential in environments with dozens of devices where manual configuration of each firewall is impractical.

## pfSense REST API Package

pfSense does not include a built-in REST API. Programmatic access is provided by the community package [pfSense-api](https://github.com/jaredhendrickson13/pfsense-api), which adds over 200 endpoints for system management.

### Installation

The package is installed from the pfSense command line:

```bash
pkg-static add https://github.com/jaredhendrickson13/pfsense-api/releases/latest/download/pfSense-2.7.2-pkg-RESTAPI.pkg
```

After installation, API settings are available under **System > REST API** in the web interface.

### Authentication

The API supports three authentication methods:

| Method | Header | Expiration | Recommendation |
|---|---|---|---|
| Basic Auth | `Authorization: Basic <base64>` | Permanent | Testing only |
| API Key | `X-API-Key: <key>` | Permanent | Scripts and automation |
| JWT | `Authorization: Bearer <token>` | Time-limited | Interactive applications |

#### Obtaining a JWT Token

```bash
curl -X POST -H "Content-Type: application/json" \
  -u admin:pfsense \
  https://pfsense.example.com/api/v2/auth/jwt
```

The response contains a token that is then passed in the header:

```bash
curl -H "Authorization: Bearer <token>" \
  https://pfsense.example.com/api/v2/firewall/rules
```

#### API Key Authentication

API keys are created in the web interface (**System > REST API > Settings**) and are bound to a user account. Key privileges are inherited from the associated account.

```bash
curl -H "X-API-Key: <your-api-key>" \
  https://pfsense.example.com/api/v2/system/info
```

## API Endpoints Overview

### System Information

```bash
# Get system information
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/system/info

# Version and update status
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/system/version
```

### Firewall Rules

```bash
# List all firewall rules
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/firewall/rules

# Create a new rule
curl -X POST -H "Content-Type: application/json" \
  -H "X-API-Key: <key>" \
  -d '{
    "type": "pass",
    "interface": "wan",
    "ipprotocol": "inet",
    "protocol": "tcp",
    "source": "any",
    "destination": "any",
    "dstport": "443",
    "descr": "Allow HTTPS inbound"
  }' \
  https://pfsense.example.com/api/v2/firewall/rules

# Apply changes (required after modifying rules)
curl -X POST -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/firewall/apply
```

### Interface Management

```bash
# List network interfaces
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/interface

# Update interface configuration
curl -X PUT -H "Content-Type: application/json" \
  -H "X-API-Key: <key>" \
  -d '{"if": "em1", "descr": "DMZ", "enable": true}' \
  https://pfsense.example.com/api/v2/interface
```

### VPN Management

```bash
# List OpenVPN servers
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/vpn/openvpn/server

# List IPsec tunnels
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/vpn/ipsec/phase1
```

## Automation with Ansible

The [pfsensible.core](https://galaxy.ansible.com/pfsensible/core) collection provides modules for declarative pfSense management through Ansible. The collection communicates with pfSense via xmlrpc and the PHP interface, requiring no additional package installations.

### Installing the Collection

```bash
ansible-galaxy collection install pfsensible.core
```

### Example Playbook

```yaml
- name: Configure pfSense firewall
  hosts: pfsense_firewalls
  gather_facts: false
  collections:
    - pfsensible.core

  tasks:
    - name: Create alias for web servers
      pfsense_alias:
        name: web_servers
        type: host
        address: "10.0.1.10 10.0.1.11 10.0.1.12"
        descr: "Production web servers"
        state: present

    - name: Allow HTTPS to web servers
      pfsense_rule:
        name: "Allow HTTPS to web servers"
        interface: WAN
        action: pass
        protocol: tcp
        source: any
        destination: web_servers
        destination_port: 443
        state: present

    - name: Configure DNS resolver
      pfsense_dns_resolver:
        enable: true
        dnssec: true
        forwarding: true
```

### Inventory Configuration

```ini
[pfsense_firewalls]
fw01 ansible_host=192.168.1.1

[pfsense_firewalls:vars]
ansible_connection=httpapi
ansible_httpapi_use_ssl=true
ansible_httpapi_validate_certs=false
ansible_user=admin
ansible_password=pfsense
ansible_network_os=pfsensible.core.pfsense
```

## Automation with Terraform

An unofficial [Terraform provider for pfSense](https://registry.terraform.io/providers/sjafferern/pfsense/) enables configuration management through declarative HCL files.

```hcl
terraform {
  required_providers {
    pfsense = {
      source  = "sjafferern/pfsense"
      version = "~> 0.1"
    }
  }
}

provider "pfsense" {
  url      = "https://192.168.1.1"
  user     = "admin"
  password = var.pfsense_password
  insecure = true
}

resource "pfsense_firewall_alias" "blocked_countries" {
  name        = "blocked_countries"
  type        = "network"
  description = "Blocked country subnets"
  entries     = ["10.99.0.0/16", "10.98.0.0/16"]
}
```

The provider is in early development. Thorough testing is recommended before using it in production environments.

## PHP Shell for Programmatic Access

pfSense includes a developer PHP shell accessible via the console (option 12) or SSH. The shell allows direct reading and modification of system configuration.

### Accessing the PHP Shell

```text
Enter an option: 12

Starting the pfSense developer shell....
pfSense shell:
```

### Common Operations

```php
// Read current configuration
parse_config(true);
print_r($config['interfaces']);
exec

// Modify configuration
$config['system']['hostname'] = "fw-prod-01";
write_config("Updated hostname via PHP shell");
exec
```

### Playback Scripts

Predefined scripts for common tasks are executed from the command line:

```bash
# From SSH or console shell
pfSsh.php playback enablesshd
pfSsh.php playback changepassword
pfSsh.php playback installpkg "Some Package"
pfSsh.php playback listpkg
```

## xmlrpc for Configuration Management

pfSense uses xmlrpc for configuration synchronization between nodes in a high-availability (CARP) cluster. The same mechanism can be used for programmatic reading and writing of configuration data.

xmlrpc is accessible at `https://<pfsense-ip>/xmlrpc.php` and accepts standard XML-RPC calls with Basic authentication.

The pfsensible.core collection uses xmlrpc as its primary transport for communicating with pfSense.

## Backup via API

The REST API enables automated configuration backup creation:

```bash
# Download configuration via API
curl -H "X-API-Key: <key>" \
  https://pfsense.example.com/api/v2/system/config \
  -o config-backup-$(date +%Y%m%d).xml
```

### Automated Backup Script

```bash
#!/bin/sh
# Daily pfSense configuration backup via API
PFSENSE_HOST="https://192.168.1.1"
API_KEY="your-api-key"
BACKUP_DIR="/backups/pfsense"
DATE=$(date +%Y%m%d-%H%M)

curl -sk -H "X-API-Key: ${API_KEY}" \
  "${PFSENSE_HOST}/api/v2/system/config" \
  -o "${BACKUP_DIR}/config-${DATE}.xml"

# Remove backups older than 30 days
find "${BACKUP_DIR}" -name "config-*.xml" -mtime +30 -delete
```

## Use Cases

### Mass Deployment

When deploying multiple pfSense devices, an Ansible playbook applies a unified configuration to all nodes. A typical workflow:

1. Base pfSense installation on each device
2. SSH access and credential configuration
3. Apply playbook with common firewall rules, VPN configuration, and DNS settings
4. Apply device-specific variables (IP addresses, hostnames)

### CI/CD for Firewall Rules

Firewall rules are stored in a Git repository as YAML files. When merged into the main branch, the CI pipeline applies changes via the API or Ansible:

```yaml
# .gitlab-ci.yml
deploy_firewall_rules:
  stage: deploy
  script:
    - ansible-playbook -i inventory/production firewall-rules.yml
  only:
    - main
  when: manual
```

### Automated Testing

After applying changes, automated checks verify configuration correctness:

```bash
# Verify accessibility after rule changes
curl -sf -o /dev/null https://pfsense.example.com/api/v2/system/info \
  -H "X-API-Key: <key>" && echo "API accessible" || echo "API unreachable"
```

## Troubleshooting

| Issue | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Invalid credentials or expired JWT | Verify API key or request a new token |
| 403 Forbidden | Insufficient user privileges for API | Assign required privileges in **System > User Manager** |
| Connection refused on API port | REST API package not installed or not running | Verify package installation and service status |
| Changes not applied | `/api/v2/firewall/apply` not called | Execute apply after modifying rules |
| xmlrpc timeout | Large config.xml or network issues | Increase timeout, verify network connectivity |
| Ansible module cannot find pfSense | Incorrect `ansible_network_os` | Set to `pfsensible.core.pfsense` |

## Related Sections

- [pfSense Custom Scripts](/docs/pfsense/development/pfsense-custom-scripts/) - shellcmd, cron jobs, and PHP scripts for local automation
- [pfSense Backup](/docs/pfsense/backup/pfsense-backup-recovery/) - manual and automated configuration backups
- [pfSense High Availability](/docs/pfsense/high-availability/) - xmlrpc configuration synchronization between CARP cluster nodes

