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 @@ + 10 1 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 %}