+
+
diff --git a/security/netbird/Makefile b/security/netbird/Makefile
index a2450c455..619b7b3a9 100644
--- a/security/netbird/Makefile
+++ b/security/netbird/Makefile
@@ -1,8 +1,8 @@
PLUGIN_NAME= netbird
-PLUGIN_VERSION= 0.1
+PLUGIN_VERSION= 0.2
PLUGIN_DEPENDS= netbird
PLUGIN_COMMENT= Peer-to-peer VPN that seamlessly connects your devices
-PLUGIN_MAINTAINER= opn-netbird@sun-ri.se
+PLUGIN_MAINTAINER= dev@netbird.io
PLUGIN_WWW= https://netbird.io
PLUGIN_DEVEL= yes
diff --git a/security/netbird/src/etc/inc/plugins.inc.d/netbird.inc b/security/netbird/src/etc/inc/plugins.inc.d/netbird.inc
index 57872cefe..1d6b7fce0 100644
--- a/security/netbird/src/etc/inc/plugins.inc.d/netbird.inc
+++ b/security/netbird/src/etc/inc/plugins.inc.d/netbird.inc
@@ -4,6 +4,7 @@
* Copyright (C) 2025 Ralph Moser, PJ Monitoring GmbH
* Copyright (C) 2025 squared GmbH
* Copyright (C) 2025 Christopher Linn, BackendMedia IT-Services GmbH
+ * Copyright (C) 2025 NetBird GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,27 +31,40 @@
function netbird_enabled()
{
- return !(new \OPNsense\Netbird\Netbird())->general->Enabled->isEmpty();
+ return !(new \OPNsense\Netbird\Settings())->general->enable->isEmpty();
}
function netbird_services()
{
- $services = array();
-
+ $services = [];
if (!netbird_enabled()) {
return $services;
}
- $services[] = array(
- 'description' => gettext('Netbird'),
- 'configd' => array(
- 'restart' => array('netbird restart'),
- 'start' => array('netbird start'),
- 'stop' => array('netbird stop'),
- ),
+ $services[] = [
+ 'description' => gettext('NetBird'),
+ 'configd' => [
+ 'restart' => ['netbird restart'],
+ 'start' => ['netbird start'],
+ 'stop' => ['netbird stop'],
+ ],
'name' => 'netbird',
'pidfile' => '/var/run/netbird.pid',
- );
+ ];
return $services;
}
+
+function netbird_configure()
+{
+ return [
+ 'netbird_sync_config' => ['netbird_configure_do']
+ ];
+}
+
+function netbird_configure_do($verbose = false)
+{
+ service_log('Sync NetBird config...', $verbose);
+ (new \OPNsense\Netbird\Settings())->syncConfig();
+ service_log("done.\n", $verbose);
+}
diff --git a/security/netbird/src/etc/rc.syshook.d/carp/30-netbird b/security/netbird/src/etc/rc.syshook.d/carp/30-netbird
deleted file mode 100755
index b4638afb7..000000000
--- a/security/netbird/src/etc/rc.syshook.d/carp/30-netbird
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/local/bin/php
-
- * Copyright (C) 2025 Ralph Moser, PJ Monitoring GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-require_once('config.inc');
-require_once('util.inc');
-require_once('interfaces.inc');
-
-
-$model = new \OPNsense\Netbird\Netbird();
-
-if ($model->general->Enabled->isEmpty()) {
- exit(0);
-}
-
-if (!$model->general->CarpIf->isEqual('')) {
- exit(0);
-}
-
-$target_vhid = $model->general->VHID;
-$subsystem = !empty($argv[1]) ? $argv[1] : '';
-$type = !empty($argv[2]) ? $argv[2] : '';
-
-if ($type != 'MASTER' && $type != 'BACKUP') {
- exit(1);
-}
-
-if (!strstr($subsystem, '@')) {
- exit(1);
-}
-
-list ($vhid, $iface) = explode('@', $subsystem);
-$friendly = convert_real_interface_to_friendly_interface_name($iface);
-
-
-if ($carpif != $friendly || $vhid != $target_vhid) {
- exit(0);
-}
-
-switch ($type) {
- case 'MASTER':
- shell_exec('/usr/local/bin/netbird up');
- break;
- case 'BACKUP':
- shell_exec('/usr/local/bin/netbird down');
- break;
-}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/AuthenticationController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/AuthenticationController.php
new file mode 100644
index 000000000..86e22cb58
--- /dev/null
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/AuthenticationController.php
@@ -0,0 +1,102 @@
+managementUrl->__toString();
+ $setupKey = $mdl->setupKey->__toString();
+
+ $defaultKey = '00000000-0000-0000-0000-000000000000';
+ if (!empty($setupKey) && $setupKey !== $defaultKey) {
+ $visiblePart = substr($setupKey, 0, 4);
+ $maskedKey = $visiblePart . str_repeat('*', max(4, strlen($setupKey) - 4));
+ }else{
+ $maskedKey = $defaultKey;
+ }
+
+ return [
+ 'authentication' => [
+ 'managementUrl' => $managementUrl,
+ 'setupKey' => $maskedKey
+ ]
+ ];
+ }
+
+ public function upAction()
+ {
+ $backend = new Backend();
+ $mdl = new Authentication();
+
+ $status = json_decode($backend->configdRun("netbird status-json"), true);
+ $connected = $status['management']['connected'] ?? false;
+
+ if (json_last_error() === JSON_ERROR_NONE && $connected === true) {
+ $backend->configdRun("netbird down");
+ }
+
+ $managementUrl = $mdl->managementUrl->__toString();
+ $setupKey = $mdl->setupKey->__toString();
+
+ $result = $backend->configdpRun("netbird up-setup-key", array($managementUrl, $setupKey));
+ return ['result' => trim($result)];
+ }
+
+ public function downAction(): array
+ {
+ $backend = new Backend();
+
+ $status = json_decode($backend->configdRun("netbird status-json"), true);
+ $connected = $status['management']['connected'] ?? false;
+
+ if (json_last_error() === JSON_ERROR_NONE && $connected === true) {
+ $result = $backend->configdRun("netbird down");
+ return ['result' => trim($result)];
+ }
+ return ['result' => 'already disconnected or not running'];
+ }
+}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/ServiceController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/ServiceController.php
index f0b5e7e14..df7000d12 100644
--- a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/ServiceController.php
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/ServiceController.php
@@ -1,9 +1,7 @@
configdRun("netbird con-status");
- if ($bckResult !== null) {
- return nl2br(htmlspecialchars($bckResult));
- }
- return "Error retrieving connection status";
- }
-
- public function searchFilter($array, $value): bool
- {
- foreach ($array as $val) {
- if (str_contains(strval($val), strtolower($value))) {
- return true;
- }
- }
- return false;
- }
-
- public function upDownStatusAction(): string
- {
- $backend = new Backend();
- $bckResult = $backend->configdRun("netbird status");
- if (!str_contains($bckResult, "is running")) {
- return json_encode(array('updown' => "NOT RUNNING", 'status' => "Netbird is not running"));
- }
- $bckResult = $backend->configdRun("netbird short-con-status");
- $txtStatus = nl2br(htmlspecialchars($bckResult));
- $bckResult = $backend->configdRun("netbird con-status-json");
- $status = json_decode($bckResult, true);
- if (!$status['publicKey']) {
- return json_encode(array('updown' => "DOWN", 'status' => $txtStatus));
- }
- return json_encode(array('updown' => "UP", 'status' => $txtStatus));
- }
-
- public function searchAction(): string
- {
- $request = $this->request;
- $backend = new Backend();
- $bckResult = $backend->configdRun("netbird status");
- if (!str_contains($bckResult, "is running")) {
- return json_encode(array('current' => 1, 'rowCount' => 0, 'total' => 0, 'rows' => array()));
- }
- $bckResult = $backend->configdRun("netbird con-status-json");
- $status = json_decode($bckResult, true);
- $itemsPerPage = $request->get('rowCount', 'int', -1);
- $currentPage = $request->get('current', 'int', 1);
- $sortBy = array('status');
- $sortDescending = false;
-
-
- $searchPhrase = strtolower($request->get('searchPhrase', 'string', ''));
- if (!$status['peers']['details']) {
- return json_encode(array('current' => 1, 'rowCount' => 0, 'total' => 0, 'rows' => array()));
- }
- $details = $status['peers']['details'];
- $details = array_filter($details, function ($item) use ($searchPhrase) {
- return $this->searchFilter($item, $searchPhrase);
- });
- $detailsFlat = array();
- foreach ($details as $detail) {
- $detailsFlat[] = $this->flattenOneLevel($detail);
- }
- if ($request->hasPost('sort') && is_array($request->get("sort")) && !empty($request->get("sort"))) {
- $sortBy = array_keys($request->get("sort"));
- if (!empty($sortBy) && $request->get("sort")[$sortBy[0]] == "desc") {
- $sortDescending = true;
- }
- }
- $sortValues = array();
- foreach ($detailsFlat as $detail) {
- $sortValues[] = $detail[$sortBy[0]];
- }
- array_multisort($sortValues, $sortDescending ? SORT_DESC : SORT_ASC, $detailsFlat);
- $page = array_slice($detailsFlat, ($currentPage - 1) * $itemsPerPage, $itemsPerPage);
- $page = $this->convertFieldsToDisplay($page);
- $result = array('current' => $currentPage, 'rowCount' => count($page), 'total' => count($detailsFlat), 'rows' => $page);
- return json_encode($result);
- }
-
- private function flattenOneLevel($array): array
- {
- $result = array();
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- foreach ($value as $subkey => $subvalue) {
- if ($key == "networks") {
- $result[$key] = implode("
", $value);
- } else {
- $result[$key . "." . $subkey] = $subvalue;
- }
- }
- } else {
- $result[$key] = $value;
- }
- }
- return $result;
- }
-
- public function setUpAction(): string
- {
- $backend = new Backend();
- try {
- return $backend->configdRun("netbird set-up");
- } catch (\Exception $e) {
- return "Error running netbird up" . "\n" . $e->getMessage();
- }
- }
-
- public function initialUpAction(): string
- {
- $backend = new Backend();
- $mdlInitial = new Initial();
- $key = $mdlInitial->initial->setupkey->__toString();
- $api = $mdlInitial->initial->mgmtservice->__toString();
- $hostname = $mdlInitial->initial->hostname->__toString();
- if ($hostname == "") {
- $hostname = gethostname();
- if (!$hostname) {
- $hostname = "OPNsense";
- } else {
- if (str_contains($hostname, ".")) {
- $hostname = explode(".", $hostname)[0];
- }
- }
-
- $mdlInitial->initial->hostname = $hostname;
- }
- $mdlInitial->initial->setupkey = "00000000-0000-0000-0000-000000000000";
- $mdlInitial->initial->initsure = 0;
-
- $mdlInitial->serializeToConfig();
- $cnf = Config::getInstance();
- $cnf->save();
-
- $bckresult = $backend->configdRun("netbird set-up-initial " . escapeshellarg($api) . " " . escapeshellarg($key) . " " . escapeshellarg($hostname));
- return nl2br(htmlspecialchars($bckresult));
- }
-
- public function setDownAction(): string
- {
- $backend = new Backend();
- try {
- return $backend->configdRun("netbird set-down");
- } catch (\Exception $e) {
- return "Error running netbird down" . "\n" . $e->getMessage();
- }
- }
-
- public function reloadAction()
- {
- $status = "failed";
- if ($this->request->isPost()) {
- try {
- $mdlNetbird = new Netbird();
- $backend = new Backend();
- if (trim($backend->configdRun('template reload OPNsense/Netbird')) == "OK") {
- $status = "ok";
- }
-
- $enabled = $mdlNetbird->general->Enabled->__toString() == 1;
- $carpEnabled = $mdlNetbird->general->CarpIf->__toString() != '';
- $disableClientRoutes = $mdlNetbird->general->DisableClientRoutes->__toString() == 1;
- $disableServerRoutes = $mdlNetbird->general->DisableServerRoutes->__toString() == 1;
- $disableDNS = $mdlNetbird->general->DisableDNS->__toString() == 1;
- $rpEnabled = $mdlNetbird->general->QuantumEnabled->__toString() == 1;
- $rpPermissive = $mdlNetbird->general->QuantumPermissive->__toString() == 1;
- $wgPort = $mdlNetbird->general->WgPort->__toString();
- $netbirdConfigJson = file_get_contents(self::NETBIRD_CONFIG_JSON);
- $netbirdConfig = json_decode($netbirdConfigJson, true);
- $netbirdConfig["DisableAutoConnect"] = $carpEnabled;
- $netbirdConfig["DisableClientRoutes"] = $disableClientRoutes;
- $netbirdConfig["DisableServerRoutes"] = $disableServerRoutes;
- $netbirdConfig["DisableDNS"] = $disableDNS;
- $netbirdConfig["RosenpassEnabled"] = $rpEnabled;
- $netbirdConfig["RosenpassPermissive"] = $rpPermissive;
- $netbirdConfig["WgPort"] = intval($wgPort);
- $netbirdConfigJson = json_encode($netbirdConfig);
- file_put_contents(self::NETBIRD_CONFIG_JSON, $netbirdConfigJson);
- $action = $enabled ? "restart" : "stop";
- $backend->configdRun("netbird $action");
- } catch (\Exception $e) {
- $status = "failed";
- syslog(LOG_ERR, "netbird: failed to reload configuration: " . $e->getMessage());
- }
- }
- return array("status" => $status);
- }
-
- /**
- * @param array $page
- * @return array
- */
- public function convertFieldsToDisplay(array $page): array
- {
- for ($i = 0; $i < count($page); $i++) {
- $page[$i]['latency'] = round($page[$i]['latency'] / 1000000, 2) . " ms";
- $received = $page[$i]['transferReceived'];
- $rcvUnit = "KiB";
- $received /= 1024;
- if ($received > 1024) {
- $received /= 1024;
- $rcvUnit = "MiB";
- }
- if ($received > 1024) {
- $received /= 1024;
- $rcvUnit = "GiB";
- }
-
- $sent = $page[$i]['transferSent'];
- $sentUnit = "KiB";
- $sent /= 1024;
- if ($sent > 1024) {
- $sent /= 1024;
- $sentUnit = "MiB";
- }
- if ($sent > 1024) {
- $sent /= 1024;
- $sentUnit = "GiB";
- }
- $page[$i]['transferReceived'] = round($received, 2) . " " . $rcvUnit;
- $page[$i]['transferSent'] = round($sent, 2) . " " . $sentUnit;
- $page[$i]['lastStatusUpdate'] = date("Y-m-d H:i:s", strtotime($page[$i]['lastStatusUpdate']));
- $page[$i]['lastWireguardHandshake'] = date("Y-m-d H:i:s", strtotime($page[$i]['lastWireguardHandshake']));
- foreach ($page[$i] as $key => $value) {
- if ($value == "true") {
- $page[$i][$key] = 1;
- } elseif ($value == "false") {
- $page[$i][$key] = 0;
- }
- }
- }
- return $page;
- }
}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/SettingsController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/SettingsController.php
index bd7ebc603..f399d2891 100644
--- a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/SettingsController.php
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/SettingsController.php
@@ -1,9 +1,7 @@
configdRun("netbird sync-config");
+ if (stripos($result, 'done') === false) {
+ return [
+ 'result' => 'failed to sync config: ' . $result,
+ ];
+ }
+
+ $status = json_decode($backend->configdRun("netbird status-json"), true);
+ $connected = $status['management']['connected'] ?? false;
+ if (json_last_error() === JSON_ERROR_NONE && $connected === true) {
+ $backend->configdRun("netbird down");
+ $backend->configdRun("netbird up");
+ }
+
+ return ['result' => 'synced'];
+ }
}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/InitialController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/StatusController.php
similarity index 73%
rename from security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/InitialController.php
rename to security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/StatusController.php
index a86b619d9..7a91116e8 100644
--- a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/InitialController.php
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/Api/StatusController.php
@@ -4,6 +4,7 @@
* Copyright (C) 2025 Ralph Moser, PJ Monitoring GmbH
* Copyright (C) 2025 squared GmbH
* Copyright (C) 2025 Christopher Linn, BackendMedia IT-Services GmbH
+ * Copyright (C) 2025 NetBird GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,13 +32,24 @@
namespace OPNsense\Netbird\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
+use OPNsense\Core\Backend;
/**
- * netbird settings controller
+ * Class StatusController
* @package OPNsense\Netbird
*/
-class InitialController extends ApiMutableModelControllerBase
+class StatusController extends ApiMutableModelControllerBase
{
- protected static $internalModelName = 'netbird';
- protected static $internalModelClass = 'OPNsense\Netbird\Initial';
+ protected static $internalModelClass = '\OPNsense\Netbird\Status';
+ protected static $internalModelName = 'Netbird';
+
+ public function statusAction(): array
+ {
+ $backend = new Backend();
+ $status = json_decode($backend->configdRun("netbird status-json"), true);
+ if (json_last_error() === JSON_ERROR_NONE && is_array($status)) {
+ return $status;
+ }
+ return [];
+ }
}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/ConstatusController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/AuthenticationController.php
similarity index 81%
rename from security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/ConstatusController.php
rename to security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/AuthenticationController.php
index 6c041912d..bc8790f2a 100644
--- a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/ConstatusController.php
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/AuthenticationController.php
@@ -1,9 +1,7 @@
view->pick('OPNsense/Netbird/constatus');
+ $this->view->authenticationForm = $this->getForm("authentication");
+ $this->view->pick('OPNsense/Netbird/authentication');
}
}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/IndexController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/SettingsController.php
similarity index 76%
rename from security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/IndexController.php
rename to security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/SettingsController.php
index 3567b469d..b3677b198 100644
--- a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/IndexController.php
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/SettingsController.php
@@ -1,9 +1,7 @@
view->generalForm = $this->getForm('general');
- $this->view->initialUpForm = $this->getForm('initialup');
- $this->view->pick('OPNsense/Netbird/index');
+ $this->view->settingsForm = $this->getForm("settings");
+ $this->view->pick('OPNsense/Netbird/settings');
}
}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/StatusController.php b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/StatusController.php
new file mode 100644
index 000000000..55797b515
--- /dev/null
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/StatusController.php
@@ -0,0 +1,41 @@
+view->pick('OPNsense/Netbird/status');
+ }
+}
diff --git a/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/forms/authentication.xml b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/forms/authentication.xml
new file mode 100644
index 000000000..75111fdaf
--- /dev/null
+++ b/security/netbird/src/opnsense/mvc/app/controllers/OPNsense/Netbird/forms/authentication.xml
@@ -0,0 +1,14 @@
+
| {{ lang._('FQDN') }} | -{{ lang._('Networks') }} | -{{ lang._('IP') }} | -{{ lang._('Direct') }} | -{{ lang._('Status') }} | -{{ lang._('Last Handshake') }} | -{{ lang._('Last Status Update') }} | -{{ lang._('Received') }} | -{{ lang._('Sent') }} | -{{ lang._('Latency') }} | -{{ lang._('Connection Type') }} | -{{ lang._('QR') }} | -{{ lang._('ICE TL') }} | -{{ lang._('ICE TR') }} | -{{ lang._('ICE EP Local') }} | -{{ lang._('ICE EP Remote') }} | -
|---|