root / plugins / postfix / greyfix @ 17f78427
Historique | Voir | Annoter | Télécharger (5,49 ko)
| 1 |
#!/usr/bin/env python |
|---|---|
| 2 |
#%# family=auto |
| 3 |
#%# capabilities=autoconf |
| 4 |
|
| 5 |
""" |
| 6 |
Munin plugin that monitors the greyfix triplet database. |
| 7 |
Author: Tom Hendrikx <tom@whyscream.net> 2011-08-05 |
| 8 |
|
| 9 |
This plugin monitors the Greyfix greylisting tool for Postfix. |
| 10 |
It creates a nice overview of the number of triplets in the greyfix database, |
| 11 |
dividing the database population in configurable ages. |
| 12 |
|
| 13 |
For more information about greyfix: http://trac.kim-minh.com/greyfix/ |
| 14 |
|
| 15 |
Installation |
| 16 |
============ |
| 17 |
|
| 18 |
To install, create a symlink to the plugin in the munin plugin directory: |
| 19 |
$ ln -s /path/to/plugin/greyfix /etc/munin/plugins/greyfix |
| 20 |
|
| 21 |
Configuration |
| 22 |
============= |
| 23 |
|
| 24 |
There are some settings that can be tweaked by adding statements to the |
| 25 |
munin-node config: |
| 26 |
|
| 27 |
[greyfix] |
| 28 |
# run plugin as the same user as postfix does |
| 29 |
user nobody |
| 30 |
# path to greyfix binary (default: /usr/sbin/greyfix) |
| 31 |
env.greyfix /usr/local/sbin/greyfix |
| 32 |
# the length of each graph step in days (default: 7) |
| 33 |
env.step_size 3 |
| 34 |
# the number of steps to graph (default: 11) |
| 35 |
env.num_steps 47 |
| 36 |
# graph the greylisted triplets separate from the whitelisted ones (default: yes) |
| 37 |
env.greylist_step no |
| 38 |
|
| 39 |
Please note that the last step has no end date, so it includes all triplets |
| 40 |
older than the second last step. I.e., the defaults (as named above) create a |
| 41 |
graph that shows 10 steps of one week each, and one last step for everything |
| 42 |
older than 10 weeks. Also, the separate greylist step is not considered |
| 43 |
when applying num_steps. |
| 44 |
|
| 45 |
""" |
| 46 |
|
| 47 |
# Settings: all of these can be redefined in the munin-node config |
| 48 |
settings = {
|
| 49 |
'greyfix': '/usr/sbin/greyfix', |
| 50 |
'step_size': 7, |
| 51 |
'num_steps': 11, |
| 52 |
'greylist_step': 'yes' |
| 53 |
} |
| 54 |
|
| 55 |
|
| 56 |
import os |
| 57 |
import sys |
| 58 |
import subprocess |
| 59 |
import datetime |
| 60 |
|
| 61 |
|
| 62 |
def greyfix_parse_triplets(): |
| 63 |
"""Parse output of greyfix --dump-triplets into something that we can use. |
| 64 |
|
| 65 |
The output of the command has the following columns: |
| 66 |
sender_ip, sender_email, recipient_email, timestamp_first_seen, timestamp_last_seen, block_count, pass_count |
| 67 |
|
| 68 |
Also see http://groups.google.com/group/greyfix/browse_thread/thread/510687a9ed94fc2c""" |
| 69 |
|
| 70 |
greyfix = subprocess.Popen(args=[ settings['greyfix'], '--dump-triplets'], stdout=subprocess.PIPE) |
| 71 |
stdout = greyfix.communicate()[0] |
| 72 |
if greyfix.returncode > 0: |
| 73 |
print '# greyfix exited with exit code %i' % (greyfix.returncode) |
| 74 |
sys.exit(greyfix.returncode) |
| 75 |
|
| 76 |
triplets = [] |
| 77 |
for line in stdout.split("\n"):
|
| 78 |
triplet = line.split("\t")
|
| 79 |
if len(triplet) == 7: |
| 80 |
triplet[3] = datetime.datetime.strptime(triplet[3], '%c') |
| 81 |
triplet[4] = datetime.datetime.strptime(triplet[4], '%c') |
| 82 |
triplet[5] = int(triplet[5]) |
| 83 |
triplet[6] = int(triplet[6]) |
| 84 |
triplets.append(triplet) |
| 85 |
|
| 86 |
return triplets |
| 87 |
|
| 88 |
|
| 89 |
def convert_step_to_days(step): |
| 90 |
"""Compute the days that are contained in a step, according to the configuration""" |
| 91 |
|
| 92 |
start = settings['step_size'] * step |
| 93 |
end = (settings['step_size'] * (step + 1)) - 1 |
| 94 |
if step >= (settings['num_steps'] -1): |
| 95 |
return (start, '') |
| 96 |
else: |
| 97 |
return (start, end) |
| 98 |
|
| 99 |
|
| 100 |
def print_fetch(): |
| 101 |
"""Generates and prints the values as retrieved from greyfix.""" |
| 102 |
|
| 103 |
triplets = greyfix_parse_triplets() |
| 104 |
now = datetime.datetime.now() |
| 105 |
steps = [0 for i in range(0, settings['num_steps'])] |
| 106 |
greylist_step = 0 |
| 107 |
|
| 108 |
for triplet in triplets: |
| 109 |
if settings['greylist_step'] == 'yes' and triplet[6] == 0: |
| 110 |
greylist_step = greylist_step +1 |
| 111 |
continue; |
| 112 |
|
| 113 |
delta = now - triplet[3] |
| 114 |
step = delta.days / settings['step_size'] |
| 115 |
step = min(step, settings['num_steps'] -1) |
| 116 |
# count the number of triplets in a group |
| 117 |
steps[ step ] = steps[ step ] +1 |
| 118 |
|
| 119 |
# print result counts for each group |
| 120 |
if settings['greylist_step'] == 'yes': |
| 121 |
print 'gl.value %i' % greylist_step |
| 122 |
for step, count in enumerate(steps): |
| 123 |
fieldname = 'd%s_%s' % convert_step_to_days(step) |
| 124 |
print '%s.value %i' % (fieldname, count) |
| 125 |
|
| 126 |
|
| 127 |
def print_config(): |
| 128 |
"""Generates and prints a munin config for a given chart.""" |
| 129 |
|
| 130 |
print 'graph_title Greyfix triplets by age' |
| 131 |
print 'graph_vlabel Number of triplets' |
| 132 |
print 'graph_info The amount of triplets in the greyfix database with a certain age' |
| 133 |
print 'graph_category mail' |
| 134 |
print 'graph_total All triplets' |
| 135 |
print 'graph_args --lower-limit 0 --base 1000' |
| 136 |
|
| 137 |
if settings['greylist_step'] == 'yes': |
| 138 |
print 'gl.label Greylisted' |
| 139 |
print 'gl.info The number of greylisted triplets. These did not have a single pass (yet)' |
| 140 |
print 'gl.draw AREASTACK' |
| 141 |
print 'gl.colour aaaaaa' |
| 142 |
|
| 143 |
steps = range(0, settings['num_steps']) |
| 144 |
for step in steps: |
| 145 |
days = convert_step_to_days(step) |
| 146 |
|
| 147 |
fieldname = 'd%s_%s' % days |
| 148 |
label = 'Whitelisted for %s - %s days' % (days[0], days[1] if days[1] != '' else '...') |
| 149 |
info = 'The number of whitelisted triplets that is between %s and %s days old' % (days[0], days[1] if days[1] != '' else '...') |
| 150 |
|
| 151 |
print '%s.label %s' % (fieldname, label) |
| 152 |
print '%s.info %s' % (fieldname, info) |
| 153 |
print '%s.draw AREASTACK' % fieldname |
| 154 |
|
| 155 |
|
| 156 |
if __name__ == '__main__': |
| 157 |
|
| 158 |
# read settings from config file / environment |
| 159 |
for key in settings.iterkeys(): |
| 160 |
if key in os.environ: |
| 161 |
if os.environ[ key ].isdigit(): |
| 162 |
settings[ key ] = int(os.environ[ key ]) |
| 163 |
else: |
| 164 |
settings[ key ] = os.environ[ key ] |
| 165 |
#print '# setting %s updated to: %s' % (key, settings[ key ]) |
| 166 |
|
| 167 |
commands = ['fetch', 'autoconf', 'config'] |
| 168 |
if len(sys.argv) > 1: |
| 169 |
command = sys.argv[1] |
| 170 |
else: |
| 171 |
command = commands[0] |
| 172 |
|
| 173 |
if command not in commands: |
| 174 |
print '# command %s unknown, choose one of: %s' % (command, commands) |
| 175 |
sys.exit(1) |
| 176 |
|
| 177 |
if command == 'fetch': |
| 178 |
print_fetch() |
| 179 |
elif command == 'config': |
| 180 |
print_config() |
| 181 |
elif command == 'autoconf': |
| 182 |
if os.path.exists( settings['greyfix'] ): |
| 183 |
print 'yes' |
| 184 |
else: |
| 185 |
print 'no (binary not found at %s)' % settings['greyfix'] |
