From b6c39f20aa26d73f877dda190345d9ac4faef3b3 Mon Sep 17 00:00:00 2001 From: Monviech Date: Sat, 26 Oct 2024 19:25:46 +0000 Subject: [PATCH 01/13] www/caddy: Add CRUD for Layer4 OpenVPN matcher with mode and static key support. --- .../Caddy/Api/ReverseProxyController.php | 100 ++++++++++++------ .../OPNsense/Caddy/Layer4Controller.php | 1 + .../OPNsense/Caddy/forms/dialogLayer4.xml | 18 +++- .../Caddy/forms/dialogLayer4Openvpn.xml | 13 +++ .../mvc/app/models/OPNsense/Caddy/Caddy.php | 36 +++++++ .../mvc/app/models/OPNsense/Caddy/Caddy.xml | 33 ++++++ .../mvc/app/views/OPNsense/Caddy/layer4.volt | 61 +++++++++-- 7 files changed, 221 insertions(+), 41 deletions(-) create mode 100644 www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4Openvpn.xml diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/ReverseProxyController.php b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/ReverseProxyController.php index 16bb4de37f..0e46c0504a 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/ReverseProxyController.php +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/ReverseProxyController.php @@ -209,40 +209,6 @@ public function toggleHandleAction($uuid, $enabled = null) return $this->toggleBase("reverseproxy.handle", $uuid, $enabled); } - - // Layer4 Section - - public function searchLayer4Action() - { - return $this->searchBase("reverseproxy.layer4", null, 'description'); - } - - public function setLayer4Action($uuid) - { - return $this->setBase("layer4", "reverseproxy.layer4", $uuid); - } - - public function addLayer4Action() - { - return $this->addBase("layer4", "reverseproxy.layer4"); - } - - public function getLayer4Action($uuid = null) - { - return $this->getBase("layer4", "reverseproxy.layer4", $uuid); - } - - public function delLayer4Action($uuid) - { - return $this->delBase("reverseproxy.layer4", $uuid); - } - - public function toggleLayer4Action($uuid, $enabled = null) - { - return $this->toggleBase("reverseproxy.layer4", $uuid, $enabled); - } - - // AccessList Section public function searchAccessListAction() @@ -349,4 +315,70 @@ public function delHeaderAction($uuid) { return $this->delBase("reverseproxy.header", $uuid); } + + + // Layer4 Proxy Section + + public function searchLayer4Action() + { + return $this->searchBase("reverseproxy.layer4", null, 'description'); + } + + public function setLayer4Action($uuid) + { + return $this->setBase("layer4", "reverseproxy.layer4", $uuid); + } + + public function addLayer4Action() + { + return $this->addBase("layer4", "reverseproxy.layer4"); + } + + public function getLayer4Action($uuid = null) + { + return $this->getBase("layer4", "reverseproxy.layer4", $uuid); + } + + public function delLayer4Action($uuid) + { + return $this->delBase("reverseproxy.layer4", $uuid); + } + + public function toggleLayer4Action($uuid, $enabled = null) + { + return $this->toggleBase("reverseproxy.layer4", $uuid, $enabled); + } + + + // Layer4 OpenVPN Section + + public function searchLayer4OpenvpnAction() + { + return $this->searchBase("reverseproxy.layer4openvpn", null, 'description'); + } + + public function setLayer4OpenvpnAction($uuid) + { + return $this->setBase("layer4openvpn", "reverseproxy.layer4openvpn", $uuid); + } + + public function addLayer4OpenvpnAction() + { + return $this->addBase("layer4openvpn", "reverseproxy.layer4openvpn"); + } + + public function getLayer4OpenvpnAction($uuid = null) + { + return $this->getBase("layer4openvpn", "reverseproxy.layer4openvpn", $uuid); + } + + public function delLayer4OpenvpnAction($uuid) + { + return $this->delBase("reverseproxy.layer4openvpn", $uuid); + } + + public function toggleLayer4OpenvpnAction($uuid, $enabled = null) + { + return $this->toggleBase("reverseproxy.layer4openvpn", $uuid, $enabled); + } } diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Layer4Controller.php b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Layer4Controller.php index 00b4548e7d..1a085eff42 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Layer4Controller.php +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Layer4Controller.php @@ -38,5 +38,6 @@ public function indexAction() { $this->view->pick('OPNsense/Caddy/layer4'); $this->view->formDialogLayer4 = $this->getForm("dialogLayer4"); + $this->view->formDialogLayer4Openvpn = $this->getForm("dialogLayer4Openvpn"); } } diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml index a2c73f6a01..f1ff3c7079 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml @@ -62,10 +62,26 @@ layer4.FromDomain select_multiple - + true + + layer4.FromOpenvpnModes + + dropdown + + + + + layer4.FromOpenvpnStaticKey + + select_multiple + + Any + 5 + + layer4.InvertMatchers diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4Openvpn.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4Openvpn.xml new file mode 100644 index 0000000000..ec225e85f3 --- /dev/null +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4Openvpn.xml @@ -0,0 +1,13 @@ + + + layer4openvpn.description + + text + + + layer4openvpn.StaticKey + + textbox + Paste an OpenVPN Static key. + + diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php index f738609ea9..84614dda9a 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php @@ -208,6 +208,42 @@ private function checkLayer4Matchers($messages) )); } + if ((string)$item->Matchers !== 'openvpn' && !empty((string)$item->FromOpenvpnModes)) { + $messages->appendMessage(new Message( + sprintf( + gettext( + 'When "%s" matcher is selected, field must be empty.' + ), + $item->Matchers + ), + $key . ".FromOpenvpnModes" + )); + } + + if ((string)$item->Matchers !== 'openvpn' && !empty((string)$item->FromOpenvpnStaticKey)) { + $messages->appendMessage(new Message( + sprintf( + gettext( + 'When "%s" matcher is selected, field must be empty.' + ), + $item->Matchers + ), + $key . ".FromOpenvpnStaticKey" + )); + } + + if ( + (string)$item->FromOpenvpnModes !== 'crypt2_client' && + count((array)$item->FromOpenvpnStaticKey) > 1 + ) { + $messages->appendMessage(new Message( + sprintf( + gettext('Only mode "crypt2_client" supports multiple keys.'), + ), + $key . ".FromOpenvpnStaticKey" + )); + } + if ((string)$item->Type === 'global' && empty((string)$item->FromPort)) { $messages->appendMessage(new Message( sprintf( diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml index 8e357ad8ee..a7e9ded5fe 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml @@ -489,6 +489,29 @@ Y Please enter one or multiple hostnames or FQDNs. + + Any + + tls-auth_sha256_normal + tls-auth_sha256_inverse + tls-auth_sha512_normal + tls-auth_sha512_inverse + tls-crypt + tls-crypt2_client + tls-crypt2_server + + + + + + OPNsense.Caddy.Caddy + reverseproxy.layer4openvpn + description + %s + + + Y + Y tlssni @@ -497,6 +520,7 @@ DNS HTTP HTTP (Host Header) + OpenVPN Postgres Proxy Protocol RDP @@ -505,6 +529,7 @@ SSH TLS TLS (SNI Client Hello) + Winbox Wireguard XMPP @@ -539,6 +564,14 @@ + + + Y + + + Y + + diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index d1bfc6cf62..1a99bf7b9b 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -36,6 +36,15 @@ toggle:'/api/caddy/ReverseProxy/toggleLayer4/', }); + $("#Layer4OpenvpnGrid").UIBootgrid({ + search:'/api/caddy/ReverseProxy/searchLayer4Openvpn/', + get:'/api/caddy/ReverseProxy/getLayer4Openvpn/', + set:'/api/caddy/ReverseProxy/setLayer4Openvpn/', + add:'/api/caddy/ReverseProxy/addLayer4Openvpn/', + del:'/api/caddy/ReverseProxy/delLayer4Openvpn/', + toggle:'/api/caddy/ReverseProxy/toggleLayer4Openvpn/', + }); + /** * Displays an alert message to the user. * @@ -86,11 +95,17 @@ } }); + // Hide all elements with style_matchers initially + $(".style_matchers").closest('tr').hide(); + $("#layer4\\.Matchers").change(function() { - if ($(this).val() !== "tlssni" && $(this).val() !== "httphost") { - $(".style_matchers").closest('tr').hide(); - } else { - $(".style_matchers").closest('tr').show(); + $(".style_matchers").closest('tr').hide(); + const selectedVal = $(this).val(); + + if (selectedVal === "tlssni" || selectedVal === "httphost") { + $(".matchers_domain").closest('tr').show(); + } else if (selectedVal === "openvpn") { + $(".matchers_openvpn").closest('tr').show(); } }); @@ -108,6 +123,7 @@
@@ -115,7 +131,7 @@

{{ lang._('Layer4 Routes') }}

-
+
@@ -125,9 +141,11 @@ - + + + @@ -152,6 +170,36 @@ + + +
+
+ +

{{ lang._('OpenVPN Static Keys') }}

+
+
{{ lang._('Routing Type') }} {{ lang._('Protocol') }} {{ lang._('Local Port') }}{{ lang._('Domain') }} {{ lang._('Matchers') }} {{ lang._('Invert Matchers') }}{{ lang._('Domain') }}{{ lang._('OpenVPN Modes') }}{{ lang._('OpenVPN Static Key') }} {{ lang._('Upstream Domain') }} {{ lang._('Upstream Port') }} {{ lang._('Remote IP') }}
+ + + + + + + + + + + + + + + +
{{ lang._('ID') }}{{ lang._('Description') }}{{ lang._('Commands') }}
+ + +
+
+
+
@@ -177,3 +225,4 @@ {{ partial("layout_partials/base_dialog",['fields':formDialogLayer4,'id':'DialogLayer4','label':lang._('Edit Layer4 Route')])}} +{{ partial("layout_partials/base_dialog",['fields':formDialogLayer4Openvpn,'id':'DialogLayer4Openvpn','label':lang._('Edit OpenVPN Static Key')])}} From e7b76031c91b5c5fdcdb7be8cbe1a355b29c8346 Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 10:18:32 +0000 Subject: [PATCH 02/13] www/caddy: Export static keys to the filesystem as uuid.key --- .../scripts/OPNsense/Caddy/caddy_certs.php | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php index f65ef168cf..c73b532a36 100755 --- a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php +++ b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php @@ -36,22 +36,22 @@ // Traverse through certificates foreach ($configObj->cert as $cert) { - $cert_refid = (string) $cert->refid; - $cert_content = base64_decode((string) $cert->crt); - $key_content = base64_decode((string) $cert->prv); + $cert_refid = (string)$cert->refid; + $cert_content = base64_decode((string)$cert->crt); + $key_content = base64_decode((string)$cert->prv); $cert_chain = $cert_content; // Handle CA and possible intermediate CA to create a certificate bundle if (!empty($cert->caref)) { foreach ($configObj->ca as $ca) { - if ((string) $cert->caref === (string) $ca->refid) { - $ca_content = base64_decode((string) $ca->crt); + if ((string) $cert->caref === (string)$ca->refid) { + $ca_content = base64_decode((string)$ca->crt); $cert_chain .= "\n" . $ca_content; if (!empty($ca->caref)) { foreach ($configObj->ca as $parent_ca) { - if ((string) $ca->caref === (string) $parent_ca->refid) { - $parent_ca_content = base64_decode((string) $parent_ca->crt); + if ((string)$ca->caref === (string)$parent_ca->refid) { + $parent_ca_content = base64_decode((string)$parent_ca->crt); $cert_chain .= "\n" . $parent_ca_content; break; } @@ -68,9 +68,20 @@ // Traverse through CA certificates and save them foreach ($configObj->ca as $ca) { - $ca_refid = (string) $ca->refid; - $ca_content = base64_decode((string) $ca->crt); + $ca_refid = (string)$ca->refid; + $ca_content = base64_decode((string)$ca->crt); // Save the CA certificate file_put_contents($temp_dir . $ca_refid . '.pem', $ca_content); } + +// Traverse through layer4 OpenVPN static keys and save them as files +if (isset($configObj->Pischem->caddy->reverseproxy->layer4openvpn)) { + foreach ($configObj->Pischem->caddy->reverseproxy->layer4openvpn as $openvpn) { + $uuid = (string) $openvpn['uuid']; + $static_key = (string) $openvpn->StaticKey; + + // Save the static key + file_put_contents($temp_dir . $uuid . '.key', $static_key); + } +} From b3ab4255744703047be1b287379c2fecc10f4fda Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 10:51:07 +0000 Subject: [PATCH 03/13] www/caddy: Remove validation that checks for multiple keys, help text is enough. --- .../opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php index 84614dda9a..33e5fd2aa9 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php @@ -232,18 +232,6 @@ private function checkLayer4Matchers($messages) )); } - if ( - (string)$item->FromOpenvpnModes !== 'crypt2_client' && - count((array)$item->FromOpenvpnStaticKey) > 1 - ) { - $messages->appendMessage(new Message( - sprintf( - gettext('Only mode "crypt2_client" supports multiple keys.'), - ), - $key . ".FromOpenvpnStaticKey" - )); - } - if ((string)$item->Type === 'global' && empty((string)$item->FromPort)) { $messages->appendMessage(new Message( sprintf( From 7e23ebb451b7a0ed5d5236cde0f0ec2f7e0e65f5 Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 11:08:45 +0000 Subject: [PATCH 04/13] www/caddy: Expand layer4 template for all supported OpenVPN modes. --- .../templates/OPNsense/Caddy/includeLayer4 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index defaf2528f..7a5a29f7d4 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -70,6 +70,43 @@ {{ invert_prefix }}http host {{ layer4.FromDomain.replace(',', ' ') }} {% elif layer4.Matchers == 'tlssni' %} {{ invert_prefix }}tls sni {{ layer4.FromDomain.replace(',', ' ') }} + {% elif layer4.Matchers == 'openvpn' and layer4.FromOpenvpnModes and layer4.FromOpenvpnStaticKey %} + {% for mode in layer4.FromOpenvpnModes.split(',') %} + {% set mode_clean = mode.strip() %} + {% if mode_clean.startswith('auth') %} + {% set digest = 'sha256' if 'sha256' in mode_clean else 'sha512' %} + {% set direction = 'normal' if 'normal' in mode_clean else 'inverse' %} + {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {{ invert_prefix }}openvpn { + modes auth + auth_digest {{ digest }} + group_key_direction {{ direction }} + group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + } + {% endfor %} + {% elif mode_clean == 'crypt' %} + {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {{ invert_prefix }}openvpn { + modes crypt + group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + } + {% endfor %} + {% elif mode_clean == 'crypt2_client' %} + {{ invert_prefix }}openvpn { + modes crypt2 + {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + client_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + {% endfor %} + } + {% elif mode_clean == 'crypt2_server' %} + {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {{ invert_prefix }}openvpn { + modes crypt2 + server_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + } + {% endfor %} + {% endif %} + {% endfor %} {% else %} {{ invert_prefix }}{{ layer4.Matchers }} {% endif %} From 2a21d1db10f40907c28efce667e51ada2c5fe71e Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 11:33:24 +0000 Subject: [PATCH 05/13] www/caddy: Prevent multiple static keys for modes other than crypt2_client. Fix helptexts. --- .../OPNsense/Caddy/forms/dialogLayer4.xml | 4 ++-- .../templates/OPNsense/Caddy/includeLayer4 | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml index f1ff3c7079..1679b056fe 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml @@ -71,7 +71,7 @@ dropdown - + layer4.FromOpenvpnStaticKey @@ -80,7 +80,7 @@ Any 5 - + layer4.InvertMatchers diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index 7a5a29f7d4..c1916cb935 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -73,10 +73,14 @@ {% elif layer4.Matchers == 'openvpn' and layer4.FromOpenvpnModes and layer4.FromOpenvpnStaticKey %} {% for mode in layer4.FromOpenvpnModes.split(',') %} {% set mode_clean = mode.strip() %} + {% set key_list = layer4.FromOpenvpnStaticKey.split(',') %} {% if mode_clean.startswith('auth') %} + {% if key_list|length > 1 %} + {% set key_list = key_list[:1] %} + {% endif %} {% set digest = 'sha256' if 'sha256' in mode_clean else 'sha512' %} {% set direction = 'normal' if 'normal' in mode_clean else 'inverse' %} - {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {% for key_uuid in key_list %} {{ invert_prefix }}openvpn { modes auth auth_digest {{ digest }} @@ -85,21 +89,28 @@ } {% endfor %} {% elif mode_clean == 'crypt' %} - {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {% if key_list|length > 1 %} + {% set key_list = key_list[:1] %} + {% endif %} + {% for key_uuid in key_list %} {{ invert_prefix }}openvpn { modes crypt group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key } {% endfor %} {% elif mode_clean == 'crypt2_client' %} + {# Multiple keys are allowed for crypt2_client #} {{ invert_prefix }}openvpn { modes crypt2 - {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {% for key_uuid in key_list %} client_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key {% endfor %} } {% elif mode_clean == 'crypt2_server' %} - {% for key_uuid in layer4.FromOpenvpnStaticKey.split(',') %} + {% if key_list|length > 1 %} + {% set key_list = key_list[:1] %} + {% endif %} + {% for key_uuid in key_list %} {{ invert_prefix }}openvpn { modes crypt2 server_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key From 15ee893126c9e2a6fa7b7104b2115b112cbf7b75 Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 15:04:42 +0000 Subject: [PATCH 06/13] www/caddy: Add unique constraint to description of openvpn static key --- .../src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml | 6 ++++++ .../src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml index a7e9ded5fe..38150113f0 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml @@ -570,6 +570,12 @@ Y + + + Description must be unique. + UniqueConstraint + + diff --git a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php index c73b532a36..a8e6c36cf1 100755 --- a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php +++ b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php @@ -44,7 +44,7 @@ // Handle CA and possible intermediate CA to create a certificate bundle if (!empty($cert->caref)) { foreach ($configObj->ca as $ca) { - if ((string) $cert->caref === (string)$ca->refid) { + if ((string)$cert->caref === (string)$ca->refid) { $ca_content = base64_decode((string)$ca->crt); $cert_chain .= "\n" . $ca_content; From 83e06c3d5ee81bee85d897f70ab94f1d647609ad Mon Sep 17 00:00:00 2001 From: Monviech Date: Sun, 27 Oct 2024 15:23:29 +0000 Subject: [PATCH 07/13] www/caddy: Changelog and version bump --- www/caddy/Makefile | 2 +- www/caddy/pkg-descr | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/www/caddy/Makefile b/www/caddy/Makefile index 1fe46006ff..bd346b669e 100644 --- a/www/caddy/Makefile +++ b/www/caddy/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= caddy -PLUGIN_VERSION= 1.7.3 +PLUGIN_VERSION= 1.7.4 PLUGIN_DEPENDS= caddy-custom PLUGIN_COMMENT= Modern Reverse Proxy with Automatic HTTPS, Dynamic DNS and Layer4 Routing PLUGIN_MAINTAINER= cedrik@pischem.com diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index af5015875c..b42145e206 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -13,6 +13,15 @@ DOC: https://docs.opnsense.org/manual/how-tos/caddy.html Plugin Changelog ================ +1.7.4 + +* Add: Layer4 OpenVPN matcher with mode, digest and static key support +* Add: Layer4 Winbox matcher +* Build: Update dependency to lang/go123 +* Build: Update caddy-l4 and caddy-dynamicdns module +* Build: DNS Providers: Update porkbun, dnsmadeeasy +* Cleanup: Layer4 default route in listener_wrappers has been removed (obsolete) + 1.7.3 * Add: Clear All button to Filter by Domain selectpicker From c0918419c8e3eddb4bc6af265209c3880538f6cf Mon Sep 17 00:00:00 2001 From: Monviech Date: Mon, 28 Oct 2024 18:58:16 +0000 Subject: [PATCH 08/13] www/caddy: Make static key optional when choosing the tls mode in openvpn matcher --- .../templates/OPNsense/Caddy/includeLayer4 | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index c1916cb935..46789745c1 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -70,52 +70,62 @@ {{ invert_prefix }}http host {{ layer4.FromDomain.replace(',', ' ') }} {% elif layer4.Matchers == 'tlssni' %} {{ invert_prefix }}tls sni {{ layer4.FromDomain.replace(',', ' ') }} - {% elif layer4.Matchers == 'openvpn' and layer4.FromOpenvpnModes and layer4.FromOpenvpnStaticKey %} + {% elif layer4.Matchers == 'openvpn' and layer4.FromOpenvpnModes %} {% for mode in layer4.FromOpenvpnModes.split(',') %} {% set mode_clean = mode.strip() %} - {% set key_list = layer4.FromOpenvpnStaticKey.split(',') %} + {% if layer4.FromOpenvpnStaticKey %} + {% set key_list = layer4.FromOpenvpnStaticKey.split(',') %} + {% endif %} {% if mode_clean.startswith('auth') %} {% if key_list|length > 1 %} {% set key_list = key_list[:1] %} {% endif %} {% set digest = 'sha256' if 'sha256' in mode_clean else 'sha512' %} {% set direction = 'normal' if 'normal' in mode_clean else 'inverse' %} - {% for key_uuid in key_list %} - {{ invert_prefix }}openvpn { - modes auth - auth_digest {{ digest }} - group_key_direction {{ direction }} - group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key - } - {% endfor %} + {{ invert_prefix }}openvpn { + modes auth + auth_digest {{ digest }} + {% if layer4.FromOpenvpnStaticKey %} + group_key_direction {{ direction }} + {% for key_uuid in key_list %} + group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + {% endfor %} + {% endif %} + } {% elif mode_clean == 'crypt' %} {% if key_list|length > 1 %} {% set key_list = key_list[:1] %} {% endif %} - {% for key_uuid in key_list %} - {{ invert_prefix }}openvpn { - modes crypt - group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key - } - {% endfor %} + {{ invert_prefix }}openvpn { + modes crypt + {% if layer4.FromOpenvpnStaticKey %} + {% for key_uuid in key_list %} + group_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + {% endfor %} + {% endif %} + } {% elif mode_clean == 'crypt2_client' %} {# Multiple keys are allowed for crypt2_client #} {{ invert_prefix }}openvpn { modes crypt2 - {% for key_uuid in key_list %} - client_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key - {% endfor %} + {% if layer4.FromOpenvpnStaticKey %} + {% for key_uuid in key_list %} + client_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + {% endfor %} + {% endif %} } {% elif mode_clean == 'crypt2_server' %} {% if key_list|length > 1 %} {% set key_list = key_list[:1] %} {% endif %} - {% for key_uuid in key_list %} - {{ invert_prefix }}openvpn { - modes crypt2 - server_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key - } - {% endfor %} + {{ invert_prefix }}openvpn { + modes crypt2 + {% if layer4.FromOpenvpnStaticKey %} + {% for key_uuid in key_list %} + server_key_file /var/db/caddy/data/caddy/certificates/temp/{{ key_uuid.strip() }}.key + {% endfor %} + {% endif %} + } {% endif %} {% endfor %} {% else %} From a4ea0cb3aad70af61b86ec258ca132ded7519bb7 Mon Sep 17 00:00:00 2001 From: Monviech Date: Thu, 31 Oct 2024 16:10:01 +0000 Subject: [PATCH 09/13] www/caddy: Prepare new Layer7 Matcher Tab for more customizable matchers in the future. --- www/caddy/pkg-descr | 1 + .../mvc/app/views/OPNsense/Caddy/layer4.volt | 28 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index b42145e206..e9eb3bd937 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -21,6 +21,7 @@ Plugin Changelog * Build: Update caddy-l4 and caddy-dynamicdns module * Build: DNS Providers: Update porkbun, dnsmadeeasy * Cleanup: Layer4 default route in listener_wrappers has been removed (obsolete) +* Fix: Error when same Access List is set to wildcard and subdomain 1.7.3 diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index 1a99bf7b9b..f631949229 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -109,6 +109,18 @@ } }); + // Layer7 Matchers dropdown + const matcherContentMap = { + 'openvpn': "OpenVPNMatcherContent" + // Prepared for more matchers that need customization options + }; + + $("#matcherTypeSelect").change(function() { + const selectedMatcher = $(this).val(); + $(".matcher-content").hide(); + $("#" + matcherContentMap[selectedMatcher]).show(); + }); + updateServiceControlUI('caddy'); }); @@ -119,11 +131,15 @@ font-size: 16px; font-style: italic; } + + #matcherTypeSelect { + margin-top: 20px; + }
@@ -171,12 +187,16 @@
- +
+ + -

{{ lang._('OpenVPN Static Keys') }}

-
+
+

{{ lang._('OpenVPN Static Keys') }}

From 24335f10e6c58663fc7bb250c5a5f8bfe9774846 Mon Sep 17 00:00:00 2001 From: Monviech Date: Fri, 1 Nov 2024 18:10:26 +0000 Subject: [PATCH 10/13] www/caddy: Add Layer4 QUIC matcher. --- www/caddy/pkg-descr | 1 + .../src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php | 9 +++++---- .../src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml | 2 ++ .../opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt | 2 +- .../service/templates/OPNsense/Caddy/includeLayer4 | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index e9eb3bd937..de3e4ebf58 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -17,6 +17,7 @@ Plugin Changelog * Add: Layer4 OpenVPN matcher with mode, digest and static key support * Add: Layer4 Winbox matcher +* Add: Layer4 QUIC matcher * Build: Update dependency to lang/go123 * Build: Update caddy-l4 and caddy-dynamicdns module * Build: DNS Providers: Update porkbun, dnsmadeeasy diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php index 33e5fd2aa9..066574cbf5 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php @@ -180,7 +180,7 @@ private function checkLayer4Matchers($messages) foreach ($this->reverseproxy->layer4->iterateItems() as $item) { if ($item->isFieldChanged()) { $key = $item->__reference; - if (in_array((string)$item->Matchers, ['httphost', 'tlssni']) && empty((string)$item->FromDomain)) { + if (in_array((string)$item->Matchers, ['httphost', 'tlssni', 'quicsni']) && empty((string)$item->FromDomain)) { $messages->appendMessage(new Message( sprintf( gettext( @@ -191,7 +191,7 @@ private function checkLayer4Matchers($messages) $key . ".FromDomain" )); } elseif ( - !in_array((string)$item->Matchers, ['httphost', 'tlssni']) && + !in_array((string)$item->Matchers, ['httphost', 'tlssni', 'quicsni']) && ( !empty((string)$item->FromDomain) && (string)$item->FromDomain != '*' @@ -270,13 +270,14 @@ private function checkLayer4Matchers($messages) (string)$item->Type !== 'global' && ( (string)$item->Matchers == 'tls' || - (string)$item->Matchers == 'http' + (string)$item->Matchers == 'http' || + (string)$item->Matchers == 'quic' ) ) { $messages->appendMessage(new Message( sprintf( gettext( - 'When routing type is "%s", matchers "HTTP" or "TLS" cannot be chosen.' + 'When routing type is "%s", matchers "HTTP", "TLS" or "QUIC" cannot be chosen.' ), $item->Type ), diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml index 38150113f0..f1ef8c65d7 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml @@ -523,6 +523,8 @@ OpenVPN Postgres Proxy Protocol + QUIC + QUIC (SNI Client Hello) RDP SOCKSv4 SOCKSv5 diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index f631949229..012633d535 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -102,7 +102,7 @@ $(".style_matchers").closest('tr').hide(); const selectedVal = $(this).val(); - if (selectedVal === "tlssni" || selectedVal === "httphost") { + if (selectedVal === "tlssni" || selectedVal === "httphost" || selectedVal === "quicsni") { $(".matchers_domain").closest('tr').show(); } else if (selectedVal === "openvpn") { $(".matchers_openvpn").closest('tr').show(); diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index 46789745c1..c6058ccd70 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -70,6 +70,8 @@ {{ invert_prefix }}http host {{ layer4.FromDomain.replace(',', ' ') }} {% elif layer4.Matchers == 'tlssni' %} {{ invert_prefix }}tls sni {{ layer4.FromDomain.replace(',', ' ') }} + {% elif layer4.Matchers == 'quicsni' %} + {{ invert_prefix }}quic sni {{ layer4.FromDomain.replace(',', ' ') }} {% elif layer4.Matchers == 'openvpn' and layer4.FromOpenvpnModes %} {% for mode in layer4.FromOpenvpnModes.split(',') %} {% set mode_clean = mode.strip() %} From 125c35a7795e3531c02bab04bb45866cb89ab0e4 Mon Sep 17 00:00:00 2001 From: Monviech Date: Fri, 1 Nov 2024 18:47:52 +0000 Subject: [PATCH 11/13] www/caddy: Rename matcherTab --- www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index 012633d535..6a66f7d2ac 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -139,7 +139,7 @@
From 949ff39365c6dbc98a47442774b900c665e83802 Mon Sep 17 00:00:00 2001 From: Monviech Date: Fri, 1 Nov 2024 18:58:31 +0000 Subject: [PATCH 12/13] www/caddy: Revert a4ea0cb3 since its non operational and will not be needed for a while anyway --- .../mvc/app/views/OPNsense/Caddy/layer4.volt | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index 6a66f7d2ac..b762112926 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -109,18 +109,6 @@ } }); - // Layer7 Matchers dropdown - const matcherContentMap = { - 'openvpn': "OpenVPNMatcherContent" - // Prepared for more matchers that need customization options - }; - - $("#matcherTypeSelect").change(function() { - const selectedMatcher = $(this).val(); - $(".matcher-content").hide(); - $("#" + matcherContentMap[selectedMatcher]).show(); - }); - updateServiceControlUI('caddy'); }); @@ -131,10 +119,6 @@ font-size: 16px; font-style: italic; } - - #matcherTypeSelect { - margin-top: 20px; - }
@@ -222,6 +202,7 @@ +
From 2464fd6a5fbecc66abeb9b668480e8e62c433ea2 Mon Sep 17 00:00:00 2001 From: Monviech Date: Mon, 4 Nov 2024 16:20:48 +0000 Subject: [PATCH 13/13] www/caddy: Changelog --- www/caddy/pkg-descr | 1 + 1 file changed, 1 insertion(+) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index de3e4ebf58..42a8d507d6 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -20,6 +20,7 @@ Plugin Changelog * Add: Layer4 QUIC matcher * Build: Update dependency to lang/go123 * Build: Update caddy-l4 and caddy-dynamicdns module +* Build: Fix that caddy-l4 does not stop when ssh is proxied * Build: DNS Providers: Update porkbun, dnsmadeeasy * Cleanup: Layer4 default route in listener_wrappers has been removed (obsolete) * Fix: Error when same Access List is set to wildcard and subdomain