diff --git a/LibreNMS/Cache/Device.php b/LibreNMS/Cache/Device.php index 4ba768e4702e..524a5d35567e 100644 --- a/LibreNMS/Cache/Device.php +++ b/LibreNMS/Cache/Device.php @@ -89,9 +89,25 @@ public function get(int|string|null $device): \App\Models\Device */ public function getByHostname($hostname): \App\Models\Device { - $device_id = array_column($this->devices, 'device_id', 'hostname')[$hostname] ?? 0; + return $this->getByField('hostname', $hostname); + } + + /** + * Get device by any device field or a number of fields + * Slower than by device_id, but attempts to prevent an sql query + */ + public function getByField(string|array $fields, mixed $value): \App\Models\Device + { + $fields = (array)$fields; - return $this->devices[$device_id] ?? $this->load($hostname, 'hostname'); + foreach ($fields as $field) { + $device_id = array_column($this->devices, 'device_id', $field)[$value] ?? 0; + if ($device_id) { + break; + } + } + + return $this->devices[$device_id] ?? $this->load($value, $fields); } /** @@ -123,9 +139,21 @@ public function has(int $device_id): bool return isset($this->devices[$device_id]); } - private function load(mixed $value, string $field = 'device_id'): \App\Models\Device + private function load(mixed $value, string|array $field = ['device_id']): \App\Models\Device { - $device = \App\Models\Device::query()->where($field, $value)->first(); + $query = \App\Models\Device::query(); + foreach ((array) $field as $column) { + if ($column == 'ip') { + $value = inet_pton($value); // convert IP to binary for query + if ($value === false) { + continue; // not an IP, skip the ip field + } + } + + $query->orWhere($column, $value); + } + + $device = $query->first(); if (! $device) { return new \App\Models\Device; @@ -135,4 +163,24 @@ private function load(mixed $value, string $field = 'device_id'): \App\Models\De return $device; } + + /** + * Insert a fake device into the cache to avoid database lookups + * Will not work with relationships unless they are pre-populated (and not using a relationship based query) + */ + public function fake(\App\Models\Device $device): \App\Models\Device + { + if (empty($device->device_id)) { + // find a free device_id + $device->device_id = 1; + while (isset($this->devices[$device->device_id])) { + $device->device_id++; + } + } + + $device->exists = true; // fake that device is saved to database + $this->devices[$device->device_id] = $device; + + return $device; + } } diff --git a/LibreNMS/Syslog.php b/LibreNMS/Syslog.php new file mode 100644 index 000000000000..75a95bf06325 --- /dev/null +++ b/LibreNMS/Syslog.php @@ -0,0 +1,211 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2023 Tony Murray + * @author Tony Murray + */ + +namespace LibreNMS; + +use App\Facades\DeviceCache; +use App\Models\Device; +use App\Models\Ipv4Address; +use Illuminate\Support\Arr; + +class Syslog +{ + private array $host_map; + private array $filter; + private array $host_xlate; + private bool $hooks_enabled; + + public function __construct() { + $this->host_map = []; + $this->filter = Arr::wrap(Config::get('syslog_filter')); + $this->host_xlate = Arr::wrap(Config::get('syslog_xlate')); + $this->hooks_enabled = (bool) Config::get('enable_syslog_hooks', false); + } + + public function process(array $entry, bool $update = true) { + foreach ($this->filter as $bi) { + if (isset($entry['msg']) && str_contains($entry['msg'], $bi)) { + return $entry; + } + } + + $entry['host'] = preg_replace('/^::ffff:/', '', $entry['host']); // remove ipv6 socket prefix for ipv4 + $entry['host'] = $this->host_xlate[$entry['host']] ?? $entry['host']; // translate host based on config + + $device = $this->findHost($entry['host']); + + if ($device->exists) { + $entry['device_id'] = $device->device_id; + $this->executeHooks($device, $entry); + $entry = $this->handleOsSpecificTweaks($device, $entry); + + // handle common case fields were msg is missing + if (! isset($entry['program'])) { + $entry['program'] = $entry['msg']; + unset($entry['msg']); + } + + if (isset($entry['program'])) { + $entry['program'] = strtoupper($entry['program']); + } + + $entry = array_map(fn($value) => is_string($value) ? trim($value) : $value, $entry); + + if ($update) { + \App\Models\Syslog::create($entry); + } + } + + return $entry; + } + + public function findHost(string $host): Device + { + if (empty($this->host_map[$host])) { + // try hostname + $device = DeviceCache::getByField(['hostname', 'sysName', 'ip'], $host); + + if (! $device->exists) { + // If failed, try by IPs on interfaces + $device_id = Ipv4Address::query()->leftJoin('ports', 'ipv4_addresses.port_id', '=', 'ports.port_id') + ->where('ipv4_address', $host)->value('device_id'); + $device = DeviceCache::get($device_id); + } + + $this->host_map[$host] = $device->device_id; // save to map + } + + return DeviceCache::get($this->host_map[$host]); + } + + private function executeHooks(Device $device, array $entry): void + { + if ($this->hooks_enabled && is_array($hooks = Config::getOsSetting($device->os, 'syslog_hook'))) { + foreach ($hooks as $hook) { + $syslogprogmsg = $entry['program'] . ': ' . $entry['msg']; + if ((isset($hook['script'])) && (isset($hook['regex'])) && preg_match($hook['regex'], $syslogprogmsg)) { + shell_exec(escapeshellcmd($hook['script']) . ' ' . escapeshellarg($device->hostname) . ' ' . escapeshellarg($device->os) . ' ' . escapeshellarg($syslogprogmsg) . ' >/dev/null 2>&1 &'); + } + } + } + } + + private function handleOsSpecificTweaks(Device $device, array $entry): array + { + // ios like + if (in_array($device->os, ['ios', 'iosxe', 'catos'])) { + // multipart message + if (strpos($entry['msg'], ':') !== false) { + $timestamp_prefix = '([\*\.]?[A-Z][a-z]{2} \d\d? \d\d:\d\d:\d\d(.\d\d\d)?( [A-Z]{3})?: )?'; + $program_match = '(?%?[A-Za-z\d\-_]+(:[A-Z]* %[A-Z\d\-_]+)?)'; + $message_match = '(?.*)'; + if (preg_match('/^' . $timestamp_prefix . $program_match . ': ?' . $message_match . '/', $entry['msg'], $matches)) { + $entry['program'] = $matches['program']; + $entry['msg'] = $matches['msg']; + } + } else { + // if this looks like a program (no groups of 2 or more lowercase letters), move it to program + if (! preg_match('/[(a-z)]{2,}/', $entry['msg'])) { + $entry['program'] = $entry['msg']; + unset($entry['msg']); + } + } + + return $entry; + } + + if ($device->os == 'linux') { + // Cisco WAP200 and similar + if ($device->version == 'Point') { + if (preg_match('#Log: \[(?P.*)\] - (?P.*)#', $entry['msg'], $matches)) { + $entry['msg'] = $matches['msg']; + $entry['program'] = $matches['program']; + } + + return $entry; + } + + // regular linux + // pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 + // pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 + if (empty($entry['program']) && isset($entry['msg']) && preg_match('#^(?P([^(:]+\([^)]+\)|[^\[:]+\[[^\]]+\])) ?: ?(?P.*)$#', $entry['msg'], $matches)) { + $entry['msg'] = $matches['msg']; + $entry['program'] = $matches['program']; + } elseif (empty($entry['program']) && ! empty($entry['facility'])) { + // SYSLOG CONNECTION BROKEN; FD='6', SERVER='AF_INET(123.213.132.231:514)', time_reopen='60' + // pam_krb5: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 + // Disabled because broke this: + // diskio.c: don't know how to handle 10 request + // elseif($pos = strpos($entry['msg'], ';') or $pos = strpos($entry['msg'], ':')) { + // $entry['program'] = substr($entry['msg'], 0, $pos); + // $entry['msg'] = substr($entry['msg'], $pos+1); + // } + // fallback, better than nothing... + $entry['program'] = $entry['facility']; + } + + return $entry; + } + + // HP ProCurve + if ($device->os == 'procurve') { + if (preg_match('/^(?P[A-Za-z]+): {2}(?P.*)/', $entry['msg'], $matches)) { + $entry['msg'] = $matches['msg'] . ' [' . $entry['program'] . ']'; + $entry['program'] = $matches['program']; + } + + return $entry; + } + + // Zwwall sends messages without all the fields, so the offset is wrong + if ($device->os == 'zywall') { + $msg = preg_replace('/" /', '";', stripslashes($entry['program'] . ':' . $entry['msg'])); + $msg = str_getcsv($msg, ';'); + $entry['program'] = null; + foreach ($msg as $param) { + [$var, $val] = explode('=', $param); + if ($var == 'cat') { + $entry['program'] = str_replace('"', '', $val); + } + } + $entry['msg'] = join(' ', $msg); + + return $entry; + } + + return $entry; + } + + public function getMap(): array{ + return $this->host_map; + } + + public function reset() + { + Config::reload(); + DeviceCache::flush(); + $this->__construct(); + } +} diff --git a/app/Models/Syslog.php b/app/Models/Syslog.php index cf24e38c1b33..06634cbeda53 100644 --- a/app/Models/Syslog.php +++ b/app/Models/Syslog.php @@ -34,4 +34,14 @@ class Syslog extends DeviceRelatedModel protected $table = 'syslog'; protected $primaryKey = 'seq'; public $timestamps = false; + protected $fillable = [ + 'device_id', + 'program', + 'facility', + 'priority', + 'level', + 'tag', + 'msg', + 'timestamp', + ]; } diff --git a/doc/Extensions/Syslog.md b/doc/Extensions/Syslog.md index e802c2a03254..8e74aac1c1fc 100644 --- a/doc/Extensions/Syslog.md +++ b/doc/Extensions/Syslog.md @@ -32,7 +32,7 @@ source s_net { }; destination d_librenms { - program("/opt/librenms/syslog.php" template ("$HOST||$FACILITY||$PRIORITY||$LEVEL||$TAG||$R_YEAR-$R_MONTH-$R_DAY $R_HOUR:$R_MIN:$R_SEC||$MSG||$PROGRAM\n") template-escape(yes)); + program("/opt/librenms/lnms handle:syslog" template ("$HOST||$FACILITY||$PRIORITY||$LEVEL||$TAG||$R_YEAR-$R_MONTH-$R_DAY $R_HOUR:$R_MIN:$R_SEC||$MSG||$PROGRAM\n") template-escape(yes)); }; log { @@ -103,7 +103,7 @@ Create a file called `/etc/rsyslog.d/30-librenms.conf`and add the following depe type="string" string= "%fromhost%||%syslogfacility%||%syslogpriority%||%syslogseverity%||%syslogtag%||%$year%-%$month%-%$day% %timegenerated:8:25%||%msg%||%programname%\n") action(type="omprog" - binary="/opt/librenms/syslog.php" + binary="/opt/librenms/lnms handle:syslog" template="librenms") & stop @@ -116,7 +116,7 @@ Create a file called `/etc/rsyslog.d/30-librenms.conf`and add the following depe $template librenms,"%fromhost%||%syslogfacility%||%syslogpriority%||%syslogseverity%||%syslogtag%||%$year%-%$month%-%$day% %timegenerated:8:25%||%msg%||%programname%\n" - *.* action(type="omprog" binary="/opt/librenms/syslog.php" template="librenms") + *.* action(type="omprog" binary="/opt/librenms/lnms handle:syslog" template="librenms") & stop @@ -128,7 +128,7 @@ Create a file called `/etc/rsyslog.d/30-librenms.conf`and add the following depe $ModLoad omprog $template librenms,"%FROMHOST%||%syslogfacility-text%||%syslogpriority-text%||%syslogseverity%||%syslogtag%||%$YEAR%-%$MONTH%-%$DAY% %timegenerated:8:25%||%msg%||%programname%\n" - $ActionOMProgBinary /opt/librenms/syslog.php + $ActionOMProgBinary /opt/librenms/lnms handle:syslog *.* :omprog:;librenms ``` @@ -164,7 +164,7 @@ syslog { output { exec { - command => "echo `echo %{host},,,,%{facility},,,,%{priority},,,,%{severity},,,,%{facility_label},,,,``date --date='%{timestamp}' '+%Y-%m-%d %H:%M:%S'``echo ',,,,%{message}'``echo ,,,,%{program} | sed 's/\x25\x7b\x70\x72\x6f\x67\x72\x61\x6d\x7d/%{facility_label}/'` | sed 's/,,,,/||/g' | /opt/librenms/syslog.php &" + command => "echo `echo %{host},,,,%{facility},,,,%{priority},,,,%{severity},,,,%{facility_label},,,,``date --date='%{timestamp}' '+%Y-%m-%d %H:%M:%S'``echo ',,,,%{message}'``echo ,,,,%{program} | sed 's/\x25\x7b\x70\x72\x6f\x67\x72\x61\x6d\x7d/%{facility_label}/'` | sed 's/,,,,/||/g' | /opt/librenms/lnms handle:syslog &" } elasticsearch { hosts => ["10.10.10.10:9200"] diff --git a/includes/html/api_functions.inc.php b/includes/html/api_functions.inc.php index d3dd3070fcac..bf8d88e954b5 100644 --- a/includes/html/api_functions.inc.php +++ b/includes/html/api_functions.inc.php @@ -2937,9 +2937,10 @@ function post_syslogsink(Illuminate\Http\Request $request) } $logs = array_is_list($json) ? $json : [$json]; + $syslog = new \LibreNMS\Syslog; foreach ($logs as $entry) { - process_syslog($entry, 1); + $syslog->process($entry); } return api_success_noresult(200, 'Syslog received: ' . count($logs)); diff --git a/includes/syslog.php b/includes/syslog.php deleted file mode 100644 index b7eceb21d31a..000000000000 --- a/includes/syslog.php +++ /dev/null @@ -1,172 +0,0 @@ - $v) { - $syslogprogmsg = $entry['program'] . ': ' . $entry['msg']; - if ((isset($v['script'])) && (isset($v['regex'])) && preg_match($v['regex'], $syslogprogmsg)) { - shell_exec(escapeshellcmd($v['script']) . ' ' . escapeshellarg($hostname) . ' ' . escapeshellarg($os) . ' ' . escapeshellarg($syslogprogmsg) . ' >/dev/null 2>&1 &'); - } - } - } - - if (in_array($os, ['ios', 'iosxe', 'catos'])) { - // multipart message - if (strpos($entry['msg'], ':') !== false) { - $matches = []; - $timestamp_prefix = '([\*\.]?[A-Z][a-z]{2} \d\d? \d\d:\d\d:\d\d(.\d\d\d)?( [A-Z]{3})?: )?'; - $program_match = '(?%?[A-Za-z\d\-_]+(:[A-Z]* %[A-Z\d\-_]+)?)'; - $message_match = '(?.*)'; - if (preg_match('/^' . $timestamp_prefix . $program_match . ': ?' . $message_match . '/', $entry['msg'], $matches)) { - $entry['program'] = $matches['program']; - $entry['msg'] = $matches['msg']; - } - unset($matches); - } else { - // if this looks like a program (no groups of 2 or more lowercase letters), move it to program - if (! preg_match('/[(a-z)]{2,}/', $entry['msg'])) { - $entry['program'] = $entry['msg']; - unset($entry['msg']); - } - } - } elseif ($os == 'linux' and get_cache($entry['host'], 'version') == 'Point') { - // Cisco WAP200 and similar - $matches = []; - if (preg_match('#Log: \[(?P.*)\] - (?P.*)#', $entry['msg'], $matches)) { - $entry['msg'] = $matches['msg']; - $entry['program'] = $matches['program']; - } - - unset($matches); - } elseif ($os == 'linux') { - $matches = []; - // pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 - // pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 - if (empty($entry['program']) and preg_match('#^(?P([^(:]+\([^)]+\)|[^\[:]+\[[^\]]+\])) ?: ?(?P.*)$#', $entry['msg'], $matches)) { - $entry['msg'] = $matches['msg']; - $entry['program'] = $matches['program']; - } elseif (empty($entry['program']) and ! empty($entry['facility'])) { - // SYSLOG CONNECTION BROKEN; FD='6', SERVER='AF_INET(123.213.132.231:514)', time_reopen='60' - // pam_krb5: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231 - // Disabled because broke this: - // diskio.c: don't know how to handle 10 request - // elseif($pos = strpos($entry['msg'], ';') or $pos = strpos($entry['msg'], ':')) { - // $entry['program'] = substr($entry['msg'], 0, $pos); - // $entry['msg'] = substr($entry['msg'], $pos+1); - // } - // fallback, better than nothing... - $entry['program'] = $entry['facility']; - } - - unset($matches); - } elseif ($os == 'procurve') { - $matches = []; - if (preg_match('/^(?P[A-Za-z]+): {2}(?P.*)/', $entry['msg'], $matches)) { - $entry['msg'] = $matches['msg'] . ' [' . $entry['program'] . ']'; - $entry['program'] = $matches['program']; - } - unset($matches); - } elseif ($os == 'zywall') { - // Zwwall sends messages without all the fields, so the offset is wrong - $msg = preg_replace('/" /', '";', stripslashes($entry['program'] . ':' . $entry['msg'])); - $msg = str_getcsv($msg, ';'); - $entry['program'] = null; - foreach ($msg as $param) { - [$var, $val] = explode('=', $param); - if ($var == 'cat') { - $entry['program'] = str_replace('"', '', $val); - } - } - $entry['msg'] = join(' ', $msg); - }//end if - - if (! isset($entry['program'])) { - $entry['program'] = $entry['msg']; - unset($entry['msg']); - } - - $entry['program'] = strtoupper($entry['program']); - $entry = array_map('trim', $entry); - - if ($update) { - dbInsert( - [ - 'device_id' => $entry['device_id'], - 'program' => $entry['program'], - 'facility' => $entry['facility'], - 'priority' => $entry['priority'], - 'level' => $entry['level'], - 'tag' => $entry['tag'], - 'msg' => $entry['msg'], - 'timestamp' => $entry['timestamp'], - ], - 'syslog' - ); - } - - unset($os); - }//end if - - return $entry; -}//end process_syslog() diff --git a/syslog.php b/syslog.php index fd46841dc1dc..e615dde3b4fa 100755 --- a/syslog.php +++ b/syslog.php @@ -11,11 +11,12 @@ $init_modules = []; require __DIR__ . '/includes/init.php'; +$syslog = new \LibreNMS\Syslog; $s = fopen('php://stdin', 'r'); while ($line = fgets($s)) { //logfile($line); [$entry['host'],$entry['facility'],$entry['priority'], $entry['level'], $entry['tag'], $entry['timestamp'], $entry['msg'], $entry['program']] = explode('||', trim($line)); - process_syslog($entry, 1); + $syslog->process($entry); unset($entry); unset($line); } diff --git a/tests/SyslogTest.php b/tests/SyslogTest.php index e29fe0505a88..75d53502df96 100644 --- a/tests/SyslogTest.php +++ b/tests/SyslogTest.php @@ -25,8 +25,22 @@ namespace LibreNMS\Tests; +use App\Facades\DeviceCache; +use App\Models\Device; +use LibreNMS\Syslog; + class SyslogTest extends TestCase { +// public function setUp(): void +// { +// \DB::enableQueryLog(); +// } +// +// public function tearDown(): void +// { +// dump(\DB::getQueryLog()); +// } + // The format is: // $SOURCEIP||$FACILITY||$PRIORITY||$LEVEL||$TAG||$YEAR-$MONTH-$DAY $HOUR:$MIN:$SEC||$MSG||$PROGRAM // There add an IP for each OS you want to test and use that in the input file @@ -59,186 +73,178 @@ private function createData($line, $resultDelta) private function checkSyslog($inputline, $modified) { $data = $this->createData($inputline, $modified); - $res = process_syslog($data['input'], 0); + $res = (new Syslog())->process($data['input'], false); $this->assertEquals($data['result'], $res); } public function testCiscoSyslog() { - // populate fake $dev_cache - global $dev_cache; - $dev_cache['1.1.1.1'] = ['device_id' => 1, 'os' => 'ios', 'version' => 1, 'hostname' => 'cisco-switch1']; + $device = DeviceCache::fake(new \App\Models\Device(['os' => 'ios', 'version' => 1, 'hostname' => 'cisco-switch1', 'ip' => '1.1.1.1'])); // ---- IOS ---- $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%CARD-SEVERITY-MSG:SLOT %FACILITY-SEVERITY-MNEMONIC: Message-text||', - ['device_id'=>1, 'program'=>'%CARD-SEVERITY-MSG:SLOT %FACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] + ['device_id'=>$device->device_id, 'program'=>'%CARD-SEVERITY-MSG:SLOT %FACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] ); $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC: Message-text||', - ['device_id'=>1, 'program'=>'%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] + 'cisco-switch1||user||info||info||0e||2016-02-28 00:23:34||%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC: Message-text||', + ['device_id'=>$device->device_id, 'program'=>'%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] ); $this->checkSyslog( '1.1.1.1||local7||info||info||be||2016-03-09 03:58:25||Mar 9 11:58:24.145 UTC: %SEC-6-IPACCESSLOGS: list MNGMNT denied 120.62.186.12 1 packet ||]', - ['device_id'=>1, 'program'=>'%SEC-6-IPACCESSLOGS', 'msg'=>'list MNGMNT denied 120.62.186.12 1 packet'] + ['device_id'=>$device->device_id, 'program'=>'%SEC-6-IPACCESSLOGS', 'msg'=>'list MNGMNT denied 120.62.186.12 1 packet'] ); $this->checkSyslog( '1.1.1.1||local7||info||info||be||2016-04-27 021:12:28||Apr 27 21:12:28: %SYS-5-CONFIG_I: Configured from console by vty0||', - ['device_id'=>1, 'program'=>'%SYS-5-CONFIG_I', 'msg'=>'Configured from console by vty0'] + ['device_id'=>$device->device_id, 'program'=>'%SYS-5-CONFIG_I', 'msg'=>'Configured from console by vty0'] ); $this->checkSyslog( '1.1.1.1||local7||info||info||be||2016-04-27 021:12:28||Mar 8 20:14:08.762: %FACILITY-SUBFACILITY-SEVERITY-MNEMONIC: Message-text||000956', - ['device_id'=>1, 'program'=>'%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] + ['device_id'=>$device->device_id, 'program'=>'%FACILITY-SUBFACILITY-SEVERITY-MNEMONIC', 'msg'=>'Message-text'] ); // ---- CatOS ---- $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%IP-3-UDP_SOCKOVFL:UDP socket overflow||', - ['device_id'=>1, 'program'=>'%IP-3-UDP_SOCKOVFL', 'msg'=>'UDP socket overflow'] + ['device_id'=>$device->device_id, 'program'=>'%IP-3-UDP_SOCKOVFL', 'msg'=>'UDP socket overflow'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||DTP-1-ILGLCFG: Illegal config (on, isl--on,dot1q) on Port [mod/port]||', - ['device_id'=>1, 'program'=>'DTP-1-ILGLCFG', 'msg'=>'Illegal config (on, isl--on,dot1q) on Port [mod/port]'] + ['device_id'=>$device->device_id, 'program'=>'DTP-1-ILGLCFG', 'msg'=>'Illegal config (on, isl--on,dot1q) on Port [mod/port]'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||Cannot enable text mode config if ACL config is cleared from nvram||', - ['device_id'=>1, 'program'=>'', 'msg'=>'Cannot enable text mode config if ACL config is cleared from nvram'] + ['device_id'=>$device->device_id, 'program'=>'', 'msg'=>'Cannot enable text mode config if ACL config is cleared from nvram'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%PAGP-5-PORTFROMSTP / %PAGP-5-PORTTOSTP||', - ['device_id'=>1, 'program'=>'%PAGP-5-PORTFROMSTP / %PAGP-5-PORTTOSTP'] + ['device_id'=>$device->device_id, 'program'=>'%PAGP-5-PORTFROMSTP / %PAGP-5-PORTTOSTP'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%SYS-3-EOBC_CHANNELREINIT||', - ['device_id'=>1, 'program'=>'%SYS-3-EOBC_CHANNELREINIT'] + ['device_id'=>$device->device_id, 'program'=>'%SYS-3-EOBC_CHANNELREINIT'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||%SYS-4-MODHPRESET:||', - ['device_id'=>1, 'program'=>'%SYS-4-MODHPRESET', 'msg'=>''] + ['device_id'=>$device->device_id, 'program'=>'%SYS-4-MODHPRESET', 'msg'=>''] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||InbandPingProcessFailure:Module x not responding over inband||', - ['device_id'=>1, 'program'=>'INBANDPINGPROCESSFAILURE', 'msg'=>'Module x not responding over inband'] + ['device_id'=>$device->device_id, 'program'=>'INBANDPINGPROCESSFAILURE', 'msg'=>'Module x not responding over inband'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||RxSBIF_SEQ_NUM_ERROR:slot=x||', - ['device_id'=>1, 'program'=>'RXSBIF_SEQ_NUM_ERROR', 'msg'=>'slot=x'] + ['device_id'=>$device->device_id, 'program'=>'RXSBIF_SEQ_NUM_ERROR', 'msg'=>'slot=x'] ); // With program from syslog $this->checkSyslog( '1.1.1.1||local7||notice||notice||bd||2016-04-04 15:18:43||Apr 4 13:18:42.670: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/32, changed state to up||345735', - ['device_id'=>1, 'program'=>'%LINEPROTO-5-UPDOWN', 'msg'=>'Line protocol on Interface GigabitEthernet0/32, changed state to up'] + ['device_id'=>$device->device_id, 'program'=>'%LINEPROTO-5-UPDOWN', 'msg'=>'Line protocol on Interface GigabitEthernet0/32, changed state to up'] ); // Incorrect time $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-04-06 15:20:35||*Apr 4 21:26:41.778 UTC: %LWAPP-3-REPLAY_ERR: 1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx||', - ['device_id'=>1, 'program'=>'%LWAPP-3-REPLAY_ERR', 'msg'=>'1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx'] + ['device_id'=>$device->device_id, 'program'=>'%LWAPP-3-REPLAY_ERR', 'msg'=>'1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx'] ); $this->checkSyslog( '1.1.1.1||user||info||info||0e||2016-04-06 15:20:35||.Apr 4 21:26:41.778 UTC: %LWAPP-3-REPLAY_ERR: 1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx||', - ['device_id'=>1, 'program'=>'%LWAPP-3-REPLAY_ERR', 'msg'=>'1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx'] + ['device_id'=>$device->device_id, 'program'=>'%LWAPP-3-REPLAY_ERR', 'msg'=>'1 wcm: Received replay error on slot 1, WLAN ID 1, count 1 from AP xxxx.xxxx.xxxx'] ); } public function testLinuxSyslog() { - // populate fake $dev_cache - global $dev_cache; - $dev_cache['1.1.1.1'] = ['device_id' => 1, 'os' => 'linux', 'version' => 1, 'hostname' => 'linux-server1']; - + $device = DeviceCache::fake(new \App\Models\Device(['os' => 'linux', 'version' => 1, 'hostname' => 'linux-server1', 'ip' => '1.1.1.2'])); +; // ---- PAM ---- $this->checkSyslog( - '1.1.1.1||authpriv||info||info||56||2016-02-28 00:23:34||pam_unix(cron:session): session opened for user librenms by (uid=0)||CRON', - ['device_id'=>1, 'program'=>'CRON', 'msg'=>'pam_unix(cron:session): session opened for user librenms by (uid=0)'] + '1.1.1.2||authpriv||info||info||56||2016-02-28 00:23:34||pam_unix(cron:session): session opened for user librenms by (uid=0)||CRON', + ['device_id'=>$device->device_id, 'program'=>'CRON', 'msg'=>'pam_unix(cron:session): session opened for user librenms by (uid=0)'] ); $this->checkSyslog( - '1.1.1.1||authpriv||info||info||55||2016-02-28 00:23:34||pam_unix(sudo:session): session opened for user librenms by root (uid=0)||sudo', - ['device_id'=>1, 'program'=>'SUDO', 'msg'=>'pam_unix(sudo:session): session opened for user librenms by root (uid=0)'] + '1.1.1.2||authpriv||info||info||55||2016-02-28 00:23:34||pam_unix(sudo:session): session opened for user librenms by root (uid=0)||sudo', + ['device_id'=>$device->device_id, 'program'=>'SUDO', 'msg'=>'pam_unix(sudo:session): session opened for user librenms by root (uid=0)'] ); $this->checkSyslog( - '1.1.1.1||auth||info||info||0e||2016-02-28 00:23:34||pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231||sshd', - ['device_id'=>1, 'program'=>'SSHD', 'msg'=>'pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231'] + 'linux-server1||auth||info||info||0e||2016-02-28 00:23:34||pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231||sshd', + ['device_id'=>$device->device_id, 'program'=>'SSHD', 'msg'=>'pam_krb5(sshd:auth): authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231'] ); $this->checkSyslog( - '1.1.1.1||auth||info||info||0e||2016-02-28 00:23:34||pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231||sshd', - ['device_id'=>1, 'program'=>'SSHD', 'msg'=>'pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231'] + '1.1.1.2||auth||info||info||0e||2016-02-28 00:23:34||pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231||sshd', + ['device_id'=>$device->device_id, 'program'=>'SSHD', 'msg'=>'pam_krb5[sshd:auth]: authentication failure; logname=root uid=0 euid=0 tty=ssh ruser= rhost=123.213.132.231'] ); // ---- Postfix ---- $this->checkSyslog( - '1.1.1.1||mail||info||info||16||2016-02-28 00:23:34||5C62E329EF: to=, relay=mail.example.com[127.0.0.1]:25, delay=0.11, delays=0.04/0.01/0/0.06, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5362E6A670E)||postfix/smtp', - ['device_id'=>1, 'program'=>'POSTFIX/SMTP', 'msg'=>'5C62E329EF: to=, relay=mail.example.com[127.0.0.1]:25, delay=0.11, delays=0.04/0.01/0/0.06, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5362E6A670E)'] + '1.1.1.2||mail||info||info||16||2016-02-28 00:23:34||5C62E329EF: to=, relay=mail.example.com[127.0.0.1]:25, delay=0.11, delays=0.04/0.01/0/0.06, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5362E6A670E)||postfix/smtp', + ['device_id'=>$device->device_id, 'program'=>'POSTFIX/SMTP', 'msg'=>'5C62E329EF: to=, relay=mail.example.com[127.0.0.1]:25, delay=0.11, delays=0.04/0.01/0/0.06, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5362E6A670E)'] ); $this->checkSyslog( - '1.1.1.1||mail||info||info||16||2016-02-28 00:23:34||D7256400EF: from=, size=882, nrcpt=1 (queue active)||postfix/qmgr', - ['device_id'=>1, 'program'=>'POSTFIX/QMGR', 'msg'=>'D7256400EF: from=, size=882, nrcpt=1 (queue active)'] + '1.1.1.2||mail||info||info||16||2016-02-28 00:23:34||D7256400EF: from=, size=882, nrcpt=1 (queue active)||postfix/qmgr', + ['device_id'=>$device->device_id, 'program'=>'POSTFIX/QMGR', 'msg'=>'D7256400EF: from=, size=882, nrcpt=1 (queue active)'] ); // ---- No program ---- $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||some random message||', - ['device_id'=>1, 'program'=>'USER', 'msg'=>'some random message'] + '1.1.1.2||user||info||info||0e||2016-02-28 00:23:34||some random message||', + ['device_id'=>$device->device_id, 'program'=>'USER', 'msg'=>'some random message'] ); // ---- Other ---- $this->checkSyslog( - '1.1.1.1||cron||info||info||4e||2016-02-28 00:23:34||(librenms) CMD ( /opt/librenms/alerts.php >> /var/log/librenms_alert.log 2>&1)||CRON', - ['device_id'=>1, 'program'=>'CRON', 'msg'=>'(librenms) CMD ( /opt/librenms/alerts.php >> /var/log/librenms_alert.log 2>&1)'] + '1.1.1.2||cron||info||info||4e||2016-02-28 00:23:34||(librenms) CMD ( /opt/librenms/alerts.php >> /var/log/librenms_alert.log 2>&1)||CRON', + ['device_id'=>$device->device_id, 'program'=>'CRON', 'msg'=>'(librenms) CMD ( /opt/librenms/alerts.php >> /var/log/librenms_alert.log 2>&1)'] ); $this->checkSyslog( - '1.1.1.1||authpriv||notice||notice||55||2016-02-28 00:23:34|| root : TTY=pts/1 ; PWD=/opt/librenms ; USER=librenms ; COMMAND=/usr/bin/git status||sudo', - ['device_id'=>1, 'program'=>'SUDO', 'msg'=>'root : TTY=pts/1 ; PWD=/opt/librenms ; USER=librenms ; COMMAND=/usr/bin/git status'] + '1.1.1.2||authpriv||notice||notice||55||2016-02-28 00:23:34|| root : TTY=pts/1 ; PWD=/opt/librenms ; USER=librenms ; COMMAND=/usr/bin/git status||sudo', + ['device_id'=>$device->device_id, 'program'=>'SUDO', 'msg'=>'root : TTY=pts/1 ; PWD=/opt/librenms ; USER=librenms ; COMMAND=/usr/bin/git status'] ); } public function testProcurveSyslog() { - // populate fake $dev_cache - global $dev_cache; - $dev_cache['1.1.1.1'] = ['device_id' => 1, 'os' => 'procurve', 'version' => 1, 'hostname' => 'procurve-switch1']; + $device = DeviceCache::fake(new \App\Models\Device(['os' => 'procurve', 'version' => 1, 'hostname' => 'procurve-switch1', 'ip' => '1.1.1.3'])); // ---- 2900/2910/3800/5400 ---- $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||chassis: Slot A Ready||00422', - ['device_id'=>1, 'program'=>'CHASSIS', 'msg'=>'Slot A Ready [00422]'] + 'procurve-switch1||user||info||info||0e||2016-02-28 00:23:34||chassis: Slot A Ready||00422', + ['device_id'=>$device->device_id, 'program'=>'CHASSIS', 'msg'=>'Slot A Ready [00422]'] ); $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||ports: port 21 is now on-line||00076', - ['device_id'=>1, 'program'=>'PORTS', 'msg'=>'port 21 is now on-line [00076]'] + '1.1.1.3||user||info||info||0e||2016-02-28 00:23:34||ports: port 21 is now on-line||00076', + ['device_id'=>$device->device_id, 'program'=>'PORTS', 'msg'=>'port 21 is now on-line [00076]'] ); $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||ports: port 21 is now off-line||00077', - ['device_id'=>1, 'program'=>'PORTS', 'msg'=>'port 21 is now off-line [00077]'] + '1.1.1.3||user||info||info||0e||2016-02-28 00:23:34||ports: port 21 is now off-line||00077', + ['device_id'=>$device->device_id, 'program'=>'PORTS', 'msg'=>'port 21 is now off-line [00077]'] ); $this->checkSyslog( - '1.1.1.1||user||warning||warning||0c||2016-02-28 00:23:34||FFI: port 21-High collision or drop rate. See help.||00331', - ['device_id'=>1, 'program'=>'FFI', 'msg'=>'port 21-High collision or drop rate. See help. [00331]'] + '1.1.1.3||user||warning||warning||0c||2016-02-28 00:23:34||FFI: port 21-High collision or drop rate. See help.||00331', + ['device_id'=>$device->device_id, 'program'=>'FFI', 'msg'=>'port 21-High collision or drop rate. See help. [00331]'] ); // ---- 2610 ---- $this->checkSyslog( - '1.1.1.1||user||warning||warning||0c||2016-02-28 00:23:34||port 21-Excessive undersized/giant packets. See help.||FFI', - ['device_id'=>1, 'program'=>'FFI', 'msg'=>'port 21-Excessive undersized/giant packets. See help.'] + '1.1.1.3||user||warning||warning||0c||2016-02-28 00:23:34||port 21-Excessive undersized/giant packets. See help.||FFI', + ['device_id'=>$device->device_id, 'program'=>'FFI', 'msg'=>'port 21-Excessive undersized/giant packets. See help.'] ); $this->checkSyslog( - '1.1.1.1||user||info||info||0e||2016-02-28 00:23:34||updated time by -4 seconds||SNTP', - ['device_id'=>1, 'program'=>'SNTP', 'msg'=>'updated time by -4 seconds'] + '1.1.1.3||user||info||info||0e||2016-02-28 00:23:34||updated time by -4 seconds||SNTP', + ['device_id'=>$device->device_id, 'program'=>'SNTP', 'msg'=>'updated time by -4 seconds'] ); } public function testZywallSyslog() { - // populate fake $dev_cache - global $dev_cache; - $dev_cache['1.1.1.1'] = ['device_id' => 1, 'os' => 'zywall', 'version' => 1, 'hostname' => 'zywall']; + $device = DeviceCache::fake(new Device(['os' => 'zywall', 'version' => 1, 'hostname' => 'zywall', 'ip' => '1.1.1.4'])); // ---- USG60W ---- $this->checkSyslog( - '1.1.1.1||local1||info||info||8e||2017-06-14 17:51:25||0" dst="0.0.0.0:0" msg="DHCP server assigned 195.159.132.109 to Chromecast(6C:AD:F8:B1:10:1D)" note="DHCP ACK" user="unknown" devID="a0e4cb7d7f52" cat="DHCP"||src="0.0.0.0', - ['device_id'=>1, 'program'=>'DHCP', 'msg'=>'src="0.0.0.0:0" dst="0.0.0.0:0" msg="DHCP server assigned 195.159.132.109 to Chromecast(6C:AD:F8:B1:10:1D)" note="DHCP ACK" user="unknown" devID="a0e4cb7d7f52" cat="DHCP"'] + '1.1.1.4||local1||info||info||8e||2017-06-14 17:51:25||0" dst="0.0.0.0:0" msg="DHCP server assigned 195.159.132.109 to Chromecast(6C:AD:F8:B1:10:1D)" note="DHCP ACK" user="unknown" devID="a0e4cb7d7f52" cat="DHCP"||src="0.0.0.0', + ['device_id'=>$device->device_id, 'program'=>'DHCP', 'msg'=>'src="0.0.0.0:0" dst="0.0.0.0:0" msg="DHCP server assigned 195.159.132.109 to Chromecast(6C:AD:F8:B1:10:1D)" note="DHCP ACK" user="unknown" devID="a0e4cb7d7f52" cat="DHCP"'] ); } }