commit 9dcf5ae67ef01c2ff2108f685d672fd89bae446e
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Wed Nov 9 10:36:52 2016 +0100
Add epylog
roles/epylog/files/epylog-default.cron | 6 +
roles/epylog/files/epylog-merged.cron | 1 +
roles/epylog/files/epylog-web.conf | 15 +
roles/epylog/files/merged/epylog.conf | 32 +
.../files/merged/modules.d/common_unparsed.conf | 14 +
roles/epylog/files/merged/modules.d/kojiload.conf | 10 +
roles/epylog/files/merged/modules.d/logins.conf | 50 ++
roles/epylog/files/merged/modules.d/mail.conf | 20 +
roles/epylog/files/merged/modules.d/notices.conf | 26 +
roles/epylog/files/merged/modules.d/packets.conf | 33 +
roles/epylog/files/merged/modules.d/rsyncd.conf | 16 +
roles/epylog/files/merged/modules.d/selinux.conf | 11 +
roles/epylog/files/merged/modules.d/spamd.conf | 28 +
roles/epylog/files/merged/modules.d/sudo.conf | 11 +
roles/epylog/files/merged/modules.d/weeder.conf | 30 +
roles/epylog/files/merged/notice_dist.xml | 87 ++
roles/epylog/files/merged/notice_local.xml | 94 +++
roles/epylog/files/merged/report_template.html | 22 +
roles/epylog/files/merged/trojans.list | 410 ++++++++++
roles/epylog/files/merged/weed_dist.cf | 179 ++++
roles/epylog/files/merged/weed_local.cf | 369 +++++++++
roles/epylog/files/modules/common_unparsed.conf | 12 +
roles/epylog/files/modules/common_unparsed_mod.py | 118 +++
roles/epylog/files/modules/kojiload.conf | 10 +
roles/epylog/files/modules/kojiload_mod.py | 102 +++
roles/epylog/files/modules/logins_mod.py | 849 ++++++++++++++++++++
roles/epylog/files/modules/rsyncd.conf | 14 +
roles/epylog/files/modules/rsyncd_mod.py | 219 +++++
roles/epylog/files/modules/selinux.conf | 11 +
roles/epylog/files/modules/selinux_mod.py | 116 +++
roles/epylog/files/modules/sudo.conf | 11 +
roles/epylog/files/modules/sudo_mod.py | 191 +++++
roles/epylog/tasks/main.yml | 51 ++
33 files changed, 3168 insertions(+), 0 deletions(-)
---
diff --git a/roles/epylog/files/epylog-default.cron
b/roles/epylog/files/epylog-default.cron
new file mode 100644
index 0000000..33daaae
--- /dev/null
+++ b/roles/epylog/files/epylog-default.cron
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Run epylog daily.
+#
+# just disable this so we don't run the global one anywhere
+# better to run a specific one (or ones)
+#/usr/sbin/epylog --cron
diff --git a/roles/epylog/files/epylog-merged.cron
b/roles/epylog/files/epylog-merged.cron
new file mode 100644
index 0000000..c7a5983
--- /dev/null
+++ b/roles/epylog/files/epylog-merged.cron
@@ -0,0 +1 @@
+30 2,8,14,20 * * * root /usr/sbin/epylog --cron -c /etc/epylog/merged/epylog.conf --last
6h
diff --git a/roles/epylog/files/epylog-web.conf b/roles/epylog/files/epylog-web.conf
new file mode 100644
index 0000000..dc7fe5a
--- /dev/null
+++ b/roles/epylog/files/epylog-web.conf
@@ -0,0 +1,15 @@
+Alias /epylog /srv/web/epylog
+
+# need these to do the auth
+LoadModule auth_basic_module modules/mod_auth_basic.so
+LoadModule authn_file_module modules/mod_authn_file.so
+
+<Directory /srv/web/epylog>
+ Options FollowSymLinks Indexes
+ AuthType Basic
+ AuthName "Fedora Log Server"
+ AuthBasicProvider file
+ AuthUserFile /srv/web/epylog/.htpasswd
+ Require valid-user
+</Directory>
+
diff --git a/roles/epylog/files/merged/epylog.conf
b/roles/epylog/files/merged/epylog.conf
new file mode 100644
index 0000000..3236791
--- /dev/null
+++ b/roles/epylog/files/merged/epylog.conf
@@ -0,0 +1,32 @@
+##
+# Main Epylog configuration file. See epylog.conf(5) for more info.
+#
+[main]
+cfgdir = /etc/epylog/merged
+tmpdir = /var/log/tmp
+vardir = /var/lib/epylog/merged
+
+[report]
+title = Merged system events: @@LOCALTIME@@
+template = /etc/epylog/merged/report_template.html
+include_unparsed = yes
+publishers = file
+
+[mail]
+method = mail
+smtpserv = /usr/sbin/sendmail -t
+mailto = sysadmin-logs-members(a)rpmfusion.org
+format = plain
+lynx = /usr/bin/lynx
+include_rawlogs = no
+rawlogs_limit = 200
+
+[file]
+method = file
+path = /srv/web/epylog/merged/
+dirmask = %Y-%b-%d_%a
+filemask = %H%M
+expire_in = 7
+notify = sysadmin-logs-members(a)rpmfusion.org
+smtpserv = /usr/sbin/sendmail -t
+pubroot =
https://admin.rpmfusion.org/epylog/merged
diff --git a/roles/epylog/files/merged/modules.d/common_unparsed.conf
b/roles/epylog/files/merged/modules.d/common_unparsed.conf
new file mode 100644
index 0000000..1369fe4
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/common_unparsed.conf
@@ -0,0 +1,14 @@
+[module]
+desc = Common Unparsed Similar Strings Module
+exec = /usr/share/epylog/modules/common_unparsed_mod.py
+files = /var/log/merged/messages.log[.#.gz], /var/log/merged/secure.log[.#.gz]
+enabled = no
+internal = yes
+outhtml = yes
+priority = 10
+
+
+[conf]
+# how similar the strings need to be 0-100 - 0 being not at all (bad idea) 100 being
almost exactly.
+match_percentage = 95
+debug_dump = 0
\ No newline at end of file
diff --git a/roles/epylog/files/merged/modules.d/kojiload.conf
b/roles/epylog/files/merged/modules.d/kojiload.conf
new file mode 100644
index 0000000..c5c7ee5
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/kojiload.conf
@@ -0,0 +1,10 @@
+[module]
+desc = KojiLoad summary
+exec = /usr/share/epylog/modules/kojiload_mod.py
+files = /var/log/merged/messages.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
diff --git a/roles/epylog/files/merged/modules.d/logins.conf
b/roles/epylog/files/merged/modules.d/logins.conf
new file mode 100644
index 0000000..9db7c05
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/logins.conf
@@ -0,0 +1,50 @@
+[module]
+desc = Logins
+exec = /usr/share/epylog/modules/logins_mod.py
+files = /var/log/merged/messages.log[.#.gz], /var/log/merged/secure.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 0
+
+[conf]
+##
+# Only enable things useful for your configuration to speed things
+# up. The more stuff you enable, the slower matching will be.
+#
+enable_pam = 1
+enable_xinetd = 1
+enable_sshd = 1
+enable_uw_imap = 0
+enable_dovecot = 0
+enable_courier = 0
+enable_imp = 0
+enable_proftpd = 0
+##
+# This is a fun setting. You can list domains that are "safe" here.
+# E.g. if your org's domain is
example.com and you generally don't
+# expect logins from hosts in
example.com domain to be suspicious, you
+# can add "example.com$" as a safe domain. This way anyone logging in from
+# a remote host not matching *.example.com will be flagged in red and the
+# full hostname of the connecting machine will be printed in the report.
+# List multiple values separated by comma.
+# E.g.: safe_domains = example.com$, foo.edu$
+# The default is .*, meaning all domains are considered safe. To turn
+# this off specify something like:
+# safe_domains = !.*
+safe_domains = .*
+##
+# If you have too many systems, wide-scale probing may turn ugly. This
+# will collapse the reports.
+systems_collapse = 10
+
+
+# comma/space separated list of users to ignore - unknown is the internal "no user
given"
+ignore_users = unknown
+# path to where we keep the logins db
+loginsdb_path = /var/lib/epylog/logins_db.sqlite
+# clean up entries in the db which are more than this many days old
+remove_older_than = 14
+# time fuzz - default time (in minutes) which is valid fuzzy match for a login to not be
listed
+time_fuzz = 60
+
diff --git a/roles/epylog/files/merged/modules.d/mail.conf
b/roles/epylog/files/merged/modules.d/mail.conf
new file mode 100644
index 0000000..3b1e93b
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/mail.conf
@@ -0,0 +1,20 @@
+[module]
+desc = Mail Report
+exec = /usr/share/epylog/modules/mail_mod.py
+files = /var/log/merged/mail.log[.#.gz]
+enabled = no
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+##
+# Enable sendmail, postfix, or both
+#
+enable_sendmail = 1
+enable_postfix = 1
+enable_qmail = 0
+##
+# Report at most this many "top things"
+#
+top_report_limit = 5
diff --git a/roles/epylog/files/merged/modules.d/notices.conf
b/roles/epylog/files/merged/modules.d/notices.conf
new file mode 100644
index 0000000..65289cc
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/notices.conf
@@ -0,0 +1,26 @@
+[module]
+desc = Notices
+exec = /usr/share/epylog/modules/notices_mod.py
+files = /var/log/merged/messages.log[.#.gz], /var/log/merged/secure.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
+##
+# Where is your notice_dist.xml file?
+#
+notice_dist = /etc/epylog/merged/notice_dist.xml
+##
+# Add your own notices into notice_local.xml, not into notice_dist.xml!
+# This way you don't risk missing future revisions to notice_dist.xml
+#
+notice_local = /etc/epylog/merged/notice_local.xml
+##
+# You can list the ids of <notice> members from notice_dist.xml here
+# namely, or you can use ALL to enable all of them. There is no need
+# to add members from notice_local.xml here -- they will be enabled
+# automatically.
+#
+enable = ALL
diff --git a/roles/epylog/files/merged/modules.d/packets.conf
b/roles/epylog/files/merged/modules.d/packets.conf
new file mode 100644
index 0000000..e1b6192
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/packets.conf
@@ -0,0 +1,33 @@
+[module]
+desc = Packet Filter
+exec = /usr/share/epylog/modules/packets_mod.py
+files = /var/log/merged/messages.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 1
+
+[conf]
+##
+# Where to look for the trojans list.
+#
+trojan_list = /etc/epylog/merged/trojans.list
+##
+# If a remote host hits this many systems, then don't list them namely,
+# but collapse them into a nice report, e.g.: [50 hosts]
+#
+systems_collapse = 5
+##
+# Useful for massive portscans. Don't list all the ports namely, but
+# present them in a collapsed view. E.g.: [50 ports]
+ports_collapse = 5
+##
+# Enable iptables, ipchains, ipfilter, or all three.
+#
+enable_iptables = 1
+enable_ipchains = 1
+enable_ipfilter = 0
+##
+# Sort by any of the following: packets, source, system, port
+#
+sortby=port
diff --git a/roles/epylog/files/merged/modules.d/rsyncd.conf
b/roles/epylog/files/merged/modules.d/rsyncd.conf
new file mode 100644
index 0000000..ea46539
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/rsyncd.conf
@@ -0,0 +1,16 @@
+[module]
+desc = Rsyncd
+exec = /usr/share/epylog/modules/rsyncd_mod.py
+files = /var/log/merged/messages.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
+##
+# Report this many "top ranking hosts"
+#
+report_top = 10
+ignore_hosts =
hv01.online.rpmfusion.org
+
diff --git a/roles/epylog/files/merged/modules.d/selinux.conf
b/roles/epylog/files/merged/modules.d/selinux.conf
new file mode 100644
index 0000000..45e4d87
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/selinux.conf
@@ -0,0 +1,11 @@
+[module]
+desc = SELinux Report
+exec = /usr/share/epylog/modules/selinux_mod.py
+files = /var/log/merged/messages.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+enable_selinux = 1
diff --git a/roles/epylog/files/merged/modules.d/spamd.conf
b/roles/epylog/files/merged/modules.d/spamd.conf
new file mode 100644
index 0000000..61e6ae1
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/spamd.conf
@@ -0,0 +1,28 @@
+[module]
+desc = Spamassassin
+exec = /usr/share/epylog/modules/spamd_mod.py
+files = /var/log/merged/mail.log[.#.gz]
+enabled = no
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
+##
+# Report this many "top ranking users"
+#
+report_top = 10
+##
+# Consider this the spam threshold when reporting the scores.
+# Anything above this will be flagged as spam. The last column shows
+# the score and then non-spam/spam in the parenthesis. E.g.:
+# -1.3 (10/3) -- the mean score is -1.3, 10 messages under spam_threshold,
+# and 3 messages over it.
+#
+spam_threshold = 5
+##
+# Rank the top users according to this parameter. Valid entries are:
+# "most spammed" -- sorts by users with the topmost score
+# "most messages" -- sorts by users who received most messages
+#
+sort_by = most spammed
diff --git a/roles/epylog/files/merged/modules.d/sudo.conf
b/roles/epylog/files/merged/modules.d/sudo.conf
new file mode 100644
index 0000000..fef8676
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/sudo.conf
@@ -0,0 +1,11 @@
+[module]
+desc = Sudo Report
+exec = /usr/share/epylog/modules/sudo_mod.py
+files = /var/log/merged/secure.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+enable_sudo = 1
diff --git a/roles/epylog/files/merged/modules.d/weeder.conf
b/roles/epylog/files/merged/modules.d/weeder.conf
new file mode 100644
index 0000000..160a131
--- /dev/null
+++ b/roles/epylog/files/merged/modules.d/weeder.conf
@@ -0,0 +1,30 @@
+[module]
+desc = Weedeater
+exec = /usr/share/epylog/modules/weeder_mod.py
+files = /var/log/merged/messages.log[.#.gz], /var/log/merged/secure.log[.#.gz]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 9
+
+[conf]
+##
+# Where to look for a weed_dist.cf file.
+#
+weed_dist = /etc/epylog/merged/weed_dist.cf
+##
+# Where to look for a weed_local.cf file
+#
+weed_local = /etc/epylog/merged/weed_local.cf
+##
+# This is where it gets interesting.
+# If you look into weed_dist.cf, you will notice that the entries
+# are listed by section titles. List here only the sections that are
+# relevant to your setup to speed things up. The more sections you
+# enable, the slower matching will go, as it has to try more
+# regexes. Note that [ADD] and [REMOVE] sections in weed_local are
+# special, any other sections in that file will be ignored.
+#
+# You can use 'ALL' to enable all sections.
+#
+enable = ALL
diff --git a/roles/epylog/files/merged/notice_dist.xml
b/roles/epylog/files/merged/notice_dist.xml
new file mode 100644
index 0000000..c717280
--- /dev/null
+++ b/roles/epylog/files/merged/notice_dist.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!--
+
+ CAUTION:
+ It is not advised to edit this file! You may miss any future
+ revisions made to it. Instead, create/edit notice_local.xml and
+ add your rules to it following the same XML layout as presented in
+ this file. All members of notice_local.xml will be added
+ indiscriminately, i.e. you don't have to specify the id of the
+ <notice> in notices.conf: any notices added in notice_local.xml
+ will be enabled in the module automatically.
+
+ $Revision: 1.4.2.5 $
+
+-->
+<notices>
+ <notice id="gconfd">
+ <regex>gconfd.*: Failed to get lock.*Failed to create</regex>
+ <regex>gconfd.*: Error releasing lockfile</regex>
+ <regex>gconfd.*: .* Could not lock temporary file</regex>
+ <regex>gconfd.*: .* another process has the lock</regex>
+ <report>GConf locking errors</report>
+ </notice>
+
+ <notice id="fatalx">
+ <regex>Fatal X error</regex>
+ <report>Fatal X errors</report>
+ </notice>
+
+ <notice id="floppy">
+ <regex>floppy0:|\(floppy\)</regex>
+ <report>Misc floppy errors</report>
+ </notice>
+
+ <notice id="ypserv">
+
<regex>ypserv.*:\srefused\sconnect\sfrom\s(\S+):\d+\sto\sprocedure\s(\S+)</regex>
+ <report>%s denied for %s</report>
+ </notice>
+
+ <notice id="linux_boot" critical="yes">
+ <regex>kernel:\sLinux\sversion\s(\S*)</regex>
+ <report>Rebooted with Linux kernel %s</report>
+ </notice>
+
+ <notice id="cdrom_vfs">
+ <regex>VFS: busy inodes on changed media</regex>
+ <report>dirty CDROM mount</report>
+ </notice>
+
+ <notice id="cdrom">
+ <regex>kernel: cdrom: This disc doesn</regex>
+ <regex>kernel: .*Make sure there is a disc in the drive.</regex>
+ <report>Misc CDROM errors</report>
+ </notice>
+
+ <notice id="dirty_floppy">
+ <regex>attempt to access beyond end of device</regex>
+ <regex>rw=\d+, want=\d+, limit=\d+</regex>
+ <regex>Directory sread .* failed</regex>
+ <regex>kernel: bread in fat_access failed</regex>
+ <report>Dirty floppy mount [non-indicative]</report>
+ </notice>
+
+ <notice id="nfs_timeout" critical="yes">
+ <regex>nfs: server (\S+) not responding</regex>
+ <regex>nfs: server (\S+) OK</regex>
+ <report>NFS timeouts to server %s</report>
+ </notice>
+
+ <notice id="insmod">
+ <regex>insmod: Hint: insmod errors</regex>
+ <report>insmod errors</report>
+ </notice>
+
+ <notice id="selinux-denied">
+
<regex>audit\S+:\s+avc:\s+denied\s+\{\s([^\}]+)\s\}.*exe=(\S+).*scontext=(\S+)</regex>
+ <report>SELinux: denied "%s" for "%s"
(scontext=%s)</report>
+ </notice>
+
+ <notice id="crond">
+ <regex>CROND\S+: \((\S+)\) CMD \(([^\)]+)\)</regex>
+ <regex>crond\S+: \((\S+)\) CMD \(([^\)]+)\)</regex>
+ <report>Cron: user '%s' (%s)</report>
+ </notice>
+
+</notices>
+
diff --git a/roles/epylog/files/merged/notice_local.xml
b/roles/epylog/files/merged/notice_local.xml
new file mode 100644
index 0000000..7091913
--- /dev/null
+++ b/roles/epylog/files/merged/notice_local.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!--
+
+This is where you should put your own notice rules. The format is
+simple:
+ <notice id="uniqueid" [critical="yes"]>
+ <regex>python compliant regex string</regex>
+ <regex>another regex, if more than one string is needed</regex>
+ <report>What should go into the report</report>
+ </notice>
+
+See notice_dist.xml for examples. Any custom notices you add here do
+not need to be added into the notices.conf file, as they will be
+enabled automatically.
+
+-->
+
+<notices>
+ <notice id="oom-killer">
+ <regex>kernel: (\S+) invoked oom-killer.*</regex>
+ <report>%s invoked oom-killer - see unparsed</report>
+ </notice>
+
+ <notice id="kerneltrace" critical="yes">
+ <regex>kernel: Call Trace</regex>
+ <report>kernel call trace - see unparsed for details</report>
+ </notice>
+<!--
+ <notice id="auditd-down" critical="yes">
+ <regex>kernel: type=.*audit\(.*</regex>
+ <report>auditd appears to be down</report>
+ </notice>
+-->
+
+ <notice id="Rootkit-hunter-warning" critical="yes">
+ <regex>Rootkit Hunter:.*Please inspect this machine, because it may be
infected.*</regex>
+ <report>Rootkit Hunter has noticed a potential issue</report>
+ </notice>
+ <notice id="brokenpuppetnodetyaml">
+ <regex>puppet_yamltest: cleaning damaged puppet yaml file:(.*)</regex>
+ <report>Corrupted yaml file %s</report>
+ </notice>
+ <notice id="nf_conntrack" critical="yes">
+ <regex>kernel: nf_conntrack: table full.*</regex>
+ <report>Connection tracking table full.</report>
+ </notice>
+
+ <notice id="openvpn_reconnect" critical="yes">
+ <regex>openvpn\[\d+\]: .*:\d+ \[(.*)\] Peer Connection Initiated with
.*:\d+</regex>
+ <report>openvpn [re]connect from %s.</report>
+ </notice>
+
+ <notice id="openvpn_tls_failed" critical="yes">
+ <regex>openvpn\[\d+\]: (.*)/.* TLS Error: TLS handshake failed</regex>
+ <report>openvpn TLS handshake failed: %s</report>
+ </notice>
+ <notice id="openvpn_tls_failed_time" critical="yes">
+ <regex>openvpn\[\d+\]: (.*)/.* TLS Error: TLS key negotiation failed ot occur
within.*</regex>
+ <report>openvpn TLS handshake failed - timeout: %s</report>
+ </notice>
+ <notice id="openvpn_bad_packet_id" critical="yes">
+ <regex>openvpn\[\d+\]: (.*)/.* Authenticate/Decrypt packet error: bad packet
ID.*</regex>
+ <report>openvpn auth/decrypt - bad packet id: %s</report>
+ </notice>
+
+ <notice id="stunnel_websocket" critical="no">
+ <regex>stunnel:.*websockets accepted connection from (.*):.*</regex>
+ <report>stunnel: websocket connection from %s</report>
+ </notice>
+
+ <notice id="nagios_alerts" critical="yes">
+ <regex>nagios:.*HOST.*ALERT:.*</regex>
+ <report>nagios alerts: </report>
+ </notice>
+
+ <notice id="nagios_pages" critical="yes">
+ <regex>nagios:.*HOST.*NOTIFICATION:.*kevin-emergency.*</regex>
+ <report>nagios pages: </report>
+ </notice>
+
+<notice id="totp-success">
+ <regex>totp\.cgi.*: Success: user=(\S+),.*host=(\S+),</regex>
+ <report>totpcgi: %s from %s (Success)</report>
+</notice>
+<notice id="totp-failure" critical="yes">
+ <regex>totp\.cgi.*: Failure: user=(\S+),.*host=(\S+),</regex>
+ <report>totpcgi: %s from %s (Failure)</report>
+</notice>
+<notice id="EXT4-error" critical="yes">
+ <regex>kernel:.* EXT4-fs error.*</regex>
+ <report>EXT4 Error/disk failure noticed</report>
+</notice>
+</notices>
+
diff --git a/roles/epylog/files/merged/report_template.html
b/roles/epylog/files/merged/report_template.html
new file mode 100644
index 0000000..4d60b38
--- /dev/null
+++ b/roles/epylog/files/merged/report_template.html
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <title>@@TITLE@@</title>
+ <style type="text/css">
+ h1 {color: gray; border-bottom: 3px double silver}
+ h2,h3 {color: gray; border-bottom: 1px solid silver}
+ </style>
+ </head>
+ <body>
+ <h1>@@HOSTNAME@@</h1>
+ <p>First event: <strong>@@STARTTIME@@</strong><br />
+ Last event: <strong>@@ENDTIME@@</strong></p>
+ <hr />
+ @@MODULE_REPORTS@@
+ <hr />
+ <h2>Unparsed Strings:</h2>
+ @@UNPARSED_STRINGS@@
+ <hr />
+ <p align="right">Brought to you by
+ <a
href="http://linux.duke.edu/projects/epylog/">@@VERSION@@<...
+ </body>
+</html>
diff --git a/roles/epylog/files/merged/trojans.list
b/roles/epylog/files/merged/trojans.list
new file mode 100644
index 0000000..09b795b
--- /dev/null
+++ b/roles/epylog/files/merged/trojans.list
@@ -0,0 +1,410 @@
+1/udp Sockets des Troie
+2/tcp Death
+30/tcp Agent 40421
+31/tcp Agent 31
+41/tcp Deep Throat
+48/tcp DRAT
+58/tcp DMSetup
+59/tcp DMSetup
+79/tcp CDK
+81/tcp RemoConChubo
+99/tcp Hidden Port
+121/tcp Attack Bot
+133/tcp Farnaz
+142/tcp NetTaxi
+146/tcp Infector
+146/udp Infector
+170/tcp A-trojan
+334/tcp Backage
+411/tcp Backage
+420/tcp Breach
+421/tcp TCP Wrappers trojan
+455/tcp Fatal Connections
+456/tcp Hackers Paradise
+513/tcp Grlogin
+514/tcp RPC Backdoor
+531/tcp Net666, Rasmin
+555/tcp Seven-Eleven
+605/tcp Secret Service
+666/tcp The Ripperz
+667/tcp SniperNet
+669/tcp DP trojan
+692/tcp GayOL
+777/tcp AimSpy
+808/tcp WinHole
+911/tcp Dark Shadow
+999/tcp Deep Throat
+1000/tcp Der Spaeher
+1001/tcp Der Spaeher
+1010/tcp Doly Trojan
+1011/tcp Doly Trojan
+1012/tcp Doly Trojan
+1015/tcp Doly Trojan
+1016/tcp Doly Trojan
+1020/tcp Vampire
+1024/tcp NetSpy
+1025/tcp Remote Storm
+1025/udp Remote Storm
+1035/tcp Multidropper
+1042/tcp BLA trojan
+1045/tcp Rasmin
+1049/tcp /sbin/initd
+1050/tcp MiniCommand
+1053/tcp The Thief
+1054/tcp AckCmd
+1080/tcp WinHole
+1081/tcp WinHole
+1082/tcp WinHole
+1083/tcp WinHole
+1090/tcp Xtreme
+1095/tcp RAT
+1097/tcp RAT
+1098/tcp RAT
+1099/tcp RAT
+1150/tcp Orion
+1151/tcp Orion
+1170/tcp PSS
+1200/udp NoBackO
+1201/udp NoBackO
+1207/tcp SoftWAR
+1208/tcp Infector
+1212/tcp Kaos
+1234/tcp SubSeven
+1243/tcp BackDoor-G
+1245/tcp VooDoo Doll
+1255/tcp Scarab
+1256/tcp Project nEXT
+1269/tcp Matrix
+1272/tcp The Matrix
+1313/tcp NETrojan
+1338/tcp Millenium Worm
+1349/tcp Bo dll
+1394/tcp GoFriller
+1441/tcp Remote Storm
+1492/tcp FTP99CMP
+1524/tcp Trinoo
+1568/tcp Remote Hack
+1600/tcp Shivka-Burka
+1703/tcp Exploiter
+1777/tcp Scarab
+1807/tcp SpySender
+1966/tcp Fake FTP
+1967/tcp WM FTP Server
+1969/tcp OpC BO
+1981/tcp Bowl, Shockrave
+1999/tcp SubSeven
+2000/tcp Der Spaeher
+2001/tcp Der Sp�her
+2023/tcp Ripper Pro
+2080/tcp WinHole
+2115/tcp Bugs
+2130/udp Mini Backlash
+2140/tcp The Invasor
+2140/udp Deep Throat
+2155/tcp Illusion Mailer
+2255/tcp Nirvana
+2283/tcp Hvl RAT
+2300/tcp Xplorer
+2311/tcp Studio 54
+2330/tcp Contact
+2331/tcp Contact
+2332/tcp Contact
+2333/tcp Contact
+2334/tcp Contact
+2335/tcp Contact
+2336/tcp Contact
+2337/tcp Contact
+2338/tcp Contact
+2339/tcp Contact
+2339/udp Voice Spy
+2345/tcp Doly Trojan
+2565/tcp Striker trojan
+2583/tcp WinCrash
+2600/tcp Digital RootBeer
+2716/tcp The Prayer
+2773/tcp SubSeven
+2774/tcp SubSeven
+2801/tcp Phineas Phucker
+2989/udp RAT
+3000/tcp Remote Shut
+3024/tcp WinCrash
+3031/tcp Microspy
+3128/tcp Ringzero
+3129/tcp Masters Paradise
+3150/tcp The Invasor
+3150/udp Deep Throat
+3456/tcp Terror trojan
+3459/tcp Eclipse 2000
+3700/tcp Portal of Doom
+3777/tcp PsychWard
+3791/tcp Total Solar Eclypse
+3801/tcp Total Solar Eclypse
+4000/tcp SkyDance
+4092/tcp WinCrash
+4242/tcp VHM
+4321/tcp BoBo
+4444/tcp Prosiak
+4567/tcp File Nail
+4590/tcp ICQ Trojan
+4950/tcp ICQ Trogen (Lm)
+5000/tcp Back Door Setup
+5001/tcp Back Door Setup
+5002/tcp cd00r
+5010/tcp Solo
+5011/tcp OOTLT
+5025/tcp WM Remote KeyLogger
+5031/tcp Net Metropolitan
+5032/tcp Net Metropolitan
+5321/tcp Firehotcker
+5333/tcp Backage
+5343/tcp wCrat
+5400/tcp Back Construction
+5401/tcp Back Construction
+5402/tcp Back Construction
+5512/tcp Illusion Mailer
+5534/tcp The Flu
+5550/tcp Xtcp
+5555/tcp ServeMe
+5556/tcp BO Facil
+5557/tcp BO Facil
+5569/tcp Robo-Hack
+5637/tcp PC Crasher
+5638/tcp PC Crasher
+5742/tcp WinCrash
+5760/tcp Portmap Remote Root Linux Exploit
+5880/tcp Y3K RAT
+5882/tcp Y3K RAT
+5882/udp Y3K RAT
+5888/tcp Y3K RAT
+5888/udp Y3K RAT
+5889/tcp Y3K RAT
+6000/tcp The Thing
+6006/tcp Bad Blood
+6272/tcp Secret Service
+6400/tcp The Thing
+6661/tcp TEMan
+6666/tcp Dark Connection Inside
+6667/tcp Dark FTP
+6669/tcp Host Control, Vampire
+6670/tcp BackWeb Server
+6711/tcp BackDoor-G
+6712/tcp Funny trojan
+6713/tcp SubSeven
+6723/tcp Mstream
+6771/tcp Deep Throat
+6776/tcp 2000 Cracks
+6838/udp Mstream
+6883/tcp Delta Source DarkStar (??)
+6912/tcp Shit Heep
+6939/tcp Indoctrination
+6969/tcp GateCrasher
+6970/tcp GateCrasher
+7000/tcp Exploit Translation Server
+7001/tcp Freak88
+7215/tcp SubSeven
+7300/tcp NetMonitor
+7301/tcp NetMonitor
+7306/tcp NetMonitor
+7307/tcp NetMonitor
+7308/tcp NetMonitor
+7424/tcp Host Control
+7424/udp Host Control
+7597/tcp Qaz
+7626/tcp Glacier
+7777/tcp God Message, Tini
+7789/tcp Back Door Setup, ICKiller
+7891/tcp The ReVeNgEr
+7983/tcp Mstream
+8080/tcp Brown Orifice
+8787/tcp Back Orifice 2000
+8988/tcp BacHack
+8989/tcp Rcon
+9000/tcp Netministrator
+9325/udp Mstream
+9400/tcp InCommand
+9872/tcp Portal of Doom
+9873/tcp Portal of Doom
+9874/tcp Portal of Doom
+9875/tcp Portal of Doom
+9876/tcp Cyber Attacker, Rux
+9878/tcp TransScout
+9989/tcp Ini-Killer
+9999/tcp The Prayer
+10000/tcp OpwinTRojan
+10005/tcp OpwinTRojan
+10067/udp Portal of Doom
+10085/tcp Syphillis
+10086/tcp Syphillis
+10100/tcp Control Total
+10101/tcp BrainSpy
+10167/udp Portal of Doom
+10520/tcp Acid Shivers
+10528/tcp Host Control
+10607/tcp Coma
+10666/udp Ambush
+11000/tcp Senna Spy Trojan Generator
+11050/tcp Host Control
+11051/tcp Host Control
+11223/tcp Progenic trojan
+12076/tcp Gjamer
+12223/tcp Hack-99 KeyLogger
+12345/tcp Ashley
+12346/tcp Fat Bitch
+12349/tcp BioNet
+12361/tcp Whack-a-mole
+12362/tcp Whack-a-mole
+12363/tcp Whack-a-mole
+12623/udp DUN Control
+12624/tcp ButtMan
+12631/tcp Whack Job
+12754/tcp Mstream
+13000/tcp Senna Spy
+13010/tcp Hacker Brasil - HBR
+13013/tcp PsychWard
+13014/tcp PsychWard
+13223/tcp Hack�99 KeyLogger
+13473/tcp Chupacabra
+14500/tcp PC Invader
+14501/tcp PC Invader
+14502/tcp PC Invader
+14503/tcp PC Invader
+15000/tcp NetDemon
+15092/tcp Host Control
+15104/tcp Mstream
+15382/tcp SubZero
+15858/tcp CDK
+16484/tcp Mosucker
+16660/tcp Stacheldraht
+16772/tcp ICQ Revenge
+16959/tcp SubSeven
+16969/tcp Priority
+17166/tcp Mosaic
+17300/tcp Kuang2 the virus
+17449/tcp Kid Terror
+17499/tcp CrazzyNet
+17500/tcp CrazzyNet
+17569/tcp Infector
+17593/tcp Audiodoor
+17777/tcp Nephron
+18753/udp Shaft
+19864/tcp ICQ Revenge
+20000/tcp Millenium
+20001/tcp Millenium
+20002/tcp AcidkoR
+20005/tcp Mosucker
+20023/tcp VP Killer
+20034/tcp NetBus
+20203/tcp Chupacabra
+20331/tcp BLA trojan
+20432/tcp Shaft
+20433/udp Shaft
+21544/tcp GirlFriend, Kid Terror
+21554/tcp Exploiter
+22222/tcp Donald Dick
+23005/tcp NetTrash
+23006/tcp NetTrash
+23023/tcp Logged
+23032/tcp Amanda
+23432/tcp Asylum
+23456/tcp Evil FTP
+23476/tcp Donald Dick
+23476/udp Donald Dick
+23477/tcp Donald Dick
+23777/tcp InetSpy
+24000/tcp Infector
+25685/tcp Moonpie
+25686/tcp Moonpie
+25982/tcp Moonpie
+26274/udp Delta Source
+26681/tcp Voice Spy
+27374/tcp Bad Blood
+27444/udp Trinoo
+27573/tcp SubSeven
+27665/tcp Trinoo
+28678/tcp Exploiter
+29104/tcp NetTrojan
+29369/tcp ovasOn
+29891/tcp The Unexplained
+30000/tcp Infector
+30001/tcp ErrOr32
+30003/tcp Lamers Death
+30029/tcp AOL trojan
+30100/tcp NetSphere
+30101/tcp NetSphere
+30102/tcp NetSphere
+30103/tcp NetSphere
+30103/udp NetSphere
+30133/tcp NetSphere
+30303/tcp Sockets des Troie
+30947/tcp Intruse
+30999/tcp Kuang2
+31335/tcp Trinoo
+31336/tcp Bo Whack, Butt Funnel
+31337/tcp Back Fire
+31337/udp Back Orifice
+31338/tcp Back Orifice
+31338/udp Deep BO
+31339/tcp NetSpy (DK)
+31666/tcp BOWhack
+31785/tcp Hack�a�Tack
+31787/tcp Hack�a�Tack
+31788/tcp Hack�a�Tack
+31789/udp Hack�a�Tack
+31790/tcp Hack�a�Tack
+31791/udp Hack�a�Tack
+31792/tcp Hack�a�Tack
+32001/tcp Donald Dick
+32100/tcp Peanut Brittle
+32418/tcp Acid Battery
+33270/tcp Trinity
+33333/tcp Blakharaz
+33577/tcp Son of PsychWard
+33777/tcp Son of PsychWard
+33911/tcp Spirit 2000
+34324/tcp Big Gluck
+34444/tcp Donald Dick
+34555/udp Trinoo
+35555/udp Trinoo
+37237/tcp Mantis
+37651/tcp Yet Another Trojan - YAT
+40412/tcp The Spy
+40421/tcp Agent 40421
+40422/tcp Masters Paradise
+40423/tcp Masters Paradise
+40425/tcp Masters Paradise
+40426/tcp Masters Paradise
+41337/tcp Storm
+41666/tcp RBT
+44444/tcp Prosiak
+44575/tcp Exploiter
+47262/udp Delta Source
+49301/tcp OnLine KeyLogger
+50130/tcp Enterprise
+50505/tcp Sockets des Troie
+50766/tcp Fore
+51966/tcp Cafeini
+52317/tcp Acid Battery 2000
+53001/tcp RWS
+54283/tcp SubSeven
+54320/tcp Back Orifice 2000
+54321/tcp Back Orifice 2000
+55165/tcp File Manager trojan
+55166/tcp WM Trojan Generator
+57341/tcp NetRaider
+58339/tcp Butt Funnel
+60000/tcp Deep Throat
+60001/tcp Trinity
+60068/tcp Xzip 6000068
+60411/tcp Connection
+61348/tcp Bunker-Hill
+61466/tcp TeleCommando
+61603/tcp Bunker-Hill
+63485/tcp Bunker-Hill
+64101/tcp Taskman
+65000/tcp Devil
+65390/tcp Eclypse
+65421/tcp Jade
+65432/tcp The Traitor (= th3tr41t0r)
+65432/udp The Traitor (= th3tr41t0r)
+65534/tcp /sbin/initd
+65535/tcp RC1 trojan
diff --git a/roles/epylog/files/merged/weed_dist.cf
b/roles/epylog/files/merged/weed_dist.cf
new file mode 100644
index 0000000..dd87e2d
--- /dev/null
+++ b/roles/epylog/files/merged/weed_dist.cf
@@ -0,0 +1,179 @@
+##
+# NOTE:
+# Editing this file is not recommended. If you do, you might miss newer
+# revisions of this list in the future versions.
+# See weed_local.cf for instructions on how to add or delete rules.
+#
+
+[pam]
+\(pam_unix\).*: session closed for
+\(pam_unix\).*: check pass;
+
+[dhcpd]
+dhcpd: DHCPREQUEST
+dhcpd: DHCPACK
+dhcpd: DHCPDISCOVER
+dhcpd: DHCPOFFER
+dhcpd: DHCPRELEASE
+dhcpd: DHCPINFORM
+
+[rpc]
+rpc.mountd: authenticated mount request from
+rpc.mountd: authenticated unmount request
+rpc.statd.*: Version .* Starting
+rpc.statd.*: Caught signal 15, un-registering and exiting
+
+[automount]
+automount.*: expired
+automount.*: attempting to mount entry
+automount.*: lookup\(file\): .* failed
+automount.*: starting automounter
+automount.*: using kernel protocol
+automount.*: shutting down
+automount.*: .* No such key in map
+
+[crond]
+CROND.*: \(mailman\) CMD \(/usr/bin/python
+CROND.*: \(root\) CMD \(.*/sbin/rmmod -as\)
+CROND.*: \(root\) CMD \(/usr/lib/sa/sa\d
+CROND.*: \(root\) CMD \(run-parts
+anacron.*: Updated timestamp for job
+
+[bind]
+named.*: lame server resolving
+named.*: .* NS points to CNAME
+named.*: Response from unexpected source
+named.*: .* All possible A RR's lame
+named.*: bad referral
+named.*: Cleaned cache
+named.*: USAGE
+named.*: NSTATS
+named.*: XSTATS
+named.*: .* points to a CNAME
+named.*: denied update from
+named.*: .* Bogus LOOPBACK
+
+[gnome]
+gnome-name-server.*: input condition is:
+gnome-name-server.*: name server starting
+gnome-name-server.*: starting
+gnome-name-server.*: name server was running
+gconfd.*: Resolved address
+gconfd.*: GConf server is not in use
+gconfd.*: Exiting
+gconfd.*: starting
+gconfd.*: .* shutting down cleanly
+gdm.*: Couldn't authenticate user
+xscreensaver.*: FAILED LOGIN
+
+[sshd]
+sshd.*: Generating new .* key.
+sshd.*: .* key generation complete
+sshd.*: Connection closed
+sshd.*: Could not reverse map address
+sshd.*: Received disconnect from
+sshd.*: error: Could not get shadow information for
+sshd.*: Invalid user .* from
+
+[xinetd]
+xinetd.*: .* Transport endpoint is not connected
+xinetd.*: EXIT:
+
+[uw-imap]
+imapd.*: AUTHENTICATE
+imapd.*: Logout
+imapd.*: Killed
+imapd.*: imap.*service init
+imapd.*: Command stream end of file
+imapd.*: Autologout
+imapd.*: Connection reset by peer
+ipop3d.*: AUTHENTICATE
+ipop3d.*: Logout
+ipop3d.*: Killed
+ipop3d.*: Autologout
+ipop3d.*: pop3.*service init
+
+[courier-imap]
+imapd.*: Connection, ip=\[\S+\]
+imapd.*: LOGOUT, user=\S+, ip=\[\S+\]
+imapd.*: Disconnected, ip=\[\S+\]
+imapd.*: DISCONNECTED, user=\S+, ip=\[\S+\]
+imapd.*: LOGOUT, ip=\[\S+\]
+pop3d.*: Connection, ip=\[\S+\]
+pop3d.*: LOGOUT, user=\S+, ip=\[\S+\]
+pop3d.*: Disconnected, ip=\[\S+\]
+pop3d.*: DISCONNECTED, user=\S+, ip=\[\S+\]
+pop3d.*: LOGOUT, ip=\[\S+\]
+
+[postfix]
+postfix/smtp\[\d+\]: connect to
+postfix/smtp\[\d+\]: warning: no MX host
+postfix/smtp\[\d+\]: warning: numeric domain name in resource data
+postfix/smtp\[\d+\]: warning: host .* with my own hostname
+postfix/smtpd.*: connect from
+postfix/smtpd.*: disconnect from
+postfix/smtpd.*: TLS connection established
+postfix/smtpd.*: lost connection
+postfix/cleanup
+postfix/pickup
+
+[sendmail]
+sendmail\[.*:.*NOQUEUE: Null connection from
+sendmail\[.*:.*timeout waiting for input
+
+[qmail]
+qmail:.* new msg
+qmail:.* end msg
+qmail:.* status:
+
+[spamd]
+spamd\[.*: info:
+spamd\[.*: processing message
+spamd\[.*: checking message
+spamd\[.*: connection from
+spamd\[.*: Creating default_prefs
+
+[printer]
+printer: ready to print
+printer: status change
+printer: printing
+printer: peripheral low-power state
+
+[pumpd]
+pumpd.*: renewed lease for interface
+pumpd.*: configured interface
+
+[afpd]
+afpd.*: ASIP session:
+afpd.*: afp_flushfork:
+afpd.*: .*B read,.*B written
+
+[ntpd]
+ntpd.*: kernel time discipline status change
+
+[kernel]
+kernel: application .* uses obsolete OSS audio interface
+kernel: SELinux: initialized
+kernel: device .* left promiscuous mode
+kernel: .*: disabled promiscuous mode
+usb-uhci.c: interrupt, status
+PCI: Found IRQ
+PCI: Sharing IRQ
+PCI: Setting latency timer
+kernel: agpgart: Found
+kernel: agpgart: Putting
+
+[misc]
+modprobe: Can't locate module
+logger: punching nameserver .* through the firewall
+HORDE\[\S*\s*\[imp\] Logout
+LOGIN ON tty.
+dhclient: DHCPREQUEST
+dhclient: DHCPACK
+dhclient: DHCPDISCOVER
+dhclient: bound to
+dbus: avc: .* buckets used
+
+
+
+## $Revision: 1.14.2.4 $ ##
diff --git a/roles/epylog/files/merged/weed_local.cf
b/roles/epylog/files/merged/weed_local.cf
new file mode 100644
index 0000000..1ff0454
--- /dev/null
+++ b/roles/epylog/files/merged/weed_local.cf
@@ -0,0 +1,369 @@
+[ADD]
+##
+# Here is where you add your own rules
+#
+
+audit:.*
+ansible.*: Invoked.*
+ansible.*: message repeated.*
+ansible-accelerate:.*
+ansible-async_wrapper:.*
+auditd.*: Audit daemon rotating log files
+autocloud_job.*
+avahi-daemon.*
+btseed.*
+bugyou-cntrl.*
+chronyd.*
+collectd.*: Filter subsystem.*
+collectd.*: Value too old.*
+collectd.*: processes plugin: Failed to read from.*
+collectd.*: 0 Success:.*value has been dispatched.
+collectd.*: message repeated.*times.*0 Success:.*
+collectd.*: exec plugin.*Unable to parse command\, ignoring line.*
+dbus.*:.*avc:.*received.*
+dbus.*:.*Reloaded configuration.*
+dbus.*:.*Successfully activated service 'org.fedoraproject.Setroubleshootd'.*
+dbus.*:.*Successfully activated service 'org.freedesktop.nm_dispatcher'
+dbus.*:.*\[system\].*Activating via systemd: service.*
+dbus.*:.*\[system\].*Activating.*using servicehelper.*
+dbus.*:.*\[system\].*Successfully activated service.*
+dbus-daemon.*:.*Successfully activated service
\'org.fedoraproject.Setroubleshootd\'.*
+dbus-daemon.*:.*Activating via systemd: service.*
+dbus-daemon.*:.*Successfully activated service.*
+dhclient.*: bound to.*
+dhclient.*: DHCPDISCOVER.*
+dhclient.*: DHCPACK.*
+dhclient.*: DHCPREQUEST.*
+dhcpd:.*Wrote.*leases file.*
+dnsmasq-dhcp.*
+# work around bug
https://bugzilla.redhat.com/show_bug.cgi?id=947989
+dhclient.*: send_packet: Operation not permitted
+dhclient.*: dhclient.c:.*: Failed to send 300 byte long packet over fallback interface.
+dhclient.*: Internet Systems Consortium DHCP Client.*
+dhclient.*: Copyright 2004-2013 Internet Systems Consortium.
+dhclient.*: All rights reserved.
+dhclient.*: For info, please visit
https://www.isc.org/software/dhcp/
+dhclient.*: Listening on.*
+dhclient.*: Sending on.*
+dhclient.*: Sending on.*
+dhclient.*: $
+dnf:.*
+dnf-automatic:.*
+docker.*
+dracut.*
+fedmsg-hub.*
+moksha-hub.*
+mailman3.*
+freshclam.*: Can't connect to port 80 of host.*
+freshclam.*: connect_error:.*
+freshclam.*: Downloading.*
+freshclam.*:.*is up to date.*
+freshclam.*:.*updated.*
+freshclam.*: Database updated.*
+freshclam.*: ClamAV update process started
+git-daemon.*: Connection from.*
+git-daemon.*: Connection reset by peer
+git-daemon.*: .* does not appear to be a git repository
+git-daemon.*: Extended attributes.*
+git-daemon.*: Request upload-pack.*
+git-daemon.*: The remote end hung up unexpectedly
+git-daemon.*: userpath.*
+git-daemon.*: Request upload-archive for.*
+git-daemon.*: fatal: write error: Connection timed out
+groupadd.*: group added to.*:
name=(mockbuild|dialout|floppy|cdrom|tape|utmp|utempter|dbus|avahi-autoipd|rpc|rpcuser|nfsnobody|ssh_keys).*
+(group|user)add.*: new (user|group):
name=(mockbuild|dialout|floppy|cdrom|tape|utmp|utempter|dbus|avahi-autoipd|rpc|rpcuser|nfsnobody|ssh_keys).*
+heartbeat.* info:.*
+heartbeat.*:info.*
+heartbeat.*:WARN: Gmain_timeout_dispatch: Dispatch function for retransmit request took
too long to execute.*
+in.tftpd.*: tftp: client does not accept options
+journal: libvirt version.*
+journal: Failed to acquire.*
+journal: cannot lookup default selinux label for.*
+journal: Domain id.*
+kernel:.*CPU.*power limit.*
+kernel:.*dma-pl330 fff3d000.dma: Reset Channel.*
+kernel: TCPv6: Possible SYN flooding on port 80. Sending cookies.
+kernel: TCPv6: Possible SYN flooding on port 80. Dropping request.
+kernel: possible SYN flooding on port 80. Sending cookies.
+kernel: EXT4-fs \(.*\): mounted filesystem with ordered data mode.*
+kernel: ioctl32\(e2fsck.*
+kernel: ioctl32\(resize2fs.*
+kernel: md: data-check of RAID array.*
+kernel: md: delaying data-check of.*
+kernel: md: md.*: data-check done.
+kernel: md: minimum _guaranteed_ speed.*
+kernel: md: using 128k window.*
+kernel: md: using maximum available idle IO bandwidth.*
+kernel: printk.*suppressed.
+kernel: __ratelimit:.*callbacks suppressed
+kernel:.*subj=.*
+kernel:.*exe=.* \(sauid=.*, hostname=.* addr=.* terminal=.*\)
+kernel:.*type=.*audit\(.*
+kernel:.*audit_printk_skb:.*callbacks suppressed
+kernel:.*usb 3-3: new full-speed USB device number.*using xhci_hcd
+kernel:.*usb 3-3: Device not responding to set address.
+kernel:.*usb 3-3: Device not responding to set address.
+kernel:.*usb 3-3: device not accepting address.*error -71
+kernel:.*kvm.*vcpu.*unhandled rdmsr.*
+kernel:.*kvm_get_msr_common:.*callbacks suppressed
+kernel:.*device vnet.*entered promiscuous mode
+kernel:.*virbr0: port.*entered.*state
+kernel:.*virbr0: topology change detected, propagating
+kernel: blk_update_request.*I/O error, dev loop0, sector.*
+kernel: blk-mq: bad return on queue.*
+kojid.*
+koschei.*
+kojira.*
+logrotate.*
+lvm.*: Another thread is handling an event. Waiting...*
+libvirtd:.*debug.*
+nagios.*
+named.*: .* general: info:.*
+named.*: .* notify: info:.*
+named.*: .* general: error: zone.*unchanged. zone may fail to transfer to slaves.
+named.*: .* resolver: notice: DNS format error from.*: invalid response
+named.*: .* resolver: notice: DNS format error from.*: non-improving referral
+named.*: .* resolver: notice: clients-per-query increased to.*
+named.*: .* security: info: client.*: view.*: query (cache).*denied
+named.*: .* edns-disabled: info: success resolving.*after.*
+named.*: .* security: info: client.*denied
+named.*: .* rate-limit: info:.*
+named.*: .* general: warning: checkhints: view.*
+named.*: .* query-errors: info: client.*rate limit slip response to.*
+named.*: .* query-errors: info: client.*rate limit drop response to.*
+NetworkManager.*: \<info\>.*
+NetworkManager.*: \<warn\>.*
+NetworkManager.*: \<error\>.*Unspecific failure
+nm-dispatcher: Dispatching action.*
+ntpd.*
+openqa:.*is not a registered asset
+openqa: no products found.*
+openqa-scheduler.*
+openqa-websockets.*
+openshift-master.*
+fedora-openqa-consumer.*
+openvpn.*: Auth read bytes.*
+openvpn.*: CLIENT_LIST.*
+openvpn.*: END
+openvpn.*: event_wait : Interrupted system call.*
+openvpn.*: GLOBAL_STATS.*
+openvpn.*: HEADER.*
+openvpn.*: OpenVPN STATISTICS
+openvpn.*: post-compress bytes.*
+openvpn.*: post-decompress bytes.*
+openvpn.*: pre-compress bytes.*
+openvpn.*: pre-decompress bytes.*
+openvpn.*: ROUTING_TABLE.*
+openvpn.*: TCP/UDP.*
+openvpn.*: TCP/UDP.*
+openvpn.*: TIME.*
+openvpn.*: TITLE.*
+openvpn.*: TUN/TAP.*
+openvpn.*: UDPv4 link (local|remote).*
+openvpn.*: SIGUSR1.*
+openvpn.*: Updated.*
+openvpn.*:.*Re-using SSL/TLS context.*
+openvpn.*:.*LZO compression.*
+openvpn.*: NOTE: the current --script-security setting may allow this configuration to
call user-defined scripts.*
+openvpn.*: WARNING: No server certificate verification method has been enabled.*
+origin-master.*
+pam_unix\(.*\): account .* has password changed in future
+pagure-stream-server.py:.*
+polkitd.*
+postfix/anvil.*: statistics.*
+postfix/bounce.*sender non-delivery notification.*
+postfix/error.*:.*delivery temporarily suspended.*Connection timed out.*
+postfix/error.*:.*delivery temporarily suspended.*No route to host.*
+postfix/error.*:.*delivery temporarily suspended.*temporarily deferred due to user
complaints.*
+postfix/error.*:.*delivery temporarily suspended.*while receiving the initial server
greeting.*
+postfix/error.*:.*delivery temporarily suspended.*Host or domain name not found.*
+postfix/error.*:.*delivery temporarily suspended.*Connection refused.*
+postfix/error.*:.*delivery temporarily suspended.*conversation with.*
+postfix/error.*:.*delivery temporarily suspended.*service not available, closing
transmission channel.*
+postfix/error.*:.*delivery temporarily suspended.*Network is unreachable.*
+postfix/error.*:.*refused to talk to me.*
+postfix/lmtp.*:.*250.*Ok.*
+postfix/lmtp.*: 503.*
+postfix/local.*: table.*has changed -- restarting.*
+postfix/master.*: daemon started.*
+postfix/master.*: terminating on signa.*
+postfix/pipe.*:.*delivered via spamassassin.*
+postfix/postfix-script.*: starting the Postfix mail system
+postfix/postfix-script.*: stopping the Postfix mail system
+postfix/postfix-script.*: waiting for the Postfix mail system to terminate
+postfix/scache.*: statistics.*
+postfix/smtp.*: 400.*
+postfix/smtp.*: 421.*
+postfix/smtp.*: 450.*
+postfix/smtp.*: 451.*
+postfix/smtp.*: 452.*
+postfix/smtp.*: 454.*
+postfix/smtp.*: 503.*
+postfix/smtp.*: conversation.* timed out.*
+postfix/smtpd.*: table.*has changed -- restarting.*
+postfix/smtpd.*: timeout.*
+postfix/smtpd.*: too many errors after RCPT.*
+postfix/smtp.*: enabling PIX.*
+postfix/smtp.*: lost connection.*
+postfix/smtp.*:.*refused to talk to me.*
+postfix/smtp.*: warning: malformed domain name.*
+postfix/smtp.*: warning: valid_hostname:.*
+postfix/smtp.*:.*yahoo.*refused to talk to me.*
+python.*: ansible-<stdin>.*
+python.*: ansible.*: Invoked.*
+python.*: ansible.* Invoked.*
+python2.*
+rabbitmq-server.*
+ResourceManager.*: info:.*
+restorecond: Reset file context /etc/aliases.*
+restorecond: Reset file context /var/db/shadow.db.*
+restorecond: Unable to watch.*
+Rootkit Hunter: Rootkit hunter.*
+Rootkit Hunter: Scanning.*
+root: time debug:.*
+registry.*
+rpc.idmapd.*: nss_getpwnam: name.*apache.*
+rpc.idmapd.*: nss_getpwnam: name.*masher.*
+rpc.idmapd.*: nss_getpwnam: name.*root(a)fedora.*
+rpc.idmapd.*: nss_getpwnam: name.*root@localdomain*
+rsyncd.*: building.*
+rsyncd.*: connect from.*
+rsyncd.*: file has vanished:
+rsyncd.*: name lookup failed for.*
+rsyncd.*: rsync: connection unexpectedly closed.*
+rsyncd.*: rsync error: error in rsync protocol data stream.*
+rsyncd.*: sent.*
+rsyncd.*: rsync: change_dir.*failed.*
+#rsync.*: rsync on.*
+rsyslogd-2163:epoll_ctl failed
+#goofy-ass rsyslogd error :(
+^\(\':\',.*
+setfiles: relabeling .*
+spamc.*: connect to spamd on.*
+spamc.*: skipped message, greater.*
+spamd.*: bayes: cannot open bayes databases.*
+spamd.*: logger: removing.*
+spamd.*: prefork.*
+spamd.*: pyzor:.* error: TERMINATED
+spamd: result:.*
+spamd.*: spamd: clean message.*
+spamd.*: spamd: clean message.*
+spamd.*: spamd: handled cleanup.*
+spamd.*: spamd: identified spam.*
+spamd.*: spamd: server killed.*
+spamd.*: spamd: server pid.*
+spamd.*: spamd: server started.*
+spamd.*: spamd: server successfully.*
+spamd.* spamd: setuid to.*
+sshd.*: Address.*maps to.*but this does not map back to the address - POSSIBLE BREAK-IN
ATTEMPT.*
+sshd.*: Did not receive identification.*
+sshd.*: Disconnecting: Too many authentication failures for root.*
+sshd.*: error: connect_to
+sshd.*: error: ssh_selinux_setup_pty:
+sshd.*: Found matching RSA key.*
+sshd.*: Found matching DSA key.*
+sshd.*: input_userauth_request: invalid user.*
+sshd.*: pam_unix\(sshd:session\): session closed for user.*
+sshd.*: pam_unix\(sshd:session\): session opened for user.*
+sshd.*: Postponed publickey for.*
+sshd.*: refused connect from.*
+sshd.*: reverse mapping checking getaddrinfo.*POSSIBLE BREAK-IN ATTEMPT.*
+sshd.*: Server listening on.*
+sshd.*: subsystem request for sftp
+sshd.*: pam_namespace.*: Unmount of \/tmp failed, Device or resource busy.*
+sshd.*: Set /proc/self/oom_score_adj.*
+sshd.*: Connection from.* port.*
+sshd.*: Transferred: sent.*, received.*bytes
+sshd.*: Closing connection to.*port.*
+sshd.*: User child is on pid.*
+sshd.*: Read error from remote host.*: Connection reset by peer
+sshd.*: Read error from remote host.*: Connection timed out
+sshd.*: error: Could not load host key: /etc/ssh/ssh_host_dsa_key
+sshd.*: Starting session.*
+sshd.*: fatal: Read from socket failed: Connection reset by peer.*
+sshd.*: Starting session: subsystem 'sftp' for root from 10.5.126.23 port.*
+sshd.*: Starting session: subsystem 'sftp' for root from 209.132.181.6 port.*
+sshd.*: Corrupted MAC on input.
+sshd.*: pam_systemd(sshd:session): Failed to create session: No such file or directory
+sshd.*: fatal: Write failed: Connection reset by peer
+sshd.*: pam_succeed_if\(sshd:auth\): requirement.*
+sshd.*: pam_unix\(sshd:auth\): authentication failure; logname= uid=0 euid=0 tty=ssh
ruser= rhost=.*
+sshd.*: PAM .* more authentication failures\; logname= uid=0 euid=0 tty=ssh ruser=
rhost=.*
+sshd.*: pam_unix\(sshd:auth\): check pass; user unknown
+sshd.*: error\: maximum authentication attempts exceeded for.*from.*port.*ssh2 [preauth]
+sshd.*: Disconnecting\: Too many authentication failures.*
+sshd.*: Disconnected from.*
+sshd.*: Read error from remote host.*
+sshd.*: error\: maximum authentication attempts exceeded for.*
+sshd.*: Close session.*user root from 10.5.126.23 port.*id 0
+sshd.*: error\: key_read: uudecode.*failed
+sshd.*: Connection reset by.*
+sshd.*: error\: buffer_get_ret\: trying to get more bytes.*
+sshd.*: error\: buffer_get_string_ret\: buffer_get failed
+sshd.*: error\: buffer_get_bignum2_ret\: invalid bignum
+sshd.*: error\: key_from_blob\: can\'t read rsa key
+sshd.*: error\: key_read\: key_from_blob
+sshd.*: Close session\: user root from 10.5.126.23 port.*
+stunnel:.*
+su: pam_unix\(su-l:session\): session .* for user.*
+runuser: pam_unix\(runuser-l:session\).* session opened for user postgres by.*
+runuser: pam_unix\(runuser-l:session\).* session closed for user postgres
+systemd-logind.*
+systemd: dev-disk-by.*
+systemd: Start.*
+systemd.*: Stop.*
+systemd.*: Reached.*
+systemd: pam_unix\(systemd-user:session\): session opened for user root by (uid=0)
+systemd: pam_unix\(systemd-user:session\): session closed for user root
+systemd: pam_unix\(systemd-user:session\): session.*
+systemd.*: Start.* Cleanup of Temporary Directories.*
+systemd-machine-id-setup.*: Initializing machine ID.*
+systemd.*: Created slice user-.*.slice.
+systemd: Created slice User Slice of.*
+systemd: Listening on D-Bus User Message Bus Socket.
+systemd.*: Removed slice user-.*slice.
+systemd.*: Received SIGRTMIN\+24 from PID.*
+systemd.*: Failed to mark scope session-.*.scope as abandoned : Stale file handle
+systemd.*: Failed to reset devices.list on /machine.slice: Invalid argument
+systemd: tmp.mount.*
+systemd: Unit git.*
+systemd-coredump:.*
+systemd-machined:.*
+systemd: Configuration file.*is marked world-inaccessible.*
+systemd: Reload.*
+systemd: Closed D-Bus User Message Bus Socket.
+systemd: Removed slice User Slice of root.
+systemd-udevd: conflicting device node.*
+supybot.*
+unix_chkpwd.*: account .* has password changed in future
+unix_chkpwd.*: password check failed for user \(root\)
+userhelper.*: running \'/usr/sbin/mock.*
+worker.*
+# Do not want any of the new gitolite stuff
+gitolite.*
+groupadd.*: new group.*
+groupadd.*: group added to /etc/g.*
+useradd.*: new user.*
+varnishd.*: Child .* said missing \)CLI.*
+varnishd.*: Child .* said nothing to repeatCLI result.*
+xinetd.*: Exiting.*
+xinetd.*: FAIL: git per_source_limit.*
+xinetd.*: readjusting service rsync
+xinetd.*: Reconfigured.*
+xinetd.*: Started.*
+xinetd.*: Starting reconfiguration
+xinetd.*: Swapping defaults
+xinetd.*: xinetd Version.*
+ykksm.*: SUCCESS.*
+ykval.*: LOG_INFO.*
+ykval.*: LOG_WARNING.*
+ykval.*: SUCCESS.*
+ykval.*: WARNING.*
+yum.*: Installed:.*
+yum.*: Updated:.*
+
+
+[REMOVE]
+##
+# Here is where you put the rules (VERBATIM) from the weed_dist.cf file
+#
diff --git a/roles/epylog/files/modules/common_unparsed.conf
b/roles/epylog/files/modules/common_unparsed.conf
new file mode 100644
index 0000000..73ec8ee
--- /dev/null
+++ b/roles/epylog/files/modules/common_unparsed.conf
@@ -0,0 +1,12 @@
+[module]
+desc = Common Unparsed Similar Strings Module
+exec = /usr/share/epylog/modules/common_unparsed_mod.py
+files = /var/log/messages[.#] /var/log/secure[.#]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+# how similar the strings need to be 0-100 - 0 being not at all (bad idea) 100 being
almost exactly.
+match_percentage = 95
diff --git a/roles/epylog/files/modules/common_unparsed_mod.py
b/roles/epylog/files/modules/common_unparsed_mod.py
new file mode 100644
index 0000000..d2aa635
--- /dev/null
+++ b/roles/epylog/files/modules/common_unparsed_mod.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python -tt
+"""
+This module should run after all other modules.
+
+Takes the remaining logs, saves them out to a tmpfile.
+Uses difflib.SequenceMatcher() to return logs which occur most often and are
+at least a set percentage similar. This lets you catch logs which are
+from multiple machines and more or less the same or similar logs (with memory
+or process id offsets) from the same machine.
+
+"""
+
+import sys
+import re
+import difflib
+from operator import itemgetter
+import tempfile
+import os
+import shutil
+
+##
+# This is for testing purposes, so you can invoke this from the
+# modules directory. See also the testing notes at the end of the
+# file.
+#
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+class common_unparsed_mod(InternalModule):
+ def __init__(self, opts, logger):
+ InternalModule.__init__(self)
+ self.logger = logger
+ rc = re.compile
+
+ self.match_percentage = int(opts.get('match_percentage', '95'))
+ self.debug_dump = int(opts.get('debug_dump', '1'))
+ self.regex_map = {rc('^.+$'):self.expand_out_line}
+ self.tmpdir = tempfile.mkdtemp(prefix='epylog-common-unparsed',
dir='/var/tmp')
+ self.matchfile = self.tmpdir + '/match_limited'
+ self.matchfile_f = open(self.matchfile, 'w')
+ self.complete = self.tmpdir + '/complete'
+ self.complete_f = open(self.complete, 'w')
+
+
+ ##
+ # Line-matching routines
+ #
+ def expand_out_line(self, linemap):
+ sys, msg, mult = self.get_smm(linemap)
+ matchout = '%s\n' % (msg)
+ com_out = '%s' % (linemap['line'])
+ for i in range(0, int(mult)):
+ self.complete_f.write(com_out)
+ self.matchfile_f.write(matchout)
+
+ #dirty like zebra
+ if not os.path.exists(self.tmpdir + '/returned'):
+ open(self.tmpdir + '/returned', 'w').close()
+ return {'match':0}
+ else:
+ return None
+
+
+ def finalize(self, rs):
+ #FIXME - enable a debug mode where it writes
+ # out to a file all of the first line and things which match it
+ # and percentages of match for later investigation
+ self.complete_f.close()
+ self.matchfile_f.close()
+ matches = {}
+ full_matches = {}
+ lines_matched = set()
+ where = 0
+ rl = open(self.matchfile, 'r')
+ for l in rl:
+ where += 1
+ sub_where = 0
+ for ol in open(self.matchfile, 'r'):
+ sub_where += 1
+ if sub_where < where: # if we're before it in the file we've
already matched it
+ continue
+ if sub_where in lines_matched:
+ continue
+
+ c = difflib.SequenceMatcher(isjunk=lambda x: x in
('1','2','3','4','5','6','7','8','9','0'),a=l,
b=ol)
+ rq_ratio = int(c.real_quick_ratio()*100) # do the fast upper bound - find
out if we should even glance at it.
+ if rq_ratio < 60:
+ continue
+ ratio = int(c.ratio()*100)
+ if ratio >= self.match_percentage:
+ lines_matched.add(sub_where)
+ if l not in matches:
+ matches[l] = 0
+ full_matches[l] = []
+ matches[l] += 1
+ full_matches[l].append((ol,ratio))
+ if self.debug_dump:
+ fm_db = open(self.tmpdir + '/debug-match', 'w')
+ for k in full_matches:
+ fm_db.write(k)
+ for (v,r) in full_matches[k]:
+ fm_db.write(' %s %s' % (r, v))
+ fm_db.close()
+
+ res = "<table border=0><tr><th>Count</th><th
align=left>Log</th></tr>\n\n"
+ for (k,v) in sorted(matches.items(), key=itemgetter(1), reverse=True)[:20]: #
take the top 20 most common provided there are more than 1
+ if v > 1:
+ res += "<tr>\n<td
bgcolor='#DDDDDD'>%s</td><td>%s</td>\n</tr>" % (v,
k)
+ res += "<tr>\n<td colspan=2>\n<h2>Complete
messages</h2>\n<pre>\n"
+ res += ''.join(sorted(open(self.complete, 'r').readlines()))
+ res += "</pre>\n</td>\n</tr>\n</table>"
+ return res
+
+
+
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(common_unparsed_mod, sys.argv)
diff --git a/roles/epylog/files/modules/kojiload.conf
b/roles/epylog/files/modules/kojiload.conf
new file mode 100644
index 0000000..6192a17
--- /dev/null
+++ b/roles/epylog/files/modules/kojiload.conf
@@ -0,0 +1,10 @@
+[module]
+desc = KojiLoad summary
+exec = /usr/share/epylog/modules/kojiload_mod.py
+files = /var/log/messages[.#]
+enabled = no
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
diff --git a/roles/epylog/files/modules/kojiload_mod.py
b/roles/epylog/files/modules/kojiload_mod.py
new file mode 100644
index 0000000..3945b8b
--- /dev/null
+++ b/roles/epylog/files/modules/kojiload_mod.py
@@ -0,0 +1,102 @@
+#!/usr/bin/python -tt
+"""
+Rsyncd log parsing module for Epylog
+"""
+
+##
+# Copyright (C) 2012 by Red Hat, Inc
+# Written by Seth Vidal <skvidal at fedoraproject.org>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+
+
+import sys
+import re
+
+##
+# This is for testing purposes, so you can invoke this from the
+# modules directory. See also the testing notes at the end of the
+# file.
+#
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+class kojiload_mod(InternalModule):
+ ##
+ # opts: is a map with extra options set in
+ # [conf] section of the module config, or on the
+ # command line using -o flag to the module.
+ # logger: A logging object. API:
+ # logger.put(loglvl, 'Message')
+ # Only critical stuff needs to go onto lvl 0.
+ # Common output goes to lvl 1.
+ # Others are debug levels.
+ #
+ def __init__(self, opts, logger):
+ ##
+ # Do a "super-init" so the class we are subclassing gets
+ # instantiated.
+ #
+ InternalModule.__init__(self)
+ self.logger = logger
+ ##
+ # Convenience
+ #
+ rc = re.compile
+ #kojiload: Load: 7.1 Total: 192.0 Use: 3.7% (Very Light Load)
+ self.regex_map = {
+ rc('kojiload: Load:.*'): self.load_results
+ }
+ # dict to store all of our data
+ self.loads = [] # list of kojiload percentages
+ self.kojiloads = rc('kojiload: Load: (.*) Total: (.*) Use: (.*)\%.*')
+
+
+ def load_results(self, linemap):
+ (sys, msg, multi) = self.get_smm(linemap)
+ load, total, use_percent = self.kojiloads.search(msg).groups()
+ for i in range(multi):
+ self.loads.append(float(use_percent))
+ return {(load,total): 1}
+
+
+ def finalize(self, resultset):
+ ##
+ # A resultset is a dictionary of all values returned by your
+ # handler functions -- except they are unique and show how many
+ # times each tuple occurs.
+ # See epylog.Result for some convenience methods to use when
+ # processing and analyzing the results.
+ #
+ if not self.loads:
+ return "No kojiloads returned, that seems odd."
+
+ max_load = max(self.loads)
+ min_load = min(self.loads)
+ avg_load = sum(self.loads)/len(self.loads)
+
+ foo = "Max: %s%%<br>\nMin: %s%%<br>\nAvg:
%.1f%%<br>\n" % (max_load,
+ min_load, avg_load)
+ return foo
+
+##
+# This is useful when testing your module out.
+# Invoke without command-line parameters to learn about the proper
+# invocation.
+#
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(kojiload_mod, sys.argv)
diff --git a/roles/epylog/files/modules/logins_mod.py
b/roles/epylog/files/modules/logins_mod.py
new file mode 100644
index 0000000..b488959
--- /dev/null
+++ b/roles/epylog/files/modules/logins_mod.py
@@ -0,0 +1,849 @@
+
+
+#!/usr/bin/python -tt
+"""
+Description will eventually go here.
+"""
+##
+# Copyright (C) 2003 by Duke University
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# $Id$
+#
+# @Author Konstantin Ryabitsev <icon(a)linux.duke.edu>
+# @version $Date$
+#
+
+import sys
+import re
+import time
+import os
+import sqlite3 as sqlite
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+
+def executeSQL(cursor, query, params=None):
+ """
+ Execute a python 2.5 (sqlite3) style query.
+
+ @param cursor: A sqlite cursor
+ @param query: The query to execute
+ @param params: An optional list of parameters to the query
+ """
+ if params is None:
+ return cursor.execute(query)
+
+ return cursor.execute(query, params)
+
+class logins_mod(InternalModule):
+ def __init__(self, opts, logger):
+ InternalModule.__init__(self)
+ self.logger = logger
+ self.opts = opts
+ rc = re.compile
+
+ self.ignore = 0
+ self.open = 1
+ self.failure = 2
+ self.root_open = 11
+ self.root_failure = 12
+ self.pam_ignore = []
+ self.xinetd_ignore = []
+ self.logins_db = opts.get('loginsdb_path',
'/var/lib/epylog/logins_db.sqlite') # where to keep the loginsdb
+ self.time_fuzz = int(opts.get('time_fuzz', 60)) # how much to fuzz the
time in minutes (default 60m)
+ remove_older_than = int(opts.get('remove_older_than', 14)) # time in days
to start remove from the db
+ self.oldest_to_keep = time.time() - (remove_older_than*86400)
+ if remove_older_than == '0': # if it is zero then don't delete any,
ever - hah your funeral
+ self.oldest_to_keep = None
+
+ ig_users = opts.get('ignore_users', '')
+ ig_users.replace(',',' ')
+ self.ignore_users = ig_users.split(' ')
+
+ self.db_cx = None
+
+ ##
+ # PAM reports
+ #
+ pam_map = {
+ rc('\(pam_unix\)\S*:.*authentication\s*failure'): self.pam_failure,
+ rc('\(pam_unix\)\S*:\ssession\sopened\sfor'): self.pam_open,
+ rc('\(pam_unix\)\S*:\sbad\susername'): self.pam_baduser,
+ rc('\(pam_unix\)\S*:\sauth\scould\snot'): self.pam_chelper_failure,
+ rc('pam_krb5\S*:\s\S+\ssucceeds\sfor'): self.pam_krb5_open,
+ rc('pam_krb5\S*:\s\S+\sfails\sfor'): self.pam_krb5_failure
+ }
+ ##
+ # XINETD reports
+ #
+ xinetd_map = {
+ rc('xinetd\S*: START:'): self.xinetd_start
+ }
+ ##
+ # SSH reports
+ #
+ sshd_map = {
+ rc('sshd\[\S*: Accepted'): self.sshd_open,
+ rc('sshd\[\S*: Failed'): self.sshd_failure
+ }
+ ##
+ # IMAPD and IPOP3D
+ #
+ uw_imap_map = {
+ rc('imapd\[\S*: Login\sfail'): self.uw_imap_failure,
+ rc('imapd\[\S*: Authenticated\suser'): self.uw_imap_open,
+ rc('imapd\[\S*: Login\suser'): self.uw_imap_open,
+ rc('ipop3d\[\S*: Login\sfail'): self.uw_imap_failure,
+ rc('ipop3d\[\S*: Login\suser'): self.uw_imap_open,
+ rc('ipop3d\[\S*: Auth\suser'): self.uw_imap_open
+ }
+ ##
+ # IMP
+ #
+ imp_map = {
+ rc('IMP\[\S*: Login'): self.imp2_open,
+ rc('IMP\[\S*: FAILED'): self.imp2_failure,
+ rc('HORDE\[\S*\s*\[imp\] Login'): self.imp3_open,
+ rc('HORDE\[\S*\s*\[imp\] FAILED'): self.imp3_failure
+ }
+ ##
+ # DOVECOT
+ #
+ dovecot_map = {
+ rc('imap-login:\sLogin:\s'): self.dovecot_open,
+ rc('imap-login:\sAborted\slogin\s'): self.dovecot_failure
+ }
+ ##
+ # Courier-IMAP
+ #
+ courier_map = {
+ rc('\sLOGIN,\suser=\S+,\sip=\[\S+\]'): self.courier_open,
+ rc('\sLOGIN FAILED,\sip=\[\S+\]'): self.courier_failure
+ }
+ ##
+ # Cyrus-IMAP
+ #
+ cyrus_map = {
+ rc('imapd\[\S*: login:'): self.cyrus_open,
+ rc('pop3d\[\S*: login:'): self.cyrus_open,
+ rc('imapd\[\S*: badlogin:'): self.cyrus_failure,
+ rc('pop3d\[\S*: badlogin:'): self.cyrus_failure
+ }
+ ##
+ # Qpopper
+ #
+ qpopper_map = {
+ rc('apop\[\S*:\s\S+\sat\s.*\s\(\S*\):\s-ERR\s\[AUTH\]'):
self.qpopper_failure,
+ rc('apop\[\S*:\s\S+\sat\s.*\s\(\S*\):\s-ERR\s\[IN-USE\]'):
self.qpopper_failure,
+ rc('apop\[\S*:\s\(\S*\)\sPOP\slogin'): self.qpopper_open
+ }
+
+ ##
+ # ProFTPD
+ #
+ proftpd_map = {
+ rc('proftpd\[\S*:.*USER.*Login successful'): self.proftpd_open,
+ rc('proftpd\[\S*:.*no such user found'): self.proftpd_failure,
+ rc('proftpd\[\S*:.*Login failed'): self.proftpd_failure
+ }
+
+ regex_map = {}
+ if opts.get('enable_pam', "1") != "0":
regex_map.update(pam_map)
+ if opts.get('enable_xinetd', "1") != "0":
regex_map.update(xinetd_map)
+ if opts.get('enable_sshd', "1") != "0":
+ regex_map.update(sshd_map)
+ self.pam_ignore.append('sshd')
+ if opts.get('enable_uw_imap', "0") != "0":
+ regex_map.update(uw_imap_map)
+ self.xinetd_ignore.append('imaps')
+ if opts.get('enable_imp', "0") != "0":
regex_map.update(imp_map)
+ if opts.get('enable_dovecot',"0") != "0":
regex_map.update(dovecot_map)
+ if opts.get('enable_courier',"0") != "0":
regex_map.update(courier_map)
+ if opts.get('enable_cyrus', "0") != "0":
regex_map.update(cyrus_map)
+ if opts.get('enable_qpopper',"0") != "0":
regex_map.update(qpopper_map)
+ if opts.get('enable_proftpd',"0") != "0":
+ regex_map.update(proftpd_map)
+ self.pam_ignore.append('ftp')
+ self.xinetd_ignore.append('ftp')
+
+ self.safe_domains = []
+ safe_domains = opts.get('safe_domains', '.*')
+ for domain in safe_domains.split(','):
+ domain = domain.strip()
+ if domain:
+ try:
+ domain_re = rc(domain)
+ self.safe_domains.append(domain_re)
+ except:
+ logger.put(0, 'Error compiling domain regex: %s' % domain)
+ logger.put(0, 'Check config for Logins module!')
+
+ self.regex_map = regex_map
+
+ self.pam_service_re = rc('(\S+)\(pam_unix\)')
+ self.pam_failure_re = rc('.*\slogname=(\S*).*\srhost=(\S*)')
+ self.pam_failure_user_re = rc('\suser=(\S*)')
+ self.pam_open_re = rc('.*for user (\S+) by\s(\S*)\s*\(uid=(\S+)\)')
+ self.pam_failure_more_re = rc('(\S+)\smore\sauthentication\sfailures')
+ self.pam_baduser_re = rc('\sbad\susername\s\[(.*)\]')
+ self.pam_chelper_re = rc('password\sfor\s\[(.*)\]')
+ self.pam_krb5_re =
rc("^(\S+?)\[*\d*\]*:\spam_krb5\S*:\sauth.*\sfor\s`(\S+)'")
+ self.xinetd_start_re = rc('START:\s*(\S*)\s')
+ self.sshd_open_ruser_re =
rc('Accepted\s(\S*)\sfor\s(\S*)\sfrom\s(\S*)\sport\s\d*\sruser\s(\S*)\s*(\S*)')
+ self.sshd_open_re =
rc('Accepted\s(\S*)\sfor\s(\S*)\sfrom\s(\S*)\sport\s\d+\s*(\S*)')
+ self.sshd_fail_re =
rc('Failed\s(\S*)\sfor.*\s(\S+)\sfrom\s(\S*)\sport\s\d*\s*(\S*)')
+ self.uw_imap_fail_re = rc('auth=(.*)\shost=.*\[(\S*)\]')
+ self.uw_imap_open_re = rc('user=(.*)\shost=.*\[(\S*)\]')
+ self.uw_imap_service_re = rc('^(\S*)\[\d*\]:')
+ self.dovecot_open_re = rc('Login:\s(\S+)\s\[(\S+)\]')
+ self.dovecot_failure_re = rc('Aborted\slogin\s\[(\S+)\]')
+ self.courier_open_re = rc('^(\S+?):.*\suser=(\S+),\sip=\[(\S+)\]')
+ self.courier_failure_re = rc('^(\S+?):.*,\sip=\[(\S+)\]')
+ self.imp2_open_re = rc('Login\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
+ self.imp2_fail_re = rc('FAILED\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
+ self.imp3_open_re = rc('success\sfor\s(\S*)\s\[(\S*)\]\sto\s\{(\S*):')
+ self.imp3_fail_re = rc('LOGIN\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
+ self.proftpd_open_re =
rc('proftpd\[\S*:.*\[(\S+)\].*USER\s(.*):\sLogin\ssuccessful')
+ self.proftpd_failure_re =
rc('proftpd\[\S*:.*\[(\S+)\].*USER\s([^:\s]*)')
+ self.qpopper_open_re = rc('user "(.*)" at \(.*\)\s(\S*)')
+ self.qpopper_fail_re = rc(':\s(.*)\sat\s(\S*)')
+ self.cyrus_open_re = rc('login:.*\[(\S*)\]\s(\S*)\s')
+ self.cyrus_fail_re = rc('badlogin:.*\[(\S*)\]\s\S\s(\S*)\sSASL')
+ self.cyrus_service_re = rc('^(\S*)\[\d*\]:')
+
+ self.sshd_methods = {'password': 'pw',
+ 'publickey': 'pk',
+ 'rhosts-rsa': 'rsa',
+ 'rsa': 'rsa',
+ 'hostbased': 'host',
+ 'none': 'none'}
+
+ self.report_wrap = '<table width="100%%" rules="cols"
cellpadding="2">%s</table>'
+ self.subreport_wrap = '<tr><th align="left"
colspan="3"><h3>%s</h3></th></tr>\n%s\n'
+
+ self.root_failures_title = '<font color="red">ROOT
FAILURES</font>'
+ self.root_logins_title = '<font color="blue">ROOT
Logins</font>'
+ self.user_failures_title = '<font color="red">User
Failures</font>'
+ self.user_logins_title = '<font color="blue">User
Logins</font>'
+
+ self.untrusted_host = '%(system)s::<font
color="red">%(rhost)s</font>'
+
+ self.flip = ' bgcolor="#dddddd"'
+
+ self.line_rep = '<tr%s><td valign="top"
width="15%%">%s</td><td valign="top"
width="15%%">%s</td><td
width="70%%">%s</td></tr>\n'
+
+ ##
+ # LINE MATCHING ROUTINES
+ #
+ def general_ignore(self, linemap):
+ restuple = (self.ignore, None, None, None)
+ return {restuple: 1}
+
+ def pam_failure(self, linemap):
+ action = self.failure
+ self.logger.put(5, 'pam_failure invoked')
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_pam_service(message)
+ mo = self.pam_failure_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam failure string: %s' % message)
+ return None
+ byuser, rhost = mo.groups()
+ mo = self.pam_failure_user_re.search(message)
+ if mo: user = mo.group(1)
+ else: user = 'unknown'
+ if ((service == 'xscreensaver' and user == 'root')
+ or service == 'sshd' or service == 'imap'):
+ ##
+ # xscreensaver will always fail as root.
+ # SSHD is better handled by sshd part itself.
+ # Imap failures are caught by imap routines.
+ # Ignore these.
+ #
+ result = self.general_ignore(linemap)
+ return result
+ mo = self.pam_failure_more_re.search(message)
+ if mo: mult += int(mo.group(1))
+ restuple = self._mk_restuple(action, system, service, user,
+ byuser, rhost, linemap['stamp'])
+ return {restuple: mult}
+
+ def pam_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_pam_service(message)
+ if service in self.pam_ignore:
+ ##
+ # the service will do a much better job.
+ #
+ result = self.general_ignore(linemap)
+ return result
+ mo = self.pam_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam open string: %s' % message)
+ return None
+ user, byuser, byuid = mo.groups()
+ if byuser == '': byuser = self.getuname(int(byuid))
+ restuple = self._mk_restuple(action, system, service, user, byuser, '',
linemap['stamp'])
+ return {restuple: mult}
+
+ def pam_baduser(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.pam_baduser_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam bad user string: %s' % message)
+ return None
+ user = mo.group(1)
+ service = self._get_pam_service(message)
+ restuple = self._mk_restuple(action, system, service, user, '',
'', linemap['stamp'])
+ return {restuple: mult}
+
+ def pam_chelper_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.pam_chelper_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam console helper string: %s' % message)
+ return None
+ user = mo.group(1)
+ service = self._get_pam_service(message)
+ restuple = self._mk_restuple(action, system, service, user, '',
'', linemap['stamp'])
+ return {restuple: mult}
+
+ def pam_krb5_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.pam_krb5_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam_krb5 succeeds line: %s' % message)
+ return None
+ service = mo.group(1)
+ user = mo.group(2)
+ if service == 'sshd':
+ ##
+ # sshd_open will do a much better job.
+ #
+ result = self.general_ignore(linemap)
+ return result
+ restuple = self._mk_restuple(action, system, service, user, '',
'', linemap['stamp'])
+ return {restuple: mult}
+
+ def pam_krb5_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.pam_krb5_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd pam_krb5 failure line: %s' % message)
+ return None
+ service = mo.group(1)
+ user = mo.group(2)
+ if ((service == 'xscreensaver' and user == 'root')
+ or service == 'sshd' or service == 'imap'):
+ ##
+ # xscreensaver will always fail as root.
+ # SSHD is better handled by sshd part itself.
+ # Imap failures are caught by imap routines.
+ # Ignore these.
+ #
+ result = self.general_ignore(linemap)
+ return result
+ restuple = self._mk_restuple(action, system, service, user, '',
'', linemap['stamp'])
+ return {restuple: mult}
+
+ def xinetd_start(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.xinetd_start_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd xinetd start string: %s' % message)
+ return None
+ service = mo.group(1)
+ if service in self.xinetd_ignore:
+ ##
+ # the service will do a much better job.
+ #
+ result = self.general_ignore(linemap)
+ return result
+ restuple = self._mk_restuple(action, system, service, '', '',
'', linemap['stamp'])
+ return {restuple: mult}
+
+ def sshd_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ ruser = ''
+ mo1 = self.sshd_open_ruser_re.search(message)
+ mo2 = self.sshd_open_re.search(message)
+ if mo1: method, user, rhost, ruser, service = mo1.groups()
+ elif mo2: method, user, rhost, service = mo2.groups()
+ else:
+ self.logger.put(3, 'Odd sshd open string: %s' % message)
+ return None
+ method = self.sshd_methods.get(method, '??')
+ rhost = self.gethost(rhost)
+ if not service: service = 'ssh1'
+ service = '%s(%s)' % (service, method)
+ restuple = self._mk_restuple(action, system, service, user,
+ ruser, rhost, linemap['stamp'])
+ return {restuple: mult}
+
+ def sshd_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.sshd_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd sshd FAILURE string: %s' % message)
+ return None
+ method, user, rhost, service = mo.groups()
+ method = self.sshd_methods.get(method, '??')
+ rhost = self.gethost(rhost)
+ if not service: service = 'ssh1'
+ service = '%s(%s)' % (service, method)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def uw_imap_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_uw_imap_service(message)
+ service = '%s(uw)' % service
+ mo = self.uw_imap_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd imap FAILURE string: %s' % message)
+ return None
+ user, rhost = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def uw_imap_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_uw_imap_service(message)
+ service = '%s(uw)' % service
+ mo = self.uw_imap_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd imap open string: %s' % message)
+ return None
+ user, rhost = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def dovecot_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ service = 'imap(dc)'
+ mo = self.dovecot_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd dovecot OPEN string: %s' % message)
+ return None
+ user, rhost = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def dovecot_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ service = 'imap(dc)'
+ mo = self.dovecot_failure_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd dovecot FAILURE string: %s' % message)
+ return None
+ rhost = mo.group(1)
+ rhost = self.gethost(rhost)
+ user = 'unknown'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def courier_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.courier_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd courier OPEN string: %s' % message)
+ return None
+ service, user, rhost = mo.groups()
+ service = '%s(cr)' % service
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def courier_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.courier_failure_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd courier FAILURE string: %s' % message)
+ return None
+ service, rhost = mo.groups()
+ service = '%s(cr)' % service
+ rhost = self.gethost(rhost)
+ user = 'unknown'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def proftpd_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.proftpd_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd ProFTPD OPEN string: %s' % message)
+ return None
+ service = 'ftp(pro)'
+ rhost, user = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def proftpd_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.proftpd_failure_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd ProFTPD FAILURE string: %s' % message)
+ return None
+ service = 'ftp(pro)'
+ rhost, user = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def imp2_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.imp2_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd IMP failure string: %s' % message)
+ return None
+ rhost, system, user = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'IMP2'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def imp2_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.imp2_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd IMP open string: %s' % message)
+ return None
+ rhost, system, user = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'IMP2'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def imp3_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.imp3_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd IMP failure string: %s' % message)
+ return None
+ rhost, system, user = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'IMP3'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def imp3_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.imp3_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd IMP open string: %s' % message)
+ return None
+ user, rhost, system = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'IMP3'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def cyrus_failure(self,linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_cyrus_service(message)
+ mo = self.cyrus_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd cyrus FAILURE string: %s' % message)
+ return None
+ rhost, user = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def cyrus_open(self,linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ service = self._get_cyrus_service(message)
+ mo = self.cyrus_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd cyrus open string: %s' % message)
+ return None
+ rhost, user = mo.groups()
+ rhost = self.gethost(rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def qpopper_failure(self, linemap):
+ action = self.failure
+ system, message, mult = self.get_smm(linemap)
+ mo = self.qpopper_fail_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd qpopper FAILURE string: %s' % message)
+ return None
+ user, rhost = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'qpopper'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+
+ def qpopper_open(self, linemap):
+ action = self.open
+ system, message, mult = self.get_smm(linemap)
+ mo = self.qpopper_open_re.search(message)
+ if not mo:
+ self.logger.put(3, 'Odd qpopper open string: %s' % message)
+ return None
+ user, rhost = mo.groups()
+ rhost = self.gethost(rhost)
+ service = 'qpopper'
+ restuple = self._mk_restuple(action, system, service, user, '', rhost,
linemap['stamp'])
+ return {restuple: mult}
+ ##
+ # HELPER METHODS
+ #
+ def _mk_restuple(self, action, system, service, user, byuser, rhost, stamp):
+ if user == '': user = 'unknown'
+ if user == 'root' or user == 'ROOT':
+ action += 10
+ remote = self._mk_userat(byuser, rhost)
+ restuple = (action, system, service, remote, stamp)
+ else:
+ if rhost:
+ match = 0
+ for domain_re in self.safe_domains:
+ if domain_re.search(rhost):
+ match = 1
+ break
+ if not match:
+ tmp = {'system': system, 'rhost': rhost}
+ system = self.untrusted_host % tmp
+ restuple = (action, user, service, system, stamp)
+ return restuple
+
+ def _mk_dots(self, str, lim):
+ if len(str) > lim:
+ start = -(lim-2)
+ str = '..' + str[start:]
+ return str
+
+ def _get_pam_service(self, str):
+ service = 'unknown'
+ mo = self.pam_service_re.search(str)
+ if mo: service = mo.group(1)
+ return service
+
+ def _get_uw_imap_service(self, str):
+ service = 'unknown'
+ mo = self.uw_imap_service_re.search(str)
+ if mo: service = mo.group(1)
+ return service
+
+ def _mk_userat(self, user, host):
+ if user and host: userat = '%s@%s' % (user, host)
+ elif user: userat = user
+ elif host: userat = '@%s' % host
+ else: userat = 'unknown'
+ return userat
+
+ def _get_cyrus_service(self, str):
+ service = 'unknown'
+ mo = self.cyrus_service_re.search(str)
+ if mo: service = mo.group(1)
+ return service
+
+
+
+ def _check_for_login(self, username, service, hostname, tid):
+ # check if we have a login which matches in the db,
+
+ if not os.path.exists(self.logins_db):
+ return False
+
+ p = (username, service, hostname, self.last_entry)
+ q = "select * from logins where username=? and service=? and host=? and pkey
<= ?"
+ if not self.db_cx:
+ self._db_cx()
+
+ cur = self.db_cx.cursor()
+ ob = executeSQL(cur, q, p)
+ for i in ob:
+ # if we get any matches if they match within the fuzzed time then
+ # don't show it
+ if abs(tid - i[7]) <= self.time_fuzz*60:
+ return True
+ return False
+
+ def _add_login(self, username, action, hostname, timestamp, service, u_from=None):
+ if not self.db_cx:
+ self._db_cx()
+
+ cur = self.db_cx.cursor()
+ t_st = time.localtime(int(timestamp))
+ time_in_day = int(t_st[3]*60) + int(t_st[4])
+ q = "insert into logins values (NULL, ?, ?, ?, ?, ?, ?, ?)"
+ p = (action, username, hostname, u_from, service, timestamp, time_in_day)
+ cur.execute(q, p)
+
+
+ def _db_cx(self):
+ if not os.path.exists(self.logins_db):
+ self.db_cx = self._setup_logins_db()
+ else:
+ self.db_cx = sqlite.Connection(self.logins_db)
+
+ q = "select max(pkey) from logins"
+ last_e = executeSQL(self.db_cx.cursor(), q)
+ val = last_e.fetchone()[0]
+ if not val:
+ self.last_entry = 0
+ else:
+ self.last_entry = val
+
+ def _setup_logins_db(self):
+ schema = [
+ """PRAGMA synchronous="OFF";""",
+ """CREATE TABLE logins ( pkey INTEGER PRIMARY KEY, action INTEGER,
+ username TEXT, host TEXT, u_from TEXT, service TEXT,
+ stamp INTEGER, time_in_day INTEGER);""",
+ ]
+
+ cx = sqlite.Connection(self.logins_db)
+ cursor = cx.cursor()
+ for cmd in schema:
+ executeSQL(cursor, cmd)
+
+ return cx
+
+ ##
+ # FINALIZE!!
+ #
+ def finalize(self, rs):
+ logger = self.logger
+ ##
+ # Prepare report
+ #
+ report = ''
+ rep = {}
+
+ # FIXME
+ # go through each item in the rs
+ # feed them into the db
+ # pull back from the db all the info you need for the report
+ # simplifies a lot of this code
+
+ # chuck it all into the db
+ for (rt,count) in rs.items():
+ if rt[0] in (self.root_failure, self.root_open):
+ (action, host, service, remote, stamp) = rt
+ user = 'root'
+ elif rt[0] in (self.open, self.failure):
+ (action, user, service, host, stamp) = rt
+ remote = 'NULL'
+ else:
+ continue
+ if user in self.ignore_users:
+ continue
+ for num in range(0, count):
+ self._add_login(user, action, host, stamp, service, remote)
+ self.db_cx.commit()
+
+ #return "lalallala"
+
+ for action in [self.root_failure, self.root_open,
+ self.failure, self.open]:
+ rep[action] = ''
+ per_user = {}
+ flipper = ''
+ q = """select distinct username, service, host from logins
where action = ? and pkey > ?"""
+ p = (action, self.last_entry)
+ act_tuple = [(i[0],i[1], i[2]) for i in executeSQL(self.db_cx.cursor(), q,
p)]
+
+ for entry in act_tuple:
+ username = entry[0]
+ if username not in per_user:
+ per_user[username] = {}
+ service = entry[1]
+ if service not in per_user[username]:
+ per_user[username][service] = []
+ hn = entry[2]
+ q = """select time_in_day from logins where username = ?
and host = ? and service = ? and action = ? and pkey > ?"""
+ p = (username, hn, service, action, self.last_entry)
+
+ this_logins_times = [row[0] for row in executeSQL(self.db_cx.cursor(),
q, p)]
+ count = 0
+ for t in this_logins_times:
+ if not self._check_for_login(username, service, hn, t):
+ # DEBUG print 'new login %s %s %s %s' % (username,
service, hn, t)
+ count += 1
+
+ if count:
+ per_user[username][service].append('%s(%d)' % (hn, count))
+
+ blank = 0
+ for username in sorted(per_user):
+ if flipper: flipper = ''
+ else: flipper = self.flip
+ for (svc,reps) in per_user[username].items():
+ if blank: key = ' '
+ else: blank = 1
+ if reps:
+ rep[action] += self.line_rep % (flipper, username,
+ svc, ', '.join(reps))
+
+ if rep[self.root_failure]:
+ report += self.subreport_wrap % (self.root_failures_title,
+ rep[self.root_failure])
+ if rep[self.root_open]:
+ report += self.subreport_wrap % (self.root_logins_title,
+ rep[self.root_open])
+ if rep[self.failure]:
+ report += self.subreport_wrap % (self.user_failures_title,
+ rep[self.failure])
+ if rep[self.open]:
+ report += self.subreport_wrap % (self.user_logins_title,
+ rep[self.open])
+
+ report = self.report_wrap % report
+
+ if self.oldest_to_keep:
+ q = """delete from logins where stamp <
?"""
+ p = (self.oldest_to_keep,)
+ executeSQL(self.db_cx.cursor(), q, p)
+ self.db_cx.commit()
+
+ return report
+
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(logins_mod, sys.argv)
+
diff --git a/roles/epylog/files/modules/rsyncd.conf
b/roles/epylog/files/modules/rsyncd.conf
new file mode 100644
index 0000000..1f1bcd6
--- /dev/null
+++ b/roles/epylog/files/modules/rsyncd.conf
@@ -0,0 +1,14 @@
+[module]
+desc = Rsyncd
+exec = /usr/share/epylog/modules/rsyncd_mod.py
+files = /var/log/messages[.#]
+enabled = no
+internal = yes
+outhtml = yes
+priority = 7
+
+[conf]
+##
+# Report this many "top ranking hosts"
+#
+report_top = 10
diff --git a/roles/epylog/files/modules/rsyncd_mod.py
b/roles/epylog/files/modules/rsyncd_mod.py
new file mode 100644
index 0000000..5ec899c
--- /dev/null
+++ b/roles/epylog/files/modules/rsyncd_mod.py
@@ -0,0 +1,219 @@
+#!/usr/bin/python -tt
+"""
+Rsyncd log parsing module for Epylog
+"""
+
+##
+# Copyright (C) 2003 by Duke University
+# Written by Seth Vidal <skvidal at phy.duke.edu>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+
+
+import sys
+import re
+
+##
+# This is for testing purposes, so you can invoke this from the
+# modules directory. See also the testing notes at the end of the
+# file.
+#
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+class rsyncd_mod(InternalModule):
+ ##
+ # opts: is a map with extra options set in
+ # [conf] section of the module config, or on the
+ # command line using -o flag to the module.
+ # logger: A logging object. API:
+ # logger.put(loglvl, 'Message')
+ # Only critical stuff needs to go onto lvl 0.
+ # Common output goes to lvl 1.
+ # Others are debug levels.
+ #
+ def __init__(self, opts, logger):
+ ##
+ # Do a "super-init" so the class we are subclassing gets
+ # instantiated.
+ #
+ InternalModule.__init__(self)
+ self.logger = logger
+ ##
+ # Convenience
+ #
+ rc = re.compile
+
+ self.regex_map = {
+ rc('rsyncd\[\d+\]: rsync on'): self.rsync_hosts,
+ rc('rsyncd\[\d+\]: (?:sent|wrote)\s\S*\sbytes'): self.rsync_results
+ }
+ self.topcount = int(opts.get('report_top', 5)) #get report_top, default
to 5 if not set
+ ig_s = opts.get('ignore_hosts', '')
+ ig_s.replace(',',' ')
+ self.ignore_hosts = ig_s.split(' ')
+ # dict to store all of our data
+ self.rsync_pid_bytes = {}
+ self.rsync_pid_host = {}
+ self.rsync_host_loc = rc('rsyncd\[(\d+)\]:
rsync\son\s(\S*)\sfrom\s.*\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)')
+ self.rsync_bytes = rc('rsyncd\[(\d+)\]: (?:sent|wrote)\s(\d+) bytes
(?:read|received)\s(\d+) bytes total size (\d+)')
+
+ def rsync_hosts(self, linemap):
+ (sys, msg, multi) = self.get_smm(linemap)
+ pid, loc, ip = self.rsync_host_loc.search(msg).groups()
+ host = self.gethost(ip)
+ if host not in self.ignore_hosts:
+ self.rsync_pid_host[pid] = (host, loc)
+ return {(loc, host): 1}
+
+ def rsync_results(self, linemap):
+ (sys, msg, multi) = self.get_smm(linemap)
+ pid, wbytes, rbytes, tbytes = self.rsync_bytes.search(msg).groups()
+ self.rsync_pid_bytes[pid] = (wbytes, rbytes, tbytes)
+ return {(pid, wbytes): 1}
+
+ def _uniq(self, s):
+ """Return a list of the elements in s, but without duplicates.
+
+ For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
+ unique("abcabc") some permutation of ["a", "b",
"c"], and
+ unique(([1, 2], [2, 3], [1, 2])) some permutation of
+ [[2, 3], [1, 2]].
+
+ For best speed, all sequence elements should be hashable. Then
+ unique() will usually work in linear time.
+
+ If not possible, the sequence elements should enjoy a total
+ ordering, and if list(s).sort() doesn't raise TypeError it's
+ assumed that they do enjoy a total ordering. Then unique() will
+ usually work in O(N*log2(N)) time.
+
+ If that's not possible either, the sequence elements must support
+ equality-testing. Then unique() will usually work in quadratic
+ time.
+ """
+
+ n = len(s)
+ if n == 0:
+ return []
+
+ # Try using a dict first, as that's the fastest and will usually
+ # work. If it doesn't work, it will usually fail quickly, so it
+ # usually doesn't cost much to *try* it. It requires that all the
+ # sequence elements be hashable, and support equality comparison.
+ u = {}
+ try:
+ for x in s:
+ u[x] = 1
+ except TypeError:
+ del u # move on to the next method
+ else:
+ return u.keys()
+
+ # We can't hash all the elements. Second fastest is to sort,
+ # which brings the equal elements together; then duplicates are
+ # easy to weed out in a single pass.
+ # NOTE: Python's list.sort() was designed to be efficient in the
+ # presence of many duplicate elements. This isn't true of all
+ # sort functions in all languages or libraries, so this approach
+ # is more effective in Python than it may be elsewhere.
+ try:
+ t = list(s)
+ t.sort()
+ except TypeError:
+ del t # move on to the next method
+ else:
+ assert n > 0
+ last = t[0]
+ lasti = i = 1
+ while i < n:
+ if t[i] != last:
+ t[lasti] = last = t[i]
+ lasti += 1
+ i += 1
+ return t[:lasti]
+
+ # Brute force is all that's left.
+ u = []
+ for x in s:
+ if x not in u:
+ u.append(x)
+ return u
+
+ def _sortByVal(self, dict, reverse=0):
+ if type(dict) is not type({}): return []
+ keys = dict.keys()
+ s = map(lambda k: (dict[k], k), keys)
+ s.sort()
+ if reverse: s.reverse()
+ return s
+
+ def finalize(self, resultset):
+ ##
+ # A resultset is a dictionary of all values returned by your
+ # handler functions -- except they are unique and show how many
+ # times each tuple occurs.
+ # See epylog.Result for some convenience methods to use when
+ # processing and analyzing the results.
+ #
+
+ hostloc = {} # key = host, val = [loc, loc, loc]
+ hosttotal = {} # key = host val = totalwbytes
+
+ foo = "<table border=0>\n\t<tr>\n"
+
+ for pid in self.rsync_pid_host.keys():
+ (host, loc) = self.rsync_pid_host[pid]
+ if self.rsync_pid_bytes.has_key(pid):
+ if not hostloc.has_key(host):
+ hostloc[host] = []
+ if not hosttotal.has_key(host):
+ hosttotal[host] = 0L
+ hostloc[host].append(loc)
+ bytes = long(self.rsync_pid_bytes[pid][0])
+ hosttotal[host] += bytes
+
+ for host in hostloc.keys():
+ hostloc[host] = self._uniq(hostloc[host])
+
+ hosts = self._sortByVal(hosttotal, 1)
+ count = 0L
+ for (tot,host) in hosts[:self.topcount]:
+ if count % 2:
+ bgcolor = "#dddddd"
+ else:
+ bgcolor = "#ffffff"
+ count+=1
+ line = '\t\t<td bgcolor=%s
valign=\"top\">%s</td>\n' % (bgcolor, host)
+ line = line + '\t\t<td bgcolor=%s valign="top">\n' %
bgcolor
+ for loc in hostloc[host]:
+ line = line + '\t\t\t%s<br>\n' % loc
+ line = line + '\t\t</td>\n'
+ size, marker = self.mk_size_unit(hosttotal[host])
+ line = line + '\t\t<td bgcolor=%s
valign="top">%s%s</td>\n' % (bgcolor, size, marker)
+ line = line + '\t</tr>\n'
+ foo = foo + line
+ foo = foo + '</table>\n'
+ return foo
+
+##
+# This is useful when testing your module out.
+# Invoke without command-line parameters to learn about the proper
+# invocation.
+#
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(rsyncd_mod, sys.argv)
diff --git a/roles/epylog/files/modules/selinux.conf
b/roles/epylog/files/modules/selinux.conf
new file mode 100644
index 0000000..49a41c1
--- /dev/null
+++ b/roles/epylog/files/modules/selinux.conf
@@ -0,0 +1,11 @@
+[module]
+desc = SELinux Report
+exec = /usr/share/epylog/modules/selinux_mod.py
+files = /var/log/messages[.#]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+enable_selinux = 1
diff --git a/roles/epylog/files/modules/selinux_mod.py
b/roles/epylog/files/modules/selinux_mod.py
new file mode 100644
index 0000000..0e40b20
--- /dev/null
+++ b/roles/epylog/files/modules/selinux_mod.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python -tt
+"""
+Reports on selinux messages
+
+Jeremy Kindy (kindyjd at
wfu.edu), Wake Forest University
+"""
+
+import sys
+import re
+
+##
+# This is for testing purposes, so you can invoke this from the
+# modules directory. See also the testing notes at the end of the
+# file.
+#
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+class selinux_mod(InternalModule):
+ def __init__(self, opts, logger):
+ InternalModule.__init__(self)
+ self.logger = logger
+ self.logger.put(3, 'initializing selinux')
+ rc = re.compile
+
+ self.ignore = 0
+ self.preventing = 1
+
+ selinux_map = {
+ rc('.*setroubleshoot\: SELinux is preventing'): self.selinux
+ }
+
+ do_selinux = int(opts.get('enable_selinux', '1'))
+
+ self.regex_map = {}
+ if do_selinux: self.regex_map.update(selinux_map)
+
+ self.selinux_message_re = rc('setroubleshoot: (.*). For complete
SELinux')
+
+ self.selinux_title = '<font color="blue">SELinux
Report</font>'
+ self.selinux_preventing_title = '<font color="blue">SELinux
Prevention Report</font>'
+
+ self.report_wrap = '<table border="0" width="100%%"
rules="cols" cellpadding="2">%s</table>'
+ self.subreport_wrap = '<tr><th colspan="2"
align="left"><h3>%s</h3></th></tr>\n%s'
+
+ self.line_rep = '<tr%s><td valign="top"
width="25%%">%s</td><td valign="top"
width="75%%">%s</td></tr>\n'
+
+ self.flip = ' bgcolor="#dddddd"'
+
+
+ ##
+ # Line-matching routines
+ #
+ def selinux(self, linemap):
+ action = self.preventing
+ self.logger.put(3, 'selinux invoked')
+ sys, msg, mult = self.get_smm(linemap)
+
+ self.logger.put(3, 'test selinux %d' % mult)
+ message = self._get_selinux_message(msg)
+ self.logger.put(3, 'selinux message: %s' % message)
+
+ restuple = self._mk_restuple(sys, action, message)
+ self.logger.put(3, 'selinux finished')
+ return {restuple: mult}
+
+
+ ##
+ # Helpers
+ #
+ def _mk_restuple(self, sys, action, message):
+ return (action, message, sys)
+
+ def _get_selinux_message(self, str):
+ message = 'unknown'
+ mo = self.selinux_message_re.search(str)
+ if mo: message = mo.group(1)
+ return message
+
+
+ ####
+ # Finalize the report
+ def finalize(self, rs):
+ logger = self.logger
+ ##
+ # Prepare report
+ #
+ report = ''
+ rep = {}
+
+ # (action, message)
+ for action in [self.preventing]:
+ rep[action] = ''
+ flipper = ''
+ for message in rs.get_distinct((action,)):
+ if flipper: flipper = ''
+ else: flipper = self.flip
+ service_rep = []
+
+ for system in rs.get_distinct((action, message,)):
+ service_rep.append(system)
+
+ system_list = ', '.join(service_rep)
+ rep[action] += self.line_rep % (flipper, message, system_list)
+
+ if rep[self.preventing]:
+ report += self.subreport_wrap % (self.selinux_preventing_title,
rep[self.preventing])
+ logger.put(3, 'selinux report: self.preventing added')
+
+ report = self.report_wrap % report
+ return report
+
+
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(selinux_mod, sys.argv)
diff --git a/roles/epylog/files/modules/sudo.conf b/roles/epylog/files/modules/sudo.conf
new file mode 100644
index 0000000..843f4c2
--- /dev/null
+++ b/roles/epylog/files/modules/sudo.conf
@@ -0,0 +1,11 @@
+[module]
+desc = Sudo Report
+exec = /usr/share/epylog/modules/sudo_mod.py
+files = /var/log/secure[.#]
+enabled = yes
+internal = yes
+outhtml = yes
+priority = 5
+
+[conf]
+enable_sudo = 1
diff --git a/roles/epylog/files/modules/sudo_mod.py
b/roles/epylog/files/modules/sudo_mod.py
new file mode 100644
index 0000000..d411b9f
--- /dev/null
+++ b/roles/epylog/files/modules/sudo_mod.py
@@ -0,0 +1,191 @@
+#!/usr/bin/python -tt
+"""
+Reports on sudo usage by users.
+
+Jeremy Kindy (kindyjd at
wfu.edu), Wake Forest University
+"""
+
+import sys
+import re
+
+##
+# This is for testing purposes, so you can invoke this from the
+# modules directory. See also the testing notes at the end of the
+# file.
+#
+sys.path.insert(0, '../py/')
+from epylog import Result, InternalModule
+
+class sudo_mod(InternalModule):
+ def __init__(self, opts, logger):
+ InternalModule.__init__(self)
+ self.logger = logger
+ self.logger.put(2, 'initializing sudo')
+ rc = re.compile
+
+ self.ignore = 0
+ self.open = 1
+ self.not_allowed = 2
+
+ sudo_map = {
+ rc('.*sudo\:\s+\S+\s\:\sTTY'): self.sudo,
+ rc('.*sudo:'): self.sudo_na
+ }
+
+ do_sudo = int(opts.get('enable_sudo', '1'))
+
+ self.regex_map = {}
+ if do_sudo: self.regex_map.update(sudo_map)
+
+ self.sudo_user_name_re = rc('sudo:\s*(\S*)')
+ self.sudo_as_user_re = rc('.*USER=(\S*)\s\;\sCOMMAND')
+ self.sudo_command_name_re = rc('.*COMMAND=(.*)')
+ self.sudo_error_message_re = rc('sudo:\s*\S*\s+:\s+(.*)\s+;\s+TTY')
+
+
+ self.sudo_title = '<font color="blue">User Sudo
Report</font>'
+ self.sudo_open_title = '<font color="blue">User Sudo
Report</font>'
+ self.sudo_not_allowed_title = '<font color="red">Disallowed
Sudo Commands</font>'
+
+ self.report_wrap = '<table border="0" width="100%%"
rules="cols" cellpadding="2">%s</table>'
+ self.subreport_wrap = '<tr><th colspan="5"
align="left"><h3>%s</h3></th></tr>\n%s'
+ self.subreport_na_wrap = '<tr><th colspan="5"
align="left"><h3>%s</h3></th></tr>\n%s'
+
+ self.line_rep = '<tr%s><td valign="top"
width="15%%">%s</td><td valign="top"
width="45%%" colspan="2">%s</td><td
width="25%%">%s</td><td
width="15%%">%s</td></tr>\n'
+ self.line_rep_na = '<tr%s><td valign="top"
width="15%%">%s</td><td valign="top"
width="30%%">%s</td><td valign="top"
width="15%%">%s</td><td
width="25%%">%s</td><td
width="15%%">%s</td></tr>\n'
+
+ self.flip = ' bgcolor="#dddddd"'
+
+
+ ##
+ # Line-matching routines
+ #
+ def sudo(self, linemap):
+ action = self.open
+ self.logger.put(2, 'sudo invoked')
+ sys, msg, mult = self.get_smm(linemap)
+
+ self.logger.put(3, 'test sudo %d' % mult)
+ user = self._get_sudo_user(msg)
+ self.logger.put(3, 'sudo user: %s' % user)
+ asuser = self._get_sudo_as_user(msg)
+ self.logger.put(3, 'sudo asuser: %s' % asuser)
+ command_name = self._get_sudo_command_name(msg)
+ self.logger.put(3, 'sudo command: %s' % command_name)
+
+ restuple = self._mk_restuple(sys, action, user, asuser, command_name, None)
+ self.logger.put(2, 'sudo finished')
+ return {restuple: mult}
+
+ def sudo_na(self, linemap):
+ action = self.not_allowed
+ self.logger.put(2, 'sudo_na invoked')
+ sys, msg, mult = self.get_smm(linemap)
+
+ self.logger.put(3, 'test sudo %d' % mult)
+ user = self._get_sudo_user(msg)
+ self.logger.put(3, 'sudo user: %s' % user)
+ asuser = self._get_sudo_as_user(msg)
+ self.logger.put(3, 'sudo asuser: %s' % asuser)
+ command_name = self._get_sudo_command_name(msg)
+ self.logger.put(3, 'sudo command: %s' % command_name)
+ error_message = self._get_sudo_error_message(msg)
+ self.logger.put(3, 'sudo error_message: %s' % error_message)
+
+ restuple = self._mk_restuple(sys, action, user, asuser, command_name,
error_message)
+ self.logger.put(2, 'sudo finished')
+ return {restuple: mult}
+
+ def sudo_ignore(self, linemap):
+ restuple = self._mk_restuple(None, self.ignore, None, None, None, None)
+ return {restuple: 1}
+
+ ##
+ # Helpers
+ #
+ def _mk_restuple(self, sys, action, user=None, asuser=None, command_name=None,
error_message=None):
+ return (action, user, command_name, asuser, error_message, sys)
+ #return (sys, action, user, asuser, command_name)
+
+ def _get_sudo_user(self, str):
+ user = 'unknown'
+ mo = self.sudo_user_name_re.search(str)
+ if mo: user = mo.group(1)
+ return user
+
+ def _get_sudo_as_user(self, str):
+ asuser = 'unknown'
+ mo = self.sudo_as_user_re.search(str)
+ if mo: asuser = mo.group(1)
+ return asuser
+
+ def _get_sudo_error_message(self, str):
+ pass_attempts = 0
+ mo = self.sudo_error_message_re.search(str)
+ if mo: pass_attempts = mo.group(1)
+ return pass_attempts
+
+ def _get_sudo_command_name(self, str):
+ command_name = 'unknown'
+ mo = self.sudo_command_name_re.search(str)
+ if mo: command_name = mo.group(1)
+ return command_name
+
+
+ ####
+ # Finalize the report
+ def finalize(self, rs):
+ logger = self.logger
+ ##
+ # Prepare report
+ #
+ report = ''
+ rep = {}
+
+ # (action, user, command_name, system, error_message)
+ for action in [self.open, self.not_allowed]:
+ rep[action] = ''
+ flipper = ''
+ for user in rs.get_distinct((action,)):
+ #logger.put(2, 'sudo user: %s' % user)
+ if flipper: flipper = ''
+ else: flipper = self.flip
+ service_rep = []
+ blank = 0
+ for command_name in rs.get_distinct((action, user)):
+ for asuser in rs.get_distinct((action, user, command_name)):
+ for error_message in rs.get_distinct((action, user, command_name,
asuser)):
+ mymap = rs.get_submap((action, user, command_name, asuser,
error_message))
+ #logger.put(2, 'sudo command_name: %s' %
command_name)
+ key2s = []
+ for key2 in mymap.keys():
+ hostname = key2[0]
+ key2s.append('%s(%d)' % (hostname, mymap[key2]))
+ hostnames = ', '.join(key2s)
+ #logger.put(2, 'sudo hostnames: %s' % hostnames)
+ service_rep.append([command_name, hostnames, asuser,
error_message])
+ for svcrep in service_rep:
+ #logger.put(2, 'sudo svcrep: %s' % svcrep)
+ if blank: user = ' '
+ else: blank = 1
+ if (action == self.open):
+ rep[action] += self.line_rep % (flipper, user, svcrep[0],
svcrep[1], svcrep[2])
+ else:
+ rep[action] += self.line_rep_na % (flipper, user, svcrep[0],
svcrep[3], svcrep[1], svcrep[2])
+
+
+ if rep[self.open]:
+ report += self.subreport_wrap % (self.sudo_open_title, rep[self.open])
+ logger.put(2, 'sudo report: self.open added')
+
+ if rep[self.not_allowed]:
+ report += self.subreport_na_wrap % (self.sudo_not_allowed_title,
rep[self.not_allowed])
+ logger.put(2, 'sudo report: self.not_allowed added')
+
+ report = self.report_wrap % report
+ return report
+
+
+if __name__ == '__main__':
+ from epylog.helpers import ModuleTest
+ ModuleTest(sudo_mod, sys.argv)
diff --git a/roles/epylog/tasks/main.yml b/roles/epylog/tasks/main.yml
new file mode 100644
index 0000000..2e206c5
--- /dev/null
+++ b/roles/epylog/tasks/main.yml
@@ -0,0 +1,51 @@
+---
+- name: install epylog package
+ yum: state=present name=epylog
+ tags:
+ - packages
+ - epylog
+
+- name: create directories needed for epylog
+ file: state=directory path={{ item }} owner=root mode=755
+ with_items:
+ - /var/lib/epylog/merged
+ - /srv/web/epylog
+ - /srv/web/epylog/merged
+
+- name: setup extra epylog modules
+ copy: src={{ item }} dest=/usr/share/epylog/{{ item }}
+ with_items:
+ - modules/selinux_mod.py
+ - modules/rsyncd_mod.py
+ - modules/sudo_mod.py
+ - modules/common_unparsed_mod.py
+ - modules/logins_mod.py
+ - modules/kojiload_mod.py
+
+- name: epylog cron
+ copy: src=epylog-default.cron dest=/etc/{{ item }}/epylog.cron
+ with_items:
+ - cron.d
+ - cron.daily
+ tags:
+ - config
+ - cron
+
+- name: copy in all the epylog merged files
+ copy: src=merged dest=/etc/epylog/
+ tags:
+ - config
+
+- name: epylog merged cron
+ copy: src=epylog-merged.cron dest=/etc/cron.d/epylog-merged.cron
+ tags:
+ - config
+ - cron
+
+- name: epylog httpd config
+ copy: src=epylog-web.conf dest=/etc/httpd/conf.d/epylog-web.conf
+ notify:
+ - reload httpd
+ tags:
+ - config
+ - apache