enhanced gdpr and first wazuh integration bits

This commit is contained in:
Toni de la Fuente
2018-12-05 11:58:43 +00:00
parent 9e0923407e
commit 1727758479
3 changed files with 288 additions and 1 deletions

View File

@@ -15,7 +15,7 @@ GROUP_ID[9]='gdpr'
GROUP_NUMBER[9]='9.0'
GROUP_TITLE[9]='GDPR Readiness - ONLY AS REFERENCE - [gdpr] ********************'
GROUP_RUN_BY_DEFAULT[9]='N' # run it when execute_all is called
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra740'
GROUP_CHECKS[9]='extra718,extra725,extra727,check12,check113,check114,extra71,extra731,extra732,extra733,check25,check39,check21,check22,check23,check24,check26,check27,check35,extra726,extra714,extra715,extra717,extra719,extra720,extra721,extra722,check43,check25,extra714,extra729,extra734,extra735,extra736,extra738,extra739,extra740'
# Resources:
# https://d1.awsstatic.com/whitepapers/compliance/GDPR_Compliance_on_AWS.pdf

View File

@@ -0,0 +1,45 @@
<!--
Rules for parsing Prowler output
Authored by Jeremy Phillips <jeremy@uranusbytes.com>
Copyright: Apache License 2.0
ID: 90000-90099
Prowler - https://github.com/toniblyx/prowler
-->
<group name="local,amazon,prowler,">
<!-- Filter 1: Only prowler events -->
<rule id="90001" level="0">
<field name="integration">prowler</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
<!-- Check Result: Pass -->
<rule id="90002" level="1">
<if_sid>90001</if_sid>
<field name="prowler.status">Pass</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
<!-- Check Result: Info -->
<rule id="90003" level="3">
<if_sid>90001</if_sid>
<field name="prowler.status">Info</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
<!-- Check Result: Error -->
<rule id="90004" level="5">
<if_sid>90001</if_sid>
<field name="prowler.status">Error</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
<!-- Check Result: Fail, Scored -->
<rule id="90005" level="9">
<if_sid>90001</if_sid>
<field name="prowler.status">Fail</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
<!-- Check Result: Fail, Not Scored -->
<rule id="90006" level="7">
<if_sid>90005</if_sid>
<field name="prowler.scored">Not Scored</field>
<description>Prowler Check Result: $(prowler.status) - Control $(prowler.control_id)</description>
</rule>
</group>

View File

@@ -0,0 +1,242 @@
#!/usr/bin/env python
#
# Authored by Jeremy Phillips <jeremy@uranusbytes.com>
# Copyright: Apache License 2.0
#
# Wrapper around prowler script to parse results and forward to Wazuh
# Prowler - https://github.com/toniblyx/prowler
#
# TODO: Add ability to disable different groups (EXTRA, etc...
# TODO: Allow to disable individual checks
# TODO: Remove all the commented out stuff
#
# Error Codes:
# 1 - Unknown
# 2 - SIGINT
# 3 - Error output from execution of Prowler
# 4 - Output row is invalid json
# 5 - Wazuh must be running
# 6 - Error sending to socket
import signal
import sys
import socket
import argparse
import subprocess
import json
from datetime import datetime
import os
import re
################################################################################
# Constants
################################################################################
WAZUH_PATH = open('/etc/ossec-init.conf').readline().split('"')[1]
DEBUG_LEVEL = 1 # Enable/disable debug mode
PATH_TO_PROWLER = '{0}/integrations/prowler'.format(WAZUH_PATH) # No trailing slash
TEMPLATE_CHECK = '''
{{
"integration": "prowler",
"prowler": {0}
}}
'''
TEMPLATE_MSG = '1:Wazuh-Prowler:{0}'
TEMPLATE_ERROR = '''{{
"aws_account_id": {aws_account_id},
"aws_profile": "{aws_profile}",
"prowler_error": "{prowler_error}",
"prowler_version": "{prowler_version}",
"timestamp": "{timestamp}",
"status": "Error"
}}
'''
WAZUH_QUEUE = '{0}/queue/ossec/queue'.format(WAZUH_PATH)
FIELD_REMAP = {
"Profile": "aws_profile",
"Control": "control",
"Account Number": "aws_account_id",
"Level": "level",
"Account Alias": "aws_account_alias",
"Timestamp": "timestamp",
"Region": "region",
"Control ID": "control_id",
"Status": "status",
"Scored": "scored",
"Message": "message"
}
CHECKS_FILES_TO_IGNORE = [
'check_sample'
]
################################################################################
# Functions
################################################################################
def _send_msg(msg):
try:
_json_msg = json.dumps(_reformat_msg(msg))
_debug("Sending Msg: {0}".format(_json_msg), 3)
_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
_socket.connect(WAZUH_QUEUE)
_socket.send(TEMPLATE_MSG.format(_json_msg).encode())
_socket.close()
except socket.error as e:
if e.errno == 111:
print('ERROR: Wazuh must be running.')
sys.exit(5)
else:
print("ERROR: Error sending message to wazuh: {}".format(e))
sys.exit(6)
except Exception as e:
print("ERROR: Error sending message to wazuh: {}".format(e))
sys.exit(6)
return
def _handler(signal, frame):
print("ERROR: SIGINT received.")
sys.exit(12)
def _debug(msg, msg_level):
if DEBUG_LEVEL >= msg_level:
print('DEBUG-{level}: {debug_msg}'.format(level=msg_level, debug_msg=msg))
def _get_script_arguments():
_parser = argparse.ArgumentParser(usage="usage: %(prog)s [options]",
description="Wazuh wodle for evaluating AWS security configuration",
formatter_class=argparse.RawTextHelpFormatter)
_parser.add_argument('-c', '--aws_account_id', dest='aws_account_id',
help='AWS Account ID for logs',
required=False)
_parser.add_argument('-d', '--debug', action='store', dest='debug', default=0, help='Enable debug')
_parser.add_argument('-p', '--aws_profile', dest='aws_profile', help='The name of credential profile to use',
default=None)
_parser.add_argument('-n', '--aws_account_alias', dest='aws_account_alias',
help='AWS Account ID Alias', default='')
_parser.add_argument('-e', '--skip_on_error', action='store_false', dest='skip_on_error',
help='If check output is invalid json, error out instead of skipping the check', default=True)
return _parser.parse_args()
def _run_prowler(prowler_args):
_debug('Running prowler with args: {0}'.format(prowler_args), 1)
_prowler_command = '{prowler}/prowler {args}'.format(prowler=PATH_TO_PROWLER, args=prowler_args)
_debug('Running command: {0}'.format(_prowler_command), 2)
_process = subprocess.Popen(_prowler_command, stdout=subprocess.PIPE, shell=True)
_output, _error = _process.communicate()
_debug('Raw prowler output: {0}'.format(_output), 3)
_debug('Raw prowler error: {0}'.format(_error), 3)
if _error is not None:
_debug('PROWLER ERROR: {0}'.format(_error), 1)
exit(3)
return _output
def _get_prowler_version(options):
_debug('+++ Get Prowler Version', 1)
# Execute prowler, but only display the version and immediately exit
return _run_prowler('-p {0} -V'.format(options.aws_profile)).rstrip()
def _get_prowler_results(options, prowler_check):
_debug('+++ Get Prowler Results - {check}'.format(check=prowler_check), 1)
# Execute prowler with all checks
# -b = disable banner
# -p = credential profile
# -M = output json
return _run_prowler('-b -c {check} -p {aws_profile} -M json'.format(check=prowler_check,
aws_profile=options.aws_profile))
def _get_prowler_checks():
_prowler_checks = []
for _directory_path, _directories, _files in os.walk('{path}/checks'.format(path=PATH_TO_PROWLER)):
_debug('Checking in : {}'.format(_directory_path), 3)
for _file in _files:
if _file in CHECKS_FILES_TO_IGNORE:
_debug('Ignoring check - {}'.format(_directory_path, _file), 3)
elif re.match("check\d+", _file):
_prowler_checks.append(_file)
elif re.match("check_extra(\d+)", _file):
_prowler_checks.append(_file[6:])
else:
_debug('Unknown check file type- {}'.format(_directory_path, _file), 3)
return _prowler_checks
def _send_prowler_results(prowler_results, _prowler_version, options):
_debug('+++ Send Prowler Results', 1)
for _check_result in prowler_results.splitlines():
# Empty row
if len(_check_result) < 1:
continue
# Something failed during prowler check
elif _check_result[:17] == 'An error occurred':
_debug('ERROR MSG --- {0}'.format(_check_result), 2)
_temp_msg = TEMPLATE_ERROR.format(
aws_account_id=options.aws_account_id,
aws_profile=options.aws_profile,
prowler_error=_check_result.replace('"', '\"'),
prowler_version=_prowler_version,
timestamp=datetime.now().isoformat()
)
_error_msg = json.loads(TEMPLATE_CHECK.format(_temp_msg))
_send_msg(_error_msg)
continue
try:
_debug('RESULT MSG --- {0}'.format(_check_result), 2)
_check_result = json.loads(TEMPLATE_CHECK.format(_check_result))
except:
_debug('INVALID JSON --- {0}'.format(TEMPLATE_CHECK.format(_check_result)), 1)
if not options.skip_on_error:
exit(4)
_check_result['prowler']['prowler_version'] = _prowler_version
_check_result['prowler']['aws_account_alias'] = options.aws_account_alias
_send_msg(_check_result)
return True
def _reformat_msg(msg):
for field in FIELD_REMAP:
if field in msg['prowler']:
msg['prowler'][FIELD_REMAP[field]] = msg['prowler'][field]
del msg['prowler'][field]
return msg
# Main
###############################################################################
def main(argv):
_debug('+++ Begin script', 1)
# Parse arguments
_options = _get_script_arguments()
if int(_options.debug) > 0:
global DEBUG_LEVEL
DEBUG_LEVEL = int(_options.debug)
_debug('+++ Debug mode on - Level: {debug}'.format(debug=_options.debug), 1)
_prowler_version = _get_prowler_version(_options)
_prowler_checks = _get_prowler_checks()
for _check in _prowler_checks:
_prowler_results = _get_prowler_results(_options, _check)
_send_prowler_results(_prowler_results, _prowler_version, _options)
_debug('+++ Finished script', 1)
return
if __name__ == '__main__':
try:
_debug('Args: {args}'.format(args=str(sys.argv)), 2)
signal.signal(signal.SIGINT, _handler)
main(sys.argv[1:])
sys.exit(0)
except Exception as e:
print("Unknown error: {}".format(e))
if DEBUG_LEVEL > 0:
raise
sys.exit(1)