File size: 5,173 Bytes
7c89ed7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# Copyright (c) 2014 Mozilla Corporation

import netaddr


def isIPv4(ip):
    try:
        return netaddr.valid_ipv4(ip)
    except:
        return False


def isIPv6(ip):
    try:
        return netaddr.valid_ipv6(ip)
    except:
        return False


def addError(message, error):
    '''add an error note to a message'''
    if 'errors' not in message:
        message['errors'] = list()
    if isinstance(message['errors'], list):
        message['errors'].append(error)


class message(object):
    def __init__(self):
        '''
        uses heuristic to find and attach the source IP address of the alert
        '''

        # Match on all alerts
        self.registration = ['*']
        self.priority = 1

    def onMessage(self, message):
        '''
        Examine possible ip addresses for the following:
          ipv6 in an ipv4 field
          ipv4 in another field
          '-' or other invalid ip in the ip field
        Also sets ipv4 in two fields:
            ipaddress (decimal mapping IP)
            ipv4address (string mapping)
            Elastic search is inconsistent about returning IPs as
            decimal or IPs.
            In a query an IP field is returned as string.
            In a facets an IP field is returned as decimal.
            No ES field type exists for ipv6, so always having
            a string version is the most flexible option.
        '''

        # here is where you do something with the incoming alert message
        if 'events' in message:
            if 'documentsource' in message['events'][0]:
                if 'details' in message['events'][0]['documentsource']:
                    event = message['events'][0]['documentsource']['details']
                    if 'details' not in message:
                        message['details'] = {}
                    # forwarded header can be spoofed, so try it first,
                    # but override later if we've a better field.
                    if 'http_x_forwarded_for' in event:
                        # should be a comma delimited list of ips with the original client listed first
                        ipText = event['http_x_forwarded_for'].split(',')[0]
                        if isIPv4(ipText) and 'sourceipaddress' not in event:
                            message['details']['sourceipaddress'] = ipText
                        if isIPv4(ipText) and 'sourceipv4address' not in event:
                            message['details']['sourceipv4address'] = ipText
                        if isIPv6(ipText) and 'sourceipv6address' not in event:
                            message['details']['sourceipv6address'] = ipText

                    if 'sourceipaddress' in event:
                        ipText = event['sourceipaddress']
                        if isIPv6(ipText):
                            event['sourceipv6address'] = ipText
                            message['details']['sourceipaddress'] = '0.0.0.0'
                            addError(message, 'plugin: {0} error: {1}'.format('ipFixUp.py', 'sourceipaddress is ipv6, moved'))
                        elif isIPv4(ipText):
                            message['details']['sourceipv4address'] = ipText
                            message['details']['sourceipaddress'] = ipText
                        else:
                            # Smells like a hostname, let's save it as source field
                            message['details']['source'] = event['sourceipaddress']
                            message['details']['sourceipaddress'] = None

                    if 'destinationipaddress' in event:
                        ipText = event['destinationipaddress']
                        if isIPv6(ipText):
                            message['details']['destinationipv6address'] = ipText
                            message['details']['destinationipaddress'] = '0.0.0.0'
                            addError(message, 'plugin: {0} error: {1}'.format('ipFixUp.py', 'destinationipaddress is ipv6, moved'))
                        elif isIPv4(ipText):
                            message['details']['destinationipv4address'] = ipText
                            message['details']['destinationipaddress'] = ipText
                        else:
                            # Smells like a hostname, let's save it as destination field
                            message['details']['destination'] = event['destinationipaddress']
                            message['details']['destinationipaddress'] = None

                    if 'cluster_client_ip' in event:
                        ipText = event['cluster_client_ip']
                        if isIPv4(ipText):
                            message['details']['sourceipaddress'] = ipText

        # you can modify the message if needed
        # plugins registered with lower (>2) priority
        # will receive the message and can also act on it
        # but even if not modified, you must return it
        return message