From a43ab626d088660e92fed70d12a760c5a29b3280 Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Tue, 7 Dec 2021 23:28:54 +0100 Subject: [PATCH 1/3] security/acme-client: add support for dynv6 HTTP API, closes #2678 --- security/acme-client/pkg-descr | 1 + .../AcmeClient/forms/dialogValidation.xml | 10 +++++ .../AcmeClient/LeValidation/DnsDynv6.php | 44 +++++++++++++++++++ .../models/OPNsense/AcmeClient/AcmeClient.xml | 4 ++ 4 files changed, 59 insertions(+) create mode 100644 security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/DnsDynv6.php diff --git a/security/acme-client/pkg-descr b/security/acme-client/pkg-descr index 670324157..ee87936d9 100644 --- a/security/acme-client/pkg-descr +++ b/security/acme-client/pkg-descr @@ -12,6 +12,7 @@ Plugin Changelog Added: * new automation: update local Unifi keystore (#2664) +* add support for dynv6 HTTP API (#2678) Fixed: * fix SFTP upload (#2671) diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml index 36a285356..2f13068c8 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml @@ -1353,4 +1353,14 @@ password + + + header + + + + validation.dns_dynv6_token + + password + diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/DnsDynv6.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/DnsDynv6.php new file mode 100644 index 000000000..c1d9115f0 --- /dev/null +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/DnsDynv6.php @@ -0,0 +1,44 @@ +acme_env['DYNV6_TOKEN'] = (string)$this->config->dns_dynv6_token; + } +} diff --git a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml index 0fa09b84c..982ffaf3b 100644 --- a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml +++ b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml @@ -417,6 +417,7 @@ DuckDNS API Dyn Managed DNS API Dynu API + dynv6 HTTP API EUserv FreeDNS API Gandi LiveDNS API @@ -1018,6 +1019,9 @@ N + + N + From 2af67affad8f6d7baa74b104242ee0d0c9ac8044 Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Wed, 8 Dec 2021 00:32:40 +0100 Subject: [PATCH 2/3] security/acme-client: add support for TLS-ALPN-01 challenge type, closes #2661 --- security/acme-client/pkg-descr | 1 + .../AcmeClient/forms/dialogValidation.xml | 36 +++++ .../OPNsense/AcmeClient/forms/settings.xml | 7 + .../OPNsense/AcmeClient/LeValidation/Base.php | 3 + .../AcmeClient/LeValidation/TlsalpnAcme.php | 143 ++++++++++++++++++ .../AcmeClient/LeValidationFactory.php | 5 +- .../models/OPNsense/AcmeClient/AcmeClient.xml | 33 +++- .../OPNsense/AcmeClient/validations.volt | 12 +- 8 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/TlsalpnAcme.php diff --git a/security/acme-client/pkg-descr b/security/acme-client/pkg-descr index ee87936d9..87339fe03 100644 --- a/security/acme-client/pkg-descr +++ b/security/acme-client/pkg-descr @@ -13,6 +13,7 @@ Plugin Changelog Added: * new automation: update local Unifi keystore (#2664) * add support for dynv6 HTTP API (#2678) +* add support for TLS-ALPN-01 challenge type (#2661) Fixed: * fix SFTP upload (#2671) diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml index 2f13068c8..3e2efdbd1 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml @@ -78,6 +78,42 @@ true Choose the local HAProxy frontends. They will automatically be configured to redirect acme challenges to the internal acme client. The HAProxy service will automatically be restarted if a certificate was renewed. + + + header + + + + validation.tlsalpn_service + + dropdown + + + + header + + + + validation.tlsalpn_acme_autodiscovery + + checkbox +
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
+
+ + validation.tlsalpn_acme_interface + + dropdown +
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
+
+ + validation.tlsalpn_acme_ipaddresses + + select_multiple + + true +
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
+ Enter IP addresses here. Finish each with TAB. +
header diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml index 4089801a0..a6b30ca04 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml @@ -36,6 +36,13 @@ true + + acmeclient.settings.TLSchallengePort + + text + + true + acmeclient.settings.restartTimeout diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php index 433572cba..3c0a6881a 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php @@ -85,6 +85,9 @@ abstract class Base extends \OPNsense\AcmeClient\LeCommon case 'http01': $this->acme_args[] = '--webroot ' . self::ACME_WEBROOT; break; + case 'tlsalpn01': + $this->acme_args[] = '--alpn'; + break; } // Store acme filenames diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/TlsalpnAcme.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/TlsalpnAcme.php new file mode 100644 index 000000000..db8466892 --- /dev/null +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/TlsalpnAcme.php @@ -0,0 +1,143 @@ +cert_id); + + // Get configured TLS port for acme.sh web server. + $configObj = Config::getInstance()->object(); + $local_tls_port = $configObj->OPNsense->AcmeClient->settings->TLSchallengePort; + $this->acme_args[] = LeUtils::execSafe('--tlsport %s', (string)$local_tls_port); + + // Collect all IP addresses here, automatic port forward will be applied for each IP + $iplist = array(); + + // Add IP addresses from auto-discovery feature + if ($this->config->tlsalpn_acme_autodiscovery == 1) { + $dnslist = explode(',', $this->cert_altnames); + $dnslist[] = $this->cert_name; + foreach ($dnslist as $fqdn) { + // NOTE: This may take some time. + $ip_found = gethostbyname("${fqdn}."); + if (!empty($ip_found)) { + $iplist[] = (string)$ip_found; + } + } + } + + // Add IP addresses from user input + $additional_ip = (string)$this->config->tlsalpn_acme_ipaddresses; + if (!empty($additional_ip)) { + foreach (explode(',', $additional_ip) as $ip) { + $iplist[] = $ip; + } + } + + // Add IP address from chosen interface + if (!empty((string)$this->config->tlsalpn_acme_interface)) { + $interface_ip = get_interface_ip((string)$this->config->tlsalpn_acme_interface); + if (!empty($interface_ip)) { + $iplist[] = $interface_ip; + } + } + + // Check if IPv6 support is enabled + if (isset($configObj->system->ipv6allow) && ($configObj->system->ipv6allow == '1')) { + $_ipv6_enabled = true; + } else { + $_ipv6_enabled = false; + } + + // Generate rules for all IP addresses + $anchor_rules = ""; + if (!empty($iplist)) { + $dedup_iplist = array_unique($iplist); + // Add one rule for every IP + foreach ($dedup_iplist as $ip) { + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + // IPv4 + $_dst = '127.0.0.1'; + $_family = 'inet'; + LeUtils::log("using IPv4 address: ${ip}"); + } elseif (($_ipv6_enabled == true) && (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))) { + // IPv6 + $_dst = '::1'; + $_family = 'inet6'; + LeUtils::log("using IPv6 address: ${ip}"); + } else { + continue; // skip broken entries + } + $anchor_rules .= "rdr pass ${_family} proto tcp from any to ${ip} port 443 -> ${_dst} port ${local_tls_port}\n"; + } + } else { + LeUtils::log_error("no IP addresses found to setup port forward"); + return false; + } + + // Abort if no rules were generated + if (empty($anchor_rules)) { + LeUtils::log_error("unable to setup a port forward (empty ruleset)"); + return false; + } + + // Create temporary port forward to allow acme challenges to get through + $anchor_setup = "rdr-anchor \"acme-client\"\n"; + file_put_contents("${configdir}/acme_anchor_setup", $anchor_setup); + chmod("${configdir}/acme_anchor_setup", 0600); + mwexec("/sbin/pfctl -f ${configdir}/acme_anchor_setup"); + file_put_contents("${configdir}/acme_anchor_rules", $anchor_rules); + chmod("${configdir}/acme_anchor_rules", 0600); + mwexec("/sbin/pfctl -a acme-client -f ${configdir}/acme_anchor_rules"); + } + + public function cleanup() + { + // Flush OPNsense port forward rules. + mwexec('/sbin/pfctl -a acme-client -F all'); + + // Workaround to solve disconnection issues reported by some users. + $backend = new \OPNsense\Core\Backend(); + $response = $backend->configdRun('filter reload'); + return true; + } +} diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationFactory.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationFactory.php index 1a2611d21..06e8be49b 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationFactory.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationFactory.php @@ -1,7 +1,7 @@ http_service; break; + case 'tlsalpn01': + $search_name = "tlsalpn_" . $obj->tlsalpn_service; + break; } // Convert to PascalCase diff --git a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml index 982ffaf3b..37e3df295 100644 --- a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml +++ b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml @@ -1,6 +1,6 @@ //OPNsense/AcmeClient - 3.1.0 + 3.2.0 A secure ACME Client plugin @@ -34,12 +34,18 @@ Staging Environment - + 43580 1024 65535 Y + + 43581 + 1024 + 65535 + Y + 600 10 @@ -339,6 +345,7 @@ HTTP-01 DNS-01 + TLS-ALPN-01 @@ -384,6 +391,28 @@ Y N + + Y + acme + + acme.sh TLS Web Server (automatic port forward) + + + + 1 + N + + + N + wan + + /^(?!0).*$/ + + + + N + Y + Y dns_freedns diff --git a/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/validations.volt b/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/validations.volt index bd1e4c42f..c095c4298 100644 --- a/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/validations.volt +++ b/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/validations.volt @@ -77,11 +77,20 @@ POSSIBILITY OF SUCH DAMAGE. } else { } }); + $("#validation\\.tlsalpn_service").change(function(){ + var service_id = 'table_tlsalpn_' + $(this).val(); + $(".table_tlsalpn").hide(); + if ($("#validation\\.method").val() == 'tlsalpn01') { + $("."+service_id).show(); + } else { + } + }); $("#validation\\.method").change(function(){ $(".method_table").hide(); $(".method_table_"+$(this).val()).show(); $("#validation\\.dns_service").change(); $("#validation\\.http_service").change(); + $("#validation\\.tlsalpn_service").change(); }); $("#validation\\.method").change(); @@ -103,7 +112,8 @@ POSSIBILITY OF SUCH DAMAGE.

{{ lang._('As defined by the ACME standard, Certificate Authorities (CAs) must validate that you control a domain name. This is done by using "challenges". The following challenge types are supported:') }}

  • {{ lang._('%sDNS-01:%s This is the most reliable challenge type and thus highly recommended when using this plugin. It requires that you control the DNS for your domain name and that your DNS provider is supported both %sby acme.sh%s and this plugin.') | format('', '', '', '') }}
  • -
  • {{ lang._("%sHTTP-01:%s This challenge type usually requires manual configuration and is not recommended. The DNS name used in the certificate must point to the OPNsense host where the ACME Client plugin is running on. The integrated web service will try to guess the correct settings for your setup, but this may not always work out-of-the-box. Furthermore this challenge type cannot be used to create %swildcard certificates with Let's Encrypt%s.") | format('', '', '', '') }}
  • +
  • {{ lang._("%sHTTP-01:%s This challenge type usually requires manual configuration and is not recommended. The DNS name used in the certificate must point to the OPNsense host where the ACME Client plugin is running on. The integrated web service will try to guess the correct settings for your setup, but this may not always work out-of-the-box. Furthermore this challenge type cannot be used to validate %swildcard certificates with Let's Encrypt%s.") | format('', '', '', '') }}
  • +
  • {{ lang._("%sTLS-ALPN-01:%s This works similar to the HTTP-01 challenge type and has the same requirements. It works if port 80 is unavailable. Other challenge types should be preferred. This challenge type cannot be used to validate %swildcard certificates with Let's Encrypt%s.") | format('', '', '', '') }}

{{ lang._('When experiencing issues with a challenge type, try setting the log level to "debug". Please provide full logs when %sreporting issues%s for a challenge type. You should also consider to ask the Certificate Authority for support, if you choose to use a commercial CA.') | format('', '') }}

From f3169f5f9da8cc591df3f7ea85e35328b0f71bd1 Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Wed, 8 Dec 2021 00:47:14 +0100 Subject: [PATCH 3/3] security/acme-client: fix path for pf config files --- security/acme-client/pkg-descr | 1 + .../library/OPNsense/AcmeClient/LeValidation/HttpOpnsense.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/security/acme-client/pkg-descr b/security/acme-client/pkg-descr index 87339fe03..167a9b7f4 100644 --- a/security/acme-client/pkg-descr +++ b/security/acme-client/pkg-descr @@ -18,6 +18,7 @@ Added: Fixed: * fix SFTP upload (#2671) * fix PHP error when acme.sh deploy hook returns an error (#2674) +* fix path for storing pf config files when using HTTP-01 3.5 diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/HttpOpnsense.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/HttpOpnsense.php index 32f6d3138..3b2d7a3c9 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/HttpOpnsense.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/HttpOpnsense.php @@ -1,7 +1,7 @@ cert_id); + // Get configured HTTP port for local lighttpd server. $configObj = Config::getInstance()->object(); $local_http_port = $configObj->OPNsense->AcmeClient->settings->challengePort;