Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

www/caddy: Add Layer4 openvpn, winbox and quic matcher #4325

Merged
merged 13 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion www/caddy/Makefile
Original file line number Diff line number Diff line change
@@ -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= [email protected]
Expand Down
12 changes: 12 additions & 0 deletions www/caddy/pkg-descr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ 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
* Add: Layer4 QUIC matcher
* Build: Update dependency to lang/go123
Monviech marked this conversation as resolved.
Show resolved Hide resolved
* 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

1.7.3

* Add: Clear All button to Filter by Domain selectpicker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,26 @@
<id>layer4.FromDomain</id>
<label>Domain</label>
<type>select_multiple</type>
<style>style_matchers tokenize</style>
<style>style_matchers tokenize matchers_domain</style>
<allownew>true</allownew>
<help><![CDATA[Enter one or multiple domains to route via SNI or Host Header. Wildcard domains and host wildcards are allowed, e.g. "*.example.com" and "*".]]></help>
</field>
<field>
<id>layer4.FromOpenvpnModes</id>
<label>OpenVPN Mode</label>
<type>dropdown</type>
<style>style_matchers matchers_openvpn</style>
<help><![CDATA[Select the mode matching the OpenVPN Server or Client.]]></help>
</field>
<field>
<id>layer4.FromOpenvpnStaticKey</id>
<label>OpenVPN Static Key</label>
<type>select_multiple</type>
<style>style_matchers selectpicker matchers_openvpn</style>
<hint>Any</hint>
<size>5</size>
<help><![CDATA[Select a Static Key to match. Multiple Static Keys are only supported for tls-crypt2_client mode.]]></help>
</field>
<field>
<id>layer4.InvertMatchers</id>
<label>Invert Matchers</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<fields>
<field>
<id>layer4openvpn.description</id>
<label>Description</label>
<type>text</type>
</field>
<field>
<id>layer4openvpn.StaticKey</id>
<label>Static Key</label>
<type>textbox</type>
<help>Paste an OpenVPN Static key.</help>
</field>
</fields>
33 changes: 29 additions & 4 deletions www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 != '*'
Expand All @@ -208,6 +208,30 @@ 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->Type === 'global' && empty((string)$item->FromPort)) {
$messages->appendMessage(new Message(
sprintf(
Expand Down Expand Up @@ -246,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
),
Expand Down
41 changes: 41 additions & 0 deletions www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,29 @@
<AsList>Y</AsList>
<ValidationMessage>Please enter one or multiple hostnames or FQDNs.</ValidationMessage>
</FromDomain>
<FromOpenvpnModes type="OptionField">
<BlankDesc>Any</BlankDesc>
<OptionValues>
<auth_sha256_normal>tls-auth_sha256_normal</auth_sha256_normal>
<auth_sha256_inverse>tls-auth_sha256_inverse</auth_sha256_inverse>
<auth_sha512_normal>tls-auth_sha512_normal</auth_sha512_normal>
<auth_sha512_inverse>tls-auth_sha512_inverse</auth_sha512_inverse>
<crypt>tls-crypt</crypt>
<crypt2_client>tls-crypt2_client</crypt2_client>
<crypt2_server>tls-crypt2_server</crypt2_server>
</OptionValues>
</FromOpenvpnModes>
<FromOpenvpnStaticKey type="ModelRelationField">
<Model>
<reverseproxy>
<source>OPNsense.Caddy.Caddy</source>
<items>reverseproxy.layer4openvpn</items>
<display>description</display>
<display_format>%s</display_format>
</reverseproxy>
</Model>
<Multiple>Y</Multiple>
</FromOpenvpnStaticKey>
<Matchers type="OptionField">
<Required>Y</Required>
<Default>tlssni</Default>
Expand All @@ -497,14 +520,18 @@
<dns>DNS</dns>
<http>HTTP</http>
<httphost>HTTP (Host Header)</httphost>
<openvpn>OpenVPN</openvpn>
<postgres>Postgres</postgres>
<proxy_protocol>Proxy Protocol</proxy_protocol>
<quic>QUIC</quic>
<quicsni>QUIC (SNI Client Hello)</quicsni>
<rdp>RDP</rdp>
<socks4>SOCKSv4</socks4>
<socks5>SOCKSv5</socks5>
<ssh>SSH</ssh>
<tls>TLS</tls>
<tlssni>TLS (SNI Client Hello)</tlssni>
<winbox>Winbox</winbox>
<wireguard>Wireguard</wireguard>
<xmpp>XMPP</xmpp>
</OptionValues>
Expand Down Expand Up @@ -539,6 +566,20 @@
</RemoteIp>
<description type="DescriptionField"/>
</layer4>
<layer4openvpn type="ArrayField">
<StaticKey type="TextField">
<Required>Y</Required>
</StaticKey>
<description type="DescriptionField">
<Required>Y</Required>
<Constraints>
<check001>
<ValidationMessage>Description must be unique.</ValidationMessage>
<type>UniqueConstraint</type>
</check001>
</Constraints>
</description>
</layer4openvpn>
</reverseproxy>
</items>
</model>
Loading