From 1832bce852334321f98056c589e9b2c95758604e Mon Sep 17 00:00:00 2001 From: cynfo3000 Date: Sat, 28 Feb 2026 14:13:54 +0100 Subject: [PATCH] OPNsense Plugin: install.sh Installer-Script (self-contained, alle Dateien inline) --- opnsense-plugin/build.sh | 83 +-- opnsense-plugin/install.sh | 519 ++++++++++++++++++ .../RMMAgent/Api/ServiceController.php | 47 +- .../RMMAgent/Api/SettingsController.php | 48 +- .../app/views/OPNsense/RMMAgent/general.volt | 52 +- 5 files changed, 688 insertions(+), 61 deletions(-) create mode 100755 opnsense-plugin/install.sh diff --git a/opnsense-plugin/build.sh b/opnsense-plugin/build.sh index 580a38c..dc69ad4 100755 --- a/opnsense-plugin/build.sh +++ b/opnsense-plugin/build.sh @@ -50,50 +50,63 @@ chmod +x "${STAGEDIR}/usr/local/etc/rc.d/rmm_agent" chmod +x "${STAGEDIR}/usr/local/opnsense/scripts/rmmagent/setup.sh" chmod +x "${STAGEDIR}/usr/local/opnsense/scripts/OPNsense/RMMAgent/generate_config.php" -# 3. Create +MANIFEST +# 3. Create +COMPACT_MANIFEST and +MANIFEST (UCL format) echo "==> Creating package manifest..." -cat > "${STAGEDIR}/+MANIFEST" << EOF -name: ${NAME} -version: ${VERSION} -origin: sysutils/${NAME} -comment: RMM Agent for OPNsense -maintainer: cm@cynfo.com -www: https://git.cynfo.net/christian/rmm -prefix: /usr/local -desc: < RMM Agent. -EOD -categories: [sysutils] -deps: - opnsense: {origin: opnsense/opnsense, version: "24.7"} -scripts: - post-install: | - /usr/local/etc/rc.d/opnsense-configd restart - echo "RMM Agent Plugin installed." - echo "Configure via: Services > RMM Agent" - pre-deinstall: | - /usr/local/etc/rc.d/rmm_agent stop 2>/dev/null || true - /usr/sbin/sysrc -x rmm_agent_enable 2>/dev/null || true - post-deinstall: | - /usr/local/etc/rc.d/opnsense-configd restart - echo "RMM Agent Plugin removed." +cat > "${STAGEDIR}/+COMPACT_MANIFEST" << EOF +{ + "name": "${NAME}", + "version": "${VERSION}", + "origin": "sysutils/${NAME}", + "comment": "RMM Agent for OPNsense", + "maintainer": "cm@cynfo.com", + "www": "https://git.cynfo.net/christian/rmm", + "abi": "FreeBSD:14:amd64", + "arch": "freebsd:14:x86:64", + "prefix": "/usr/local", + "desc": "RMM Agent for OPNsense - Remote Monitoring and Management. Configurable via WebGUI under Services > RMM Agent.", + "categories": ["sysutils"] +} EOF -# 4. Create package (tar format compatible with `pkg add`) +# +MANIFEST = +COMPACT_MANIFEST plus files/scripts +cp "${STAGEDIR}/+COMPACT_MANIFEST" "${STAGEDIR}/+MANIFEST" + +# Post-install script +cat > "${STAGEDIR}/+POST_INSTALL" << 'SCRIPT' +#!/bin/sh +/usr/local/etc/rc.d/opnsense-configd restart 2>/dev/null +echo "" +echo "=====================================" +echo " RMM Agent Plugin installed." +echo " Configure: Services > RMM Agent" +echo "=====================================" +SCRIPT + +# Pre-deinstall script +cat > "${STAGEDIR}/+PRE_DEINSTALL" << 'SCRIPT' +#!/bin/sh +/usr/local/etc/rc.d/rmm_agent stop 2>/dev/null || true +/usr/sbin/sysrc -x rmm_agent_enable 2>/dev/null || true +SCRIPT + +# Post-deinstall script +cat > "${STAGEDIR}/+POST_DEINSTALL" << 'SCRIPT' +#!/bin/sh +/usr/local/etc/rc.d/opnsense-configd restart 2>/dev/null +echo "RMM Agent Plugin removed." +SCRIPT + +chmod +x "${STAGEDIR}/+POST_INSTALL" "${STAGEDIR}/+PRE_DEINSTALL" "${STAGEDIR}/+POST_DEINSTALL" + +# 4. Create package using tar (FreeBSD txz pkg format) echo "==> Creating package..." cd "${STAGEDIR}" -# Generate file list -find usr -type f | sort > /tmp/rmm-pkg-files.txt - -# Create the pkg using tar (FreeBSD pkg format) -tar -czf "${PKGFILE}" +MANIFEST -C "${STAGEDIR}" $(cat /tmp/rmm-pkg-files.txt) +# pkg expects: +COMPACT_MANIFEST, +MANIFEST at root, files under usr/ +tar -cJf "${PKGFILE}" +COMPACT_MANIFEST +MANIFEST +POST_INSTALL +PRE_DEINSTALL +POST_DEINSTALL $(find usr -type f | sort) cd .. rm -rf "${STAGEDIR}" -rm -f /tmp/rmm-pkg-files.txt echo "" echo "==> Package erstellt: ${PKGFILE}" diff --git a/opnsense-plugin/install.sh b/opnsense-plugin/install.sh new file mode 100755 index 0000000..7842576 --- /dev/null +++ b/opnsense-plugin/install.sh @@ -0,0 +1,519 @@ +#!/bin/sh +# +# RMM Agent Plugin Installer fuer OPNsense +# +# Usage: +# scp install.sh rmm-agent root@opnsense:/tmp/ +# ssh root@opnsense '/bin/sh /tmp/install.sh' +# +# Oder remote: +# cat install.sh | ssh root@opnsense '/bin/sh' +# + +set -e + +BASEDIR="/tmp" +BINARY="${BASEDIR}/rmm-agent" + +echo "========================================" +echo " RMM Agent Plugin Installer" +echo "========================================" + +# Pruefen ob Binary vorhanden +if [ ! -f "${BINARY}" ]; then + echo "FEHLER: ${BINARY} nicht gefunden." + echo "Bitte vorher das Binary kopieren:" + echo " scp rmm-agent root@opnsense:/tmp/" + exit 1 +fi + +# Alten Agent stoppen falls vorhanden +echo "[1/7] Alten Agent stoppen..." +/usr/local/etc/rc.d/rmm_agent stop 2>/dev/null || true +pkill -9 -f rmm-agent 2>/dev/null || true + +# Binary installieren +echo "[2/7] Binary installieren..." +mkdir -p /usr/local/rmm +cp "${BINARY}" /usr/local/rmm/rmm-agent +chmod +x /usr/local/rmm/rmm-agent + +# rc.d Script +echo "[3/7] rc.d Service installieren..." +cat > /usr/local/etc/rc.d/rmm_agent << 'RCEOF' +#!/bin/sh +# +# PROVIDE: rmm_agent +# REQUIRE: NETWORKING +# KEYWORD: shutdown + +. /etc/rc.subr + +name="rmm_agent" +rcvar="rmm_agent_enable" + +load_rc_config ${name} + +: ${rmm_agent_enable:="NO"} +: ${rmm_agent_config:="/usr/local/rmm/config.yaml"} +: ${rmm_agent_logfile:="/var/log/rmm-agent.log"} +: ${rmm_agent_insecure:="YES"} + +pidfile="/var/run/${name}.pid" +command="/usr/local/rmm/rmm-agent" + +start_cmd="${name}_start" +stop_cmd="${name}_stop" +status_cmd="${name}_status" + +rmm_agent_start() +{ + if [ ! -f "${command}" ]; then + echo "rmm-agent binary not found: ${command}" + return 1 + fi + if [ ! -f "${rmm_agent_config}" ]; then + echo "Config not found: ${rmm_agent_config}" + return 1 + fi + echo "Starting ${name}." + INSECURE_FLAG="" + if checkyesno rmm_agent_insecure; then + INSECURE_FLAG="--insecure" + fi + /usr/sbin/daemon -f -p ${pidfile} -o ${rmm_agent_logfile} \ + ${command} --config ${rmm_agent_config} ${INSECURE_FLAG} +} + +rmm_agent_stop() +{ + if [ -f "${pidfile}" ]; then + echo "Stopping ${name}." + kill $(cat ${pidfile}) 2>/dev/null + rm -f ${pidfile} + else + echo "${name} is not running." + fi +} + +rmm_agent_status() +{ + if [ -f "${pidfile}" ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then + echo "${name} is running as pid $(cat ${pidfile})." + else + echo "${name} is not running." + return 1 + fi +} + +run_rc_command "$1" +RCEOF +chmod +x /usr/local/etc/rc.d/rmm_agent + +# MVC Plugin installieren +echo "[4/7] OPNsense MVC Plugin installieren..." + +# Model +mkdir -p /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/Menu +cat > /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/RMMAgent.xml << 'XMLEOF' + + //OPNsense/rmmagent + 1.0.0 + RMM Agent Settings + + + + 0 + Y + + + Y + /^https?:\/\/.+/ + Gueltige URL erforderlich (https://host:port) + + + Y + /^.{8,}$/ + API-Key muss mindestens 8 Zeichen lang sein + + + N + + + 60 + 10 + 3600 + Intervall zwischen 10 und 3600 Sekunden + + + 1 + Y + + + + +XMLEOF + +cat > /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/RMMAgent.php << 'PHPEOF' + /usr/local/opnsense/mvc/app/models/OPNsense/RMMAgent/Menu/Menu.xml << 'MENUEOF' + + + + + + +MENUEOF + +# Controllers +mkdir -p /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api +mkdir -p /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/forms + +cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/GeneralController.php << 'PHPEOF' +view->generalForm = $this->getForm('general'); + $this->view->pick('OPNsense/RMMAgent/general'); + } +} +PHPEOF + +cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php << 'PHPEOF' + array()); + foreach ($mdl->general->iterateItems() as $key => $node) { + $result['general'][$key] = (string)$node; + } + return $result; + } + + public function setAction() + { + $result = array('result' => 'failed'); + if ($this->request->isPost()) { + $mdl = new RMMAgent(); + $post = $this->request->getPost('general'); + if (!empty($post)) { + foreach ($post as $key => $value) { + $node = $mdl->general->$key; + if ($node !== null) { + $node->setValue($value); + } + } + } + $valMsgs = $mdl->performValidation(); + if ($valMsgs->count() > 0) { + $result['validations'] = array(); + foreach ($valMsgs as $msg) { + $result['validations']['general.' . $msg->getField()] = $msg->getMessage(); + } + } else { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'saved'; + } + } + return $result; + } +} +PHPEOF + +cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php << 'PHPEOF' +configdRun('rmmagent status')); + if (strpos($response, 'is running') !== false) { + return array('status' => 'running', 'message' => $response); + } + return array('status' => 'stopped', 'message' => $response); + } + + public function startAction() + { + $backend = new \OPNsense\Core\Backend(); + return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent start'))); + } + + public function stopAction() + { + $backend = new \OPNsense\Core\Backend(); + return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent stop'))); + } + + public function restartAction() + { + $backend = new \OPNsense\Core\Backend(); + return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent restart'))); + } + + public function reconfigureAction() + { + $backend = new \OPNsense\Core\Backend(); + return array('status' => 'ok', 'message' => trim($backend->configdRun('rmmagent reconfigure'))); + } +} +PHPEOF + +# Forms +cat > /usr/local/opnsense/mvc/app/controllers/OPNsense/RMMAgent/forms/general.xml << 'FORMEOF' +
+ + rmmagent.general.Enabled + + checkbox + RMM Agent Dienst aktivieren/deaktivieren + + + rmmagent.general.BackendUrl + + text + URL des RMM Backends (z.B. https://192.168.85.13:8443) + + + rmmagent.general.ApiKey + + password + API-Schluessel fuer die Authentifizierung am Backend + + + rmmagent.general.AgentName + + text + Optionaler Name (Standard: Hostname der Firewall) + + + rmmagent.general.Interval + + text + Intervall in Sekunden (Standard: 60) + + + rmmagent.general.Insecure + + checkbox + TLS-Pruefung deaktivieren (fuer self-signed Backend-Zertifikate) + +
+FORMEOF + +# View +mkdir -p /usr/local/opnsense/mvc/app/views/OPNsense/RMMAgent +cat > /usr/local/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt << 'VOLTEOF' + +
+
+

{{ lang._('RMM Agent') }} ...

+
+
+
+ {{ partial("layout_partials/base_form", ['fields': generalForm, 'id': 'frm_GeneralSettings']) }} +
+
+
+
+ + +
+
+VOLTEOF + +# configd actions +echo "[5/7] configd Actions installieren..." +cat > /usr/local/opnsense/service/conf/actions.d/actions_rmmagent.conf << 'ACTEOF' +[start] +command:/usr/local/etc/rc.d/rmm_agent start +parameters: +type:script +message:Starting RMM Agent + +[stop] +command:/usr/local/etc/rc.d/rmm_agent stop +parameters: +type:script +message:Stopping RMM Agent + +[restart] +command:/usr/local/etc/rc.d/rmm_agent restart +parameters: +type:script +message:Restarting RMM Agent + +[status] +command:/usr/local/etc/rc.d/rmm_agent status; exit 0 +parameters: +type:script_output +message:Request RMM Agent status + +[reconfigure] +command:/usr/local/opnsense/scripts/rmmagent/setup.sh +parameters: +type:script +message:Reconfiguring RMM Agent +ACTEOF + +# Setup-Script +echo "[6/7] Setup-Script installieren..." +mkdir -p /usr/local/opnsense/scripts/rmmagent +cat > /usr/local/opnsense/scripts/rmmagent/setup.sh << 'SETUPEOF' +#!/bin/sh +CONFIGDIR="/usr/local/rmm" +CONFIGFILE="${CONFIGDIR}/config.yaml" + +mkdir -p "${CONFIGDIR}" + +# Config aus OPNsense Model generieren +BACKEND_URL=$(pluginctl -g OPNsense.rmmagent.general.BackendUrl 2>/dev/null) +API_KEY=$(pluginctl -g OPNsense.rmmagent.general.ApiKey 2>/dev/null) +INTERVAL=$(pluginctl -g OPNsense.rmmagent.general.Interval 2>/dev/null) +AGENT_NAME=$(pluginctl -g OPNsense.rmmagent.general.AgentName 2>/dev/null) +INSECURE=$(pluginctl -g OPNsense.rmmagent.general.Insecure 2>/dev/null) +ENABLED=$(pluginctl -g OPNsense.rmmagent.general.Enabled 2>/dev/null) + +if [ -z "${INTERVAL}" ]; then + INTERVAL="60" +fi + +cat > "${CONFIGFILE}" << EOF +backend_url: "${BACKEND_URL}" +api_key: "${API_KEY}" +interval_seconds: ${INTERVAL} +EOF + +if [ -n "${AGENT_NAME}" ]; then + echo "agent_name: \"${AGENT_NAME}\"" >> "${CONFIGFILE}" +fi + +# Insecure Flag +if [ "${INSECURE}" = "1" ]; then + sysrc rmm_agent_insecure="YES" > /dev/null 2>&1 +else + sysrc rmm_agent_insecure="NO" > /dev/null 2>&1 +fi + +# Service steuern +if [ "${ENABLED}" = "1" ]; then + sysrc rmm_agent_enable="YES" > /dev/null 2>&1 + /usr/local/etc/rc.d/rmm_agent restart + echo "RMM Agent gestartet" +else + /usr/local/etc/rc.d/rmm_agent stop 2>/dev/null + sysrc rmm_agent_enable="NO" > /dev/null 2>&1 + echo "RMM Agent gestoppt" +fi +SETUPEOF +chmod +x /usr/local/opnsense/scripts/rmmagent/setup.sh + +# Plugin Hook +echo "[7/7] Plugin-Hook registrieren..." +cat > /usr/local/etc/inc/plugins.inc.d/rmmagent.inc << 'INCEOF' +general->Enabled == '1') { + $services[] = array( + 'description' => gettext('RMM Agent'), + 'configd' => array( + 'restart' => array('rmmagent restart'), + 'start' => array('rmmagent start'), + 'stop' => array('rmmagent stop'), + ), + 'name' => 'rmm_agent', + 'pidfile' => '/var/run/rmm_agent.pid', + ); + } + return $services; +} +INCEOF + +# Cache loeschen und configd neustarten +rm -f /var/lib/php/cache/_usr_local_opnsense_mvc_app_views_opnsense_rmmagent_*.php +service configd restart + +echo "" +echo "========================================" +echo " RMM Agent Plugin installiert!" +echo "" +echo " WebGUI: Services > RMM Agent" +echo " - Backend URL + API Key eintragen" +echo " - Aktiviert ankreuzen" +echo " - Speichern & Anwenden" +echo "========================================" diff --git a/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php b/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php index 7d5ac3c..6ec651a 100644 --- a/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php +++ b/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/ServiceController.php @@ -2,12 +2,47 @@ namespace OPNsense\RMMAgent\Api; -use OPNsense\Base\ApiMutableServiceControllerBase; +use OPNsense\Base\ApiControllerBase; -class ServiceController extends ApiMutableServiceControllerBase +class ServiceController extends ApiControllerBase { - protected static $internalServiceClass = '\OPNsense\RMMAgent\RMMAgent'; - protected static $internalServiceTemplate = 'OPNsense/RMMAgent/config.yaml'; - protected static $internalServiceEnabled = 'general.Enabled'; - protected static $internalServiceName = 'rmmagent'; + public function statusAction() + { + $backend = new \OPNsense\Core\Backend(); + $response = trim($backend->configdRun('rmmagent status')); + + if (strpos($response, 'is running') !== false) { + return array('status' => 'running', 'message' => $response); + } else { + return array('status' => 'stopped', 'message' => $response); + } + } + + public function startAction() + { + $backend = new \OPNsense\Core\Backend(); + $response = trim($backend->configdRun('rmmagent start')); + return array('status' => 'ok', 'message' => $response); + } + + public function stopAction() + { + $backend = new \OPNsense\Core\Backend(); + $response = trim($backend->configdRun('rmmagent stop')); + return array('status' => 'ok', 'message' => $response); + } + + public function restartAction() + { + $backend = new \OPNsense\Core\Backend(); + $response = trim($backend->configdRun('rmmagent restart')); + return array('status' => 'ok', 'message' => $response); + } + + public function reconfigureAction() + { + $backend = new \OPNsense\Core\Backend(); + $response = trim($backend->configdRun('rmmagent reconfigure')); + return array('status' => 'ok', 'message' => $response); + } } diff --git a/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php b/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php index 0fc8c37..7a6502f 100644 --- a/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php +++ b/opnsense-plugin/src/opnsense/mvc/app/controllers/OPNsense/RMMAgent/Api/SettingsController.php @@ -2,20 +2,54 @@ namespace OPNsense\RMMAgent\Api; -use OPNsense\Base\ApiMutableModelControllerBase; +use OPNsense\Base\ApiControllerBase; +use OPNsense\RMMAgent\RMMAgent; +use OPNsense\Core\Config; -class SettingsController extends ApiMutableModelControllerBase +class SettingsController extends ApiControllerBase { - protected static $internalModelName = 'rmmagent'; - protected static $internalModelClass = 'OPNsense\RMMAgent\RMMAgent'; - public function getAction() { - return $this->getBase('general', 'general'); + $mdl = new RMMAgent(); + $result = array('general' => array()); + + foreach ($mdl->general->iterateItems() as $key => $node) { + $result['general'][$key] = (string)$node; + } + + return $result; } public function setAction() { - return $this->setBase('general', 'general'); + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = new RMMAgent(); + + $post = $this->request->getPost('general'); + if (!empty($post)) { + foreach ($post as $key => $value) { + $node = $mdl->general->$key; + if ($node !== null) { + $node->setValue($value); + } + } + } + + $valMsgs = $mdl->performValidation(); + if ($valMsgs->count() > 0) { + $result['validations'] = array(); + foreach ($valMsgs as $msg) { + $result['validations']['general.' . $msg->getField()] = $msg->getMessage(); + } + } else { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'saved'; + } + } + + return $result; } } diff --git a/opnsense-plugin/src/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt b/opnsense-plugin/src/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt index eb5b0fa..0dfdd37 100644 --- a/opnsense-plugin/src/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt +++ b/opnsense-plugin/src/opnsense/mvc/app/views/OPNsense/RMMAgent/general.volt @@ -9,15 +9,47 @@ mapDataToFormUI(data_get_map).done(function () { formatTokenizersUI(); $('.selectpicker').selectpicker('refresh'); - updateServiceControlUI('rmmagent'); + }); + + // Service status abrufen + ajaxCall('/api/rmmagent/service/status', {}, function (data, status) { + if (data && data['status']) { + var badge = data['status'] === 'running' + ? 'Running' + : 'Stopped'; + $('#service_status_container').html(badge); + } }); $('#saveAct').unbind('click').click(function () { saveFormToEndpoint('/api/rmmagent/settings/set', 'frm_GeneralSettings', function () { $('#saveAct_progress').addClass('fa fa-spinner fa-pulse'); ajaxCall('/api/rmmagent/service/reconfigure', {}, function (data, status) { - updateServiceControlUI('rmmagent'); $('#saveAct_progress').removeClass('fa fa-spinner fa-pulse'); + // Status aktualisieren + ajaxCall('/api/rmmagent/service/status', {}, function (data, status) { + if (data && data['status']) { + var badge = data['status'] === 'running' + ? 'Running' + : 'Stopped'; + $('#service_status_container').html(badge); + } + }); + }); + }); + }); + + $('#restartAct').unbind('click').click(function () { + $('#restartAct_progress').addClass('fa fa-spinner fa-pulse'); + ajaxCall('/api/rmmagent/service/restart', {}, function (data, status) { + $('#restartAct_progress').removeClass('fa fa-spinner fa-pulse'); + ajaxCall('/api/rmmagent/service/status', {}, function (data, status) { + if (data && data['status']) { + var badge = data['status'] === 'running' + ? 'Running' + : 'Stopped'; + $('#service_status_container').html(badge); + } }); }); }); @@ -26,7 +58,7 @@
-

{{ lang._('RMM Agent Einstellungen') }}

+

{{ lang._('RMM Agent') }} ...

@@ -36,16 +68,10 @@

+
- -
-
-

{{ lang._('Dienst-Status') }}

-
-
- {{ partial("layout_partials/service_control", ['serviceName': 'rmmagent']) }} -
-