mirror of
https://github.com/opnsense/plugins.git
synced 2026-06-09 08:56:23 -04:00
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 <noreply@anthropic.com>
This commit is contained in:
parent
f16b0a5cbf
commit
3a6cb1c622
21 changed files with 720 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
8
net/cloudflared/Makefile
Normal file
8
net/cloudflared/Makefile
Normal file
|
|
@ -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"
|
||||
7
net/cloudflared/pkg-descr
Normal file
7
net/cloudflared/pkg-descr
Normal file
|
|
@ -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 <alancpmartines@hotmail.com>.
|
||||
89
net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc
Normal file
89
net/cloudflared/src/etc/inc/plugins.inc.d/cloudflared.inc
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
* 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'];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
* 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');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<form>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>General Settings</label>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.enabled</id>
|
||||
<label>Enable</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable the Cloudflare Tunnel service.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.token</id>
|
||||
<label>Tunnel Token</label>
|
||||
<type>text</type>
|
||||
<help>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.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.protocol</id>
|
||||
<label>Protocol</label>
|
||||
<type>dropdown</type>
|
||||
<help>Transport protocol. Leave on auto unless you have a specific reason to force QUIC or HTTP/2.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.post_quantum</id>
|
||||
<label>Post-Quantum Encryption</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable post-quantum cryptography for the tunnel connection (requires QUIC).</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.quic_disable_pmtu_discovery</id>
|
||||
<label>Disable QUIC PMTU Discovery</label>
|
||||
<type>checkbox</type>
|
||||
<help>Disable Path MTU Discovery for QUIC connections. May help in environments where ICMP is blocked.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>cloudflared.general.log_level</id>
|
||||
<label>Log Level</label>
|
||||
<type>dropdown</type>
|
||||
<help>Verbosity of cloudflared logging. Defaults to info. Use debug for troubleshooting; warn or error for quieter operation.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<acl>
|
||||
<page-cloudflared-general>
|
||||
<name>Services: Cloudflare Tunnel</name>
|
||||
<patterns>
|
||||
<pattern>ui/cloudflared/*</pattern>
|
||||
<pattern>api/cloudflared/*</pattern>
|
||||
</patterns>
|
||||
</page-cloudflared-general>
|
||||
<page-cloudflared-log>
|
||||
<name>Services: Cloudflare Tunnel: Log File</name>
|
||||
<patterns>
|
||||
<pattern>ui/diagnostics/log/core/cloudflared/*</pattern>
|
||||
<pattern>api/diagnostics/log/core/cloudflared/*</pattern>
|
||||
</patterns>
|
||||
</page-cloudflared-log>
|
||||
</acl>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
* 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
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<model>
|
||||
<mount>//OPNsense/Cloudflared</mount>
|
||||
<version>1.0.0</version>
|
||||
<description>Cloudflare Tunnel settings</description>
|
||||
<items>
|
||||
<general>
|
||||
<enabled type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<token type="TextField">
|
||||
<Required>Y</Required>
|
||||
<Mask>/^[a-zA-Z0-9\._\-]+$/</Mask>
|
||||
<ValidationMessage>Token may only contain letters, digits, dots, underscores and hyphens.</ValidationMessage>
|
||||
</token>
|
||||
<protocol type="OptionField">
|
||||
<Default>auto</Default>
|
||||
<Required>Y</Required>
|
||||
<OptionValues>
|
||||
<auto>auto</auto>
|
||||
<quic>quic</quic>
|
||||
<http2>http2</http2>
|
||||
</OptionValues>
|
||||
</protocol>
|
||||
<post_quantum type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</post_quantum>
|
||||
<quic_disable_pmtu_discovery type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</quic_disable_pmtu_discovery>
|
||||
<log_level type="OptionField">
|
||||
<Default>info</Default>
|
||||
<Required>Y</Required>
|
||||
<OptionValues>
|
||||
<info>info</info>
|
||||
<debug>debug</debug>
|
||||
<warn>warn</warn>
|
||||
<error>error</error>
|
||||
<fatal>fatal</fatal>
|
||||
</OptionValues>
|
||||
</log_level>
|
||||
</general>
|
||||
</items>
|
||||
</model>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<menu>
|
||||
<Services>
|
||||
<Cloudflared VisibleName="Cloudflare Tunnel" cssClass="fa fa-cloud fa-fw">
|
||||
<Settings order="10" url="/ui/cloudflared/"/>
|
||||
<Log VisibleName="Log File" order="20" url="/ui/diagnostics/log/core/cloudflared"/>
|
||||
</Cloudflared>
|
||||
</Services>
|
||||
</menu>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
{#
|
||||
# Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
# 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.
|
||||
#}
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
function updateSysctlWarning() {
|
||||
var protocol = $("#cloudflared\\.general\\.protocol").val();
|
||||
if (protocol !== 'auto' && protocol !== 'quic') {
|
||||
$("#sysctl_warning").hide();
|
||||
return;
|
||||
}
|
||||
ajaxCall("/api/cloudflared/settings/sysctlCheck", {}, function(data) {
|
||||
var issues = [];
|
||||
$.each({'kern.ipc.maxsockbuf': 16777216, 'net.inet.udp.recvspace': 8388608}, function(key, min) {
|
||||
if (data[key] && !data[key].ok) {
|
||||
issues.push(key + " {{ lang._('(current:') }} " + data[key].value +
|
||||
"{{ lang._(', recommended: ≥') }} " + min + ")");
|
||||
}
|
||||
});
|
||||
if (issues.length > 0) {
|
||||
$("#sysctl_issues").html(issues.join("<br>"));
|
||||
$("#sysctl_warning").show();
|
||||
} else {
|
||||
$("#sysctl_warning").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mapDataToFormUI({'frm_GeneralSettings': "/api/cloudflared/settings/get"}).done(function() {
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
updateServiceControlUI('cloudflared');
|
||||
updateSysctlWarning();
|
||||
});
|
||||
|
||||
$("#cloudflared\\.general\\.protocol").on('change', function() {
|
||||
updateSysctlWarning();
|
||||
});
|
||||
|
||||
$("#reconfigureAct").SimpleActionButton({
|
||||
onPreAction: function() {
|
||||
const dfObj = $.Deferred();
|
||||
saveFormToEndpoint("/api/cloudflared/settings/set", 'frm_GeneralSettings', dfObj.resolve, true, dfObj.reject);
|
||||
return dfObj;
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="content-box">
|
||||
{{ partial('layout_partials/base_form', ['fields': general, 'id': 'frm_GeneralSettings']) }}
|
||||
<div id="sysctl_warning" class="alert alert-warning" role="alert" style="margin: 10px; display: none;">
|
||||
{{ lang._("QUIC performance: the following UDP buffer sysctl(s) are below the recommended values. Set them under") }}
|
||||
<a href="/ui/core/tunables">{{ lang._("System > Settings > Tuneables") }}</a>
|
||||
{{ lang._("for optimal tunnel throughput.") }}
|
||||
<br><span id="sysctl_issues"></span>
|
||||
</div>
|
||||
<div class="alert alert-warning" role="alert" style="margin: 10px;">
|
||||
{{ lang._("Traffic received via the Cloudflare Tunnel bypasses OPNsense firewall rules. Access control for tunnelled services must be enforced within Cloudflare Access. Backend services must also be reachable from the router's own IP address, as cloudflared forwards connections from the router itself.") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/cloudflared/service/reconfigure', 'data_service_widget': 'cloudflared'}) }}
|
||||
34
net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh
Executable file
34
net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/reconfigure.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
# 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
|
||||
38
net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh
Executable file
38
net/cloudflared/src/opnsense/scripts/OPNsense/Cloudflared/recover.sh
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
# 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
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
"""
|
||||
Copyright (C) 2026 Richard Aspden <rick+github@insanityinside.net>
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
rc.conf.d:/etc/rc.conf.d/cloudflared
|
||||
config.yml:/usr/local/etc/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 %}
|
||||
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
###################################################################
|
||||
# Local syslog-ng configuration [cloudflared].
|
||||
###################################################################
|
||||
filter f_local_cloudflared {
|
||||
program("cloudflared");
|
||||
};
|
||||
Loading…
Reference in a new issue