From ffe23160039a47a8644558af34bf02877107965d Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sat, 23 Feb 2019 19:44:10 +0100 Subject: [PATCH] added configuration for remote logging --- lam/help/help.inc | 4 +- lam/lib/config.inc | 2 +- lam/lib/security.inc | 46 +++++++++++++++++++++- lam/templates/config/mainmanage.php | 60 ++++++++++++++++++++++------- 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/lam/help/help.inc b/lam/help/help.inc index d176dba1..ede21b37 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -5,7 +5,7 @@ use \LAM\TYPES\TypeManager; This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) Copyright (C) 2003 - 2006 Michael Duergner - 2003 - 2018 Roland Gruber + 2003 - 2019 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -175,6 +175,8 @@ $helpArray = array ( "Text" => _('Please specify the URL (e.g. "https://api.pwnedpasswords.com/range/{SHA1PREFIX}") of your external password check.')), "250" => array ("Headline" => _("Filter"), "Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-insensitive.")), + "251" => array ("Headline" => _("Remote serrver"), + "Text" => _("Please enter the syslog remote server in format \"server:port\".")), "260" => array ("Headline" => _("Additional LDAP filter"), "Text" => _('Use this to enter an additional LDAP filter (e.g. "(cn!=admin)") to reduce the number of visible elements for this account type.') . ' ' . _('You can use the wildcard @@LOGIN_DN@@ which will be substituted with the DN of the user who is currently logged in to LAM.') diff --git a/lam/lib/config.inc b/lam/lib/config.inc index ceeb7dfa..5c7b4db6 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -2309,7 +2309,7 @@ class LAMCfgMain { /** log level */ public $logLevel; - /** log destination ("SYSLOG":syslog, "/...":file, "NONE":none) */ + /** log destination ("SYSLOG":syslog, "/...":file, "NONE":none, "REMOTE":server:port) */ public $logDestination; /** list of hosts which may access LAM */ diff --git a/lam/lib/security.inc b/lam/lib/security.inc index a840baf8..c32e382a 100644 --- a/lam/lib/security.inc +++ b/lam/lib/security.inc @@ -249,7 +249,11 @@ function isDebugLoggingEnabled() { * @param string $message log message */ function logNewMessage($level, $message) { - $possibleLevels = array(LOG_DEBUG => 'DEBUG', LOG_NOTICE => 'NOTICE', LOG_WARNING => 'WARNING', LOG_ERR => 'ERROR'); + $possibleLevels = array( + LOG_DEBUG => 'DEBUG', + LOG_NOTICE => 'NOTICE', + LOG_WARNING => 'WARNING', + LOG_ERR => 'ERROR'); if (!in_array($level, array_keys($possibleLevels))) { StatusMessage('ERROR', 'Invalid log level!', $level); } @@ -272,6 +276,10 @@ function logNewMessage($level, $message) { if ($cfg->logDestination == 'SYSLOG') { syslog($level, $message); } + // remote logging + if (strpos($cfg->logDestination, 'REMOTE') === 0) { + lamLogRemoteMessage($level, $message, $cfg); + } // log to file else { @touch($cfg->logDestination); @@ -743,4 +751,40 @@ function lamEncryptionAlgo() { return 'AES256'; } +/** + * Logs a message to a remote logging service. + * + * @param int $level log level + * @param string $message log message + * @param LAMCfgMain $cfgMain main configuration + */ +function lamLogRemoteMessage($level, $message, $cfgMain) { + include_once __DIR__ . '/3rdParty/Monolog/Logger.php'; + include_once __DIR__ . '/3rdParty/Monolog/Formatter/LineFormatter.php'; + include_once __DIR__ . '/3rdParty/Monolog/Handler/SyslogUdpHandler.php'; + $remoteParts = explode(':', $cfgMain->logDestination); + $server = $remoteParts[0]; + $port = $remoteParts[1]; + $output = "%channel%.%level_name%: %message%"; + $formatter = new Monolog\Formatter\LineFormatter($output); + $logger = new Monolog\Logger('lam'); + $syslogHandler = new Monolog\Handler\SyslogUdpHandler($server, $port); + $syslogHandler->setFormatter($formatter); + $logger->pushHandler($syslogHandler); + switch ($level) { + case LOG_DEBUG: + $logger->addDebug($message); + break; + case LOG_NOTICE: + $logger->addNotice($message); + break; + case LOG_WARNING: + $logger->addWarning($message); + break; + case LOG_ERR: + $logger->addError($message); + break; + } +} + ?> \ No newline at end of file diff --git a/lam/templates/config/mainmanage.php b/lam/templates/config/mainmanage.php index 450f8202..e17273cf 100644 --- a/lam/templates/config/mainmanage.php +++ b/lam/templates/config/mainmanage.php @@ -25,7 +25,7 @@ use \htmlHiddenInput; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2003 - 2018 Roland Gruber + Copyright (C) 2003 - 2019 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -96,7 +96,9 @@ if (isset($_POST['submitFormData'])) { $msg = _("New master password set successfully."); unset($_SESSION["mainconf_password"]); } - else $errors[] = _("Master passwords are different or empty!"); + else { + $errors[] = _("Master passwords are different or empty!"); + } } // set license if (isLAMProVersion()) { @@ -125,7 +127,9 @@ if (isset($_POST['submitFormData'])) { } $allowedHosts = implode(",", $allowedHostsList); } - else $allowedHosts = ""; + else { + $allowedHosts = ""; + } $cfg->allowedHosts = $allowedHosts; // set allowed hosts for self service if (isLAMProVersion()) { @@ -147,7 +151,9 @@ if (isset($_POST['submitFormData'])) { } $allowedHostsSelfService = implode(",", $allowedHostsSelfServiceList); } - else $allowedHostsSelfService = ""; + else { + $allowedHostsSelfService = ""; + } $cfg->allowedHostsSelfService = $allowedHostsSelfService; } // set session encryption @@ -161,13 +167,26 @@ if (isset($_POST['submitFormData'])) { // set log level $cfg->logLevel = $_POST['logLevel']; // set log destination - if ($_POST['logDestination'] == "none") $cfg->logDestination = "NONE"; - elseif ($_POST['logDestination'] == "syslog") $cfg->logDestination = "SYSLOG"; + if ($_POST['logDestination'] == "none") { + $cfg->logDestination = "NONE"; + } + elseif ($_POST['logDestination'] == "syslog") { + $cfg->logDestination = "SYSLOG"; + } + elseif ($_POST['logDestination'] == "remote") { + $cfg->logDestination = "REMOTE:" . $_POST['logRemote']; + $remoteParts = explode(':', $_POST['logRemote']); + if ((sizeof($remoteParts) !== 2) || !get_preg($remoteParts[0], 'DNSname') || !get_preg($remoteParts[1], 'digit')) { + $errors[] = _("Please enter a valid remote server in format \"server:port\"."); + } + } else { if (isset($_POST['logFile']) && ($_POST['logFile'] != "") && preg_match("/^[a-z0-9\\/\\\\:\\._-]+$/i", $_POST['logFile'])) { $cfg->logDestination = $_POST['logFile']; } - else $errors[] = _("The log file is empty or contains invalid characters! Valid characters are: a-z, A-Z, 0-9, /, \\, ., :, _ and -."); + else { + $errors[] = _("The log file is empty or contains invalid characters! Valid characters are: a-z, A-Z, 0-9, /, \\, ., :, _ and -."); + } } // password policies $cfg->passwordMinLength = $_POST['passwordMinLength']; @@ -380,9 +399,9 @@ $rulesCountOptions = array(_('all') => '-1', '3' => '3', '4' => '4'); $rulesCountSelect = new htmlResponsiveSelect('passwordRulesCount', $rulesCountOptions, array($cfg->checkedRulesCount), _('Number of rules that must match'), '246'); $rulesCountSelect->setHasDescriptiveElements(true); $row->add($rulesCountSelect, 12); -$passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true') ? true : false; +$passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true'); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContainUser',$passwordMustNotContainUser , _('Password must not contain user name'), '247'), 12); -$passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true') ? true : false; +$passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true'); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'), 12); if (function_exists('curl_init')) { $row->addVerticalSpacer('1rem'); @@ -395,9 +414,15 @@ $levelOptions = array(_("Debug") => LOG_DEBUG, _("Notice") => LOG_NOTICE, _("War $levelSelect = new htmlResponsiveSelect('logLevel', $levelOptions, array($cfg->logLevel), _("Log level"), '239'); $levelSelect->setHasDescriptiveElements(true); $row->add($levelSelect, 12); -$destinationOptions = array(_("No logging") => "none", _("System logging") => "syslog", _("File") => 'file'); +$destinationOptions = array( + _("No logging") => "none", + _("System logging") => "syslog", + _("File") => 'file', + _("Remote") => 'remote', +); $destinationSelected = 'file'; $destinationPath = $cfg->logDestination; +$destinationRemote = ''; if ($cfg->logDestination == 'NONE') { $destinationSelected = 'none'; $destinationPath = ''; @@ -406,17 +431,27 @@ elseif ($cfg->logDestination == 'SYSLOG') { $destinationSelected = 'syslog'; $destinationPath = ''; } +elseif (strpos($cfg->logDestination, 'REMOTE') === 0) { + $destinationSelected = 'remote'; + $remoteParts = explode(':', $cfg->logDestination, 2); + $destinationRemote = empty($remoteParts[1]) ? '' : $remoteParts[1]; + $destinationPath = ''; +} $logDestinationSelect = new htmlResponsiveSelect('logDestination', $destinationOptions, array($destinationSelected), _("Log destination"), '240'); $logDestinationSelect->setTableRowsToHide(array( - 'none' => array('logFile'), - 'syslog' => array('logFile'), + 'none' => array('logFile', 'logRemote'), + 'syslog' => array('logFile', 'logRemote'), + 'remote' => array('logFile'), + 'file' => array('logRemote'), )); $logDestinationSelect->setTableRowsToShow(array( 'file' => array('logFile'), + 'remote' => array('logRemote'), )); $logDestinationSelect->setHasDescriptiveElements(true); $row->add($logDestinationSelect, 12); $row->add(new htmlResponsiveInputField(_('File'), 'logFile', $destinationPath), 12); +$row->add(new htmlResponsiveInputField(_('Remote server'), 'logRemote', $destinationRemote, '251'), 12); $errorLogOptions = array( _('PHP system setting') => LAMCfgMain::ERROR_REPORTING_SYSTEM, _('default') => LAMCfgMain::ERROR_REPORTING_DEFAULT, @@ -472,7 +507,6 @@ parseHtml(null, $box, array(), false, $tabindex, 'user'); * @return String formated time */ function formatSSLTimestamp($time) { - $matches = array(); if (!empty($time)) { $timeZone = 'UTC'; $sysTimeZone = @date_default_timezone_get();