www/caddy: Cleanup model, php (PSR-12), python (PEP 8) and jinja2 files (#4060)

* www/caddy: Remove unsused code in validation.

* www/caddy: Cleanup Caddyfile template for better readability with improved indentation.

* www/caddy: Cleanup rc.conf.d/caddy

* www/caddy: Cleanup model.

* www/caddy: Cleanup Caddyfile template some more.

* www/caddy: Roll back changes to Caddyfile structure, don't fix what ain't broken.

* www/caddy: Fix caddy_certs.php style warning. A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both.

* www/caddy: Ignore php style warnings for migration scripts, since the class names have to ignore PascalCase.

* www/caddy: Correct style of php files so that no lines exceed 120 characters.

* www/caddy: Refactor caddy_diagnostics.py and caddy_control.py for Python PEP 8. Fix: Shadows name 'action' from outer scope

* www/caddy: Changelog add code cleanup.

* www/caddy: Fix minor regression in refactored caddy_control.py script.

* www/caddy: Fix indentation of Caddyfile template for better code readability. The template gets edited and read a lot so this fix really helps with maintainability.

* www/caddy: Add changelog.

* www/caddy: Fix caddy_certs.php style issue by removing the declared function and execute logic with side effects directly.

* www/caddy: Re-add ValidationMessage to IntegerField because there are custom constraints that are not displayed by the default validation message.

* www/caddy: Add error handling to caddy_control.py when action is empty. Make use of service_action and cmd_action clearer.

* www/caddy: Remove unnecessary variable in caddy_diagnostics.py

* www/caddy: Add validation message since there is a no IP constraint. Add same constraint to subdomains.

* www/caddy: Re-add validation message to ToDomain field since multiple are allowed

* www/caddy: Re-add validation message to clientIps field since multiple are allowed
This commit is contained in:
Monviech
2024-07-10 11:38:46 +02:00
committed by GitHub
parent 7a680f60a9
commit d777a60592
12 changed files with 331 additions and 307 deletions
+5 -1
View File
@@ -28,7 +28,11 @@ Plugin Changelog
1.6.0
* Add: New Dashboard widgets for 24.7, showing domain status and certificate validity status.
* Fix: Caddyfile template fixed for IPv6 addresses and custom port.
* Cleanup: PHP files refactored for PSR-12, Python files refactored for PEP-8.
* Cleanup: Templates Caddyfile and rc.conf.d/caddy refactored for maintainability.
* Cleanup: Spurious keys removed from Caddy.xml model.
* Cleanup: Unused code removed from Caddy.php.
* Fix: Caddyfile template fixed when IPv6 addresses and ports are used in Upstream. IPv6 address wraps into brackets now.
* Add: forward_auth directive with Authelia as Authz Provider.
* Add: Default HTTP and HTTPS ports can be changed in general settings.
* Add: Introduce HTTP version to handler. HTTP/1.1, HTTP/2 and HTTP/3 can be chosen.
@@ -50,7 +50,7 @@ class DiagnosticsController extends ApiMutableModelControllerBase
// Decode JSON to PHP array
$responseArray = json_decode($response, true);
// Since errors are handled by the caddy_diagnostics script and returned as json, check for an error key in the response
// Errors are handled by the caddy_diagnostics script and returned, check for an error key in the response
if (isset($responseArray['error'])) {
return ["status" => "failed", "message" => $responseArray['message']];
}
@@ -74,7 +74,6 @@ class DiagnosticsController extends ApiMutableModelControllerBase
// Decode JSON to PHP array
$responseArray = json_decode($response, true);
// Since errors are handled by the caddy_diagnostics script and returned as json, check for an error key in the response
if (isset($responseArray['error'])) {
return ["status" => "failed", "message" => $responseArray['message']];
}
@@ -94,7 +93,6 @@ class DiagnosticsController extends ApiMutableModelControllerBase
// Decode JSON to PHP array
$responseArray = json_decode($response, true);
// Since errors are handled by the caddy_diagnostics script and returned as json, check for an error key in the response
if (isset($responseArray['error'])) {
return ["status" => "failed", "message" => $responseArray['message']];
}
@@ -26,7 +26,6 @@
* 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\Caddy\Api;
@@ -70,14 +69,15 @@ class ReverseProxyController extends ApiMutableModelControllerBase
}
/**
* Centralized and generalized helper function for searching across different sections of the reverse proxy setup.
* Generalized helper function for searching across different sections of the reverse proxy setup.
* This function mostly helps when model relation fields are used.
* It filters entries based on UUIDs provided as an argument. The section or key used for the UUID
* can be specified, allowing for direct or indirect UUID referencing.
*
* @param string $modelPath The data model path identifier, pointing to the section of the model being searched.
* @param string $uuidSearchBase The request parameter name for the comma-separated list of UUIDs to filter the search results.
* @param string|null $uuidReferenceKey The specific attribute key used to fetch the UUID for filtering. If null, defaults to the item's own UUID.
* @param string $modelPath The data model path identifier, pointing to section of model being searched.
* @param string $uuidSearchBase The request parameter name for the comma-separated list of UUIDs.
* @param string|null $uuidReferenceKey Attribute key used to fetch the UUID for filtering.
* If null, uses item's own UUID.
* @return array Filtered search results.
*/
private function searchActionHelper($modelPath, $uuidSearchBase, $uuidReferenceKey = null)
@@ -89,8 +89,12 @@ class ReverseProxyController extends ApiMutableModelControllerBase
// Define a filter function to determine which items to include based on the UUID.
$filterFunction = function ($modelItem) use ($uuidArray, $uuidReferenceKey) {
// Extract UUID from the item, using the specified UUID key if provided, otherwise default to direct UUID access.
$modelUUID = ($uuidReferenceKey !== null) ? (string)$modelItem->$uuidReferenceKey : (string)$modelItem->getAttributes()['uuid'];
// Extract UUID from the item, using the specified UUID key if provided, else default to direct UUID access.
if ($uuidReferenceKey !== null) {
$modelUUID = (string)$modelItem->$uuidReferenceKey;
} else {
$modelUUID = (string)$modelItem->getAttributes()['uuid'];
}
// Include the item if the UUID array is empty or if the item's UUID is in the array.
return empty($uuidArray) || in_array($modelUUID, $uuidArray, true);
};
@@ -245,8 +249,10 @@ class ReverseProxyController extends ApiMutableModelControllerBase
{
if ($this->request->isPost()) {
$postData = $this->request->getPost();
if (isset($postData['basicauth']['basicauthpass']) && !empty(trim($postData['basicauth']['basicauthpass']))) {
if (
isset($postData['basicauth']['basicauthpass'])
&& !empty(trim($postData['basicauth']['basicauthpass']))
) {
$plainPassword = $postData['basicauth']['basicauthpass'];
$hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);
$_POST['basicauth']['basicauthpass'] = $hashedPassword;
@@ -260,8 +266,10 @@ class ReverseProxyController extends ApiMutableModelControllerBase
{
if ($this->request->isPost()) {
$postData = $this->request->getPost();
if (isset($postData['basicauth']['basicauthpass']) && !empty(trim($postData['basicauth']['basicauthpass']))) {
if (
isset($postData['basicauth']['basicauthpass'])
&& !empty(trim($postData['basicauth']['basicauthpass']))
) {
$plainPassword = $postData['basicauth']['basicauthpass'];
$hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);
$_POST['basicauth']['basicauthpass'] = $hashedPassword;
@@ -59,9 +59,15 @@ class Caddy extends BaseModel
if (isset($combos[$comboKey])) {
// Use dynamic $key for message referencing
$messages->appendMessage(new Message(
sprintf(gettext("Duplicate entry: The combination of '%s' and port '%s' is already used. Each combination of domain and port must be unique."), $fromDomain, $port),
$key . ".FromDomain", // Adjusted to use dynamic key
"DuplicateDomainPort"
sprintf(
gettext(
'Duplicate entry: The combination of %s and port %s is already used. ' .
'Each combination of domain and port must be unique.'
),
$fromDomain,
$port
),
$key . ".FromDomain"
));
} else {
$combos[$comboKey] = true;
@@ -98,9 +104,14 @@ class Caddy extends BaseModel
if (!$isValid) {
$key = $subdomain->__reference; // Dynamic key based on subdomain reference
$messages->appendMessage(new Message(
sprintf(gettext("Invalid subdomain configuration: '%s' does not fall under any configured wildcard domain."), $subdomainName),
$key . ".FromDomain", // Use dynamic key for message referencing
"InvalidSubdomain"
sprintf(
gettext(
'Invalid subdomain configuration: %s does not fall ' .
'under any configured wildcard domain.'
),
$subdomainName
),
$key . ".FromDomain"
));
}
}
@@ -140,7 +151,18 @@ class Caddy extends BaseModel
if (!empty($overlap) && $tlsAutoHttpsSetting !== 'off') {
$portOverlap = implode(', ', $overlap);
$messages->appendMessage(new Message(
sprintf(gettext('To use "Auto HTTPS", resolve these conflicting ports (%s) that are currently configured for the OPNsense WebGUI. Go to "System - Settings - Administration". To release port 80, enable "Disable web GUI redirect rule". To release port %s, change "TCP port" to a non-standard port, e.g., 8443.'), $portOverlap, $httpsPort),
sprintf(
gettext(
'To use "Auto HTTPS", resolve these conflicting ports %s ' .
'that are currently configured for the OPNsense WebGUI. ' .
'Go to "System - Settings - Administration". ' .
'To release port 80, enable "Disable web GUI redirect rule". ' .
'To release port %s, change "TCP port" to a non-standard port, ' .
'e.g., 8443.'
),
$portOverlap,
$httpsPort
),
"general.TlsAutoHttps"
));
}
@@ -193,12 +215,23 @@ class Caddy extends BaseModel
public function performValidation($validateFullModel = false)
{
$messages = parent::performValidation($validateFullModel);
// 1. Check domain-port combinations
$this->checkForUniquePortCombos($this->reverseproxy->reverse->iterateItems(), $messages);
$this->checkForUniquePortCombos(
$this->reverseproxy->reverse->iterateItems(),
$messages
);
// 2. Check that subdomains are under a wildcard or exact domain
$this->checkSubdomainsAgainstDomains($this->reverseproxy->subdomain->iterateItems(), $this->reverseproxy->reverse->iterateItems(), $messages);
$this->checkSubdomainsAgainstDomains(
$this->reverseproxy->subdomain->iterateItems(),
$this->reverseproxy->reverse->iterateItems(),
$messages
);
// 3. Check WebGUI conflicts
$this->checkWebGuiSettings($messages);
// 4. Check for ACME Email requirement
$this->checkAcmeEmailAutoHttps($messages);
// 5. Check for TLS conflicts in Domain
@@ -10,9 +10,7 @@
</enabled>
<HttpPort type="PortField"/>
<HttpsPort type="PortField"/>
<TlsEmail type="EmailField">
<ValidationMessage>Please enter a valid email address.</ValidationMessage>
</TlsEmail>
<TlsEmail type="EmailField"/>
<TlsAutoHttps type="OptionField">
<BlankDesc>On (default)</BlankDesc>
<OptionValues>
@@ -106,9 +104,7 @@
<FATAL>FATAL</FATAL>
</OptionValues>
</LogLevel>
<DynDnsSimpleHttp type="UrlField">
<ValidationMessage>Please enter a valid URL, starting with http or https.</ValidationMessage>
</DynDnsSimpleHttp>
<DynDnsSimpleHttp type="UrlField"/>
<DynDnsInterface type="InterfaceField"/>
<DynDnsInterval type="IntegerField">
<MinimumValue>1</MinimumValue>
@@ -150,17 +146,11 @@
</enabled>
<FromDomain type="HostnameField">
<Required>Y</Required>
<ValidationMessage>Please enter a valid 'from' domain.</ValidationMessage>
<IpAllowed>N</IpAllowed>
<HostWildcardAllowed>Y</HostWildcardAllowed>
<FqdnWildcardAllowed>Y</FqdnWildcardAllowed>
<ZoneRootAllowed>N</ZoneRootAllowed>
<ValidationMessage>Please enter a valid domain name.</ValidationMessage>
</FromDomain>
<FromPort type="PortField">
<ValidationMessage>Please enter a valid 'from' port number.</ValidationMessage>
<EnableWellKnown>Y</EnableWellKnown>
<EnableRanges>N</EnableRanges>
</FromPort>
<FromPort type="PortField"/>
<accesslist type="ModelRelationField">
<Model>
<reverseproxy>
@@ -189,10 +179,7 @@
<CustomCertificate type="CertificateField"/>
<AccessLog type="BooleanField"/>
<DynDns type="BooleanField"/>
<AcmePassthrough type="HostnameField">
<ValidationMessage>Please enter a valid 'to' domain or IP address.</ValidationMessage>
<IpAllowed>Y</IpAllowed>
</AcmePassthrough>
<AcmePassthrough type="HostnameField"/>
<DisableTls type="BooleanField"/>
</reverse>
<subdomain type="ArrayField">
@@ -213,8 +200,8 @@
</reverse>
<FromDomain type="HostnameField">
<Required>Y</Required>
<ValidationMessage>Please enter a valid 'from' Subdomain that is based upon the wildcard domain.</ValidationMessage>
<ZoneRootAllowed>N</ZoneRootAllowed>
<IpAllowed>N</IpAllowed>
<ValidationMessage>Please enter a valid domain name.</ValidationMessage>
</FromDomain>
<accesslist type="ModelRelationField">
<Model>
@@ -241,10 +228,7 @@
<Required>Y</Required>
</description>
<DynDns type="BooleanField"/>
<AcmePassthrough type="HostnameField">
<ValidationMessage>Please enter a valid 'to' domain or IP address.</ValidationMessage>
<IpAllowed>Y</IpAllowed>
</AcmePassthrough>
<AcmePassthrough type="HostnameField"/>
</subdomain>
<handle type="ArrayField">
<enabled type="BooleanField">
@@ -282,7 +266,7 @@
</HandleType>
<HandlePath type="TextField">
<Mask>/^(\/.*)?$/u</Mask>
<ValidationMessage>Please enter a valid 'Handle Path' that starts with '/'.</ValidationMessage>
<ValidationMessage>Please enter a valid Path that starts with '/'.</ValidationMessage>
</HandlePath>
<header type="ModelRelationField">
<Model>
@@ -297,19 +281,14 @@
</header>
<ToDomain type="HostnameField">
<Required>Y</Required>
<ValidationMessage>Please enter a valid 'to' domain or IP address.</ValidationMessage>
<IpAllowed>Y</IpAllowed>
<FieldSeparator>,</FieldSeparator>
<AsList>Y</AsList>
<ValidationMessage>Please enter one or multiple valid IP addresses, hostnames or FQDNs.</ValidationMessage>
</ToDomain>
<ToPort type="PortField">
<ValidationMessage>Please enter a valid 'to' port number.</ValidationMessage>
<EnableWellKnown>Y</EnableWellKnown>
<EnableRanges>N</EnableRanges>
</ToPort>
<ToPort type="PortField"/>
<ToPath type="TextField">
<Mask>/^(\/.*)?$/u</Mask>
<ValidationMessage>Please enter a valid 'Backend Path' that starts with '/'.</ValidationMessage>
<ValidationMessage>Please enter a valid Path that starts with '/'.</ValidationMessage>
</ToPath>
<PassiveHealthFailDuration type="IntegerField">
<MinimumValue>1</MinimumValue>
@@ -351,30 +330,21 @@
<HttpTlsTrustedCaCerts type="CertificateField">
<Type>ca</Type>
</HttpTlsTrustedCaCerts>
<HttpTlsServerName type="HostnameField">
<ValidationMessage>Please enter a valid hostname or IP address.</ValidationMessage>
<IpAllowed>Y</IpAllowed>
<HostWildcardAllowed>Y</HostWildcardAllowed>
<FqdnWildcardAllowed>Y</FqdnWildcardAllowed>
<ZoneRootAllowed>N</ZoneRootAllowed>
</HttpTlsServerName>
<HttpTlsServerName type="HostnameField"/>
<description type="DescriptionField">
<Required>Y</Required>
</description>
</handle>
<accesslist type="ArrayField">
<accesslistName type="TextField">
<accesslistName type="DescriptionField">
<Required>Y</Required>
<Mask>/^([\t\n\v\f\r 0-9a-zA-Z.,_*-\x{00A0}-\x{FFFF}]){1,255}$/u</Mask>
<ValidationMessage>Please provide a valid Access List Name.</ValidationMessage>
</accesslistName>
<clientIps type="NetworkField">
<Required>Y</Required>
<NetMaskAllowed>Y</NetMaskAllowed>
<FieldSeparator>,</FieldSeparator>
<AsList>Y</AsList>
<Strict>Y</Strict>
<ValidationMessage>Please enter valid IP address(es) or network(s), separated by commas.</ValidationMessage>
<ValidationMessage>Please enter one or multiple valid IP addresses or networks.</ValidationMessage>
</clientIps>
<accesslistInvert type="BooleanField"/>
<HttpResponseCode type="IntegerField">
@@ -31,7 +31,9 @@ namespace OPNsense\Caddy\Migrations;
use OPNsense\Base\BaseModelMigration;
use OPNsense\Core\Config;
// @codingStandardsIgnoreStart
class M1_1_3 extends BaseModelMigration
// @codingStandardsIgnoreEnd
{
public function run($model)
{
@@ -31,7 +31,9 @@ namespace OPNsense\Caddy\Migrations;
use OPNsense\Base\BaseModelMigration;
use OPNsense\Core\Config;
// @codingStandardsIgnoreStart
class M1_1_8 extends BaseModelMigration
// @codingStandardsIgnoreEnd
{
public function run($model)
{
@@ -2,8 +2,7 @@
<?php
/*
* Copyright (C) 2023-2024 Cedrik Pischem
* Copyright (C) 2015 Deciso B.V.
* Copyright (C) 2024 Cedrik Pischem
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,55 +27,50 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("config.inc");
require_once "config.inc";
use OPNsense\Core\Config;
$configObj = Config::getInstance()->object();
$temp_dir = '/var/db/caddy/data/caddy/certificates/temp/';
function extract_and_save_certificates($configObj, $temp_dir)
{
// 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_chain = $cert_content;
// 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_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);
$cert_chain .= "\n" . $ca_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);
$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);
$cert_chain .= "\n" . $parent_ca_content;
break;
}
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);
$cert_chain .= "\n" . $parent_ca_content;
break;
}
}
}
}
}
// Save the certificate chain and private key
file_put_contents($temp_dir . $cert_refid . '.pem', $cert_chain);
file_put_contents($temp_dir . $cert_refid . '.key', $key_content);
}
// Traverse through CA certificates and save them
foreach ($configObj->ca as $ca) {
$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);
}
// Save the certificate chain and private key
file_put_contents($temp_dir . $cert_refid . '.pem', $cert_chain);
file_put_contents($temp_dir . $cert_refid . '.key', $key_content);
}
extract_and_save_certificates($configObj, $temp_dir);
// Traverse through CA certificates and save them
foreach ($configObj->ca as $ca) {
$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);
}
@@ -30,19 +30,23 @@ import subprocess
import json
import sys
def run_service_command(action, action_message):
def run_service_command(service_action, action_message):
result = {"message": action_message}
if action == "validate":
if service_action == "validate":
try:
# Validate the Caddyfile with explicit --config flag, capturing both stdout and stderr
validation_output = subprocess.check_output(["caddy", "validate", "--config", "/usr/local/etc/caddy/Caddyfile"], stderr=subprocess.STDOUT, text=True)
validation_output = subprocess.check_output(
["caddy", "validate", "--config", "/usr/local/etc/caddy/Caddyfile"], stderr=subprocess.STDOUT,
text=True)
if "Valid configuration" in validation_output:
result["status"] = "ok"
result["message"] = "Caddy configuration is valid."
else:
# Search for the specific error message
error_msg = next((line for line in validation_output.split('\n') if line.startswith("Error:")), "Caddy configuration is not valid.")
error_msg = next((line for line in validation_output.split('\n') if line.startswith("Error:")),
"Caddy configuration is not valid.")
result["status"] = "failed"
result["message"] = error_msg
except subprocess.CalledProcessError as e:
@@ -52,7 +56,7 @@ def run_service_command(action, action_message):
result["message"] = error_msg
else:
try:
subprocess.run(["service", "caddy", action], check=True)
subprocess.run(["service", "caddy", service_action], check=True)
result["status"] = "ok"
except subprocess.CalledProcessError as e:
result["status"] = "failed"
@@ -60,29 +64,36 @@ def run_service_command(action, action_message):
return json.dumps(result)
# Updated actions dictionary
# "cmd_action": "service_action"
actions = {
"start": "start",
"stop": "stop",
"restart": "restart",
"reload": "reloadssl", # Forces the reload even if the config in the Caddyfile is unchanged, using an extra command of the rc.d script, forcing certificates in the filesystem to reload.
"reload": "reloadssl",
# Reloadssl reloads even if the config in the Caddyfile is unchanged, using an extra command of the rc.d script,
# forcing certificates in the filesystem to be reloaded.
"validate": "validate" # Validate action
}
if __name__ == "__main__":
action = sys.argv[1] # Get the action from the command-line argument
if action in actions:
service_action = actions[action]
message = f"{action.capitalize()}ing Caddy service" if action != "validate" else "Validating Caddy configuration"
if len(sys.argv) > 1:
action = sys.argv[1] # Get the action from the command-line argument
if action in actions:
cmd_action = action
service_action = actions[action]
message = f"{cmd_action.capitalize()} Caddy service"
# Call setup script for 'validate' and 'reloadssl' actions
# This is needed because the setup script triggers the caddy_certs.php script, which exports all certificates into the filesystem.
# Caddy reloads certificates when reloadssl is used. Because it is a non standard command, the caddy_setup script will not be triggered in /etc/rc.conf.d/caddy.
# The validate command needs it to make sure all certificates are in the filesystem, because otherwise the validation fails.
if service_action in ["validate", "reloadssl"]:
subprocess.run(["/usr/local/opnsense/scripts/OPNsense/Caddy/setup.sh"], check=True)
# Call setup script for 'validate' and 'reloadssl' actions. This is needed because the setup script triggers
# the caddy_certs.php script, which exports all certificates into the filesystem. Caddy reloads certificates
# when reloadssl is used. Because it is a non standard command, the caddy_setup script will not be triggered
# in /etc/rc.conf.d/caddy. The validate command needs it to make sure all certificates are in the filesystem,
# because otherwise the validation fails.
if service_action in ["validate", "reloadssl"]:
subprocess.run(["/usr/local/opnsense/scripts/OPNsense/Caddy/setup.sh"], check=True)
# Continue with the service action
print(run_service_command(service_action, message))
print(run_service_command(service_action, message))
else:
print(json.dumps({"status": "failed", "message": f"Unknown action: {action}"}))
else:
print(json.dumps({"status": "failed", "message": f"Unknown action: {action}"}))
print(json.dumps({"status": "failed", "message": "No action provided"}))
@@ -33,6 +33,7 @@ import subprocess
import asyncio
from datetime import datetime
# Function to show the Caddy configuration from a JSON file
def show_caddy_config():
config_path = "/var/db/caddy/config/caddy/autosave.json"
@@ -48,10 +49,12 @@ def show_caddy_config():
except FileNotFoundError:
print(json.dumps({"error": "File not found", "message": "Caddy autosave.json configuration file not found"}))
except json.JSONDecodeError:
print(json.dumps({"error": "Invalid JSON", "message": "Error decoding the Caddy autosave.json, the file is not valid JSON"}))
print(json.dumps(
{"error": "Invalid JSON", "message": "Error decoding the Caddy autosave.json, the file is not valid JSON"}))
except Exception as e:
print(json.dumps({"error": "General Error", "message": str(e)}))
def show_caddyfile():
caddyfile_path = "/usr/local/etc/caddy/Caddyfile"
@@ -65,6 +68,7 @@ def show_caddyfile():
except Exception as e:
print(json.dumps({"error": "General Error", "message": str(e)}))
# Function to extract certificate information using openssl command
async def extract_certificate_info(cert_path):
try:
@@ -105,6 +109,7 @@ async def extract_certificate_info(cert_path):
except Exception as e:
raise RuntimeError(f"Error extracting certificate info for {cert_path}: {str(e)}")
# Function to find certificates and create tasks to extract info
async def find_certificates(base_dir):
tasks = []
@@ -123,6 +128,7 @@ async def find_certificates(base_dir):
results = await asyncio.gather(*tasks, return_exceptions=True)
return [result for result in results if not isinstance(result, Exception)]
# Function to show certificates, processing all found in the given directory
async def show_certificates():
# Function to show certificates, processing all found in the given directory
@@ -136,25 +142,26 @@ async def show_certificates():
except Exception as e:
print(json.dumps({"error": "General Error", "message": str(e)}))
# Action handler
def perform_action(action):
def perform_action(cmd_action):
actions = {
"config": show_caddy_config,
"caddyfile": show_caddyfile,
"certificate": lambda: asyncio.run(show_certificates())
}
action_func = actions.get(action)
action_func = actions.get(cmd_action)
if action_func:
action_func()
else:
# Output error details in JSON format if action is unknown
print(json.dumps({"error": "Unknown Action", "message": f"Unknown action: {action}"}))
print(json.dumps({"error": "Unknown Action", "message": f"Unknown action: {cmd_action}"}))
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
perform_action(action)
perform_action(sys.argv[1])
else:
# Output error details in JSON format if no action is specified
print(json.dumps({"error": "No Action Specified", "message": "No action specified"}))
@@ -37,11 +37,11 @@
#}
log {
{% if generalSettings.LogAccessPlain|default("0") == "0" %}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" and reverse.AccessLog|default("0") == "1" %}
include http.log.access.{{ reverse['@uuid'] }}
{% endif %}
{% endfor %}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" and reverse.AccessLog|default("0") == "1" %}
include http.log.access.{{ reverse['@uuid'] }}
{% endif %}
{% endfor %}
{% endif %}
output net unixgram//var/caddy/var/run/log {
}
@@ -49,7 +49,7 @@
time_format rfc3339
}
{% if generalSettings.LogLevel %}
level {{ generalSettings.LogLevel }}
level {{ generalSettings.LogLevel }}
{% endif %}
}
@@ -89,10 +89,10 @@
{% if hasAccessList or hasLogCredentials %}
servers {
{% if hasAccessList %}
trusted_proxies static {{ accessList.clientIps.split(',') | join(' ') }}
trusted_proxies static {{ accessList.clientIps.split(',') | join(' ') }}
{% endif %}
{% if hasLogCredentials %}
log_credentials
log_credentials
{% endif %}
}
{% endif %}
@@ -175,11 +175,11 @@
{% endif %}
domains {
{% for domain in dynDnsDomains %}
{{ domain }}
{{ domain }}
{% endfor %}
}
{% if dynDnsSimpleHttp %}
ip_source simple_http {{ dynDnsSimpleHttp }}
ip_source simple_http {{ dynDnsSimpleHttp }}
{% endif %}
{% if dynDnsInterface %}
{% set physicalInterfaceNames = [] %}
@@ -189,16 +189,16 @@
ip_source interface {{ physicalInterfaceNames | join(',') }}
{% endif %}
{% if dynDnsCheckInterval %}
check_interval {{ dynDnsCheckInterval }}s
check_interval {{ dynDnsCheckInterval }}s
{% endif %}
{% if dynDnsIpVersions %}
versions {{ dynDnsIpVersions }}
versions {{ dynDnsIpVersions }}
{% endif %}
{% if dynDnsTtl %}
ttl {{ dynDnsTtl }}s
ttl {{ dynDnsTtl }}s
{% endif %}
{% if dynDnsUpdateOnly|default("0") == "1" %}
update_only
update_only
{% endif %}
}
{% endif %}
@@ -211,11 +211,11 @@
#}
{% set emailValue = helpers.toList('Pischem.caddy.general.TlsEmail') | first %}
{% if emailValue %}
email {{ emailValue }}
email {{ emailValue }}
{% endif %}
{% set autoHttpsValue = helpers.toList('Pischem.caddy.general.TlsAutoHttps') | first %}
{% if autoHttpsValue %}
auto_https {{ autoHttpsValue }}
auto_https {{ autoHttpsValue }}
{% endif %}
{#
# Important: Grace Period influences how fast the server can finish reloads with open connections, by forcing termination.
@@ -299,8 +299,8 @@
# It uses a 'handle' object that specifies which headers to manipulate based on their @UUIDs.
# Each handle can have multiple of these HTTP headers assigned.
# Parameters:
# @param handle (@object):
# - @uuid (@string)
# @param handle (object):
# - @uuid (string)
# - HeaderUpDown (string): Determines the direction of the header.
# - HeaderType (string): Specifies the name of the header.
# - HeaderValue (string, optional): The new value to set for the header, if any.
@@ -341,15 +341,15 @@
# Purpose: Sets up the handle with the reverse proxy configurations. The TLS Settings are generated here for the Upstream.
# Integrated Macros: header_manipulation
# Parameters:
# @param handle (@object):
# - @uuid (@string)
# @param handle (object):
# - @uuid (string)
# - HandleType (string): Specifies the handling strategy.
# - HandlePath (string, optional): The path the handle should match on.
# - ToDomain (string): Target domain for the reverse proxy.
# - ToPort (string, optional): Target port on the ToDomain.
# - ToPath (string, optional): Destination path on the ToDomain.
# - HttpTls (boolean, optional): Enable TLS for the connection.
# - HttpNtlm (boolean, optional): Enable NTLM authentication for the connection.
# - HttpNtlm (boolean, optional): Enable NTLM authentication for the connection. Not all HTTP options apply to NTLM.
# - HttpTlsInsecureSkipVerify (boolean, optional): If true, the server's SSL certificate is not verified.
# - HttpTlsTrustedCaCerts (string, optional): The config extracted name of a CA certificate.
# - HttpTlsServerName (string, optional): Specifies the server name for the TLS handshake.
@@ -363,7 +363,7 @@
{% include "OPNsense/Caddy/includeAuthProvider" %}
{% endif %}
{% if handle.ToPath|default("") != "" %}
rewrite * {{ handle.ToPath }}{uri}
rewrite * {{ handle.ToPath }}{uri}
{% endif %}
reverse_proxy {% for domain in handle.ToDomain.split(',') %}
{# Check if the domain is IPv6 and wrap in square brackets if necessary #}
@@ -373,22 +373,22 @@
{% endfor %} {
{{ header_manipulation(handle) }}
{% if handle.PassiveHealthFailDuration|default("") %}
fail_duration {{ handle.PassiveHealthFailDuration }}s
fail_duration {{ handle.PassiveHealthFailDuration }}s
{% endif %}
{% if handle.HttpTls|default("0") == "1" or handle.HttpTlsInsecureSkipVerify|default("0") == "1" or handle.HttpTlsTrustedCaCerts or handle.HttpTlsServerName or handle.HttpVersion or handle.HttpKeepalive %}
{% if handle.HttpNtlm|default("0") == "1" %}
transport http_ntlm {
{% if handle.HttpTls|default("0") == "1" %}
tls
tls
{% endif %}
{% if handle.HttpTlsInsecureSkipVerify|default("0") == "1" %}
tls_insecure_skip_verify
tls_insecure_skip_verify
{% endif %}
{% if handle.HttpTlsTrustedCaCerts %}
tls_trust_pool file /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
tls_trust_pool file /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
{% endif %}
{% if handle.HttpTlsServerName %}
tls_server_name {{ handle.HttpTlsServerName }}
tls_server_name {{ handle.HttpTlsServerName }}
{% endif %}
}
{% else %}
@@ -406,16 +406,16 @@
{% endif %}
{% endif %}
{% if handle.HttpTls|default("0") == "1" %}
tls
tls
{% endif %}
{% if handle.HttpTlsInsecureSkipVerify|default("0") == "1" %}
tls_insecure_skip_verify
tls_insecure_skip_verify
{% endif %}
{% if handle.HttpTlsTrustedCaCerts %}
tls_trust_pool file /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
tls_trust_pool file /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
{% endif %}
{% if handle.HttpTlsServerName %}
tls_server_name {{ handle.HttpTlsServerName }}
tls_server_name {{ handle.HttpTlsServerName }}
{% endif %}
}
{% endif %}
@@ -431,10 +431,10 @@
# only get to the reverse proxy, when the access list matches. Invert is also possible, to explicitely deny IPs.
# The assembly is handled by the "Section: Reverse Proxy Configurations".
# Parameters:
# @param accesslist (@object):
# - @uuid (@string)
# - clientIps (@string): A comma-separated list of client IP addresses
# - invert (@boolean): A flag that inverts the logic of the access list
# @param accesslist (object):
# - uuid (string)
# - clientIps (string): A comma-separated list of client IP addresses
# - invert (boolean): A flag that inverts the logic of the access list
#}
{% macro access_list_configuration(accesslist, invert) %}
{% set client_ips = accesslist.clientIps.split(',') %}
@@ -448,11 +448,11 @@
# Macro: basicauth_configuration
# Purpose: Implements basic authentication with a username and password for access.
# Parameters:
# @param basicauth_uuids (@string): A comma-separated list of UUIDs, each UUID corresponding to
# @param basicauth_uuids (string): A comma-separated list of UUIDs, each UUID corresponding to
# a specific user credentials (username and password).
# - @uuid (@string)
# - basicauthuser (@string): The username required for authentication.
# - basicauthpass (@string): The password associated with the username.
# - @uuid (string)
# - basicauthuser (string): The username required for authentication.
# - basicauthpass (string): The password associated with the username.
#}
{% macro basicauth_configuration(basicauth_uuids) %}
{% if basicauth_uuids %}
@@ -482,139 +482,139 @@
# - Order of Wildcard Domains and Subdomains: Handles for wildcard domains come after all subdomains.
#}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" %}
# Reverse Proxy Domain: "{{ reverse['@uuid'] }}"
{# The default are encrypted connections, uncencrypted connections have to render http:// #}
{% if reverse.DisableTls|default("0") == "1" %}http://{% endif %}{{ reverse.FromDomain|default("") }}{% if reverse.FromPort %}:{{ reverse.FromPort }}{% endif %} {
{% if reverse.AccessLog|default("0") == "1" %}
{% if generalSettings.LogAccessPlain|default("0") == "0" %}
log {{ reverse['@uuid'] }}
{% else %}
log {
output file /var/log/caddy/access/{{ reverse['@uuid'] }}.log {
roll_keep_for {{ generalSettings.LogAccessPlainKeep|default("10") }}d
}
}
{% endif %}
{% endif %}
{% set customCert = reverse.CustomCertificate|default("") %}
{% set dnsChallenge = reverse.DnsChallenge|default("0") %}
{{ tls_configuration(dnsProvider, dnsApiKey, customCert, dnsChallenge, dnsSecretApiKey, TlsDnsOptionalField1, TlsDnsOptionalField2, TlsDnsOptionalField3, TlsDnsOptionalField4) }}
{% if not reverse.accesslist %}
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% endif %}
{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
{% if subdomain.enabled|default("0") == "1" and subdomain.reverse == reverse['@uuid'] %}
@{{ subdomain['@uuid'] }} {
host {{ subdomain.FromDomain }}
}
handle @{{ subdomain['@uuid'] }} {
{% if not subdomain.accesslist %}
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% endif %}
{% if subdomain.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% if reverse.enabled|default("0") == "1" %}
# Reverse Proxy Domain: "{{ reverse['@uuid'] }}"
{# The default are encrypted connections, uncencrypted connections have to render http:// #}
{% if reverse.DisableTls|default("0") == "1" %}http://{% endif %}{{ reverse.FromDomain|default("") }}{% if reverse.FromPort %}:{{ reverse.FromPort }}{% endif %} {
{% if reverse.AccessLog|default("0") == "1" %}
{% if generalSettings.LogAccessPlain|default("0") == "0" %}
log {{ reverse['@uuid'] }}
{% else %}
log {
output file /var/log/caddy/access/{{ reverse['@uuid'] }}.log {
roll_keep_for {{ generalSettings.LogAccessPlainKeep|default("10") }}d
}
}
{% endif %}
{% endif %}
{% set customCert = reverse.CustomCertificate|default("") %}
{% set dnsChallenge = reverse.DnsChallenge|default("0") %}
{{ tls_configuration(dnsProvider, dnsApiKey, customCert, dnsChallenge, dnsSecretApiKey, TlsDnsOptionalField1, TlsDnsOptionalField2, TlsDnsOptionalField3, TlsDnsOptionalField4) }}
{% if not reverse.accesslist %}
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% endif %}
{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
{% if subdomain.enabled|default("0") == "1" and subdomain.reverse == reverse['@uuid'] %}
@{{ subdomain['@uuid'] }} {
host {{ subdomain.FromDomain }}
}
handle @{{ subdomain['@uuid'] }} {
{% if not subdomain.accesslist %}
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% endif %}
{% if subdomain.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
}
{% else %}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% if subdomain.accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% if reverse.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
}
{% else %}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{% if accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
{% endfor %}
}
{% else %}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% if subdomain.accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
}
{% endif %}
{% endfor %}
{% if reverse.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
}
{% else %}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{% if accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
}
{% endif %}
{% endfor %}
import /usr/local/etc/caddy/caddy.d/*.conf
@@ -1,13 +1,8 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
{% if helpers.exists('Pischem.caddy.general.enabled') %}
{%- set general_enabled = helpers.toList('Pischem.caddy.general.enabled') | first %}
{%- if general_enabled == '1' %}
{% set generalSettings = helpers.getNodeByTag('Pischem.caddy.general') %}
{% if generalSettings.enabled|default("0") == "1" %}
caddy_enable="YES"
# Path to the Caddy setup script
caddy_setup="/usr/local/opnsense/scripts/OPNsense/Caddy/setup.sh"
{%- else %}
caddy_enable="NO"
{%- endif %}
{%- else %}
{% else %}
caddy_enable="NO"
{% endif %}