From 3a6cb1c622ca73a1013131c1bcd19f29c4444a83 Mon Sep 17 00:00:00 2001 From: Richard Aspden Date: Tue, 12 May 2026 15:52:30 +0100 Subject: [PATCH 1/2] net/cloudflared: new plugin for Cloudflare Tunnel integration Wraps the net/cloudflared FreeBSD port to manage the cloudflared daemon via the standard OPNsense service control interface. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 1 + net/cloudflared/Makefile | 8 ++ net/cloudflared/pkg-descr | 7 ++ .../src/etc/inc/plugins.inc.d/cloudflared.inc | 89 +++++++++++++++ .../Cloudflared/Api/ServiceController.php | 49 +++++++++ .../Cloudflared/Api/SettingsController.php | 51 +++++++++ .../OPNsense/Cloudflared/IndexController.php | 38 +++++++ .../OPNsense/Cloudflared/forms/general.xml | 42 +++++++ .../models/OPNsense/Cloudflared/ACL/ACL.xml | 16 +++ .../OPNsense/Cloudflared/Cloudflared.php | 35 ++++++ .../OPNsense/Cloudflared/Cloudflared.xml | 46 ++++++++ .../models/OPNsense/Cloudflared/Menu/Menu.xml | 8 ++ .../app/views/OPNsense/Cloudflared/index.volt | 85 ++++++++++++++ .../OPNsense/Cloudflared/reconfigure.sh | 34 ++++++ .../scripts/OPNsense/Cloudflared/recover.sh | 38 +++++++ .../scripts/syslog/logformats/cloudflared.py | 104 ++++++++++++++++++ .../conf/actions.d/actions_cloudflared.conf | 37 +++++++ .../templates/OPNsense/Cloudflared/+TARGETS | 2 + .../templates/OPNsense/Cloudflared/config.yml | 14 +++ .../templates/OPNsense/Cloudflared/rc.conf.d | 10 ++ .../OPNsense/Syslog/local/cloudflared.conf | 6 + 21 files changed, 720 insertions(+) create mode 100644 net/cloudflared/Makefile create mode 100644 net/cloudflared/pkg-descr create mode 100644 net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc create mode 100644 net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/ServiceController.php create mode 100644 net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/SettingsController.php create mode 100644 net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/IndexController.php create mode 100644 net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/forms/general.xml create mode 100644 net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/ACL/ACL.xml create mode 100644 net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.php create mode 100644 net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.xml create mode 100644 net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Menu/Menu.xml create mode 100644 net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt create mode 100755 net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh create mode 100755 net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh create mode 100644 net/cloudflared/src/opnsense/scripts/syslog/logformats/cloudflared.py create mode 100644 net/cloudflared/src/opnsense/service/conf/actions.d/actions_cloudflared.conf create mode 100644 net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/+TARGETS create mode 100644 net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/config.yml create mode 100644 net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/rc.conf.d create mode 100644 net/cloudflared/src/opnsense/service/templates/OPNsense/Syslog/local/cloudflared.conf diff --git a/README.md b/README.md index fa5c613514..c3643cb7ae 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ misc/theme-rebellion -- A suitably dark theme misc/theme-tukan -- The tukan theme - blue/white misc/theme-vicuna -- The vicuna theme - blue sapphire net/chrony -- Chrony time synchronisation +net/cloudflared -- Cloudflare Tunnel Service net/freeradius -- RADIUS Authentication, Authorization and Accounting Server net/frr -- The FRRouting Protocol Suite net/ftp-proxy -- Control ftp-proxy processes diff --git a/net/cloudflared/Makefile b/net/cloudflared/Makefile new file mode 100644 index 0000000000..d0b81f2bb2 --- /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 integration +PLUGIN_MAINTAINER= rick+github@insanityinside.net +PLUGIN_DEPENDS= cloudflared + +.include "../../Mk/plugins.mk" diff --git a/net/cloudflared/pkg-descr b/net/cloudflared/pkg-descr new file mode 100644 index 0000000000..89293ce14a --- /dev/null +++ b/net/cloudflared/pkg-descr @@ -0,0 +1,7 @@ +Integrates the Cloudflare Tunnel daemon (cloudflared) into OPNsense, +allowing self-hosted services to be exposed through Cloudflare Zero Trust +without requiring inbound firewall rules or a public IP address. + +WWW: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ + +Inspired by original work by Alan Martines . 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..d3f5c449ca --- /dev/null +++ b/net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc @@ -0,0 +1,89 @@ + + * 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() +{ + return (string)(new \OPNsense\Cloudflared\Cloudflared())->general->enabled == '1'; +} + +function cloudflared_configure() +{ + return [ + 'newwanip' => ['cloudflared_configure_newwanip'], + ]; +} + +function cloudflared_configure_newwanip($verbose = false) +{ + if (!cloudflared_enabled()) { + return; + } + if (file_exists('/var/run/cloudflared.pid')) { + return; + } + (new \OPNsense\Core\Backend())->configdRun('cloudflared start'); +} + +function cloudflared_services() +{ + global $config; + + $services = []; + + if ( + isset($config['OPNsense']['Cloudflared']['general']['enabled']) && + $config['OPNsense']['Cloudflared']['general']['enabled'] == '1' + ) { + $services[] = [ + 'description' => gettext('Cloudflare Tunnel'), + 'configd' => [ + 'start' => ['cloudflared start'], + 'restart' => ['cloudflared restart'], + 'stop' => ['cloudflared stop'], + ], + 'name' => 'cloudflared', + 'pidfile' => '/var/run/cloudflared.pid', + ]; + } + + return $services; +} + +function cloudflared_xmlrpc_sync() +{ + $result = []; + + $result[] = [ + 'description' => gettext('Cloudflare Tunnel'), + 'section' => 'OPNsense.Cloudflared', + 'id' => 'cloudflared', + 'services' => ['cloudflared'], + ]; + + return $result; +} 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..c51aa44160 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/ServiceController.php @@ -0,0 +1,49 @@ + + * 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\Api; + +use OPNsense\Base\ApiMutableServiceControllerBase; +use OPNsense\Core\Backend; + +class ServiceController extends ApiMutableServiceControllerBase +{ + protected static $internalServiceClass = '\OPNsense\Cloudflared\Cloudflared'; + protected static $internalServiceTemplate = 'OPNsense/Cloudflared'; + protected static $internalServiceEnabled = 'general.enabled'; + protected static $internalServiceName = 'cloudflared'; + + public function reconfigureAction() + { + if ($this->request->isPost()) { + (new Backend())->configdRun('cloudflared reconfigure'); + return ['status' => 'ok']; + } + return ['status' => 'failed']; + } +} 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..7cd7a48fba --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/Api/SettingsController.php @@ -0,0 +1,51 @@ + + * 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\Api; + +use OPNsense\Base\ApiMutableModelControllerBase; + +class SettingsController extends ApiMutableModelControllerBase +{ + protected static $internalModelClass = '\OPNsense\Cloudflared\Cloudflared'; + protected static $internalModelName = 'cloudflared'; + + public function sysctlCheckAction() + { + $checks = [ + 'kern.ipc.maxsockbuf' => 16777216, + 'net.inet.udp.recvspace' => 8388608, + ]; + $result = []; + foreach ($checks as $key => $minimum) { + $value = intval(trim(shell_exec('sysctl -n ' . escapeshellarg($key) . ' 2>/dev/null'))); + $result[$key] = ['value' => $value, 'minimum' => $minimum, 'ok' => $value >= $minimum]; + } + return $result; + } +} diff --git a/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/IndexController.php b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/IndexController.php new file mode 100644 index 0000000000..802d32968c --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/IndexController.php @@ -0,0 +1,38 @@ + + * 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; + +class IndexController extends \OPNsense\Base\IndexController +{ + public function indexAction() + { + $this->view->general = $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..3c3346279f --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/controllers/OPNsense/Cloudflared/forms/general.xml @@ -0,0 +1,42 @@ +
+ + header + + + + cloudflared.general.enabled + + checkbox + Enable the Cloudflare Tunnel service. + + + cloudflared.general.token + + text + Tunnel token from the Cloudflare Zero Trust dashboard (Networks > Tunnels). The token is stored in a root-readable file and passed to cloudflared via an environment variable. + + + cloudflared.general.protocol + + dropdown + Transport protocol. Leave on auto unless you have a specific reason to force QUIC or HTTP/2. + + + cloudflared.general.post_quantum + + checkbox + Enable post-quantum cryptography for the tunnel connection (requires QUIC). + + + cloudflared.general.quic_disable_pmtu_discovery + + checkbox + Disable Path MTU Discovery for QUIC connections. May help in environments where ICMP is blocked. + + + cloudflared.general.log_level + + dropdown + Verbosity of cloudflared logging. Defaults to info. Use debug for troubleshooting; warn or error for quieter operation. + +
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..a2c9f369e5 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/ACL/ACL.xml @@ -0,0 +1,16 @@ + + + Services: Cloudflare Tunnel + + ui/cloudflared/* + api/cloudflared/* + + + + Services: Cloudflare Tunnel: Log File + + ui/diagnostics/log/core/cloudflared/* + api/diagnostics/log/core/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..ebb137c715 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.php @@ -0,0 +1,35 @@ + + * 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\BaseModel; + +class Cloudflared extends BaseModel +{ +} diff --git a/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.xml b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.xml new file mode 100644 index 0000000000..c241c4887c --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Cloudflared.xml @@ -0,0 +1,46 @@ + + //OPNsense/Cloudflared + 1.0.0 + Cloudflare Tunnel settings + + + + 0 + Y + + + Y + /^[a-zA-Z0-9\._\-]+$/ + Token may only contain letters, digits, dots, underscores and hyphens. + + + auto + Y + + auto + quic + http2 + + + + 0 + Y + + + 0 + Y + + + info + Y + + info + debug + warn + error + fatal + + + + + 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..0eba648e6a --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/models/OPNsense/Cloudflared/Menu/Menu.xml @@ -0,0 +1,8 @@ + + + + + + + + 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..07439c1406 --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt @@ -0,0 +1,85 @@ +{# + # Copyright (C) 2026 Richard Aspden + # 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. + #} + + + +
+ {{ partial('layout_partials/base_form', ['fields': general, 'id': 'frm_GeneralSettings']) }} + + +
+ +{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/cloudflared/service/reconfigure', 'data_service_widget': 'cloudflared'}) }} 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 100755 index 0000000000..832bf5f989 --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# Copyright (C) 2026 Richard Aspden +# 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. + +configctl template reload OPNsense/Cloudflared +chmod 600 /etc/rc.conf.d/cloudflared + +if grep -q 'cloudflared_enable="YES"' /etc/rc.conf.d/cloudflared 2>/dev/null; then + service cloudflared restart +else + service cloudflared stop 2>/dev/null || true +fi diff --git a/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh new file mode 100755 index 0000000000..e6eacbedea --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Copyright (C) 2026 Richard Aspden +# 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. + +# Start cloudflared only if it is enabled but not currently running. +# Used by the cron recovery job so healthy tunnels are not disrupted. + +if ! grep -q 'cloudflared_enable="YES"' /etc/rc.conf.d/cloudflared 2>/dev/null; then + exit 0 +fi + +if service cloudflared status > /dev/null 2>&1; then + exit 0 +fi + +service cloudflared start diff --git a/net/cloudflared/src/opnsense/scripts/syslog/logformats/cloudflared.py b/net/cloudflared/src/opnsense/scripts/syslog/logformats/cloudflared.py new file mode 100644 index 0000000000..ccbf2ac277 --- /dev/null +++ b/net/cloudflared/src/opnsense/scripts/syslog/logformats/cloudflared.py @@ -0,0 +1,104 @@ +""" +Copyright (C) 2026 Richard Aspden +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. +""" + +import re +from . import NewBaseLogFormat + +# zerolog 3-letter level codes → syslog numeric severity +_SEVERITY = { + 'DBG': 7, + 'INF': 6, + 'WRN': 4, + 'ERR': 3, + 'FTL': 2, + 'PNC': 1, +} + +# zerolog: 2026-05-12T11:51:06Z INF message +_RE_ZEROLOG = re.compile( + r'^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2}))\s+([A-Z]{3})\s+(.*)' +) + +# Go stdlib log (e.g. quic-go): 2026/05/12 13:35:44 message — no level field +_RE_GOSTDLIB = re.compile( + r'^(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2})\s+(.*)' +) + + +class CloudflaredLogFormat(NewBaseLogFormat): + def __init__(self, filename): + super().__init__(filename) + self._timestamp = None + self._severity = None + self._message = None + + def match(self, line): + if 'cloudflared' not in self._filename: + return False + return bool(_RE_ZEROLOG.match(line) or _RE_GOSTDLIB.match(line)) + + def set_line(self, line): + m = _RE_ZEROLOG.match(line) + if m: + ts_raw = m.group(1) + # Normalise Z suffix so fromisoformat() accepts it (Python < 3.11) + self._timestamp = ts_raw.replace('Z', '+00:00') + self._severity = _SEVERITY.get(m.group(2), 6) + self._message = m.group(3) + return + + m = _RE_GOSTDLIB.match(line) + if m: + # Go stdlib format has no level; default to Informational + self._timestamp = m.group(1).replace('/', '-', 2).replace(' ', 'T') + self._severity = 6 + self._message = m.group(2) + return + + self._timestamp = None + self._severity = 6 + self._message = line + + @property + def timestamp(self): + return self._timestamp + + @property + def process_name(self): + # zerolog format has no process field; hardcode so the column isn't blank + return 'cloudflared' + + @property + def pid(self): + return None + + @property + def severity(self): + return self._severity + + @property + def line(self): + return self._message 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..676c6a4307 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/conf/actions.d/actions_cloudflared.conf @@ -0,0 +1,37 @@ +[start] +command:service cloudflared start +parameters: +type:script +message:Starting Cloudflare Tunnel service + +[stop] +command:service cloudflared stop +parameters: +type:script +message:Stopping Cloudflare Tunnel service + +[restart] +command:service cloudflared restart +parameters: +type:script +message:Restarting Cloudflare Tunnel service +description:Restart Cloudflare Tunnel + +[status] +command:service cloudflared status; exit 0 +parameters: +type:script_output +message:Requesting Cloudflare Tunnel status + +[recover] +command:/usr/local/opnsense/scripts/OPNsense/Cloudflared/recover.sh +parameters: +type:script +message:Recovering Cloudflare Tunnel +description:Recover Cloudflare Tunnel (start if not running) + +[reconfigure] +command:/usr/local/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh +parameters: +type:script +message:Reconfiguring Cloudflare Tunnel 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..e06a2b07d1 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/+TARGETS @@ -0,0 +1,2 @@ +rc.conf.d:/etc/rc.conf.d/cloudflared +config.yml:/usr/local/etc/cloudflared/config.yml diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/config.yml b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/config.yml new file mode 100644 index 0000000000..7fdd94e3f5 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/config.yml @@ -0,0 +1,14 @@ +metrics: localhost:2000 +no-autoupdate: true +{% if helpers.exists('OPNsense.Cloudflared.general.post_quantum') and OPNsense.Cloudflared.general.post_quantum == '1' %} +post-quantum: true +{% endif %} +{% if helpers.exists('OPNsense.Cloudflared.general.quic_disable_pmtu_discovery') and OPNsense.Cloudflared.general.quic_disable_pmtu_discovery == '1' %} +quic-disable-pmtu-discovery: true +{% endif %} +{% if helpers.exists('OPNsense.Cloudflared.general.protocol') and OPNsense.Cloudflared.general.protocol != 'auto' %} +protocol: {{ OPNsense.Cloudflared.general.protocol }} +{% endif %} +{% if helpers.exists('OPNsense.Cloudflared.general.log_level') and OPNsense.Cloudflared.general.log_level != 'info' %} +loglevel: {{ OPNsense.Cloudflared.general.log_level }} +{% 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..e9a51de11d --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Cloudflared/rc.conf.d @@ -0,0 +1,10 @@ +{% if helpers.exists('OPNsense.Cloudflared.general.enabled') and OPNsense.Cloudflared.general.enabled == '1' %} +cloudflared_enable="YES" +{% else %} +cloudflared_enable="NO" +{% endif %} +cloudflared_conf="/usr/local/etc/cloudflared/config.yml" +{% if helpers.exists('OPNsense.Cloudflared.general.token') %} +cloudflared_env="TUNNEL_TOKEN={{ OPNsense.Cloudflared.general.token|trim }}" +{% endif %} +cloudflared_mode_options="run" diff --git a/net/cloudflared/src/opnsense/service/templates/OPNsense/Syslog/local/cloudflared.conf b/net/cloudflared/src/opnsense/service/templates/OPNsense/Syslog/local/cloudflared.conf new file mode 100644 index 0000000000..4e199de513 --- /dev/null +++ b/net/cloudflared/src/opnsense/service/templates/OPNsense/Syslog/local/cloudflared.conf @@ -0,0 +1,6 @@ +################################################################### +# Local syslog-ng configuration [cloudflared]. +################################################################### +filter f_local_cloudflared { + program("cloudflared"); +}; From 4a94664279b3a2433127babe06f656a7432badbe Mon Sep 17 00:00:00 2001 From: Richard Aspden Date: Tue, 12 May 2026 18:12:02 +0100 Subject: [PATCH 2/2] net/cloudflared: replace sysctl JS warning with AbstractStatus banner Co-Authored-By: Claude Sonnet 4.6 --- .../Cloudflared/Api/SettingsController.php | 14 ----- .../Status/CloudflaredOverrideStatus.php | 56 +++++++++++++++++++ .../app/views/OPNsense/Cloudflared/index.volt | 37 ------------ 3 files changed, 56 insertions(+), 51 deletions(-) create mode 100644 net/cloudflared/src/opnsense/mvc/app/library/OPNsense/System/Status/CloudflaredOverrideStatus.php 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 index 7cd7a48fba..f9b385cad9 100644 --- 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 @@ -34,18 +34,4 @@ class SettingsController extends ApiMutableModelControllerBase { protected static $internalModelClass = '\OPNsense\Cloudflared\Cloudflared'; protected static $internalModelName = 'cloudflared'; - - public function sysctlCheckAction() - { - $checks = [ - 'kern.ipc.maxsockbuf' => 16777216, - 'net.inet.udp.recvspace' => 8388608, - ]; - $result = []; - foreach ($checks as $key => $minimum) { - $value = intval(trim(shell_exec('sysctl -n ' . escapeshellarg($key) . ' 2>/dev/null'))); - $result[$key] = ['value' => $value, 'minimum' => $minimum, 'ok' => $value >= $minimum]; - } - return $result; - } } diff --git a/net/cloudflared/src/opnsense/mvc/app/library/OPNsense/System/Status/CloudflaredOverrideStatus.php b/net/cloudflared/src/opnsense/mvc/app/library/OPNsense/System/Status/CloudflaredOverrideStatus.php new file mode 100644 index 0000000000..91e464c40e --- /dev/null +++ b/net/cloudflared/src/opnsense/mvc/app/library/OPNsense/System/Status/CloudflaredOverrideStatus.php @@ -0,0 +1,56 @@ + + * 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\System\Status; + +use OPNsense\System\AbstractStatus; +use OPNsense\System\SystemStatusCode; + +class CloudflaredOverrideStatus extends AbstractStatus +{ + public function __construct() + { + $this->internalPriority = 2; + $this->internalPersistent = true; + $this->internalIsBanner = true; + $this->internalTitle = gettext('Cloudflare Tunnel'); + $this->internalScope = [ + '/ui/cloudflared/' + ]; + } + + public function collectStatus() + { + $this->internalMessage = gettext( + 'Cloudflare Tunnel traffic bypasses OPNsense firewall rules; access control must be enforced in ' . + 'Cloudflare Access. For optimal QUIC performance, set the recommended kernel tunables. ' . + 'See the plugin documentation for details.' + ); + $this->internalStatus = SystemStatusCode::NOTICE; + } +} 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 index 07439c1406..052df28980 100644 --- a/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt +++ b/net/cloudflared/src/opnsense/mvc/app/views/OPNsense/Cloudflared/index.volt @@ -26,37 +26,9 @@