1<?php 2 3/** 4 * Observium 5 * 6 * This file is part of Observium. 7 * 8 * @package observium 9 * @subpackage syslog 10 * @author Adam Armstrong <adama@observium.org> 11 * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited 12 * 13 */ 14 15// FIXME use db functions properly 16 17// DOCME needs phpdoc block 18// TESTME needs unit testing 19function get_cache($host, $value) 20{ 21 global $dev_cache; 22 23 if (empty($host)) { return; } 24 25 // Check cache expiration 26 $now = time(); 27 $expired = TRUE; 28 if (isset($dev_cache[$host]['lastchecked'])) 29 { 30 if (($now - $dev_cache[$host]['lastchecked']) < 600) { $expired = FALSE; } // will expire after 10 min 31 } 32 if ($expired) { $dev_cache[$host]['lastchecked'] = $now; } 33 34 if (!isset($dev_cache[$host][$value]) || $expired) 35 { 36 switch($value) 37 { 38 case 'device_id': 39 // Try by map in config 40 if (isset($GLOBALS['config']['syslog']['host_map'][$host])) 41 { 42 $new_host = $GLOBALS['config']['syslog']['host_map'][$host]; 43 if (is_numeric($new_host)) 44 { 45 // Check if device id exist 46 $dev_cache[$host]['device_id'] = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `device_id` = ?', array($new_host)); 47 } else { 48 $dev_cache[$host]['device_id'] = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `hostname` = ? OR `sysName` = ?', array($new_host, $new_host)); 49 } 50 // If syslog host map correct, return device id or try onward 51 if ($dev_cache[$host]['device_id']) 52 { 53 return $dev_cache[$host]['device_id']; 54 } 55 } 56 57 // Localhost IPs, try detect as local system 58 if (in_array($host, array('127.0.0.1', '::1'))) 59 { 60 if ($localhost_id = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `hostname` = ?', array(get_localhost()))) 61 { 62 $dev_cache[$host]['device_id'] = $localhost_id; 63 } 64 else if ($localhost_id = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `sysName` = ?', array(get_localhost()))) 65 { 66 $dev_cache[$host]['device_id'] = $localhost_id; 67 } 68 // NOTE in other cases localhost IPs associated with random device 69 } else { 70 // Try by hostname 71 $dev_cache[$host]['device_id'] = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `hostname` = ? OR `sysName` = ?', array($host, $host)); 72 } 73 74 // If failed, try by IP 75 if (!is_numeric($dev_cache[$host]['device_id'])) 76 { 77 $ip = $host; 78 79 $ip_version = get_ip_version($ip); 80 if ($ip_version !== FALSE) 81 { 82 if ($ip_version == 6 && preg_match('/::ffff:(\d+\.\d+\.\d+\.\d+)/', $ip, $matches)) 83 { 84 // IPv4 mapped to IPv6, like ::ffff:192.0.2.128 85 // See: http://jira.observium.org/browse/OBSERVIUM-1274 86 $ip = $matches[1]; 87 $ip_version = 4; 88 } 89 else if ($ip_version == 6) 90 { 91 $ip = Net_IPv6::uncompress($ip, TRUE); 92 } 93 94 // Detect associated device by IP address, exclude deleted ports 95 // IS NULL allow to search addresses without associated port 96 $query = 'SELECT * FROM `ipv'.$ip_version.'_addresses` LEFT JOIN `ports` USING (`port_id`) WHERE `ipv'.$ip_version.'_address` = ? AND (`deleted` = ? OR `deleted` IS NULL);'; 97 $addresses = dbFetchRows($query, array($ip, 0)); 98 $address_count = count($addresses); 99 100 if ($address_count) 101 { 102 $dev_cache[$host]['device_id'] = $addresses[0]['device_id']; 103 104 // Additional checks if multiple addresses found 105 if ($address_count > 1) 106 { 107 foreach ($addresses as $entry) 108 { 109 $device_tmp = device_by_id_cache($entry['device_id']); 110 if ($device_tmp['disabled'] || !$device_tmp['status']) { continue; } // Skip disabled and down devices 111 else if ($entry['ifAdminStatus'] == 'down' || // Skip disabled ports 112 in_array($entry['ifOperStatus'], array('down', 'lowerLayerDown'))) { continue; } // Skip down ports 113 114 // Override cached host device_id 115 $dev_cache[$host]['device_id'] = $entry['device_id']; 116 break; // End loop on first founded entry 117 } 118 unset($device_tmp); 119 } 120 121 } 122 } 123 } 124 break; 125 case 'os': 126 case 'version': 127 if ($device_id = get_cache($host, 'device_id')) 128 { 129 $dev_cache[$host][$value] = dbFetchCell('SELECT `'.$value.'` FROM `devices` WHERE `device_id` = ?', array($device_id)); 130 } else { 131 return NULL; 132 } 133 break; 134 case 'os_group': 135 $os = get_cache($host, 'os'); 136 $dev_cache[$host]['os_group'] = (isset($GLOBALS['config']['os'][$os]['group']) ? $GLOBALS['config']['os'][$os]['group'] : ''); 137 break; 138 default: 139 return NULL; 140 } 141 } 142 143 return $dev_cache[$host][$value]; 144} 145 146function cache_syslog_rules() 147{ 148 149 $rules = array(); 150 foreach(dbFetchRows("SELECT * FROM `syslog_rules` WHERE `la_disable` = ?", array('0')) as $lat) 151 { 152 $rules[$lat['la_id']] = $lat; 153 } 154 155 return $rules; 156 157} 158 159function cache_syslog_rules_assoc() 160{ 161 $device_rules = array(); 162 foreach (dbFetchRows("SELECT * FROM `syslog_rules_assoc`") as $laa) 163 { 164 165 //print_r($laa); 166 167 if ($laa['entity_type'] == 'group') 168 { 169 $devices = get_group_entities($laa['entity_id']); 170 foreach($devices as $dev_id) 171 { 172 $device_rules[$dev_id][$laa['la_id']] = TRUE; 173 } 174 } 175 else if ($laa['entity_type'] == 'device') 176 { 177 $device_rules[$laa['entity_id']][$laa['la_id']] = TRUE; 178 } 179 } 180 return $device_rules; 181} 182 183 184// DOCME needs phpdoc block 185// TESTME needs unit testing 186function process_syslog($line, $update) 187{ 188 global $config; 189 global $rules; 190 global $device_rules; 191 global $maint; 192 193 $entry = process_syslog_line($line); 194 195 if ($entry === FALSE) 196 { 197 // Incorrect/filtered syslog entry 198 return FALSE; 199 } 200 else if ($entry['device_id']) 201 { 202 // Main syslog entry with detected device 203 if ($update) 204 { 205 $log_id = dbInsert( 206 array( 207 'device_id' => $entry['device_id'], 208 'host' => $entry['host'], 209 'program' => $entry['program'], 210 'facility' => $entry['facility'], 211 'priority' => $entry['priority'], 212 'level' => $entry['level'], 213 'tag' => $entry['tag'], 214 'msg' => $entry['msg'], 215 'timestamp' => $entry['timestamp'] 216 ), 217 'syslog' 218 ); 219 } 220 221//$req_dump = print_r(array($entry, $rules, $device_rules), TRUE); 222//$fp = fopen('/tmp/syslog.log', 'a'); 223//fwrite($fp, $req_dump); 224//fclose($fp); 225 226 // Add syslog alert into notification queue 227 $notification_type = 'syslog'; 228 229 /// FIXME, I not know how 'syslog_rules_assoc' is filled, I pass rules to all devices 230 /// FIXME, this is copy-pasted from above, while not have WUI for syslog_rules_assoc 231 foreach ($rules as $la_id => $rule) 232 { 233 // Skip processing syslog rule if device rule not cached (see: cache_syslog_rules_assoc() ) 234 if (!empty($device_rules) && !isset($device_rules[$entry['device_id']][$la_id])) 235 { 236 continue; 237 } 238 239 if (preg_match($rule['la_rule'], $entry['msg_orig'], $matches)) // Match syslog by rule pattern 240 { 241 242 // Mark no notification during maintenance 243 if (isset($maint['device'][$entry['device_id']]) || (isset($maint['global']) && $maint['global'] > 0)) 244 { 245 $notified = '-1'; 246 } else { 247 $notified = '0'; 248 } 249 250 // Detect some common entities patterns in syslog message 251 252 $log_id = dbInsert(array('device_id' => $entry['device_id'], 253 'la_id' => $la_id, 254 'syslog_id' => $log_id, 255 'timestamp' => $entry['timestamp'], 256 'program' => $entry['program'], 257 'message' => $entry['msg'], // Use cleared msg instead original (see process_syslog_line() tests) 258 'notified' => $notified), 'syslog_alerts'); 259 260 // Add notification to queue 261 if ($notified != '-1') 262 { 263 $alert_unixtime = strtotime($entry['timestamp']); 264 265 $device = device_by_id_cache($entry['device_id']); 266 $message_tags = array( 267 'ALERT_STATE' => "SYSLOG", 268 'ALERT_URL' => generate_url(array('page' => 'device', 269 'device' => $device['device_id'], 270 'tab' => 'alert', 271 'entity_type' => 'syslog')), 272 273 'ALERT_UNIXTIME' => $alert_unixtime, // Standart unixtime 274 'ALERT_TIMESTAMP' => $entry['timestamp'] . date(' P'), // ie: 2000-12-21 16:01:07 +02:00 275 'ALERT_TIMESTAMP_RFC2822' => date('r', $alert_unixtime), // RFC 2822, ie: Thu, 21 Dec 2000 16:01:07 +0200 276 'ALERT_TIMESTAMP_RFC3339' => date(DATE_RFC3339, $alert_unixtime), // RFC 3339, ie: 2005-08-15T15:52:01+00:00 277 'ALERT_ID' => $la_id, 278 'ALERT_MESSAGE' => $rule['la_descr'], 279 'CONDITIONS' => $rule['la_rule'], 280 'METRICS' => $entry['msg'], 281 282 // Syslog TAGs 283 'SYSLOG_RULE' => $rule['la_rule'], 284 'SYSLOG_MESSAGE' => $entry['msg'], 285 'SYSLOG_PROGRAM' => $entry['program'], 286 'SYSLOG_TAG' => $entry['tag'], 287 'SYSLOG_FACILITY' => $entry['facility'], 288 289 // Device TAGs 290 'DEVICE_HOSTNAME' => $device['hostname'], 291 'DEVICE_SYSNAME' => $device['sysName'], 292 //'DEVICE_SYSDESCR' => $device['sysDescr'], 293 'DEVICE_ID' => $device['device_id'], 294 'DEVICE_LINK' => generate_device_link($device, NULL, array('tab' => 'alerts', 'entity_type' => 'syslog')), 295 'DEVICE_HARDWARE' => $device['hardware'], 296 'DEVICE_OS' => $device['os_text'] . ' ' . $device['version'] . ($device['features'] ? ' (' . $device['features'] . ')' : ''), 297 //'DEVICE_TYPE' => $device['type'], 298 'DEVICE_LOCATION' => $device['location'], 299 'DEVICE_UPTIME' => deviceUptime($device), 300 'DEVICE_REBOOTED' => format_unixtime($device['last_rebooted']), 301 ); 302 $message_tags['TITLE'] = alert_generate_subject($device, 'SYSLOG', $message_tags); 303 304 // Get contacts for $la_id 305 $contacts = get_alert_contacts($entry['device_id'], $la_id, $notification_type); 306 307 foreach($contacts AS $contact) { 308 309 $notification = array( 310 'device_id' => $entry['device_id'], 311 'log_id' => $log_id, 312 'aca_type' => $notification_type, 313 'severity' => $entry['priority'], 314 'endpoints' => json_encode($contact), 315 //'message_graphs' => $message_tags['ENTITY_GRAPHS_ARRAY'], 316 'notification_added' => time(), 317 'notification_lifetime' => 300, // Lifetime in seconds 318 'notification_entry' => json_encode($entry), // Store full alert entry for use later if required (not sure that this needed) 319 ); 320 //unset($message_tags['ENTITY_GRAPHS_ARRAY']); 321 $notification['message_tags'] = json_encode($message_tags); 322 $notification_id = dbInsert($notification, 'notifications_queue'); 323 } // End foreach($contacts) 324 } // End if($notified) 325 } // End if syslog rule matches 326 } // End foreach($rules) 327 328 unset($os); 329 } 330 else if ($config['syslog']['unknown_hosts']) 331 { 332 // EXPERIMENTAL. Host not known, currently not used. 333 if ($update) 334 { 335 // Store entries for unknown hosts with NULL device_id 336 $log_id = dbInsert( 337 array( 338 //'device_id' => $entry['device_id'], // Default is NULL 339 'host' => $entry['host'], 340 'program' => $entry['program'], 341 'facility' => $entry['facility'], 342 'priority' => $entry['priority'], 343 'level' => $entry['level'], 344 'tag' => $entry['tag'], 345 'msg' => $entry['msg'], 346 'timestamp' => $entry['timestamp'] 347 ), 348 'syslog' 349 ); 350 //var_dump($entry); 351 } 352 } 353 354 return $entry; 355} 356 357/** 358 * Process syslog line. Convert raw syslog line (with observium format) into array. 359 * Also rewrite some entries by device os. 360 * 361 * Observium template: 362 * host||facility||priority||level||tag||timestamp||msg||program 363 * 364 * @param string $line Raw syslog line by observium template 365 * @return array|false Array with processed syslog entry, or FALSE if incorrect/filtered msg. 366 */ 367function process_syslog_line($line) 368{ 369 global $config; 370 371 // Compatability with old param as array 372 if (is_array($line)) { return $line; } 373 374 $entry = array(); // Init 375 376 $entry_array = explode('||', trim($line)); 377 $entry['host'] = array_shift($entry_array); 378 $entry['facility'] = array_shift($entry_array); 379 $entry['priority'] = array_shift($entry_array); 380 $entry['level'] = array_shift($entry_array); 381 $entry['tag'] = array_shift($entry_array); 382 $entry['timestamp'] = array_shift($entry_array); 383 if (count($entry_array) > 2) 384 { 385 // Some time message have || inside: 386 // 127.0.0.1||9||6||6||CRON[3196]:||2018-03-13 06:25:01|| (root) CMD (test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ))||CRON 387 $entry['program'] = array_pop($entry_array); 388 $entry['msg'] = implode('||', $entry_array); // reimplode msg string with "||" inside 389 } else { 390 $entry['msg'] = array_shift($entry_array); 391 $entry['program'] = array_shift($entry_array); 392 } 393 394 // Filter by msg string 395 if (str_contains($entry['msg'], $config['syslog']['filter'])) 396 { 397 return FALSE; 398 } 399 //foreach ($config['syslog']['filter'] as $bi) 400 //{ 401 // if (strpos($entry['msg'], $bi) !== FALSE) 402 // { 403 // //echo('D-'.$bi); 404 // return FALSE; 405 // } 406 //} 407 408 $entry['msg_orig'] = $entry['msg']; 409 410 // Initial rewrites 411 $entry['host'] = strtolower(trim($entry['host'])); 412 413 if (isset($config['syslog']['debug']) && $config['syslog']['debug'] && 414 !defined('__PHPUNIT_PHAR__')) // Skip on Unit tests 415 { 416 // Store RAW syslog line into debug.log 417 logfile('debug.'.$entry['host'].'.syslog', $line); 418 } 419 420 // Rewrite priority/level/facility from strings to numbers 421 $entry['priority'] = priority_string_to_numeric($entry['priority']); 422 $entry['level'] = priority_string_to_numeric($entry['level']); 423 if (isset($config['syslog']['facilities'][$entry['facility']])) 424 { 425 // Convert numeric facility to string 426 $entry['facility'] = $config['syslog']['facilities'][$entry['facility']]['name']; 427 } 428 //$entry['facility'] = facility_string_to_numeric($entry['facility']); 429 430 $entry['device_id'] = get_cache($entry['host'], 'device_id'); 431 //print_vars($entry); 432 //print_vars($GLOBALS['dev_cache']); 433 if ($entry['device_id']) 434 { 435 // Process msg/program for known os/os_group 436 $os = get_cache($entry['host'], 'os'); 437 $os_group = get_cache($entry['host'], 'os_group'); 438 439 // Detect message repeated 440 if (strpos($entry['msg'], 'repeated ') !== FALSE && preg_match('/repeated \d+ times(?:\:\ +\[\s*(?<msg>.+)\])?\s*$/', $entry['msg'], $matches)) 441 { 442 //var_dump($matches); 443 if (isset($matches['msg'])) 444 { 445 $entry['msg'] = $matches['msg']; 446 } else { 447 // Always skip unusefull entries 'message repeated X times' (without any message) 448 return FALSE; 449 } 450 } 451 452 // OS definition based syslog msg format 453 if (isset($config['os'][$os]['syslog_msg'])) 454 { 455 foreach ($config['os'][$os]['syslog_msg'] as $pattern) 456 { 457 if (preg_match($pattern, $entry['msg'], $matches)) 458 { 459 if (OBS_DEBUG) 460 { 461 print_cli_table(array(array('syslog msg', $entry['msg']), array('matched pattern', $pattern)), NULL); 462 } 463 // Override founded msg/tag/program references 464 if (isset($matches['msg'])) { $entry['msg'] = $matches['msg']; } 465 if (isset($matches['program'])) { $entry['program'] = $matches['program']; } 466 if (isset($matches['tag'])) { $entry['tag'] = $matches['tag']; } 467 // Tags, also allowed multiple tagsX (0-9), started from 0 468 $i = 0; 469 while (isset($matches['tag'.$i]) && $matches['tag'.$i]) 470 { 471 $entry['tag'] = rtrim($entry['tag'], ':'); // remove last : 472 $entry['tag'] .= ','.$matches['tag'.$i]; 473 $i++; 474 } 475 break; // Stop other loop if pattern found 476 } 477 } 478 } 479 // OS definition based syslog program format 480 if (isset($config['os'][$os]['syslog_program']) && strlen($entry['program'])) 481 { 482 foreach ($config['os'][$os]['syslog_program'] as $pattern) 483 { 484 if (preg_match($pattern, $entry['program'], $matches)) 485 { 486 if (OBS_DEBUG) 487 { 488 print_cli_table(array(array('syslog program', $entry['program']), array('matched pattern', $pattern)), NULL); 489 } 490 // Override founded tag/program references 491 if (isset($matches['program'])) { $entry['program'] = $matches['program']; } 492 if (isset($matches['tag'])) { $entry['tag'] = $matches['tag']; } 493 /* 494 // Tags, also allowed multiple tagsX (0-9), started from 0 495 $i = 0; 496 while (isset($matches['tag'.$i]) && $matches['tag'.$i]) 497 { 498 $entry['tag'] = rtrim($entry['tag'], ':'); // remove last : 499 $entry['tag'] .= ','.$matches['tag'.$i]; 500 $i++; 501 } 502 */ 503 break; // Stop other loop if pattern found 504 } 505 } 506 } 507 508 // Additional syslog cases, when regex from definition not possible 509 if ($os_group == 'cisco') 510 { 511 // Cisco by default store in tag/program syslog fields just seq no, 512 // this not useful for this fields 513 if ($entry['priority'] > 6 && (is_numeric($entry['program']) || empty($entry['program']))) 514 { 515 $entry['program'] = 'debug'; 516 $entry['tag'] = 'debug'; 517 // Remove prior seqno and timestamp from msg 518 $entry['msg'] = preg_replace('/^\s*(?<seq>\d+:)*\s*(?<timestamp>.*?\d+\:\d+\:\d+(?:\.\d+)?(?:\ [\w\-\+]+)?): /', '', $entry['msg']); 519 } 520 } 521 // CLEANME. MOVED to cisco group definition 522 // //NOTE. Please include examples for syslog entries, to know why need some preg_replace() 523 // if (strstr($entry['msg'], '%')) 524 // { 525 // //10.0.0.210||23||4||4||26644:||2013-11-08 07:19:24|| 033884: Nov 8 07:19:23.993: %FW-4-TCP_OoO_SEG: Dropping TCP Segment: seq:-1169729434 1500 bytes is out-of-order; expected seq:3124765814. Reason: TCP reassembly queue overflow - session 10.10.32.37:56316 to 93.186.239.142:80 on zone-pair Local->Internet class All_Inspection||26644 526 // //hostname||17||5||5||192462650:||2014-06-17 11:16:01|| %SSH-5-SSH2_SESSION: SSH2 Session request from 10.95.0.42 (tty = 0) using crypto cipher 'aes256-cbc', hmac 'hmac-sha1' Succeeded||192462650 527 // if (strpos($entry['msg'], ': %')) 528 // { 529 // list(,$entry['msg']) = explode(': %', $entry['msg'], 2); 530 // $entry['msg'] = "%" . $entry['msg']; 531 // } 532 // $entry['msg'] = preg_replace("/^%(.+?):\ /", "\\1||", $entry['msg']); 533 // } else { 534 // $entry['msg'] = preg_replace("/^.*[0-9]:/", "", $entry['msg']); 535 // $entry['msg'] = preg_replace("/^[0-9][0-9]\ [A-Z]{3}:/", "", $entry['msg']); 536 // $entry['msg'] = preg_replace("/^(.+?):\ /", "\\1||", $entry['msg']); 537 // } 538 // //$entry['msg'] = preg_replace("/^.+\.[0-9]{3}:/", "", $entry['msg']); /// FIXME. Show which entries this should replace. It's broke all entries with 'IP:PORT'. 539 // $entry['msg'] = preg_replace("/^.+-Traceback=/", "Traceback||", $entry['msg']); 540 // 541 // list($entry['program'], $entry['msg']) = explode("||", $entry['msg'], 2); 542 // $entry['msg'] = preg_replace("/^[0-9]+:/", "", $entry['msg']); 543 // 544 // if (!$entry['program']) 545 // { 546 // $entry['msg'] = preg_replace("/^([0-9A-Z\-]+?):\ /", "\\1||", $entry['msg']); 547 // list($entry['program'], $entry['msg']) = explode("||", $entry['msg'], 2); 548 // } 549 // 550 // if (!$entry['msg']) { $entry['msg'] = $entry['program']; unset ($entry['program']); } 551 //} 552 // CLEANME. MOVED to os definition 553 //else if ($os == 'iosxr') 554 //{ 555 // //1.1.1.1||23||5||5||920:||2014-11-26 17:29:48||RP/0/RSP0/CPU0:Nov 26 16:29:48.161 : bgp[1046]: %ROUTING-BGP-5-ADJCHANGE : neighbor 1.1.1.2 Up (VRF: default) (AS: 11111) ||920 556 // //1.1.1.2||23||6||6||253:||2014-11-26 17:30:21||RP/0/RSP0/CPU0:Nov 26 16:30:21.710 : SSHD_[65755]: %SECURITY-SSHD-6-INFO_GENERAL : Client closes socket connection ||253 557 // //1.1.1.3||local0||err||err||83||2015-01-14 07:29:45||oly-er-01 LC/0/0/CPU0:Jan 14 07:29:45.556 CET: pfilter_ea[301]: %L2-PFILTER_EA-3-ERR_IM_CAPS : uidb set acl failed on interface Bundle-Ether1.1501.ip43696. (null) ||94795 558 // 559 // // This also provides two unused entries containing the actual process on IOS-XE as well as the module which originated the message 560 // 561 // if (preg_match('/%(?<program>\S+) :(?<msg>.+)/', $entry['msg'], $matches)) 562 // { 563 // $entry['program'] = $matches['program']; 564 // $entry['msg'] = $matches['msg']; 565 // } 566 // //list($entry['temp_mod'], $entry['temp_prog'], $entry['msg']) = explode(': ', $entry['msg'], 3); 567 // //list($entry['temp_mod'],) = explode(":", $entry['temp_mod']); 568 // //list($entry['program'], $entry['msg']) = explode(' : ', $entry['msg'], 2); 569 //} 570 else if (in_array($os, array('junos', 'junose'))) 571 { 572 //1.1.1.1||9||6||6||/usr/sbin/cron[1305]:||2015-04-08 14:30:01|| (root) CMD ( /usr/libexec/atrun)|| 573 if (str_contains($entry['tag'], '/')) 574 { 575 $entry['tag'] = end(explode('/', $entry['tag'])); // /usr/sbin/cron[1305]: -> cron[1305]: 576 } 577 if (empty($entry['program'])) 578 { 579 list($entry['program']) = explode('[', rtrim($entry['tag'], ':')); // cron[1305]: -> cron 580 } 581 //1.1.1.1||3||4||4||mib2d[1230]:||2015-04-08 14:30:11|| SNMP_TRAP_LINK_DOWN: ifIndex 602, ifAdminStatus up(1), ifOperStatus down(2), ifName ge-0/1/0||mib2d 582 //1.1.1.1||3||6||6||chassism[1210]:||2015-04-08 14:30:16|| ethswitch_eth_devstop: called for port ge-0/1/1||chassism 583 //1.1.1.1||3||3||3||chassism[1210]:||2015-04-08 14:30:22|| ETH:if_ethgetinfo() returns error||chassism 584 } 585 else if ($os == 'linux' && get_cache($entry['host'], 'version') == 'Point') 586 { 587 // Cisco WAP200 and similar 588 $matches = array(); 589 if (preg_match('#Log: \[(?P<program>.*)\] - (?P<msg>.*)#', $entry['msg'], $matches)) 590 { 591 $entry['msg'] = $matches['msg']; 592 $entry['program'] = $matches['program']; 593 } 594 unset($matches); 595 596 } 597 else if ($os_group == 'unix') 598 { 599 //1.1.1.1||9||6||6||/usr/sbin/cron[1305]:||2015-04-08 14:30:01|| (root) CMD ( /usr/libexec/atrun)|| 600 if (str_contains($entry['tag'], '/')) 601 { 602 $entry['tag'] = end(explode('/', $entry['tag'])); // /usr/sbin/cron[1305]: -> cron[1305]: 603 // And same for program if it based on tag (from os definitions) 604 if (str_contains($entry['program'], '/')) 605 { 606 $entry['program'] = end(explode('/', $entry['program'])); 607 } 608 } 609 if (empty($entry['program'])) 610 { 611 list($entry['program']) = explode('[', rtrim($entry['tag'], ':')); // cron[1305]: -> cron 612 } 613 614 // User_CommonName/123.213.132.231:39872 VERIFY OK: depth=1, /C=PL/ST=Malopolska/O=VLO/CN=v-lo.krakow.pl/emailAddress=root@v-lo.krakow.pl 615 if ($entry['facility'] == 'daemon' && preg_match('#/([0-9]{1,3}\.) {3}[0-9]{1,3}:[0-9]{4,} ([A-Z]([A-Za-z])+( ?)) {2,}:#', $entry['msg'])) 616 { 617 $entry['program'] = 'OpenVPN'; 618 } 619 // pop3-login: Login: user=<username>, method=PLAIN, rip=123.213.132.231, lip=123.213.132.231, TLS 620 // POP3(username): Disconnected: Logged out top=0/0, retr=0/0, del=0/1, size=2802 621 else if ($entry['facility'] == 'mail' && preg_match('/^(((pop3|imap)\-login)|((POP3|IMAP)\(.*\))):/', $entry['msg'])) 622 { 623 $entry['program'] = 'Dovecot'; 624 } 625 // CLEANME. MOVED to unix group definition 626 // pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 627 // pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 628 //else if (preg_match('/^(?P<program>(\S((\(|\[).*(\)|\])))):(?P<msg>.*)$/', $entry['msg'], $matches)) 629 //{ 630 // $entry['msg'] = $matches['msg']; 631 // $entry['program'] = $matches['program']; 632 //} 633 // pam_krb5: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 634 // diskio.c: don't know how to handle 10 request 635 // MOVED to unix group definition 636 //else if (preg_match('/^(?P<program>[^\s\(\[]*):\ (?P<msg>.*)$/', $entry['msg'], $matches)) 637 //{ 638 // $entry['msg'] = $matches['msg']; 639 // $entry['program'] = $matches['program']; 640 //} 641 //// Wed Mar 26 12:54:17 2014 : Auth: Login incorrect (mschap: External script says Logon failure (0xc000006d)): [username] (from client 10.100.1.3 port 0 cli a4c3612a4077 via TLS tunnel) 642 //else if (!empty($entry['program']) && preg_match('/^.*:\ '.$entry['program'].':\ (?P<msg>[^(]+\((?P<tag>[^:]+):.*)$/', $entry['msg'], $matches)) 643 //{ 644 // $entry['msg'] = $matches['msg']; 645 // $entry['tag'] = $matches['tag']; 646 //} 647 // SYSLOG CONNECTION BROKEN; FD='6', SERVER='AF_INET(123.213.132.231:514)', time_reopen='60' 648 // 1.1.1.1||5||3||3||rsyslogd-2039:||2016-10-06 23:03:27|| Could no open output pipe '/dev/xconsole': No such file or directory [try http://www.rsyslog.com/e/2039 ]||rsyslogd-2039 649 $entry['program'] = preg_replace('/\-\d+$/', '', $entry['program']); 650 $entry['program'] = str_replace('rsyslogd0', 'rsyslogd', $entry['program']); 651 unset($matches); 652 if (str_contains($entry['program'], '/')) 653 { 654 // postfix/smtp 655 list($entry['program'], $tag) = explode('/', $entry['program'], 2); 656 $entry['tag'] .= ','.$tag; 657 } 658 } 659 // CLEANME. Moved to os definition 660 //else if ($os == 'ftos') 661 //{ 662 // if (empty($entry['program'])) 663 // { 664 // //1.1.1.1||23||5||5||||2014-11-23 21:48:10|| Nov 23 21:48:10.745: hostname: %STKUNIT0-M:CP %SEC-5-LOGOUT: Exec session is terminated for user rancid on line vty0|| 665 // list(,, $entry['program'], $entry['msg']) = explode(': ', $entry['msg'], 4); 666 // list(, $entry['program']) = explode(' %', $entry['program'], 2); 667 // } 668 // //Jun 3 02:33:23.489: %STKUNIT0-M:CP %SNMP-3-SNMP_AUTH_FAIL: SNMP Authentication failure for SNMP request from host 176.10.35.241 669 // //Jun 1 17:11:50.806: %STKUNIT0-M:CP %ARPMGR-2-MAC_CHANGE: IP-4-ADDRMOVE: IP address 11.222.30.53 is moved from MAC address 52:54:00:7b:37:ad to MAC address 52:54:00:e4:ec:06 . 670 // //if (strpos($entry['msg'], '%STKUNIT') === 0) 671 // //{ 672 // // list(, $entry['program'], $entry['msg']) = explode(': ', $entry['msg'], 3); 673 // // //$entry['timestamp'] = date("Y-m-d H:i:s", strtotime($entry['timestamp'])); // convert to timestamp 674 // // list(, $entry['program']) = explode(' %', $entry['program'], 2); 675 // //} 676 //} 677 else if ($os == 'netscaler') 678 { 679 //10/03/2013:16:49:07 GMT dk-lb001a PPE-4 : UI CMD_EXECUTED 10367926 : User so_readonly - Remote_ip 10.70.66.56 - Command "stat lb vserver" - Status "Success" 680 list(,,,$entry['msg']) = explode(' ', $entry['msg'], 4); 681 list($entry['program'], $entry['msg']) = explode(' : ', $entry['msg'], 3); 682 } 683 else if (str_starts($entry['tag'], '(')) 684 { 685 // Ubiquiti Unifi devices 686 // Wtf is BZ2LR and BZ@.. 687 /** 688 *Old: 10.10.34.10||3||6||6||hostapd:||2014-07-18 11:29:35|| ath2: STA c8:dd:c9:d1:d4:aa IEEE 802.11: associated||hostapd 689 *New: 10.10.34.10||3||6||6||(BZ2LR,00272250c1cd,v3.2.5.2791)||2014-12-12 09:36:39|| hostapd: ath2: STA dc:a9:71:1b:d6:c7 IEEE 802.11: associated||(BZ2LR,00272250c1cd,v3.2.5.2791) 690 *New2: 10.10.34.11||1||6||6||("BZ2LR,00272250c119,v3.7.8.5016")||2016-10-06 18:20:25|| syslog: wevent.ubnt_custom_event(): EVENT_STA_LEAVE ath0: dc:a9:71:1b:d6:c7 / 3||("BZ2LR,00272250c119,v3.7.8.5016") 691 * 10.10.34.7||1||6||6||("U7LR,44d9e7f618f2,v3.7.17.5220")||2016-10-06 18:21:22|| libubnt[16915]: wevent.ubnt_custom_event(): EVENT_STA_JOIN ath0: fc:64:ba:c1:7d:28 / 1||("U7LR,44d9e7f618f2,v3.7.17.5220") 692 */ 693 if (preg_match('/^\s*(?<tag>(?<program>\S+?)(\[\d+\])?): +(?<msg>.*)/', $entry['msg'], $matches)) 694 { 695 $entry['msg'] = $matches['msg']; 696 $entry['program'] = $matches['program']; 697 $entry['tag'] = $matches['tag']; 698 } 699 700 } 701 else if (str_contains($entry['program'], ',')) 702 { 703 // Microtik (and some other) 704 // mikrotik||user||5||notice||0d||2018-03-23 07:48:39||dhcp105 assigned 192.168.58.84 to 80:BE:05:7A:73:6E||dhcp,info 705 list($entry['program'], $entry['tag']) = explode(',', $entry['program'], 2); 706 } 707 708 // Always clear timestamp from beginig of message (if still leaved), test strings: 709 //2018-10-16T18:13:03+02:00 hostname 710 $pettern_timestamp_rfc3339 = '/^\s*\*?(?<year>[0-9]{4})\-(?<month>[0-9]{2})\-(?<day>[0-9]{2})(?:[Tt](?<hour>[0-9]{2}):(?<minute>[0-9]{2}):(?<second>(?:[0-9]{2})(?:\.[0-9]+)?)?)(?<tz>(?:[Zz]|[+\-](?:[0-9]{2}):(?:[0-9]{2})))?/'; 711 //Wed Mar 26 12:54:17 2014 : 712 //May 30 15:33:20.636 UTC : 713 //May 30 15:33:20.636 2014 UTC : 714 //Mar 19 06:48:12.692: 715 //Mar 19 15:12:23 MSK/MSD: 716 //Apr 24 2013 16:00:28 INT-FW01 : 717 //LC/0/0/CPU0:Oct 19 09:17:07.433 : 718 //oly-er-01 LC/0/0/CPU0:Jan 14 07:29:45.556 CET: 719 //003174: Jan 26 04:27:09.174 MSK: 720 //001743: *Apr 25 04:16:54.749: 721 //033884: Nov 8 07:19:23.993: 722 // Should be false: 723 //CompDHCP assigned 10.0.0.222 to 4C:32:75:90:69:33 724 $pattern_timestamp = '/^(?<max2words>\s*(?:\S+\s+)?\S+?:)?\s*\*?(?<wmd>(?<week>[a-z]{3,} +)?(?<month>[a-z]{3,} +)(?<day>\d{1,2} +)(?<year0>[12]\d{3} +)?)?(?<hms>\d{1,2}\:\d{1,2}\:\d{1,2}(?:\.\d+)?)(?<year>\s+[12]\d{3})?(?<tz>\s+[a-z][\w\/\-]+)?\s*:\s/i'; 725 // without TZ, example: 726 //Mar 21 13:07:05 netflow syslogd: 727 $pattern_timestamp_wo_tz = '/^\s*\*?(?<wmd>(?<week>[a-z]{3,} +)?(?<month>[a-z]{3,} +)(?<date>\d{1,2} +)(?<year0>[12]\d{3} +)?)?(?<hms>\d{1,2}\:\d{1,2}\:\d{1,2}(?:\.\d+)?)(?<year>\s+[12]\d{3})?/i'; 728 $entry['msg'] = preg_replace(array($pattern_timestamp, $pattern_timestamp_wo_tz, $pettern_timestamp_rfc3339), '', $entry['msg']); 729 730 if (!strlen($entry['msg'])) 731 { 732 // Something wrong, msg empty 733 return FALSE; 734 } 735 736 // Wed Mar 26 12:54:17 2014 : Auth: Login incorrect (mschap: External script says Logon failure (0xc000006d)): [username] (from client 10.100.1.3 port 0 cli a4c3612a4077 via TLS tunnel) 737 if (strlen($entry['program'])) 738 { 739 // Always clear program from begining of message, ie Auth:, blabla[27346]: 740 $pattern_program = '/^\s*'.preg_quote($entry['program'], '/').'(\[\d+\])?\s*:/i'; 741 $entry['msg'] = preg_replace($pattern_program, '', $entry['msg']); 742 } 743 else if (strlen($entry['facility'])) 744 { 745 // fallback, better than nothing... 746 $entry['program'] = $entry['facility']; 747 } else { 748 $entry['program'] = 'generic'; // Derp, do not leave empty program 749 } 750 751 // Last point clear 752 $entry['program'] = strtoupper($entry['program']); 753 $entry['tag'] = trim($entry['tag'], ',: '); 754 755 } 756 757 //array_walk($entry, 'trim'); 758 return array_map('trim', $entry); 759} 760 761// EOF 762