# Разработка пакетов для pfSense

> Структура пакетов pfSense, XML-манифест, PHP-файлы, интеграция в меню, создание страниц настроек, FreeBSD-порты, тестирование и публикация пакетов

Source: https://opennix.org/docs/pfsense/development/pfsense-package-development/


Пакеты расширяют функциональность pfSense без модификации базовой системы. Каждый пакет представляет собой FreeBSD-порт со специфичной для pfSense структурой: XML-манифест определяет метаданные, XML-файлы конфигурации описывают интерфейс, а PHP-файлы реализуют логику. Package Manager устанавливает пакеты из официального репозитория, но разработчики могут собирать и устанавливать собственные пакеты вручную.

## Структура порта пакета

Пакеты pfSense именуются по схеме `pfSense-pkg-<Name>` и размещаются в категорийных каталогах FreeBSD-портов (например, `sysutils/`, `net/`, `security/`). Разработка ведется в ветке `devel` репозитория [pfSense FreeBSD-ports](https://github.com/pfsense/FreeBSD-ports).

Типичная структура порта:

```
sysutils/pfSense-pkg-MyPackage/
├── Makefile                           # Версия, зависимости, сборка
├── pkg-descr                          # Текстовое описание пакета
├── pkg-plist                          # Список устанавливаемых файлов
└── files/
    ├── pkg-install.in                 # Скрипт установки
    ├── pkg-deinstall.in               # Скрипт удаления
    └── usr/local/
        ├── bin/                       # Исполняемые скрипты
        ├── pkg/
        │   ├── mypackage.xml          # Конфигурация GUI
        │   └── mypackage.inc          # PHP-модуль с логикой
        ├── share/pfSense-pkg-MyPackage/
        │   └── info.xml               # Манифест пакета
        └── www/packages/mypackage/
            └── mypackage.php          # Веб-страницы пакета
```

### Makefile

Makefile определяет версию пакета, зависимости и процедуру сборки. Версия задается через переменную `PORTVERSION`, а зависимости от бинарных пакетов FreeBSD указываются в `RUN_DEPENDS`:

```makefile
PORTNAME=       pfSense-pkg-MyPackage
PORTVERSION=    0.1.0
CATEGORIES=     sysutils
MASTER_SITES=   # пусто для локальных пакетов

MAINTAINER=     developer@example.com
COMMENT=        My custom pfSense package

RUN_DEPENDS+=   bash:shells/bash

NO_BUILD=       yes
NO_MTREE=       yes

.include <bsd.port.mk>
```

При каждом обновлении пакета необходимо увеличивать `PORTVERSION`. Без изменения версии система не обнаружит обновление.

### pkg-plist

Файл `pkg-plist` перечисляет все файлы, устанавливаемые пакетом, относительно `/usr/local/`:

```
share/pfSense-pkg-MyPackage/info.xml
pkg/mypackage.xml
pkg/mypackage.inc
www/packages/mypackage/mypackage.php
```

При добавлении новых файлов в пакет обязательно обновляйте `pkg-plist`, иначе файлы не будут включены в сборку.

### Скрипты установки и удаления

Файлы `pkg-install.in` и `pkg-deinstall.in` одинаковы для всех пакетов pfSense и обеспечивают интеграцию с Package Manager. Копируйте их из любого существующего пакета без изменений.

## XML-манифест (info.xml)

Манифест `info.xml` размещается в `files/usr/local/share/pfSense-pkg-<Name>/info.xml` и содержит метаданные пакета:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<pfsensepkgs>
  <package>
    <name>mypackage</name>
    <descr>Description of my package</descr>
    <pkginfolink>https://example.com/docs</pkginfolink>
    <version>%%PKGVERSION%%</version>
    <configurationfile>
      https://packages.pfsense.org/packages/config/mypackage/mypackage.xml
    </configurationfile>
  </package>
</pfsensepkgs>
```

Переменная `%%PKGVERSION%%` автоматически подставляется из `PORTVERSION` в Makefile при сборке.

## Конфигурация GUI (XML-файл пакета)

XML-файл конфигурации (например, `mypackage.xml`) определяет интеграцию пакета с веб-интерфейсом pfSense: пункты меню, страницы настроек, поля форм и хуки жизненного цикла.

### Интеграция в меню

Элемент `<menu>` регистрирует пакет в навигации pfSense:

```xml
<packagegui>
  <menu>
    <name>MyPackage</name>
    <section>Services</section>
    <url>/packages/mypackage/mypackage.php</url>
  </menu>
</packagegui>
```

Пакет появится в разделе **Services** главного меню после установки.

### Определение полей настроек

Раздел `<fields>` описывает элементы формы на странице настроек пакета:

```xml
<fields>
  <field>
    <fielddescr>Enable Service</fielddescr>
    <fieldname>enable</fieldname>
    <type>checkbox</type>
    <description>Enable or disable the service</description>
  </field>
  <field>
    <fielddescr>Listen Port</fielddescr>
    <fieldname>port</fieldname>
    <type>input</type>
    <size>5</size>
    <description>Port number for the service (default: 8080)</description>
  </field>
  <field>
    <fielddescr>Network Interface</fielddescr>
    <fieldname>interface</fieldname>
    <type>interfaces_selection</type>
    <description>Select the interface to bind</description>
  </field>
</fields>
```

Поддерживаемые типы полей: `input`, `password`, `textarea`, `checkbox`, `select`, `interfaces_selection`, `info`, `button`.

Для группировки полей в одну строку используйте `<combinefields>`. Для табличного ввода нескольких записей - `<rowhelper>`. Для списков с добавлением, удалением и редактированием - `<adddeleteeditpagefields>`.

### Хуки жизненного цикла

PHP-функции, выполняемые на различных этапах работы пакета:

| Хук | Назначение |
|---|---|
| `custom_php_install_command` | Действия после установки пакета |
| `custom_php_deinstall_command` | Очистка при удалении пакета |
| `custom_php_resync_config_command` | Синхронизация конфигурации (вызывается при сохранении) |
| `custom_php_validation_command` | Валидация введенных данных перед сохранением |

Хуки указываются в XML как имена PHP-функций, определенных в `.inc`-файле пакета.

## PHP-файлы пакета

### Include-файл (.inc)

Файл `mypackage.inc` в каталоге `files/usr/local/pkg/` содержит основную логику пакета: функции валидации, применения конфигурации, генерации конфигурационных файлов сервисов.

```php
<?php
require_once("config.inc");
require_once("util.inc");
require_once("service-utils.inc");

function mypackage_validate_input($post, &$input_errors) {
    if (!is_port($post['port'])) {
        $input_errors[] = "Invalid port number.";
    }
}

function mypackage_resync_config() {
    global $config;
    $settings = $config['installedpackages']['mypackage']['config'][0];

    if ($settings['enable'] != "on") {
        mypackage_stop_service();
        return;
    }

    // Generate configuration file
    $conf = "listen_port={$settings['port']}\n";
    file_put_contents("/usr/local/etc/mypackage.conf", $conf);

    mypackage_restart_service();
}

function mypackage_restart_service() {
    mwexec("/usr/local/bin/mypackage restart");
}

function mypackage_stop_service() {
    mwexec("/usr/local/bin/mypackage stop");
}
```

### Веб-страницы (.php)

PHP-файлы в `files/usr/local/www/packages/mypackage/` отвечают за отображение страниц пакета в веб-интерфейсе. Для большинства пакетов достаточно стандартного шаблона, который автоматически генерирует страницу настроек на основе XML-конфигурации.

## Зависимости пакета

Зависимости от бинарных пакетов FreeBSD указываются в Makefile через `RUN_DEPENDS`. XML-конфигурация пакета не управляет зависимостями - это задача системы портов.

```makefile
RUN_DEPENDS+=   python3:lang/python3 \
                curl:ftp/curl
```

pfSense использует собственный репозиторий пакетов, основанный на FreeBSD `pkg`. Не все пакеты из стандартного репозитория FreeBSD доступны - проверяйте наличие нужных зависимостей в репозитории pfSense перед их использованием.

## Компиляция ПО для pfSense

pfSense намеренно не включает средства компиляции (make, заголовочные файлы) из соображений безопасности. Для компиляции стороннего ПО:

1. Разверните виртуальную машину с версией FreeBSD, соответствующей вашей версии pfSense
2. Скомпилируйте программу в этой среде
3. Перенесите готовые бинарные файлы или пакеты на межсетевой экран

Альтернативный вариант - использовать предкомпилированные пакеты FreeBSD через `pkg install` при условии совместимости версий.

## Сборка и тестирование пакета

### Сборка пакета

Для сборки пакета из порта используется стандартный механизм FreeBSD:

```bash
cd /usr/ports/sysutils/pfSense-pkg-MyPackage
make package
```

Результатом будет файл `.pkg` (или `.txz`), готовый к установке.

### Установка на тестовый экземпляр

Установите собранный пакет на тестовый pfSense:

```bash
pkg add pfSense-pkg-MyPackage-0.1.0.pkg
```

После установки:

- Проверьте появление пункта в меню
- Протестируйте сохранение и загрузку настроек
- Убедитесь, что хуки жизненного цикла работают корректно
- Проверьте корректную работу при удалении и повторной установке пакета

### Отладка

При ошибках в пакете просматривайте журнал системы:

```bash
tail -f /var/log/system.log
```

PHP-ошибки пакетов отображаются на странице **Status > System Logs** в разделе **System > General**.

## Публикация пакета

Для включения пакета в официальный репозиторий pfSense:

1. Убедитесь, что пакет соответствует [руководству по стилю](https://docs.netgate.com/pfsense/en/latest/references/developer-style-guide.html)
2. Создайте запись в [Redmine pfSense](https://redmine.pfsense.org) с описанием пакета
3. Отправьте pull request в репозиторий [pfSense FreeBSD-ports](https://github.com/pfsense/FreeBSD-ports) в ветку `devel`
4. Дождитесь ревью от разработчиков Netgate

Pull request должен содержать ссылку на запись в Redmine, описание функциональности и результаты тестирования.

## Типичные ошибки

| Проблема | Причина | Решение |
|---|---|---|
| Пакет не обновляется | Не увеличен `PORTVERSION` в Makefile | Инкрементируйте версию при каждом изменении |
| Файлы не устанавливаются | Не обновлен `pkg-plist` | Добавьте все новые файлы в `pkg-plist` |
| Ошибки PHP после обновления pfSense | Несовместимость с PHP 8.x | Замените устаревшие функции, используйте `elseif` вместо `else if` |
| Пакет работает на 2.7, но не на 2.8 | Изменения в базовой версии FreeBSD | Проверяйте совместимость с целевой версией FreeBSD |
| Пробелы вместо табуляции | Нарушение стиля кодирования | Используйте табуляцию с шириной 8 для отступов |
| XSS-уязвимости | Вывод пользовательского ввода без экранирования | Используйте `htmlspecialchars()` для всех выводимых данных |

## Связанные разделы

- [Пользовательские скрипты pfSense](/docs/pfsense/development/pfsense-custom-scripts/) - скрипты автоматизации, Shellcmd, Cron и PHP Shell
- [API и автоматизация](/docs/pfsense/development/pfsense-api-automation/) - программное управление pfSense через REST API
- [Пакеты pfSense](/docs/pfsense/packages/) - установка и управление пакетами через Package Manager

