Skip to content

Commit

Permalink
Merge pull request #448 from sebix/bugfixes2
Browse files Browse the repository at this point in the history
RT Collector, pip installation, logcheck rules
  • Loading branch information
sebix committed Feb 16, 2016
2 parents c6929d6 + d1511ec commit 9e03bc8
Show file tree
Hide file tree
Showing 27 changed files with 243 additions and 88 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ dist
htmlcov/
*.pem
*.key
src/
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install -r REQUIREMENTS2; fi
- if [[ ${TRAVIS_PYTHON_VERSION%.?} == 3 ]]; then pip install -r REQUIREMENTS; fi
- for file in intelmq/bots/*/*/REQUIREMENTS.txt; do pip install -r $file; done
- "python setup_travis.py install"
- pip install coveralls
- pip install codecov
# command to run tests
- pip install .
script: nosetests --with-coverage --cover-package=intelmq
services:
- redis-server

after_success:
- codecov
- coveralls
3 changes: 0 additions & 3 deletions MANIFEST

This file was deleted.

8 changes: 8 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
exclude .gitignore
exclude .pep8
exclude .travis.yml
graft contrib
graft docs
include COPYRIGHT
include LICENSE
include README.md
6 changes: 6 additions & 0 deletions contrib/logcheck/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# logcheck ruleset

logcheck is a simple and effective log monitoring tool checking for known and
unknown patterns in the logfiles. The given rules do ignore all messages with
loglevels INFO and DEBUG, and parts of tracebacks. ERROR and CRITICAL are
treated as violations, WARNINGS are thus normal irregular log messages.
8 changes: 8 additions & 0 deletions contrib/logcheck/ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
^[0-9-]{10} [0-9:,]{12} - [a-zA-Z0-9_-]* - (INFO|DEBUG) -
^
^Traceback
^[a-zA-Z]*Error
^InvalidValue
^During handling of the above exception, another exception occurred:$
^intelmq\.
^redis\.
1 change: 1 addition & 0 deletions contrib/logcheck/logcheck.logfiles
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/opt/intelmq/var/log/*.log
1 change: 1 addition & 0 deletions contrib/logcheck/violations
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
^[0-9-]{10} [0-9:,]{12} - [a-zA-Z0-9_-]* - (ERROR|CRITICAL) -
6 changes: 6 additions & 0 deletions docs/Developers-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ How do you test if things are easy? Let them new programmers test-drive your fea

Similarly, if code does not get accepted upstream by the main developers, it is usually only because of the ease-of-use argument. Do not give up , go back to the drawing board, and re-submit again.

## Installation

Install intelmq with `pip -e`, which gives you a so called *editable* installation. All changed files in the local copy are directly changed in the local installation too!

pip install -e .

## Testing

All changes have to be tested and new contributions must must be accompanied by according tests. You can run the tests by changing to the directory with intelmq repository and running either `unittest` or `nosetests`:
Expand Down
4 changes: 2 additions & 2 deletions docs/User-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ cd /tmp/intelmq
sudo -s

pip3 install -r REQUIREMENTS
python3 setup.py install
pip3 install .

useradd -d /opt/intelmq -U -s /bin/bash intelmq
echo 'export PATH="$PATH:$HOME/bin"' > /opt/intelmq/.profile
Expand All @@ -138,7 +138,7 @@ git clone https://github.com/certtools/intelmq.git /tmp/intelmq
cd /tmp/intelmq

pip2 install -r REQUIREMENTS2
python2.7 setup.py install
pip2 install .

useradd -d /opt/intelmq -U -s /bin/bash intelmq
echo 'export PATH="$PATH:$HOME/bin"' > /opt/intelmq/.profile
Expand Down
28 changes: 25 additions & 3 deletions intelmq/bots/BOTS
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,28 @@
"rate_limit": 28800
}
},
"Request Tracker": {
"description": "Request Tracker Collector fetches attachments from an RTIR instance and optionally decrypts them with gnupg.",
"module": "intelmq.bots.collectors.rt.collector_rt",
"parameters": {
"attachment_regex": "\\.csv\\.zip$",
"feed": "Request Tracker",
"gnupg_decrypt": false,
"gnupg_homedir": "/opt/intelmq/",
"gnupg_passphrase": null,
"gnupg_trust": true,
"password": "password",
"rate_limit": 3600,
"search_owner": "nobody",
"search_queue": "Incident Reports",
"search_status": "new",
"search_subject_like": "Report",
"take_ticket": true,
"unzip_attachment": true,
"uri": "http://localhost/rt/REST/1.0",
"user": "root"
}
},
"ShadowServer Chargen": {
"description": "ShadowServer Chargen Collector is the bot responsible to get the report from source of information. https://www.shadowserver.org/wiki/pmwiki.php/Services/Open-Chargen",
"module": "intelmq.bots.collectors.mail.mail-url",
Expand Down Expand Up @@ -495,11 +517,11 @@
}
},
"Spamhaus CERT": {
"description": "Spamhaus CERT Collector is the bot responsible to get the report from source of information.",
"description": "Spamhaus CERT Insight Portal. Access limited to CERTs and CSIRTs with national or regional responsibility. https://www.spamhaus.org/news/article/705/spamhaus-launches-cert-insight-portal",
"module": "intelmq.bots.collectors.http.collector_http",
"parameters": {
"feed": "Spamhaus CERT",
"http_url": "https://portal.spamhaus.org/cert/api.php?cert=<CERTNAME>&key=<APIKEY>",
"http_url": "<your CERT portal URL>",
"rate_limit": 3600
}
},
Expand All @@ -508,7 +530,7 @@
"module": "intelmq.bots.collectors.http.collector_http",
"parameters": {
"feed": "Spamhaus Drop",
"http_url": "https://www.spamhaus.org/drop/drop.lasso",
"http_url": "https://www.spamhaus.org/drop/drop.txt",
"rate_limit": 3600
}
},
Expand Down
1 change: 1 addition & 0 deletions intelmq/bots/collectors/rt/REQUIREMENTS.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-e git://git.nic.cz/python-rt.git@52e74e590b27605f33923c54b552ec00ece85a6c#egg=rt
Empty file.
87 changes: 87 additions & 0 deletions intelmq/bots/collectors/rt/collector_rt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
"""
TODO: Arbitrary order of decrypt and unzip
"""
from __future__ import unicode_literals

import io
import re
import sys
import zipfile

from intelmq.lib.bot import Bot
from intelmq.lib.harmonization import DateTime
from intelmq.lib.message import Report

import rt

try:
import gnupg
except ImportError:
gnupg = None


class RTCollectorBot(Bot):

def init(self):
if self.parameters.gnupg_decrypt:
if gnupg is None:
raise ValueError('gnupg module is not available')
self.gpg = gnupg.GPG(gnupghome=self.parameters.gnupg_homedir)

def process(self):
RT = rt.Rt(self.parameters.uri, self.parameters.user,
self.parameters.password)
if not RT.login():
raise ValueError('Login failed.')

query = RT.search(Queue=self.parameters.search_queue,
Subject__like=self.parameters.search_subject_like,
Owner=self.parameters.search_owner,
Status=self.parameters.search_status)
self.logger.info('{} results on search query.'.format(len(query)))

for ticket in query:
ticket_id = int(ticket['id'].split('/')[1])
self.logger.debug('Process ticket {}.'.format(ticket_id))
for (att_id, att_name, _, _) in RT.get_attachments(ticket_id):
if re.search(self.parameters.attachment_regex, att_name):
self.logger.debug('Found attachment {}: {!r}.'
''.format(att_id, att_name))
break
else:
self.logger.debug('No matching attachement name found.')
continue
attachment = RT.get_attachment_content(ticket_id, att_id)

if self.parameters.unzip_attachment:
file_obj = io.BytesIO(attachment)
zipped = zipfile.ZipFile(file_obj)
raw = zipped.read(zipped.namelist()[0])
else:
raw = attachment

if self.parameters.gnupg_decrypt:
raw = str(self.gpg.decrypt(raw,
always_trust=self.parameters.gnupg_trust,
passphrase=self.parameters.gnupg_passphrase))
self.logger.info('Successfully decrypted attachment.')

self.logger.debug(raw)
report = Report()
report.add("raw", raw, sanitize=True)
report.add("rtir_id", ticket_id, sanitize=True)
report.add("feed.name", self.parameters.feed, sanitize=True)
report.add("feed.accuracy", self.parameters.accuracy,
sanitize=True)
time_observation = DateTime().generate_datetime_now()
report.add('time.observation', time_observation, sanitize=True)
self.send_message(report)

if self.parameters.take_ticket:
RT.edit_ticket(ticket_id, Owner=self.parameters.user)


if __name__ == "__main__":
bot = RTCollectorBot(sys.argv[1])
bot.start()
4 changes: 4 additions & 0 deletions intelmq/conf/harmonization.conf
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@
"length": 500000000,
"type": "Base64"
},
"rtir_id": {
"description": "Request Tracker Incident Response incident id.",
"type": "Integer"
},
"time.observation": {
"description": "The time a source bot saw the event. This timestamp becomes especially important should you perform your own attribution on a host DNS name for example. The mechanism to denote the attributed elements with reference to the source provided is detailed below in Reported Identity IOC.(ISO8660).",
"type": "DateTime"
Expand Down
7 changes: 4 additions & 3 deletions intelmq/lib/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
from intelmq.lib.pipeline import PipelineFactory


__all__ = ['Bot']


class Bot(object):

def __init__(self, bot_id):
Expand Down Expand Up @@ -48,9 +51,8 @@ def __init__(self, bot_id):
syslog = self.parameters.logging_syslog
else:
syslog = False
self.logger = utils.log(self.bot_id,
self.logger = utils.log(self.bot_id, syslog=syslog,
log_level=self.parameters.logging_level)
# syslog=syslog)
except:
self.log_buffer.append(('critical', traceback.format_exc()))
self.stop()
Expand Down Expand Up @@ -301,7 +303,6 @@ def dump_message(self, error_traceback):
self.logger.info('Message dumped.')
self.current_message = None


def load_defaults_configuration(self):
self.log_buffer.append(('debug', "Loading defaults configuration."))
config = utils.load_configuration(DEFAULTS_CONF_FILE)
Expand Down
3 changes: 3 additions & 0 deletions intelmq/lib/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import intelmq.lib.utils as utils


__all__ = ['Cache']


class Cache():

def __init__(self, host, port, db, ttl):
Expand Down
6 changes: 6 additions & 0 deletions intelmq/lib/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
import traceback


__all__ = ['InvalidArgument', 'ConfigurationError', 'IntelMQException',
'IntelMQHarmonizationException', 'InvalidKey', 'InvalidValue',
'KeyExists', 'KeyNotExists', 'PipelineError',
]


class IntelMQException(Exception):

def __init__(self, message):
Expand Down
3 changes: 3 additions & 0 deletions intelmq/lib/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from intelmq.lib import utils


__all__ = ['Event', 'Message', 'MessageFactory', 'Report']


harm_config = utils.load_configuration(HARMONIZATION_CONF_FILE)


Expand Down
3 changes: 3 additions & 0 deletions intelmq/lib/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import intelmq.lib.utils as utils


__all__ = ['Pipeline', 'PipelineFactory', 'Redis', 'Pythonlist']


class PipelineFactory(object):

@staticmethod
Expand Down
9 changes: 6 additions & 3 deletions intelmq/lib/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
from intelmq import PIPELINE_CONF_FILE, RUNTIME_CONF_FILE, SYSTEM_CONF_FILE


__all__ = ['BotTestCase']


BOT_CONFIG = {
"logging_level": "DEBUG",
"http_proxy": None,
Expand Down Expand Up @@ -65,7 +68,7 @@ def mock(conf_file):


def mocked_logger(logger):
def log(name, log_path=None, log_level=None):
def log(name, log_path=None, log_level=None, stream=None, syslog=None):
return logger
return log

Expand Down Expand Up @@ -352,9 +355,9 @@ def assertRegexpMatchesLog(self, pattern):

self.assertIsNotNone(self.loglines_buffer)
try:
self.assertRegexpMatches(self.loglines_buffer, pattern)
except AttributeError:
self.assertRegex(self.loglines_buffer, pattern)
except AttributeError: # Py2
self.assertRegexpMatches(self.loglines_buffer, pattern)

def assertNotRegexpMatchesLog(self, pattern):
"""Asserts that pattern doesn't match against log."""
Expand Down
10 changes: 6 additions & 4 deletions intelmq/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
except ImportError:
unicodecsv = None

__all__ = ['decode', 'encode', 'base64_encode', 'base64_decode',
'load_configuration', 'load_parameters', 'log', 'reverse_readline',
'parse_logline']

__all__ = ['base64_decode', 'base64_encode', 'decode', 'encode',
'load_configuration', 'load_parameters', 'log', 'parse_logline',
'reverse_readline',
]

# Used loglines format
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
Expand Down Expand Up @@ -199,7 +201,7 @@ class instance with items of configs as attributes


def log(name, log_path=DEFAULT_LOGGING_PATH, log_level="DEBUG", stream=None,
syslog=False):
syslog=None):
"""
Returns a logger instance logging to file and sys.stderr or other stream.
Expand Down
Empty file.
5 changes: 5 additions & 0 deletions intelmq/tests/bots/collectors/rt/test_collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
"""
"""

import intelmq.bots.collectors.rt.collector_rt
5 changes: 4 additions & 1 deletion intelmq/tests/lib/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ def test_file_logger(self):

line_format = [line.format(name) for line in LINES['long']]
for ind, line in enumerate(file_lines):
self.assertRegexpMatches(line, line_format[ind])
try:
self.assertRegex(line, line_format[ind])
except AttributeError: # Py2
self.assertRegexpMatches(line, line_format[ind])

def test_stream_logger(self):
"""Tests if a logger for a stream can be generated with log()."""
Expand Down
Loading

0 comments on commit 9e03bc8

Please sign in to comment.