#!/bin/sh # # RMM Agent Plugin Installer fuer OPNsense # # Usage (automatischer Download): # fetch -o /tmp/install.sh "https://git.cynfo.net/christian/rmm/raw/branch/main/opnsense-plugin/install.sh" # sh /tmp/install.sh # # Oder mit lokalen Binaries: # scp install.sh rmm-agent rmm-updater-freebsd root@opnsense:/tmp/ # ssh root@opnsense '/bin/sh /tmp/install.sh' # set -e BASEDIR="/tmp" BINARY="${BASEDIR}/rmm-agent" UPDATER_BINARY="${BASEDIR}/rmm-updater" BACKEND_URL="${RMM_BACKEND:-https://your-backend:8443}" API_KEY="${RMM_API_KEY:-YOUR_API_KEY}" echo "========================================" echo " RMM Agent Plugin Installer" echo "========================================" # Binaries herunterladen wenn nicht lokal vorhanden if [ ! -f "${BINARY}" ]; then echo "[*] Binaries nicht lokal gefunden — lade vom Backend..." /usr/bin/fetch --no-verify-peer --no-verify-hostname -o "${BASEDIR}/rmm-bundle.zip" \ "${BACKEND_URL}/api/v1/firmware/download?platform=freebsd&api_key=${API_KEY}" if [ ! -f "${BASEDIR}/rmm-bundle.zip" ]; then echo "FEHLER: Download fehlgeschlagen." echo "Pruefe Backend-Erreichbarkeit: ${BACKEND_URL}" exit 1 fi cd "${BASEDIR}" unzip -o rmm-bundle.zip 2>/dev/null || true # ZIP enthaelt rmm-agent und rmm-updater-freebsd if [ -f "${BASEDIR}/rmm-updater-freebsd" ] && [ ! -f "${UPDATER_BINARY}" ]; then mv "${BASEDIR}/rmm-updater-freebsd" "${UPDATER_BINARY}" fi rm -f rmm-bundle.zip echo " Download OK" if [ ! -f "${BINARY}" ]; then echo "FEHLER: ${BINARY} nach Download nicht vorhanden." echo "Firmware-ZIP muss 'rmm-agent' enthalten." exit 1 fi fi # Alten Agent stoppen falls vorhanden echo "[1/8] Alte Dienste stoppen..." /usr/local/etc/rc.d/rmm_updater stop 2>/dev/null || true /usr/local/etc/rc.d/rmm_agent stop 2>/dev/null || true pkill -9 -f rmm-agent 2>/dev/null || true pkill -9 -f rmm-updater 2>/dev/null || true # Binaries installieren echo "[2/8] Binaries installieren..." mkdir -p /usr/local/rmm cp "${BINARY}" /usr/local/rmm/rmm-agent chmod +x /usr/local/rmm/rmm-agent if [ -f "${UPDATER_BINARY}" ]; then cp "${UPDATER_BINARY}" /usr/local/rmm/rmm-updater chmod +x /usr/local/rmm/rmm-updater echo " Updater installiert" else echo " Updater-Binary nicht gefunden — uebersprungen" fi # rc.d Script echo "[3/8] 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 # Updater rc.d Service cat > /usr/local/etc/rc.d/rmm_updater << 'RCEOF2' #!/bin/sh # # PROVIDE: rmm_updater # REQUIRE: NETWORKING rmm_agent # KEYWORD: shutdown . /etc/rc.subr name="rmm_updater" rcvar="${name}_enable" command="/usr/local/rmm/rmm-updater" pidfile="/var/run/${name}.pid" logfile="/var/log/rmm-updater.log" start_cmd="${name}_start" stop_cmd="${name}_stop" status_cmd="${name}_status" rmm_updater_start() { if [ ! -f "${command}" ]; then echo "rmm-updater binary not found: ${command}" return 1 fi if [ -f "$pidfile" ] && kill -0 $(cat "$pidfile") 2>/dev/null; then echo "${name} already running" return 0 fi echo "Starting ${name}." /usr/sbin/daemon -f -p "$pidfile" -o "$logfile" "$command" } rmm_updater_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_updater_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 } load_rc_config $name : ${rmm_updater_enable:="NO"} run_rc_command "$1" RCEOF2 chmod +x /usr/local/etc/rc.d/rmm_updater # MVC Plugin installieren echo "[4/8] 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('general' => array())); foreach ($mdl->general->iterateItems() as $key => $node) { $result['rmmagent']['general'][$key] = (string)$node; } return $result; } public function setAction() { $result = array('result' => 'failed'); if ($this->request->isPost()) { $mdl = new RMMAgent(); $post = null; // OPNsense sends data as rmmagent[general][KEY] $rmmagent = $this->request->getPost('rmmagent'); if (!empty($rmmagent) && isset($rmmagent['general'])) { $post = $rmmagent['general']; } // Fallback: JSON body if (empty($post)) { $rawBody = $this->request->getRawBody(); if (!empty($rawBody)) { $jsonData = json_decode($rawBody, true); if (isset($jsonData['rmmagent']['general'])) { $post = $jsonData['rmmagent']['general']; } elseif (isset($jsonData['general'])) { $post = $jsonData['general']; } } } if (!empty($post)) { foreach ($post as $key => $value) { try { $node = $mdl->general->$key; if ($node !== null) { $node->setValue($value); } } catch (\Exception $e) { // skip } } } $valMsgs = $mdl->performValidation(); if ($valMsgs->count() > 0) { $result['validations'] = array(); foreach ($valMsgs as $msg) { $result['validations']['rmmagent.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://your-backend: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/8] 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/8] 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/8] 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 # Initialen config.xml Eintrag erstellen (falls noch nicht vorhanden) echo " Initialen config.xml Eintrag pruefen..." cat > /tmp/rmm_init_config.php << 'INITEOF' object(); if (!isset($xml->OPNsense->rmmagent)) { $rmm = $xml->OPNsense->addChild('rmmagent'); $gen = $rmm->addChild('general'); $gen->addChild('Enabled', '0'); $gen->addChild('BackendUrl', ''); $gen->addChild('ApiKey', ''); $gen->addChild('AgentName', ''); $gen->addChild('Interval', '60'); $gen->addChild('Insecure', '1'); $config->save(); echo " Initialer rmmagent Eintrag in config.xml erstellt\n"; } else { echo " rmmagent Eintrag bereits vorhanden\n"; } INITEOF php /tmp/rmm_init_config.php rm -f /tmp/rmm_init_config.php # Cache loeschen und configd neustarten rm -f /var/lib/php/cache/_usr_local_opnsense_mvc_app_views_opnsense_rmmagent_*.php service configd restart # Updater aktivieren echo "[8/8] Updater aktivieren..." if [ -f /usr/local/rmm/rmm-updater ]; then sysrc rmm_updater_enable=YES 2>/dev/null || true /usr/local/etc/rc.d/rmm_updater start 2>/dev/null || true echo " Updater aktiviert und gestartet" else echo " Kein Updater-Binary — uebersprungen" fi 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 "========================================"