Skip to content

Instantly share code, notes, and snippets.

@mcnewton
Forked from greem/logstash-config-exim
Last active September 1, 2021 19:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mcnewton/4831079dfa5dcc6b77fd to your computer and use it in GitHub Desktop.
Save mcnewton/4831079dfa5dcc6b77fd to your computer and use it in GitHub Desktop.
input {
file {
path => "/path/to/exim/mainlog"
start_position => 'beginning'
sincedb_path => "/dev/null"
}
# udp {
# port => 5000
# type => syslog
# }
}
filter {
# Strip out lines we don't need using the regexp in the patterns file
if [message] =~ /%{EXIM_EXCLUDE_TERMS}/ {
drop { }
}
# Really, really dirty hack to workaround bug in grok code
# which won't handle multiple matches on the same field
mutate {
add_field => {
"message_1" => "%{message}"
"message_2" => "%{message}"
"message_3" => "%{message}"
"message_4" => "%{message}"
"message_5" => "%{message}"
}
}
grok {
patterns_dir => "/etc/logstash/patterns/"
break_on_match => false
keep_empty_captures => true
match => [
"message_1", "(%{SYSLOGBASE} )(%{EXIM_DATE:exim_date} )?(%{EXIM_PID:exim_pid} )?(%{EXIM_MSGID:exim_msg_id} )(%{EXIM_FLAGS:exim_flags} )(%{GREEDYDATA})"
]
match => [
"message_2", "(%{EXIM_MSGID} )(<= )(%{NOTSPACE:env_sender} )(%{EXIM_REMOTE_HOST} )?(%{EXIM_INTERFACE} )?(%{EXIM_PROTOCOL} )?(X=%{NOTSPACE:tls_info} )?(A=%{NOTSPACE:exim_authenticator}:%{NOTSPACE:exim_username} )?(%{EXIM_MSG_SIZE} )?(%{EXIM_HEADER_ID} )?(%{EXIM_SUBJECT})"
]
match => [
"message_3", "(%{EXIM_MSGID} )([=-]> )(%{NOTSPACE:env_rcpt} )(<%{NOTSPACE:env_rcpt_outer}> )?(F=<%{NOTSPACE:env_sender}> )?(R=%{NOTSPACE:exim_router} )(T=%{NOTSPACE:exim_transport} )(%{EXIM_REMOTE_HOST} )(X=%{NOTSPACE:tls_info} )?(QT=%{EXIM_QT:exim_qt})?"
]
match => [
"message_4", "(%{SYSLOGBASE} )(%{EXIM_DATE:exim_date} )?(%{EXIM_PID:exim_pid} )?(%{EXIM_MSGID:exim_msg_id} )(Completed)( QT=%{EXIM_QT:exim_qt})?"
]
match => [
"message_5", "(%{SYSLOGBASE} )(%{EXIM_DATE:exim_date} )?(%{EXIM_PID:exim_pid} )?(%{EXIM_MSGID:exim_msg_id} )?(%{EXIM_REMOTE_HOST} )?(%EXIM_INTERFACE} )?(F=<%{NOTSPACE:env_sender}> )?(.+(rejected after DATA|rejected \(but fed to sa-learn\)|rejected [A-Z]+ (or [A-Z]+ %{NOTSPACE}?|<%{NOTSPACE:env_rcpt}>)?): (?<exim_rej_reason>.+))"
]
}
date {
match => [ "timestamp", "MMM dd HH:mm:ss", "MMM d HH:mm:ss", "ISO8601" ]
}
if "_grokparsefailure" in [tags] {
drop { }
}
mutate {
add_field => { "exim_msg_state" => "not_defined" }
}
if [exim_flags] == "<=" {
mutate {
update => [ "exim_msg_state", "received" ]
}
} else if [exim_flags] == "=>" {
mutate {
update => [ "exim_msg_state", "delivered" ]
}
} else if [exim_flags] == "->" {
mutate {
update => [ "exim_msg_state", "delivered" ]
}
} else if [exim_flags] == ">>" {
mutate {
update => [ "exim_msg_state", "cutthrough_delivery" ]
}
} else if [exim_flags] == "*>" {
mutate {
update => [ "exim_msg_state", "suppressed_delivery" ]
}
} else if [exim_flags] == "==" {
mutate {
update => [ "exim_msg_state", "deferred" ]
}
} else if [exim_flags] == "**" {
mutate {
update => [ "exim_msg_state", "failed" ]
}
} else if "Completed QT=" in [message] {
mutate {
update => [ "exim_msg_state", "completed" ]
}
} else if [message] =~ /(rejected after DATA|rejected \(but fed to sa-learn\))/ {
mutate {
update => [ "exim_msg_state", "rejected_after_data" ]
}
} else if " rejected " in [message] {
mutate {
update => [ "exim_msg_state", "rejected_smtp_transaction" ]
}
}
# Ignore feeding sa-learn
#
if [message] =~ /R=feed_sa_learn T=feed_sa_learn/ {
drop { }
}
# Look back through ES table for existence of previous entry
# for this exim_msg_id being rejected & fed to Spam Assassin.
# If entry is found, drop this log line (it's irrelevant).
#
if [exim_msg_state] == "completed" and [host_type] == "MX" {
elasticsearch {
query => 'exim_msg_id:"%{exim_msg_id}" AND exim_msg_state:"rejected_after_data"'
fields => [ "exim_msg_state", "exim_msg_state2" ]
#sort => "@timestamp:desc, ignore_unmapped:true"
sort => "ignore_unmapped:true"
fail_on_error => "false"
}
if [exim_msg_state2] == "rejected_after_data" {
drop { }
}
mutate {
remove_field => [ "query_failed" ]
}
}
# Look back through ES table for the incoming message and
# extract the envelope sender. Permits later stats creation
# listing deliveries per env_sender
if [exim_msg_state] == "delivered" {
elasticsearch {
query => 'exim_msg_id:"%{exim_msg_id}" AND exim_msg_state:"received"'
#fields => [ "env_sender", "env_sender" ]
fields => [ "env_sender", "env_sender", "remote_host", "remote_host", "remote_hostname", "remote_hostname" ]
sort => "ignore_unmapped:true"
fail_on_error => "false"
}
mutate {
remove_field => [ "query_failed" ]
}
}
# Not interested in non-actioned retries
#
if [exim_flags] == "==" and "retry time not reached" in [message] {
drop { }
}
# Remove the really, really dirty hack to workaround bug in grok code
# which won't handle multiple matches on the same field
mutate {
remove_field => [ "message_1","message_2","message_3","message_4","message_5" ]
}
}
output {
elasticsearch {
host => localhost
index_type => "%{[exim_msg_state]}"
index => "exim-%{+YYYY.MM.dd}"
flush_size => 2
}
}
@longmikel
Copy link

Hi mcnewton,

I'm having trouble logstash with cPanel's exim. Do you have any advice to help me?

Thanks,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment