%PDF- %PDF-
| Direktori : /proc/self/root/backups/router/usr/local/etc/inc/plugins.inc.d/ |
| Current File : //proc/self/root/backups/router/usr/local/etc/inc/plugins.inc.d/webgui.inc |
<?php
/*
* Copyright (C) 2016-2024 Franco Fichtner <franco@opnsense.org>
* Copyright (C) 2004-2007 Scott Ullrich <sullrich@gmail.com>
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
* 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.
*/
function webgui_configure()
{
return [
'early' => ['webgui_configure_do'],
'local' => ['webgui_configure_do'],
'newwanip' => ['webgui_configure_do:2'],
'webgui' => ['webgui_configure_do'],
];
}
function webgui_services()
{
return [[
'pidfile' => '/var/run/lighty-webConfigurator.pid',
'description' => gettext('Web GUI'),
'php' => ['restart' => ['webgui_configure_defer']],
'name' => 'webgui',
'locked' => true,
]];
}
function webgui_configure_defer($verbose = false, $sleep = 3)
{
service_log('Starting web GUI...', $verbose);
configdp_run('webgui restart', [$sleep]);
service_log("deferred.\n", $verbose);
}
function webgui_configure_do($verbose = false, $interface_map = null)
{
global $config;
if (!plugins_argument_map($interface_map)) {
return;
}
$interfaces = [];
if (!empty($config['system']['webgui']['interfaces'])) {
$interfaces = explode(',', $config['system']['webgui']['interfaces']);
/* place loopback with good IPv4 first for server.bind */
array_unshift($interfaces, 'lo0');
}
if (!empty($interface_map)) {
/*
* Match explicit interfaces reload request to bound interfaces.
* If none are configured we do not reload either as we are bound
* to all.
*/
if (!count(array_intersect($interface_map, $interfaces))) {
return;
}
}
service_log('Starting web GUI...', $verbose);
$listeners = count($interfaces) ? [] : ['0.0.0.0', '::'];
foreach (interfaces_addresses($interfaces) as $tmpaddr => $info) {
if (!$info['bind'] || ($info['family'] == 'inet6' && $info['tentative'])) {
continue;
}
$listeners[] = $tmpaddr;
}
if (!count($listeners)) {
service_log("empty.\n", $verbose);
return;
}
chdir('/usr/local/www');
/* defaults */
$portarg = '80';
$crt = '';
$key = '';
$ca = '';
/* non-standard port? */
if (isset($config['system']['webgui']['port']) && $config['system']['webgui']['port'] != '') {
$portarg = "{$config['system']['webgui']['port']}";
}
if ($config['system']['webgui']['protocol'] == "https") {
$cert =& lookup_cert($config['system']['webgui']['ssl-certref']);
if (!is_array($cert) && !$cert['crt'] && !$cert['prv']) {
/* XXX for now only if a certificate is not available */
webgui_create_selfsigned(false);
$cert =& lookup_cert($config['system']['webgui']['ssl-certref']);
}
$crt = base64_decode($cert['crt']);
$key = base64_decode($cert['prv']);
if (!$config['system']['webgui']['port']) {
$portarg = '443';
}
$ca = ca_chain($cert);
}
$confdir = '/usr/local/etc/lighttpd_webgui';
$conftxt = webgui_generate_config($portarg, $crt, $key, $ca, $listeners, $confdir);
$fp = fopen("{$confdir}/lighttpd.conf", 'a+e');
if (!$fp || !flock($fp, LOCK_EX | LOCK_NB)) {
fclose($fp);
service_log("locked.\n", $verbose);
return;
}
ftruncate($fp, 0);
fwrite($fp, $conftxt);
fflush($fp);
/* regenerate the php.ini files in case the setup has changed */
configd_run('template reload OPNsense/WebGui');
/* flush Phalcon volt templates */
foreach (glob('/usr/local/opnsense/mvc/app/cache/*.php') as $filename) {
unlink($filename);
}
/* we need a marker file for development mode to ensure quick action */
if (empty($config['system']['deployment'])) {
@unlink('/var/run/development');
} else {
@touch('/var/run/development');
}
/* stop the frontend when the generation was completed */
killbypid('/var/run/lighty-webConfigurator.pid', 'INT');
/* start lighttpd (the flock may be overkill but this has always been fragile so keep it) */
if (mwexecf('/usr/local/bin/flock -ne /var/run/lighty-webConfigurator.pid /usr/local/sbin/lighttpd -f %s/lighttpd.conf', [$confdir])) {
service_log("failed.\n", $verbose);
} else {
service_log("done.\n", $verbose);
}
flock($fp, LOCK_UN);
fclose($fp);
}
function webgui_create_selfsigned($verbose = false)
{
global $config;
$a_ca = &config_read_array('ca');
$a_cert = &config_read_array('cert');
service_log('Creating self-signed web GUI certificate...', $verbose);
$cert = [];
$cert['refid'] = uniqid();
$cert['descr'] = 'Web GUI TLS certificate';
$dns = $config['system']['hostname'] . "." . $config['system']['domain'];
mwexecf(
'/usr/local/bin/openssl req -new -extensions server_cert ' .
'-config /usr/local/etc/ssl/opnsense.cnf ' .
'-newkey rsa:4096 -sha256 -days 397 -nodes -x509 ' .
'-subj "/CN="%s"/C=NL/ST=Zuid-Holland/L=Middelharnis/O="%s" self-signed web certificate" ' .
'-addext "subjectAltName = DNS:"%s -keyout /tmp/ssl.key -out /tmp/ssl.crt',
[$dns, product::getInstance()->name(), $dns]
);
$crt = file_get_contents('/tmp/ssl.crt');
$key = file_get_contents('/tmp/ssl.key');
unlink('/tmp/ssl.key');
unlink('/tmp/ssl.crt');
cert_import($cert, $crt, $key);
$a_cert[] = $cert;
$config['system']['webgui']['ssl-certref'] = $cert['refid'];
write_config('Created web GUI TLS certificate');
service_log("done.\n", $verbose);
}
function webgui_generate_config($port, $cert, $key, $ca, $listeners, $confdir)
{
global $config;
$cert_location = "{$confdir}/cert.pem";
$key_location = "{$confdir}/key.pem";
@mkdir('/tmp/lighttpdcompress');
shell_safe('rm -rf /tmp/lighttpdcompress/*');
$http_rewrite_rules = <<<EOD
# Phalcon ui and api routing
alias.url += ( "/ui/" => "/usr/local/opnsense/www/" )
alias.url += ( "/api/" => "/usr/local/opnsense/www/" )
url.rewrite-if-not-file = ( "^/ui/([^\?]+)(\?(.*))?" => "/ui/index.php?$3" ,
"^/api/([^\?]+)(\?(.*))?" => "/api/api.php?$3"
)
EOD;
$server_upload_dirs = "server.upload-dirs = ( \"/root/\", \"/tmp/\", \"/var/\" )\n";
$cgi_config = "cgi.assign = ( \".cgi\" => \"\" )";
$lighty_port = $port;
$lighty_use_syslog = '';
if (empty($config['syslog']['nologlighttpd'])) {
$lighty_use_syslog = <<<EOD
## where to send error/access-messages to
server.syslog-facility = "daemon"
server.errorlog-use-syslog="enable"
EOD;
}
if (!empty($config['system']['webgui']['httpaccesslog'])) {
$lighty_use_syslog .= 'accesslog.use-syslog="enable"' . "\n";
}
$fast_cgi_path = "/tmp/php-fastcgi.socket";
$fastcgi_config = <<<EOD
#### fastcgi module
## read fastcgi.txt for more info
fastcgi.server = ( ".php" =>
( "localhost" =>
(
"socket" => "{$fast_cgi_path}",
"max-procs" => 2,
"bin-environment" => (
"PHP_FCGI_CHILDREN" => "20",
"PHP_FCGI_MAX_REQUESTS" => "100"
),
"bin-path" => "/usr/local/bin/php-cgi"
)
)
)
EOD;
$lighty_modules = !empty($config['system']['webgui']['httpaccesslog']) ? ', "mod_accesslog"' : "";
$lighty_config = <<<EOD
#
# lighttpd configuration file
#
# use a it as base for lighttpd 1.0.0 and above
#
############ Options you really have to take care of ####################
## modules to load
server.modules = (
"mod_access", "mod_expire", "mod_deflate", "mod_redirect", "mod_setenv",
"mod_cgi", "mod_fastcgi", "mod_alias", "mod_rewrite", "mod_openssl" {$lighty_modules}
)
# additional optional modules to load or additional module configurations
include "{$confdir}/conf.d/*.conf"
server.max-keep-alive-requests = 15
server.max-keep-alive-idle = 30
## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root = "/usr/local/www/"
server.tag = "OPNsense"
{$http_rewrite_rules}
# Maximum idle time with nothing being written (php downloading)
server.max-write-idle = 999
# Set shutdown time to 2 seconds for SIGINT handling
server.feature-flags = ( "server.graceful-shutdown-timeout" => 2 )
{$lighty_use_syslog}
# files to check for if .../ is requested
server.indexfiles = ( "index.php", "index.html",
"index.htm", "default.htm" )
# mimetype mapping
mimetype.assign = (
"wpad.dat" => "application/x-ns-proxy-autoconfig",
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "audio/x-wav",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".svg" => "image/svg+xml",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar"
)
# Use the "Content-Type" extended attribute to obtain mime type if possible
#mimetypes.use-xattr = "enable"
## deny access the file-extensions
#
# ~ is for backupfiles from vi, emacs, joe, ...
# .core is for core dumps that may be created during PHP execution
# .inc is often used for code includes which should in general not be part
# of the document-root
url.access-deny = ( "~", , ".core", ".inc" )
######### Options that are good to be but not necessary to be changed #######
## bind to port (default: 80)
EOD;
$lighty_config .= "server.bind = \"{$listeners[0]}\"\n";
$lighty_config .= "server.port = {$lighty_port}\n";
$cert = str_replace("\r", "", $cert);
$key = str_replace("\r", "", $key);
$ca = str_replace("\r", "", $ca);
$cert = str_replace("\n\n", "\n", $cert);
$key = str_replace("\n\n", "\n", $key);
$ca = str_replace("\n\n", "\n", $ca);
if (!empty($cert) && !empty($key)) {
$chain = $cert;
if (!empty($ca) && strlen(trim($ca))) {
$chain .= "\n" . $ca;
}
file_put_contents($cert_location, $chain);
touch($key_location);
chmod($key_location, 0600);
file_put_contents($key_location, $key);
$lighty_config .= "\n## ssl configuration\n";
$lighty_config .= "ssl.engine = \"enable\"\n";
$lighty_config .= "ssl.privkey = \"{$key_location}\"\n";
$lighty_config .= "ssl.pemfile = \"{$cert_location}\"\n";
if (empty($config['system']['webgui']['ssl-ciphers'])) {
/* harden TLS for PCI conformance */
$lighty_config .= <<<EOD
ssl.openssl.ssl-conf-cmd = (
"MinProtocol" => "TLSv1.2",
"Options" => "-ServerPreference",
"CipherString" => "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384"
)
EOD;
} else {
// use the same supported ciphers source as system_advanced_admin.php page do (its not a full list. but its openssl defaults)
$sys_ciphers = json_decode(configd_run("system ssl ciphers"), true);
$tls13_suites = array_keys(array_filter($sys_ciphers, function ($val) {
return $val['version'] == "TLSv1.3";
}));
$suites_selected = explode(":", $config['system']['webgui']['ssl-ciphers']);
$tls_suites_selected = array_diff($suites_selected, $tls13_suites);
$tls13_suites_selected = array_intersect($tls13_suites, $suites_selected);
$tlsminproto = empty($tls_suites_selected) ? 'TLSv1.3' : 'TLSv1';
$lighty_config .= "ssl.openssl.ssl-conf-cmd = (\n";
$lighty_config .= " \"MinProtocol\" => \"{$tlsminproto}\"";
if ($tls13_suites_selected) {
$lighty_config .= ",\n \"Ciphersuites\" => \"" . implode(":", $tls13_suites_selected) . "\"";
}
if ($tls_suites_selected) {
$lighty_config .= ",\n \"CipherString\" => \"" . implode(":", $tls_suites_selected) . "\"";
}
$lighty_config .= "\n)\n";
}
if (!empty($config['system']['webgui']['ssl-hsts'])) {
$lighty_config .= "\$HTTP[\"scheme\"] == \"https\" {\n";
$lighty_config .= " setenv.add-response-header = (\"Strict-Transport-Security\" => \"max-age=31536000\" )\n";
$lighty_config .= "}\n";
}
$lighty_config .= "\n";
}
foreach ($listeners as $listener) {
if (is_ipaddrv6($listener)) {
$listener = "[{$listener}]";
}
$lighty_config .= "\$SERVER[\"socket\"] == \"{$listener}:{$lighty_port}\" {\n";
$lighty_config .= "\t\$HTTP[\"url\"] =~ \"^/api/\" {\n\t\tserver.stream-response-body = 2\n\t}\n";
if ($config['system']['webgui']['protocol'] == "https") {
$lighty_config .= "\tssl.engine = \"enable\"" . PHP_EOL;
}
$lighty_config .= "}\n";
}
$lighty_config .= <<<EOD
## error-handler for status 404
#server.error-handler-404 = "/error-handler.html"
server.error-handler-404 = "/ui/404"
## to help the rc.scripts
server.pid-file = "/var/run/lighty-webConfigurator.pid"
## enable debugging
debug.log-request-header = "disable"
debug.log-response-header = "disable"
debug.log-request-handling = "disable"
debug.log-file-not-found = "disable"
# compression
deflate.mimetypes = ("text/plain", "text/css", "text/javascript", "text/xml")
deflate.allowed-encodings = ( "br", "gzip", "deflate" )
deflate.cache-dir = "/tmp/lighttpdcompress/"
{$server_upload_dirs}
server.max-request-size = 2097152
server.max-request-field-size = 16384
{$fastcgi_config}
{$cgi_config}
expire.url = ( "" => "access 50 hours" )
EOD;
/* add HTTP to HTTPS redirect */
if (
$config['system']['webgui']['protocol'] == 'https' &&
!isset($config['system']['webgui']['disablehttpredirect'])
) {
$redirectport = $lighty_port != "443" ? ":{$lighty_port}" : '';
foreach ($listeners as $listener) {
if (is_ipaddrv6($listener)) {
$listener = "[{$listener}]";
}
$lighty_config .= <<<EOD
\$SERVER["socket"] == "{$listener}:80" {
\$HTTP["host"] =~ "(.*)" {
url.redirect = ( "^/(.*)" => "https://%1{$redirectport}/$1" )
}
ssl.engine = "disable"
}
EOD;
}
}
return $lighty_config;
}