diff --git a/net/cloudflared/LICENSE b/net/cloudflared/LICENSE new file mode 100644 index 0000000000..7aef97141f --- /dev/null +++ b/net/cloudflared/LICENSE @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2026, Alan Martines + +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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. diff --git a/net/cloudflared/Makefile b/net/cloudflared/Makefile new file mode 100644 index 0000000000..5c7f11aa64 --- /dev/null +++ b/net/cloudflared/Makefile @@ -0,0 +1,8 @@ +PLUGIN_NAME= cloudflared +PLUGIN_VERSION= 0.1.0 +PLUGIN_REVISION= 1 +PLUGIN_COMMENT= Cloudflare Tunnel (cloudflared) integration +PLUGIN_MAINTAINER= alancpmartines@hotmail.com +PLUGIN_DEPENDS= fetch + +.include "../../Mk/plugins.mk" diff --git a/net/cloudflared/pkg-descr b/net/cloudflared/pkg-descr new file mode 100644 index 0000000000..99a12e8f1b --- /dev/null +++ b/net/cloudflared/pkg-descr @@ -0,0 +1,17 @@ +Cloudflare Tunnel (cloudflared) integration for OPNsense. + +Provides a native MVC interface to manage token-based Cloudflare Zero +Trust tunnels without opening firewall ports or requiring a static IP. +Follows Method 1: Token-based Setup using binaries from the kjake +FreeBSD fork of cloudflared. + +Features: +- Token-based tunnel authentication via Cloudflare Zero Trust +- Integrated binary installer with automatic FreeBSD version and + architecture detection +- QUIC kernel tuning (kern.ipc.maxsockbuf, net.inet.udp.recvspace) +- Post-quantum encryption support (--post-quantum) +- Real-time tunnel health status in the UI +- Appears in System: Diagnostics: Services + +WWW: https://github.com/AlanMartines/os-cloudflared diff --git a/net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc b/net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc new file mode 100644 index 0000000000..4a5de3d572 --- /dev/null +++ b/net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc @@ -0,0 +1,53 @@ + + * 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. + */ + +function cloudflared_enabled() +{ + $model = new \OPNsense\Cloudflared\Cloudflared(); + return (string)$model->general->enabled == '1'; +} + +function cloudflared_services() +{ + $services = []; + + if (cloudflared_enabled()) { + $services[] = [ + 'description' => gettext('Cloudflare Tunnel'), + 'configd' => [ + 'restart' => ['cloudflared restart'], + 'start' => ['cloudflared start'], + 'stop' => ['cloudflared stop'], + ], + 'name' => 'cloudflared', + 'pidfile' => '/var/run/cloudflared.pid', + ]; + } + + return $services; +} diff --git a/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/ServiceController.php b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/ServiceController.php new file mode 100644 index 0000000000..82220a87c5 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/ServiceController.php @@ -0,0 +1,56 @@ +request->isPost()) { + $backend = new Backend(); + $backend->configdRun("cloudflared reconfigure"); + return ['status' => 'ok']; + } + return ['status' => 'failed']; + } + + public function tunnelStatusAction() + { + $backend = new Backend(); + $response = $backend->configdRun("cloudflared tunnel_status"); + $data = json_decode(trim($response), true); + if ($data === null) { + return ['tunnel' => 'unknown']; + } + return $data; + } + + public function installAction() + { + if ($this->request->isPost()) { + $backend = new Backend(); + $response = $backend->configdRun("cloudflared install_binary"); + if ($response === null) { + return ['response' => 'ERROR: configd did not respond. Run "service configd restart" on OPNsense.']; + } + $response = trim($response); + if ($response === '' || $response === 'FAILED') { + return ['response' => 'ERROR: Action not found. Run "service configd restart" on OPNsense to reload actions.']; + } + return ['response' => $response]; + } + return ['response' => 'error']; + } +} diff --git a/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/SettingsController.php b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/SettingsController.php new file mode 100644 index 0000000000..1f9668ca1a --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/SettingsController.php @@ -0,0 +1,12 @@ + + * 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. + */ + +namespace OPNsense\Cloudflared; + +use OPNsense\Base\IndexController as BaseIndexController; + +class IndexController extends BaseIndexController +{ + public function indexAction() + { + $this->view->generalForm = $this->getForm("general"); + $this->view->pick('OPNsense/Cloudflared/index'); + } +} diff --git a/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/forms/general.xml b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/forms/general.xml new file mode 100644 index 0000000000..6625d4c0cf --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/forms/general.xml @@ -0,0 +1,38 @@ +
+ + Cloudflared.general.enabled + + checkbox + Enable Cloudflare Tunnel + + + Cloudflared.general.token + + password + The token for your Cloudflare Tunnel. Get it from one.dash.cloudflare.com > Access > Tunnels. + + + Cloudflared.general.no_autoupdate + + checkbox + Pass --no-autoupdate to cloudflared. Recommended when managing updates manually via the Install/Update Binary button. + + + Cloudflared.general.post_quantum + + checkbox + Pass --post-quantum to enable post-quantum cryptography for the tunnel connection. + + + Cloudflared.general.kern_ipc_maxsockbuf + + text + Recommended value for QUIC performance. Default is 16777216. + + + Cloudflared.general.net_inet_udp_recvspace + + text + Recommended value for QUIC performance. Default is 8388608. + +
diff --git a/net/cloudflared/src/opnsense/mvc/app/languages/en_US.po b/net/cloudflared/src/opnsense/mvc/app/languages/en_US.po new file mode 100644 index 0000000000..9c2029b08d --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/languages/en_US.po @@ -0,0 +1,93 @@ +msgid "" +msgstr "" +"Project-Id-Version: os-cloudflared\n" +"Language: en_US\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Menu & ACL +msgid "Cloudflare Tunnel" +msgstr "Cloudflare Tunnel" + +msgid "Settings" +msgstr "Settings" + +msgid "Allow access to Cloudflare Tunnel settings" +msgstr "Allow access to Cloudflare Tunnel settings" + +msgid "Allow access to Cloudflare Tunnel service status/control" +msgstr "Allow access to Cloudflare Tunnel service status/control" + +# General Form +msgid "General Settings" +msgstr "General Settings" + +msgid "Enable" +msgstr "Enable" + +msgid "Tunnel Token" +msgstr "Tunnel Token" + +msgid "Disable Auto-Update" +msgstr "Disable Auto-Update" + +msgid "Enable Post-Quantum Encryption" +msgstr "Enable Post-Quantum Encryption" + +msgid "Max Socket Buffer (kern.ipc.maxsockbuf)" +msgstr "Max Socket Buffer (kern.ipc.maxsockbuf)" + +msgid "UDP Recv Space (net.inet.udp.recvspace)" +msgstr "UDP Recv Space (net.inet.udp.recvspace)" + +msgid "Enable Cloudflare Tunnel" +msgstr "Enable Cloudflare Tunnel" + +msgid "The token for your Cloudflare Tunnel. Get it from one.dash.cloudflare.com > Access > Tunnels." +msgstr "The token for your Cloudflare Tunnel. Get it from one.dash.cloudflare.com > Access > Tunnels." + +msgid "Pass --no-autoupdate to cloudflared. Recommended when managing updates manually via the Install/Update Binary button." +msgstr "Pass --no-autoupdate to cloudflared. Recommended when managing updates manually via the Install/Update Binary button." + +msgid "Pass --post-quantum to enable post-quantum cryptography for the tunnel connection." +msgstr "Pass --post-quantum to enable post-quantum cryptography for the tunnel connection." + +msgid "Recommended value for QUIC performance. Default is 16777216." +msgstr "Recommended value for QUIC performance. Default is 16777216." + +msgid "Recommended value for QUIC performance. Default is 8388608." +msgstr "Recommended value for QUIC performance. Default is 8388608." + +# UI Buttons +msgid "Apply" +msgstr "Apply" + +msgid "Install/Update Binary" +msgstr "Install/Update Binary" + +msgid "Start" +msgstr "Start" + +msgid "Stop" +msgstr "Stop" + +msgid "Restart" +msgstr "Restart" + +msgid "Running" +msgstr "Running" + +msgid "Stopped" +msgstr "Stopped" + +msgid "No response from server. Check if configd is running." +msgstr "No response from server. Check if configd is running." + +msgid "Tunnel" +msgstr "Tunnel" + +msgid "Healthy" +msgstr "Healthy" + +msgid "Connecting" +msgstr "Connecting" diff --git a/net/cloudflared/src/opnsense/mvc/app/languages/pt_BR.po b/net/cloudflared/src/opnsense/mvc/app/languages/pt_BR.po new file mode 100644 index 0000000000..f4f4a06628 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/languages/pt_BR.po @@ -0,0 +1,93 @@ +msgid "" +msgstr "" +"Project-Id-Version: os-cloudflared\n" +"Language: pt_BR\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Menu & ACL +msgid "Cloudflare Tunnel" +msgstr "Cloudflare Tunnel" + +msgid "Settings" +msgstr "Configurações" + +msgid "Allow access to Cloudflare Tunnel settings" +msgstr "Permitir acesso às configurações do Cloudflare Tunnel" + +msgid "Allow access to Cloudflare Tunnel service status/control" +msgstr "Permitir acesso ao status/controle do serviço Cloudflare Tunnel" + +# General Form +msgid "General Settings" +msgstr "Configurações Gerais" + +msgid "Enable" +msgstr "Habilitar" + +msgid "Tunnel Token" +msgstr "Token do Túnel" + +msgid "Disable Auto-Update" +msgstr "Desativar Atualização Automática" + +msgid "Enable Post-Quantum Encryption" +msgstr "Habilitar Criptografia Pós-Quântica" + +msgid "Max Socket Buffer (kern.ipc.maxsockbuf)" +msgstr "Buffer Máximo de Socket (kern.ipc.maxsockbuf)" + +msgid "UDP Recv Space (net.inet.udp.recvspace)" +msgstr "Espaço de Recebimento UDP (net.inet.udp.recvspace)" + +msgid "Enable Cloudflare Tunnel" +msgstr "Habilitar Cloudflare Tunnel" + +msgid "The token for your Cloudflare Tunnel. Get it from one.dash.cloudflare.com > Access > Tunnels." +msgstr "O token do seu Cloudflare Tunnel. Obtenha em one.dash.cloudflare.com > Access > Tunnels." + +msgid "Pass --no-autoupdate to cloudflared. Recommended when managing updates manually via the Install/Update Binary button." +msgstr "Passa --no-autoupdate ao cloudflared. Recomendado quando as atualizações são gerenciadas manualmente pelo botão Instalar/Atualizar Binário." + +msgid "Pass --post-quantum to enable post-quantum cryptography for the tunnel connection." +msgstr "Passa --post-quantum para habilitar criptografia pós-quântica na conexão do túnel." + +msgid "Recommended value for QUIC performance. Default is 16777216." +msgstr "Valor recomendado para desempenho do QUIC. O padrão é 16777216." + +msgid "Recommended value for QUIC performance. Default is 8388608." +msgstr "Valor recomendado para desempenho do QUIC. O padrão é 8388608." + +# UI Buttons +msgid "Apply" +msgstr "Aplicar" + +msgid "Install/Update Binary" +msgstr "Instalar/Atualizar Binário" + +msgid "Start" +msgstr "Iniciar" + +msgid "Stop" +msgstr "Parar" + +msgid "Restart" +msgstr "Reiniciar" + +msgid "Running" +msgstr "Executando" + +msgid "Stopped" +msgstr "Parado" + +msgid "No response from server. Check if configd is running." +msgstr "Sem resposta do servidor. Verifique se o configd está em execução." + +msgid "Tunnel" +msgstr "Túnel" + +msgid "Healthy" +msgstr "Saudável" + +msgid "Connecting" +msgstr "Conectando" diff --git a/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/ACL/ACL.xml b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/ACL/ACL.xml new file mode 100644 index 0000000000..164256e3d6 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/ACL/ACL.xml @@ -0,0 +1,9 @@ + + + Services: Cloudflare Tunnel + + ui/cloudflared/* + api/cloudflared/* + + + diff --git a/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.php b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.php new file mode 100644 index 0000000000..b922e98d0c --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.php @@ -0,0 +1,9 @@ + + //OPNsense/Cloudflared + Cloudflare Tunnel + + + + 0 + + + Y + /^[a-zA-Z0-9\._\-]+$/ + + + 1 + + + 0 + + + 16777216 + + + 8388608 + + + + diff --git a/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Menu/Menu.xml b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Menu/Menu.xml new file mode 100644 index 0000000000..10abb8ff8d --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Menu/Menu.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt b/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt new file mode 100644 index 0000000000..053bed02fd --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt @@ -0,0 +1,178 @@ +{# + # Copyright (C) 2026 Alan Martines + # All rights reserved. + #} + + + + + +
+
+
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general'])}} +
+
+ + +
+
+
+
+ +
+ + + + + +
+ {{ lang._('Cloudflare Tunnel') }} +   + + + + + + +
+
diff --git a/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/install_binary.sh b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/install_binary.sh new file mode 100644 index 0000000000..63956bfca2 --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/install_binary.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +log_msg() { + logger -t cloudflared-install "$1" + echo "$1" +} + +DEST="/usr/local/bin/cloudflared" +GITHUB_API="https://api.github.com/repos/kjake/cloudflared/releases/latest" +API_CACHE="/tmp/cloudflared_release.json" + +OS_VERSION=$(uname -r | cut -d'.' -f1) +ARCH=$(uname -m) + +log_msg "Detected FreeBSD ${OS_VERSION} / ${ARCH}" + +# Fetch latest release metadata +log_msg "Fetching latest release version from GitHub..." +fetch -o "${API_CACHE}" "${GITHUB_API}" > /dev/null 2>&1 + +if [ ! -s "${API_CACHE}" ]; then + log_msg "ERROR: Could not reach GitHub API. Check internet connectivity." + exit 1 +fi + +# Parse tag_name using Python (reliable on single-line JSON) +LATEST_TAG=$(python3 -c "import json,sys; print(json.load(open('${API_CACHE}'))['tag_name'])" 2>/dev/null) + +if [ -z "${LATEST_TAG}" ]; then + log_msg "ERROR: Could not parse release version from GitHub API response." + exit 1 +fi + +rm -f "${API_CACHE}" +log_msg "Latest version: ${LATEST_TAG}" + +BINARY_NAME="cloudflared-freebsd${OS_VERSION}-${ARCH}" +BINARY_URL="https://github.com/kjake/cloudflared/releases/download/${LATEST_TAG}/${BINARY_NAME}" + +log_msg "Downloading ${BINARY_NAME}..." +fetch -o "${DEST}" "${BINARY_URL}" > /dev/null 2>&1 + +if [ $? -eq 0 ] && [ -s "${DEST}" ]; then + chmod +x "${DEST}" + log_msg "Installation of cloudflared ${LATEST_TAG} successful." +else + rm -f "${DEST}" + log_msg "ERROR: Download failed. Binary may not exist for FreeBSD ${OS_VERSION} / ${ARCH}." + log_msg "URL attempted: ${BINARY_URL}" + exit 1 +fi diff --git a/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh new file mode 100644 index 0000000000..37e0a3e387 --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +log_msg() { + logger -t cloudflared-reconfigure "$1" + echo "$1" +} + +log_msg "Starting cloudflared reconfiguration..." + +# Dados estáticos: criar diretórios apenas se não existirem +if [ ! -d /usr/local/etc/cloudflared ]; then + mkdir -p /usr/local/etc/cloudflared + chmod 750 /usr/local/etc/cloudflared + log_msg "Created /usr/local/etc/cloudflared" +fi + +if [ ! -d /usr/local/etc/sysctl.conf.d ]; then + mkdir -p /usr/local/etc/sysctl.conf.d + log_msg "Created /usr/local/etc/sysctl.conf.d" +fi + +# Dados dinâmicos: recarregar templates (rc.conf.d e token sempre atualizados) +log_msg "Reloading configuration templates..." +/usr/local/sbin/configctl template reload OPNsense/Cloudflared + +# Proteger token: chmod 600 (nunca deve ser world-readable) +if [ -f /usr/local/etc/cloudflared/token ]; then + chmod 600 /usr/local/etc/cloudflared/token + log_msg "Token file permissions set to 600." +fi + +# Apply sysctl tunables se existirem +if [ -f /usr/local/etc/sysctl.conf.d/cloudflared.conf ]; then + log_msg "Applying sysctl tunables..." + while IFS= read -r line; do + case "$line" in + \#*|'') continue ;; + esac + key=$(echo "$line" | cut -d'=' -f1) + val=$(echo "$line" | cut -d'=' -f2-) + if sysctl -w "${key}=${val}" > /dev/null 2>&1; then + log_msg "sysctl ${key}=${val} applied." + else + log_msg "WARNING: sysctl ${key}=${val} failed (may require reboot)." + fi + done < /usr/local/etc/sysctl.conf.d/cloudflared.conf +fi + +# Verificar se o binário existe antes de tentar iniciar +if [ ! -x /usr/local/bin/cloudflared ]; then + log_msg "WARNING: /usr/local/bin/cloudflared not found. Use 'Install/Update Binary' first." + exit 0 +fi + +# Reiniciar serviço +log_msg "Restarting cloudflared service..." +service cloudflared restart + +log_msg "Reconfiguration complete." diff --git a/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/tunnel_status.sh b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/tunnel_status.sh new file mode 100644 index 0000000000..9c5cea5e95 --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/tunnel_status.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# Verifica se o processo está rodando via PID file +if [ ! -f /var/run/cloudflared.pid ]; then + echo '{"tunnel":"stopped"}' + exit 0 +fi + +PID=$(cat /var/run/cloudflared.pid 2>/dev/null) +if [ -z "${PID}" ] || ! kill -0 "${PID}" 2>/dev/null; then + echo '{"tunnel":"stopped"}' + exit 0 +fi + +# Consulta o health check local do cloudflared (padrão: localhost:2000) +# HTTP 200 = tunnel conectado ao Cloudflare +# HTTP 503 / falha = processo rodando mas ainda conectando +fetch -T 3 -qo /dev/null "http://localhost:2000/healthcheck" 2>/dev/null +if [ $? -eq 0 ]; then + echo '{"tunnel":"healthy"}' +else + echo '{"tunnel":"connecting"}' +fi diff --git a/net/cloudflared/src/opnsense/service/conf/actions.d/actions_cloudflared.conf b/net/cloudflared/src/opnsense/service/conf/actions.d/actions_cloudflared.conf new file mode 100644 index 0000000000..b64996182f --- /dev/null +++ b/net/cloudflared/src/opnsense/service/conf/actions.d/actions_cloudflared.conf @@ -0,0 +1,41 @@ +[start] +command:/usr/local/etc/rc.d/cloudflared start +parameters: +type:script +message:Starting Cloudflare Tunnel + +[stop] +command:/usr/local/etc/rc.d/cloudflared stop +parameters: +type:script +message:Stopping Cloudflare Tunnel + +[restart] +command:/usr/local/etc/rc.d/cloudflared restart +parameters: +type:script +message:Restarting Cloudflare Tunnel + +[status] +command:/usr/local/etc/rc.d/cloudflared status;exit 0 +parameters: +type:script_output +message:Probing Cloudflare Tunnel + +[reconfigure] +command:/bin/sh /usr/local/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh +parameters: +type:script +message:Reconfiguring Cloudflare Tunnel + +[install_binary] +command:/bin/sh /usr/local/opnsense/scripts/OPNsense/Cloudflared/install_binary.sh;exit 0 +parameters: +type:script_output +message:Installing Cloudflare Tunnel binary + +[tunnel_status] +command:/bin/sh /usr/local/opnsense/scripts/OPNsense/Cloudflared/tunnel_status.sh;exit 0 +parameters: +type:script_output +message:Checking Cloudflare Tunnel status diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/+TARGETS b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/+TARGETS new file mode 100644 index 0000000000..894e65d9f0 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/+TARGETS @@ -0,0 +1,3 @@ +rc.conf.d:/etc/rc.conf.d/cloudflared +cloudflared.token:/usr/local/etc/cloudflared/token +tunables.conf:/usr/local/etc/sysctl.conf.d/cloudflared.conf diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/cloudflared.token b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/cloudflared.token new file mode 100644 index 0000000000..8b8dc38f5a --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/cloudflared.token @@ -0,0 +1 @@ +{% if helpers.exists('OPNsense.Cloudflared.general.token') %}{{ OPNsense.Cloudflared.general.token|trim }}{% endif %} diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/rc.conf.d b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/rc.conf.d new file mode 100644 index 0000000000..3de87ac7f3 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/rc.conf.d @@ -0,0 +1,9 @@ +# +# This file is automatically generated. Do not manually edit this file. +# +{% if helpers.exists('OPNsense.Cloudflared.general.enabled') and OPNsense.Cloudflared.general.enabled == '1' %} +cloudflared_enable="YES" +{% else %} +cloudflared_enable="NO" +{% endif %} +cloudflared_mode="tunnel{% if helpers.exists('OPNsense.Cloudflared.general.no_autoupdate') and OPNsense.Cloudflared.general.no_autoupdate == '1' %} --no-autoupdate{% endif %} run{% if helpers.exists('OPNsense.Cloudflared.general.post_quantum') and OPNsense.Cloudflared.general.post_quantum == '1' %} --post-quantum{% endif %}" diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/tunables.conf b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/tunables.conf new file mode 100644 index 0000000000..71ab483005 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/tunables.conf @@ -0,0 +1,6 @@ +# +# This file is automatically generated. Do not manually edit this file. +# +kern.ipc.maxsockbuf={% if helpers.exists('OPNsense.Cloudflared.general.kern_ipc_maxsockbuf') %}{{ OPNsense.Cloudflared.general.kern_ipc_maxsockbuf }}{% else %}16777216{% endif %} + +net.inet.udp.recvspace={% if helpers.exists('OPNsense.Cloudflared.general.net_inet_udp_recvspace') %}{{ OPNsense.Cloudflared.general.net_inet_udp_recvspace }}{% else %}8388608{% endif %} diff --git a/net/cloudflared/src/usr/local/etc/cloudflared/.keep b/net/cloudflared/src/usr/local/etc/cloudflared/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/net/cloudflared/src/usr/local/etc/rc.d/cloudflared b/net/cloudflared/src/usr/local/etc/rc.d/cloudflared new file mode 100644 index 0000000000..41ba963fce --- /dev/null +++ b/net/cloudflared/src/usr/local/etc/rc.d/cloudflared @@ -0,0 +1,32 @@ +#!/bin/sh + +# PROVIDE: cloudflared +# REQUIRE: NETWORKING SERVERS +# KEYWORD: shutdown + +. /etc/rc.subr + +name="cloudflared" +rcvar="cloudflared_enable" +logfile="/var/log/cloudflared.log" +pidfile="/var/run/cloudflared.pid" +procname="/usr/local/bin/cloudflared" + +load_rc_config $name + +: ${cloudflared_enable:="NO"} +: ${cloudflared_mode:="tunnel"} + +# Load token from secure file +# --metrics é flag global do cloudflared: deve vir antes do subcomando tunnel +if [ -f /usr/local/etc/cloudflared/token ]; then + token=$(cat /usr/local/etc/cloudflared/token) + command_args="--metrics localhost:2000 ${cloudflared_mode} --token ${token}" +else + command_args="--metrics localhost:2000 ${cloudflared_mode}" +fi + +command="/usr/sbin/daemon" +command_args="-o ${logfile} -p ${pidfile} -f ${procname} ${command_args}" + +run_rc_command "$1" diff --git a/net/cloudflared/src/usr/local/etc/sysctl.conf.d/.keep b/net/cloudflared/src/usr/local/etc/sysctl.conf.d/.keep new file mode 100644 index 0000000000..e69de29bb2