Skip to content
Snippets Groups Projects
Commit 9959a297 authored by hahn's avatar hahn
Browse files

2064-update appmonitor client

* Client wurde aktualisiert.
* IP-Restriction gesetzt
* neu 8 Checks (vorher 2)
parent e1202aa0
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ define("RESULT_OK", 0); ...@@ -4,6 +4,7 @@ define("RESULT_OK", 0);
define("RESULT_UNKNOWN", 1); define("RESULT_UNKNOWN", 1);
define("RESULT_WARNING", 2); define("RESULT_WARNING", 2);
define("RESULT_ERROR", 3); define("RESULT_ERROR", 3);
/** /**
* APPMONITOR CLIENT CHECKS<br> * APPMONITOR CLIENT CHECKS<br>
* <br> * <br>
...@@ -21,6 +22,7 @@ define("RESULT_ERROR", 3); ...@@ -21,6 +22,7 @@ define("RESULT_ERROR", 3);
* --- HISTORY:<br> * --- HISTORY:<br>
* 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br> * 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br>
* 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br> * 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br>
* 2018-06-29 0.24 axel.hahn@iml.unibe.ch add file and directory checks<br>
* --------------------------------------------------------------------------------<br> * --------------------------------------------------------------------------------<br>
* @version 0.9 * @version 0.9
* @author Axel Hahn * @author Axel Hahn
...@@ -30,7 +32,6 @@ define("RESULT_ERROR", 3); ...@@ -30,7 +32,6 @@ define("RESULT_ERROR", 3);
* @package IML-Appmonitor * @package IML-Appmonitor
*/ */
class appmonitorcheck { class appmonitorcheck {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// CONFIG // CONFIG
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
...@@ -110,9 +111,11 @@ class appmonitorcheck { ...@@ -110,9 +111,11 @@ class appmonitorcheck {
private function _checkArrayKeys($aConfig, $sKeyList) { private function _checkArrayKeys($aConfig, $sKeyList) {
foreach (explode(",", $sKeyList) as $sKey) { foreach (explode(",", $sKeyList) as $sKey) {
if (!array_key_exists($sKey, $aConfig)) { if (!array_key_exists($sKey, $aConfig)) {
header('HTTP/1.0 503 Service Unavailable');
die('ERROR in ' . __CLASS__ . "<br>array requires the keys [$sKeyList] - but key '$sKey' was not found in config array <pre>" . print_r($aConfig, true)); die('ERROR in ' . __CLASS__ . "<br>array requires the keys [$sKeyList] - but key '$sKey' was not found in config array <pre>" . print_r($aConfig, true));
} }
if (is_null($aConfig[$sKey])) { if (is_null($aConfig[$sKey])) {
header('HTTP/1.0 503 Service Unavailable');
die('ERROR in ' . __CLASS__ . "<br> key '$sKey' is empty in config array <pre>" . print_r($aConfig, true)); die('ERROR in ' . __CLASS__ . "<br> key '$sKey' is empty in config array <pre>" . print_r($aConfig, true));
} }
} }
...@@ -140,6 +143,7 @@ class appmonitorcheck { ...@@ -140,6 +143,7 @@ class appmonitorcheck {
* @return array * @return array
*/ */
public function makeCheck($aConfig) { public function makeCheck($aConfig) {
$this->_iStart = microtime(true);
$this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig, "name,description,check");
$this->_checkArrayKeys($aConfig["check"], "function"); $this->_checkArrayKeys($aConfig["check"], "function");
...@@ -148,6 +152,7 @@ class appmonitorcheck { ...@@ -148,6 +152,7 @@ class appmonitorcheck {
$sCheck = "check" . $this->_aConfig["check"]["function"]; $sCheck = "check" . $this->_aConfig["check"]["function"];
if (!method_exists($this, $sCheck)) { if (!method_exists($this, $sCheck)) {
header('HTTP/1.0 503 Service Unavailable');
die(__CLASS__ . " check not found: $sCheck <pre>" . print_r($aConfig, true)); die(__CLASS__ . " check not found: $sCheck <pre>" . print_r($aConfig, true));
} }
$aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array(); $aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array();
...@@ -155,6 +160,7 @@ class appmonitorcheck { ...@@ -155,6 +160,7 @@ class appmonitorcheck {
// call the check ... // call the check ...
call_user_func(array($this, $sCheck), $aParams); call_user_func(array($this, $sCheck), $aParams);
$this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
// echo "<pre>"; print_r($this->listChecks()); die(); // echo "<pre>"; print_r($this->listChecks()); die();
// ... and send response // ... and send response
return $this->respond(); return $this->respond();
...@@ -190,16 +196,47 @@ class appmonitorcheck { ...@@ -190,16 +196,47 @@ class appmonitorcheck {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
/** /**
* most simple check: set values * check a file
* @return type * @param array $aParams
* array(
* "filename" directory that must exist
* "writable" flag to check that it must be writable too
* )
* @return boolean
*/ */
private function checkSimple($aParams) { public function checkFile($aParams) {
$aHelp = array( $aOK=array();
"result" => "(integer) result value", $aErrors=array();
"value" => "(string) explaination" $this->_checkArrayKeys($aParams, "filename");
); $sFile=$aParams["filename"];
$this->_checkArrayKeys($aParams, "result,value");
return $this->_setReturn((int) $aParams["result"], $aParams["value"]); if (isset($aParams['exists'])){
$sMyflag='exists='.($aParams['exists'] ? 'yes' : 'no');
if (file_exists($sFile) && $aParams['exists']){
$aOK[]=$sMyflag;
} else {
$aErrors[]=$sMyflag;
}
}
foreach(array('dir', 'executable', 'file', 'link', 'readable', 'writable') as $sFiletest){
if (isset($aParams[$sFiletest])){
$sTestCmd='return is_'.$sFiletest.'("'.$sFile.'");';
if (eval($sTestCmd) && $aParams[$sFiletest]){
$aOK[]=$sFiletest . '='.($aParams[$sFiletest] ? 'yes' : 'no');
} else {
$aErrors[]=$sFiletest . '='.($aParams[$sFiletest] ? 'yes' : 'no');
}
}
}
$sMessage=(count($aOK) ? ' flags OK: ' .implode('|', $aOK) : '')
.' '. (count($aErrors) ? ' flags FAILED: '.implode('|', $aErrors) : '')
;
if(count($aErrors)){
$this->_setReturn(RESULT_ERROR, 'file test ['. $sFile . '] '.$sMessage);
} else {
$this->_setReturn(RESULT_OK, 'file test ['. $sFile . '] '.$sMessage);
}
return true;
} }
/** /**
...@@ -209,10 +246,12 @@ class appmonitorcheck { ...@@ -209,10 +246,12 @@ class appmonitorcheck {
* "url" url to fetch * "url" url to fetch
* "contains" string that must exist in response body * "contains" string that must exist in response body
* ) * )
* @param integer $iTimeout value in sec; default: 5sec
*/ */
private function checkHttpContent($aParams, $iTimeout = 5) { private function checkHttpContent($aParams, $iTimeout = 5) {
$this->_checkArrayKeys($aParams, "url,contains"); $this->_checkArrayKeys($aParams, "url,contains");
if (!function_exists("curl_init")) { if (!function_exists("curl_init")) {
header('HTTP/1.0 503 Service Unavailable');
die("ERROR: PHP CURL module is not installed."); die("ERROR: PHP CURL module is not installed.");
} }
$ch = curl_init($aParams["url"]); $ch = curl_init($aParams["url"]);
...@@ -258,33 +297,6 @@ class appmonitorcheck { ...@@ -258,33 +297,6 @@ class appmonitorcheck {
} }
} }
/**
* check sqlite connection
* @param array $aParams
* array(
* "db"
* )
* @return boolean
*/
private function checkSqliteConnect($aParams) {
$this->_checkArrayKeys($aParams, "db");
if (!file_exists($aParams["db"])) {
$this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist.");
return false;
}
try {
// $db = new SQLite3($sqliteDB);
// $db = new PDO("sqlite:".$sqliteDB);
$o = new PDO("sqlite:" . $aParams["db"]);
$this->_setReturn(RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected");
return true;
} catch (Exception $exc) {
$this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . mysqli_connect_error());
return false;
}
}
/** /**
* check if system is listening to a given port * check if system is listening to a given port
* @param array $aParams * @param array $aParams
...@@ -319,23 +331,38 @@ class appmonitorcheck { ...@@ -319,23 +331,38 @@ class appmonitorcheck {
return true; return true;
} }
} }
/** /**
* DEPRECATED - use checkPortTcp instead * most simple check: set values
* check if system is listening to a given port * @return type
*/
private function checkSimple($aParams) {
$this->_checkArrayKeys($aParams, "result,value");
return $this->_setReturn((int) $aParams["result"], $aParams["value"]);
}
/**
* check sqlite connection
* @param array $aParams * @param array $aParams
* array( * array(
* "port" * "db"
* ) * )
* @return boolean * @return boolean
*/ */
private function checkListeningIp($aParams) { private function checkSqliteConnect($aParams) {
$this->_checkArrayKeys($aParams, "port"); $this->_checkArrayKeys($aParams, "db");
$sResult = exec('netstat -tulen | grep ":' . $aParams["port"] . ' "'); if (!file_exists($aParams["db"])) {
if ($sResult) { $this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist.");
$this->_setReturn(RESULT_OK, "OK: Port " . $aParams["port"] . " was found: " . $sResult); return false;
}
try {
// $db = new SQLite3($sqliteDB);
// $db = new PDO("sqlite:".$sqliteDB);
$o = new PDO("sqlite:" . $aParams["db"]);
$this->_setReturn(RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected");
return true; return true;
} else { } catch (Exception $exc) {
$this->_setReturn(RESULT_ERROR, "ERROR: Port " . $aParams["port"] . " is not in use."); $this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . mysqli_connect_error());
return false; return false;
} }
} }
......
<?php <?php
/** /**
* APPMONITOR CLIENT<br> * APPMONITOR CLIENT<br>
* <br> * <br>
...@@ -52,11 +53,7 @@ class appmonitor { ...@@ -52,11 +53,7 @@ class appmonitor {
* @var array * @var array
*/ */
private $_aChecks = array(); private $_aChecks = array();
protected $_iStart = false;
/**
* @var array
*/
private $_aMustKeysChecks = array("name", "description", "result", "value");
/** /**
* constructor: init data * constructor: init data
...@@ -74,18 +71,19 @@ class appmonitor { ...@@ -74,18 +71,19 @@ class appmonitor {
* @return boolean * @return boolean
*/ */
private function _createDefaultMetadata() { private function _createDefaultMetadata() {
$this->_iStart = microtime(true);
$this->_aMeta = array( $this->_aMeta = array(
"host" => false, "host" => false,
"website" => false, "website" => false,
"ttl" => false, "ttl" => false,
"result" => false "result" => false,
"time" => false
); );
// fill with default values // fill with default values
$this->setHost(); $this->setHost();
$this->setWebsite(); $this->setWebsite();
$this->setTTL(); $this->setTTL();
return true; return true;
} }
...@@ -111,7 +109,7 @@ class appmonitor { ...@@ -111,7 +109,7 @@ class appmonitor {
* @return bool * @return bool
*/ */
public function setWebsite($s = false) { public function setWebsite($s = false) {
if (!$s) { if (!$s && isset($_SERVER["HTTP_HOST"])) {
$s = $_SERVER["HTTP_HOST"]; $s = $_SERVER["HTTP_HOST"];
} }
return $this->_aMeta["website"] = $s; return $this->_aMeta["website"] = $s;
...@@ -160,6 +158,107 @@ class appmonitor { ...@@ -160,6 +158,107 @@ class appmonitor {
return $this->_aChecks[] = $aCheck; return $this->_aChecks[] = $aCheck;
} }
/**
* add an item to notifications meta data
*
* @param string $sType type ... one of email|slack
* @param type $sValue value
* @param type $sKey optional key (for key->value instead of list of values)
* @return boolean
*/
protected function _addNotification($sType, $sValue, $sKey = false) {
$sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType));
if (!isset($this->_aMeta['notifications'])) {
$this->_aMeta['notifications'] = array();
}
if (!isset($this->_aMeta['notifications'][$sTypeCleaned])) {
$this->_aMeta['notifications'][$sTypeCleaned] = array();
}
if ($sKey) {
$this->_aMeta['notifications'][$sTypeCleaned][$sKey] = $sValue;
} else {
$this->_aMeta['notifications'][$sTypeCleaned][] = $sValue;
}
return true;
}
/**
* add an email to notifications list
*
* @param string $sEmailAddress email address to add
* @return boolean
*/
public function addEmail($sEmailAddress) {
return $this->_addNotification('email', $sEmailAddress);
}
/**
* Add slack channel for notification
* @param string $sLabel
* @param string $sSlackWebhookUrl
* @return type
*/
public function addSlackWebhook($sLabel, $sSlackWebhookUrl) {
return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel);
}
/**
* add a tag for grouping in the server gui
*
* @param string $sLabel
* @param string $sSlackWebhookUrl
* @return type
*/
public function addTag($sTag) {
if(!isset($this->_aMeta['tags'])){
$this->_aMeta['tags']=array();
}
$this->_aMeta['tags'][]=$sTag;
return true;
}
/**
* check referers IP address if it matches any entry in the list
* requires http request; CLI is always allowed
* On deny this method exits with 403 response
*
* @param array $aAllowedIps array of allowed ip addresses / ranges
* the ip must match from the beginning, i.e.
* "127.0." will allow requests from 127.0.X.Y
*/
public function checkIp($aAllowedIps = array()) {
if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) {
return true;
}
$sIP = $_SERVER['REMOTE_ADDR'];
foreach ($aAllowedIps as $sIp2Check) {
if (strpos($sIP, $sIp2Check) === 0) {
return true;
}
}
header('HTTP/1.0 403 Forbidden');
die('ERROR: Your ip address [' . $sIP . '] has no access.');
}
/**
* Check a token
* requires http request; CLI is always allowed
* On deny this method exits with 403 response
*
* @param type $sVarname
* @param type $sToken
* @return boolean
*/
public function checkToken($sVarname, $sToken) {
if (!isset($_GET)) {
return true;
}
if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) {
return true;
}
header('HTTP/1.0 403 Forbidden');
die('ERROR: A token is required.');
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// getter // getter
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
...@@ -196,8 +295,9 @@ class appmonitor { ...@@ -196,8 +295,9 @@ class appmonitor {
if (count($aErrors)) { if (count($aErrors)) {
header('HTTP/1.0 503 Service Unavailable');
echo "<h1>Errors detected</h1><ol><li>" . implode("<li>", $aErrors) . "</ol><hr>"; echo "<h1>Errors detected</h1><ol><li>" . implode("<li>", $aErrors) . "</ol><hr>";
echo "<pre>" . print_r($this->_generateOutputArray(), true) . "</pre><hr>"; echo "<pre>" . print_r($this->getResults(), true) . "</pre><hr>";
die("ABORT"); die("ABORT");
} }
} }
...@@ -210,7 +310,7 @@ class appmonitor { ...@@ -210,7 +310,7 @@ class appmonitor {
* get full array for response with metadata and Checks * get full array for response with metadata and Checks
* @return type * @return type
*/ */
private function _generateOutputArray() { public function getResults() {
return array( return array(
"meta" => $this->_aMeta, "meta" => $this->_aMeta,
"checks" => $this->_aChecks, "checks" => $this->_aChecks,
...@@ -224,6 +324,7 @@ class appmonitor { ...@@ -224,6 +324,7 @@ class appmonitor {
*/ */
public function render($bPretty = false, $bHighlight = false) { public function render($bPretty = false, $bHighlight = false) {
$this->_checkData(); $this->_checkData();
$this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
// JSON_PRETTY_PRINT reqires PHP 5.4 // JSON_PRETTY_PRINT reqires PHP 5.4
if (!defined('JSON_PRETTY_PRINT')) { if (!defined('JSON_PRETTY_PRINT')) {
...@@ -231,9 +332,9 @@ class appmonitor { ...@@ -231,9 +332,9 @@ class appmonitor {
} }
if (!$bPretty) { if (!$bPretty) {
$bHighlight = false; $bHighlight = false;
$sOut = json_encode($this->_generateOutputArray()); $sOut = json_encode($this->getResults());
} else { } else {
$sOut = json_encode($this->_generateOutputArray(), JSON_PRETTY_PRINT); $sOut = json_encode($this->getResults(), JSON_PRETTY_PRINT);
if ($bHighlight) { if ($bHighlight) {
$aMsg = array( $aMsg = array(
0 => "OK", 0 => "OK",
...@@ -257,7 +358,6 @@ class appmonitor { ...@@ -257,7 +358,6 @@ class appmonitor {
$sOut = '<!DOCTYPE html><html><head>' $sOut = '<!DOCTYPE html><html><head>'
. '<style>' . '<style>'
. 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}' . 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}'
. 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}' . 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}'
. 'blockquote blockquote:hover{; }' . 'blockquote blockquote:hover{; }'
...@@ -267,7 +367,6 @@ class appmonitor { ...@@ -267,7 +367,6 @@ class appmonitor {
. '.result1{background:#666; border-right: 0em solid #ccc;}' . '.result1{background:#666; border-right: 0em solid #ccc;}'
. '.result2{background:#fc9; border-right: 0em solid #860;}' . '.result2{background:#fc9; border-right: 0em solid #860;}'
. '.result3{background:#800; border-right: 0em solid #f00;}' . '.result3{background:#800; border-right: 0em solid #f00;}'
. '</style>' . '</style>'
. '<title>' . __CLASS__ . '</title>' . '<title>' . __CLASS__ . '</title>'
. '</head><body>' . '</head><body>'
......
<?php
/* ______________________________________________________________________
*
* A P P M O N I T O R :: CLIENT - CHECK :: GENERAL INCLUDE
* ______________________________________________________________________
*
* @author: Axel Hahn
* ----------------------------------------------------------------------
* 2018-06-30 v0.1
*/
// ----------------------------------------------------------------------
// SECURITY STUFF ... protect access to monitoring data
// ----------------------------------------------------------------------
// --- restrict ip access
// check local ips and IML networks (includes the monitor)
// appmonitor is not available on EDUROAM or VPN
$oMonitor->checkIp(array(
'127.0.0.1',
'::1',
'10.0.2.2',
'130.92.30.11',
'130.92.30.44',
'130.92.79.49',
));
// --- check a token
// an incoming request must have the GET param "token=123"
// $oMonitor->checkTokem('token', '123');
// ----------------------------------------------------------------------
// NOTIFICATION
// ----------------------------------------------------------------------
// $oMonitor->addEmail('sysadmin@example.com');
// $oMonitor->addSlackWebhook(array("mywebhook"=> "https://hooks.slack.com/services/(...)"));
$oMonitor->addEmail('axel.hahn@iml.unibe.ch');
\ No newline at end of file
<?php <?php
require_once('appmonitor-client.class.php'); require_once('classes/appmonitor-client.class.php');
$oMonitor = new appmonitor(); $oMonitor = new appmonitor();
@include 'general_include.php';
$oMonitor->addCheck( $oMonitor->addCheck(
array( array(
...@@ -19,6 +21,62 @@ $oMonitor->addCheck( ...@@ -19,6 +21,62 @@ $oMonitor->addCheck(
require_once '../../config/inc_projects_config.php'; require_once '../../config/inc_projects_config.php';
// ----------------------------------------------------------------------
// needed directories
// ----------------------------------------------------------------------
$oMonitor->addCheck(
array(
"name" => "tmp subdir",
"description" => "Check storage for temp directories, git checkouts for logmessages exists and is writable",
"check" => array(
"function" => "File",
"params" => array(
"filename" => $aConfig['tmpDir'],
"dir" => true,
"writable" => true,
),
),
)
);
$oMonitor->addCheck(
array(
"name" => "workdir",
"description" => "Check if base workdir exists and is writable",
"check" => array(
"function" => "File",
"params" => array(
"filename" => $aConfig['workDir'],
"dir" => true,
"writable" => true,
),
),
)
);
foreach(array('dataDir', 'buildDir', 'packageDir', 'archiveDir') as $sDirKey){
$oMonitor->addCheck(
array(
"name" => "dir [$sDirKey]",
"description" => "Check if workdir $sDirKey exists and is writable",
"check" => array(
"function" => "File",
"params" => array(
"filename" => $aConfig[$sDirKey],
"dir" => true,
"writable" => true,
),
),
)
);
}
// ----------------------------------------------------------------------
// databases
// ----------------------------------------------------------------------
$sSqlitefile=$aConfig['dataDir'].'/database/logs.db'; $sSqlitefile=$aConfig['dataDir'].'/database/logs.db';
$oMonitor->addCheck( $oMonitor->addCheck(
array( array(
...@@ -32,6 +90,19 @@ $oMonitor->addCheck( ...@@ -32,6 +90,19 @@ $oMonitor->addCheck(
), ),
) )
); );
$sSqlitefile2=$_SERVER['DOCUMENT_ROOT'].'valuestore/data/versioncache.db';
$oMonitor->addCheck(
array(
"name" => "Sqlite DB version cache",
"description" => "Connect sqlite db ". basename($sSqlitefile),
"check" => array(
"function" => "SqliteConnect",
"params" => array(
"db"=>$sSqlitefile2
),
),
)
);
// Gesamt-Ergebnis - ohne Param=aut. max. Wert nehmen // Gesamt-Ergebnis - ohne Param=aut. max. Wert nehmen
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment