diff --git a/www/caddy/Makefile b/www/caddy/Makefile
index 56926373f..cde2e3915 100644
--- a/www/caddy/Makefile
+++ b/www/caddy/Makefile
@@ -1,5 +1,5 @@
PLUGIN_NAME= caddy
-PLUGIN_VERSION= 1.6.0
+PLUGIN_VERSION= 1.6.1
PLUGIN_DEPENDS= caddy-custom
PLUGIN_COMMENT= Easy to configure Reverse Proxy with Automatic HTTPS and Dynamic DNS
PLUGIN_MAINTAINER= cedrik@pischem.com
diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr
index 530635276..70b6e238b 100644
--- a/www/caddy/pkg-descr
+++ b/www/caddy/pkg-descr
@@ -25,6 +25,11 @@ DOC: https://docs.opnsense.org/manual/how-tos/caddy.html
Plugin Changelog
================
+1.6.1
+
+Add: Run Caddy as "www" user and group, by enabling "Disable Superuser" in General Settings.
+Cleanup: Validations in general.volt are all appended to their form keys, instead of triggering a Bootstrap Dialog.
+
1.6.0
* Add: New Dashboard widgets for 24.7, showing domain status and certificate validity status.
diff --git a/www/caddy/src/etc/syslog-ng.conf.d/caddy.conf b/www/caddy/src/etc/syslog-ng.conf.d/caddy.conf
index 11c96c45d..0a5eb450a 100644
--- a/www/caddy/src/etc/syslog-ng.conf.d/caddy.conf
+++ b/www/caddy/src/etc/syslog-ng.conf.d/caddy.conf
@@ -5,7 +5,7 @@
#
# Define Unix socket source for Caddy
source s_caddy {
- unix-dgram("/var/caddy/var/run/log");
+ unix-dgram("/var/run/caddy/log.sock");
};
# Parser for Caddy log levels
diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml
index 4ac84664e..4e9a159a7 100644
--- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml
+++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml
@@ -21,6 +21,13 @@
true
+
+ caddy.general.DisableSuperuser
+
+ checkbox
+
+ true
+ caddy.general.TlsEmail
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 42972f8d1..beca19c9c 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
@@ -211,6 +211,61 @@ class Caddy extends BaseModel
}
}
+ /**
+ * 6. Check that when Superuser is disabled, all ports are 1024 and above.
+ * In General settings where this triggers, a validation dialog will show the hidden validation of the domain ports.
+ * The default HTTP and HTTPS ports are not allowed to be empty, since then they are 80 and 443.
+ * Domain ports are allowed to be empty, since then they have the same value as the HTTP and HTTPS default ports.
+ * Any value that is below 1024 will trigger the validation.
+ */
+ private function checkSuperuserPorts($messages)
+ {
+ if ((string)$this->general->DisableSuperuser === '1') {
+ $httpPort = !empty((string)$this->general->HttpPort) ? (string)$this->general->HttpPort : 80;
+ $httpsPort = !empty((string)$this->general->HttpsPort) ? (string)$this->general->HttpsPort : 443;
+
+ // Check default HTTP port
+ if ($httpPort < 1024) {
+ $messages->appendMessage(new Message(
+ gettext(
+ 'Superuser is disabled, HTTP port must not be empty and must be 1024 or above.'
+ ),
+ "general.HttpPort"
+ ));
+ }
+
+ // Check default HTTPS port
+ if ($httpsPort < 1024) {
+ $messages->appendMessage(new Message(
+ gettext(
+ 'Superuser is disabled, HTTPS port must not be empty and must be 1024 or above.'
+ ),
+ "general.HttpsPort"
+ ));
+ }
+
+ // Check ports under domain configurations
+ foreach ($this->reverseproxy->reverse->iterateItems() as $item) {
+ $fromPort = !empty((string)$item->FromPort) ? (string)$item->FromPort : null;
+
+ if ($fromPort !== null && $fromPort < 1024) {
+ $messages->appendMessage(new Message(
+ gettext(
+ 'Superuser is disabled, port must be empty or must be 1024 or above.'
+ ),
+ $item->__reference . ".FromPort"
+ ));
+ $messages->appendMessage(new Message(
+ gettext(
+ 'Ports in "Reverse Proxy - Domains" must be empty or must be 1024 or above.'
+ ),
+ "general.DisableSuperuser"
+ ));
+ }
+ }
+ }
+ }
+
// Perform the actual validation
public function performValidation($validateFullModel = false)
{
@@ -234,9 +289,13 @@ class Caddy extends BaseModel
// 4. Check for ACME Email requirement
$this->checkAcmeEmailAutoHttps($messages);
+
// 5. Check for TLS conflicts in Domain
$this->checkDisableTlsConflicts($messages);
+ // 6. Check DisableSuperuser Port conflicts
+ $this->checkSuperuserPorts($messages);
+
return $messages;
}
}
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 f70f54857..3d66238d0 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
@@ -79,6 +79,7 @@
+ 101
diff --git a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/setup.sh b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/setup.sh
index 334bd5672..e2d00c66a 100755
--- a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/setup.sh
+++ b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/setup.sh
@@ -1,26 +1,71 @@
#!/bin/sh
+#
+# Copyright (c) 2023-2024 Cedrik Pischem
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+# The directories are created as root:www with rwx permissions for both,
+# so the user can change in the GUI if caddy runs as root or www
+# If only ports 1024 and above are used, caddy can run as www user and group.
+
# Define directories
-CADDY_DIR="/usr/local/etc/caddy"
-CADDY_CERTS_DIR="/var/db/caddy/data/caddy/certificates/temp"
-CADDY_LOG_DIR="/var/log/caddy/access"
-CADDY_CONF_DIR="${CADDY_DIR}/caddy.d"
-
-# Create custom directories with appropriate permissions
-mkdir -p "${CADDY_CERTS_DIR}"
-chown -R root:wheel "${CADDY_CERTS_DIR}"
-chmod -R 600 "${CADDY_CERTS_DIR}"
-
-mkdir -p "${CADDY_LOG_DIR}"
-chown -R root:wheel "${CADDY_LOG_DIR}"
-chmod -R 750 "${CADDY_LOG_DIR}"
+CADDY_CONF_DIR="/usr/local/etc/caddy"
+CADDY_DATA_DIR="/var/db/caddy"
+CADDY_LOG_DIR="/var/log/caddy"
+CADDY_RUN_DIR="/var/run/caddy"
+CADDY_CONF_CUSTOM_DIR="${CADDY_CONF_DIR}/caddy.d"
+CADDY_DATA_CUSTOM_DIR="${CADDY_DATA_DIR}/data/caddy/certificates/temp"
+CADDY_LOG_CUSTOM_DIR="${CADDY_LOG_DIR}/access"
mkdir -p "${CADDY_CONF_DIR}"
-chown -R root:wheel "${CADDY_CONF_DIR}"
-chmod -R 750 "${CADDY_CONF_DIR}"
+mkdir -p "${CADDY_DATA_DIR}"
+mkdir -p "${CADDY_LOG_DIR}"
+mkdir -p "${CADDY_RUN_DIR}"
+mkdir -p "${CADDY_CONF_CUSTOM_DIR}"
+mkdir -p "${CADDY_DATA_CUSTOM_DIR}"
+mkdir -p "${CADDY_LOG_CUSTOM_DIR}"
-# Format and overwrite the Caddyfile
-(cd "${CADDY_DIR}" && /usr/local/bin/caddy fmt --overwrite)
+chown -R root:www "${CADDY_CONF_DIR}"
+chown -R root:www "${CADDY_DATA_DIR}"
+chown -R root:www "${CADDY_LOG_DIR}"
+chown -R root:www "${CADDY_RUN_DIR}"
-# Write custom certs from the OPNsense Trust Store into a directory where Caddy can read them
+# Directories need execute permissions to be accessible
+find "${CADDY_CONF_DIR}" -type d -exec chmod 770 {} +
+find "${CADDY_DATA_DIR}" -type d -exec chmod 770 {} +
+find "${CADDY_LOG_DIR}" -type d -exec chmod 770 {} +
+find "${CADDY_RUN_DIR}" -type d -exec chmod 770 {} +
+
+# Files can have read/write permissions
+find "${CADDY_CONF_DIR}" -type f -exec chmod 660 {} +
+find "${CADDY_DATA_DIR}" -type f -exec chmod 660 {} +
+find "${CADDY_LOG_DIR}" -type f -exec chmod 660 {} +
+find "${CADDY_RUN_DIR}" -type f -exec chmod 660 {} +
+
+# Format and overwrite the Caddyfile, this makes whitespace control in jinja2 unnecessary
+(cd "${CADDY_CONF_DIR}" && /usr/local/bin/caddy fmt --overwrite)
+
+# Write custom certs from the OPNsense Trust Store to CADDY_DATA_CUSTOM_DIR
/usr/local/opnsense/scripts/OPNsense/Caddy/caddy_certs.php
diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
index c06342fc9..f93826623 100644
--- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
+++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
@@ -28,6 +28,13 @@
{% set generalSettings = helpers.getNodeByTag('Pischem.caddy.general') %}
+{# Print as comments if Caddy runs as root or www user, as information in support cases. #}
+{% if generalSettings.DisableSuperuser|default("0") == "1" %}
+# caddy_user=www
+{% else %}
+# caddy_user=root
+{% endif %}
+
# Global Options
{
{#
@@ -43,7 +50,7 @@
{% endif %}
{% endfor %}
{% endif %}
- output net unixgram//var/caddy/var/run/log {
+ output net unixgram//var/run/caddy/log.sock {
}
format json {
time_format rfc3339
diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/rc.conf.d/caddy b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/rc.conf.d/caddy
index d8ab2eda2..da6745447 100644
--- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/rc.conf.d/caddy
+++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/rc.conf.d/caddy
@@ -1,8 +1,13 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
+
{% set generalSettings = helpers.getNodeByTag('Pischem.caddy.general') %}
{% if generalSettings.enabled|default("0") == "1" %}
caddy_enable="YES"
caddy_setup="/usr/local/opnsense/scripts/OPNsense/Caddy/setup.sh"
+{% if generalSettings.DisableSuperuser|default("0") == "1" %}
+caddy_user=www
+caddy_group=www
+{% endif %}
{% else %}
caddy_enable="NO"
{% endif %}