diff --git a/logstash/conf.d/30-syslog.conf b/logstash/conf.d/30-syslog.conf new file mode 100644 index 0000000..5a5998e --- /dev/null +++ b/logstash/conf.d/30-syslog.conf @@ -0,0 +1,119 @@ +input { + syslog { + id => "synology_syslog" + port => 10514 + type => "syslog" + add_field => { + "agent.type" => "syslog" + "service.type" => "nas" + "data_stream.dataset" => "synology.syslog" + } + } +} + +filter { + if [type] == "syslog" { + # Enhanced syslog parsing with grok for better field extraction + grok { + match => { + "message" => "<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{IPORHOST:syslog_server} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" + } + add_field => { "parsed_by" => "syslog_grok" } + tag_on_failure => ["_grokparsefailure_syslog"] + } + + # Calculate syslog facility and severity + if [syslog_pri] { + ruby { + code => " + pri = event.get('syslog_pri').to_i + event.set('syslog_facility_code', pri >> 3) + event.set('syslog_severity_code', pri & 7) + + # Map facility codes to names + facilities = { + 0 => 'kernel', 1 => 'user', 2 => 'mail', 3 => 'daemon', + 4 => 'auth', 5 => 'syslog', 6 => 'lpr', 7 => 'news', + 8 => 'uucp', 9 => 'cron', 10 => 'authpriv', 11 => 'ftp', + 16 => 'local0', 17 => 'local1', 18 => 'local2', 19 => 'local3', + 20 => 'local4', 21 => 'local5', 22 => 'local6', 23 => 'local7' + } + + # Map severity codes to names + severities = { + 0 => 'emergency', 1 => 'alert', 2 => 'critical', 3 => 'error', + 4 => 'warning', 5 => 'notice', 6 => 'informational', 7 => 'debug' + } + + facility_code = event.get('syslog_facility_code') + severity_code = event.get('syslog_severity_code') + + event.set('syslog_facility', facilities[facility_code] || 'unknown') + event.set('syslog_severity', severities[severity_code] || 'unknown') + " + } + } + + # Parse timestamp with timezone support + if [syslog_timestamp] { + date { + match => ["syslog_timestamp","MMM d HH:mm:ss","MMM dd HH:mm:ss"] + timezone => "Europe/Prague" + target => "@timestamp" + } + mutate { + remove_field => ["syslog_timestamp"] + } + } + + # Add host information if available + if [syslog_server] { + mutate { + add_field => { "host.name" => "%{syslog_server}" } + add_field => { "host.hostname" => "%{syslog_server}" } + } + } + + # Process program and PID fields + if [syslog_program] { + mutate { + add_field => { "process.name" => "%{syslog_program}" } + } + } + + if [syslog_pid] { + mutate { + convert => { "syslog_pid" => "integer" } + add_field => { "process.pid" => "%{syslog_pid}" } + } + } + + # Move the actual log message to a structured field + if [syslog_message] { + mutate { + add_field => { "log.original" => "%{message}" } + replace => { "message" => "%{syslog_message}" } + } + } + + # Add log level based on severity + if [syslog_severity] { + if [syslog_severity] in ["emergency", "alert", "critical"] { + mutate { add_field => { "log.level" => "error" } } + } else if [syslog_severity] == "error" { + mutate { add_field => { "log.level" => "error" } } + } else if [syslog_severity] == "warning" { + mutate { add_field => { "log.level" => "warn" } } + } else if [syslog_severity] in ["notice", "informational"] { + mutate { add_field => { "log.level" => "info" } } + } else if [syslog_severity] == "debug" { + mutate { add_field => { "log.level" => "debug" } } + } + } + + # Clean up original syslog fields that are no longer needed + mutate { + remove_field => ["syslog_pri", "syslog_message"] + } + } +} \ No newline at end of file diff --git a/logstash/conf.d/31-syslog-enrichment.conf b/logstash/conf.d/31-syslog-enrichment.conf new file mode 100644 index 0000000..0779e61 --- /dev/null +++ b/logstash/conf.d/31-syslog-enrichment.conf @@ -0,0 +1,51 @@ +filter { + if [type] == "syslog" { + # GeoIP enrichment for remote syslog sources + if [host.name] and [host.name] !~ /^(localhost|127\.0\.0\.1|::1|.*\.local)$/ { + geoip { + source => "host.name" + target => "geoip" + add_field => { "enriched_by" => "geoip" } + } + } + + # Add event timestamp enrichment + ruby { + code => " + require 'time' + event.set('event.ingested', Time.now.utc.iso8601) + + # Calculate event duration if we have both timestamp and ingestion time + if event.get('@timestamp') + ingested_time = Time.now.utc + event_time = Time.parse(event.get('@timestamp').to_s) + delay_seconds = ingested_time - event_time + event.set('event.delay_seconds', delay_seconds.round(2)) + end + " + } + + # Add tags based on content analysis + if [message] { + # Security-related patterns + if [message] =~ /(?i)(fail|error|denied|invalid|unauthorized|attack|intrusion|malware|virus)/ { + mutate { add_tag => ["security_concern"] } + } + + # Performance-related patterns + if [message] =~ /(?i)(slow|timeout|performance|latency|memory|cpu|disk)/ { + mutate { add_tag => ["performance"] } + } + + # Network-related patterns + if [message] =~ /(?i)(network|connection|socket|tcp|udp|dns|dhcp)/ { + mutate { add_tag => ["network"] } + } + } + + # Clean up temporary fields + mutate { + remove_field => ["parsed_by", "enriched_by"] + } + } +}