Skip to content

Commit

Permalink
Supported custom falco rules
Browse files Browse the repository at this point in the history
  • Loading branch information
eliasgranderubio committed Jan 26, 2017
1 parent a99cc01 commit 36e43fc
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Before **Dagda** usage, you must have installed Python >= 3.4.5 and the next req
* Docker-py
* Flask
* Flask-cors
* PyYAML

The requirements can be installed with pip:
```
Expand Down
45 changes: 37 additions & 8 deletions dagda/analysis/runtime/sysdig_falco_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import json
import platform
import subprocess
import datetime
from shutil import copyfile
from exception.dagda_error import DagdaError
from log.dagda_logger import DagdaLogger

Expand All @@ -15,15 +17,21 @@ class SysdigFalcoMonitor:
# -- Private attributes

_falco_output_filename = '/tmp/falco_output.json'
_falco_custom_rules_filename = '/tmp/custom_falco_rules.yaml'

# -- Public methods

# SysdigFalcoMonitor Constructor
def __init__(self, docker_driver, mongodb_driver):
def __init__(self, docker_driver, mongodb_driver, falco_rules_filename):
super(SysdigFalcoMonitor, self).__init__()
self.mongodb_driver = mongodb_driver
self.docker_driver = docker_driver
self.running_container_id = ''
if falco_rules_filename is None:
self.falco_rules = ''
else:
copyfile(falco_rules_filename, SysdigFalcoMonitor._falco_custom_rules_filename)
self.falco_rules = ' -o rules_file=/host' + SysdigFalcoMonitor._falco_custom_rules_filename

# Pre check for Sysdig falco container
def pre_check(self):
Expand Down Expand Up @@ -75,19 +83,26 @@ def pre_check(self):

# Runs SysdigFalcoMonitor
def run(self):
self.running_container_id = self._start_container('falco -pc -o json_output=true -o file_output.enabled=true '
' -o file_output.filename=/host' +
SysdigFalcoMonitor._falco_output_filename)
self.running_container_id = self._start_container('falco -pc -o json_output=true -o file_output.enabled=true ' +
'-o file_output.filename=/host' +
SysdigFalcoMonitor._falco_output_filename +
self.falco_rules)

# Wait 3 seconds for sysdig/falco start up and creates the output file
time.sleep(3)

# Check file
if not os.path.isfile(SysdigFalcoMonitor._falco_output_filename):
# Check output file and running docker container
if not os.path.isfile(SysdigFalcoMonitor._falco_output_filename) or \
len(self.docker_driver.get_docker_container_ids_by_image_name('sysdig/falco')) == 0:
raise DagdaError('Sysdig/falco output file not found.')

# Review sysdig/falco logs after rules parser
sysdig_falco_logs = self.docker_driver.docker_logs(self.running_container_id, True, True, False)
if "Rule " in sysdig_falco_logs:
self._parse_log_and_show_dagda_warnings(sysdig_falco_logs)

# Read file
with open(SysdigFalcoMonitor._falco_output_filename, 'rb', ) as f:
with open(SysdigFalcoMonitor._falco_output_filename, 'rb') as f:
last_file_position = 0
fbuf = io.BufferedReader(f)
while True:
Expand Down Expand Up @@ -138,10 +153,24 @@ def _start_container(self, entrypoint=None):
'/dev:/host/dev',
'/proc:/host/proc:ro',
'/boot:/host/boot:ro',
'/lib/modules:/host/lib/modules:ro',
'/lib/modules:/host/lib/modules:rw',
'/usr:/host/usr:ro',
'/tmp:/host/tmp:rw'
],
privileged=True))
self.docker_driver.docker_start(container_id)
return container_id

# Parse sysdig/falco logs after rules parser
def _parse_log_and_show_dagda_warnings(self, sysdig_falco_logs):
date_prefix = datetime.datetime.now().strftime("%A")[:3] + ' '
lines = sysdig_falco_logs.split("\n")
warning = ''
for line in lines:
if line.startswith(date_prefix) is not True:
line = line.strip()
if line.startswith('Rule '):
if warning:
DagdaLogger.get_logger().warning(warning.strip())
warning = ''
warning+=' ' + line
5 changes: 3 additions & 2 deletions dagda/api/dagda_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ class DagdaServer:

# DagdaServer Constructor
def __init__(self, dagda_server_host='127.0.0.1', dagda_server_port=5000, mongodb_host='127.0.0.1',
mongodb_port=27017):
mongodb_port=27017, falco_rules_filename=None):
super(DagdaServer, self).__init__()
self.dagda_server_host = dagda_server_host
self.dagda_server_port = dagda_server_port
InternalServer.set_mongodb_driver(mongodb_host, mongodb_port)
self.sysdig_falco_monitor = SysdigFalcoMonitor(InternalServer.get_docker_driver(),
InternalServer.get_mongodb_driver())
InternalServer.get_mongodb_driver(),
falco_rules_filename)

# Runs DagdaServer
def run(self):
Expand Down
21 changes: 19 additions & 2 deletions dagda/cli/command/start_cli_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import sys
import yaml
from log.dagda_logger import DagdaLogger


Expand All @@ -15,6 +16,7 @@ def __init__(self):
self.parser.add_argument('-p', '--server_port', type=int)
self.parser.add_argument('-m', '--mongodb_host', type=str)
self.parser.add_argument('-mp', '--mongodb_port', type=int)
self.parser.add_argument('--falco_rules_file', type=argparse.FileType('r'))
self.args, self.unknown = self.parser.parse_known_args(sys.argv[2:])
# Verify command line arguments
status = self.verify_args(self.args)
Expand All @@ -39,17 +41,28 @@ def get_mongodb_host(self):
def get_mongodb_port(self):
return self.args.mongodb_port

# Gets falco rules
def get_falco_rules_filename(self):
return self.args.falco_rules_file.name

# -- Static methods

# Verify command line arguments
@staticmethod
def verify_args(args):
if args.server_port and args.server_port not in range(1, 65536):
DagdaLogger.get_logger().error('Arguments -p/--server_port: The port must be between 1 and 65535.')
DagdaLogger.get_logger().error('Argument -p/--server_port: The port must be between 1 and 65535.')
return 1
elif args.mongodb_port and args.mongodb_port not in range(1, 65536):
DagdaLogger.get_logger().error('Arguments -mp/--mongodb_port: The port must be between 1 and 65535.')
DagdaLogger.get_logger().error('Argument -mp/--mongodb_port: The port must be between 1 and 65535.')
return 2
elif args.falco_rules_file:
with args.falco_rules_file as content_file:
try:
yaml.load(content_file.read())
except:
DagdaLogger.get_logger().error('Argument --falco_rules_file: Malformed yaml file.')
return 3
# Else
return 0

Expand All @@ -72,6 +85,7 @@ def format_help(self):

start_parser_text = '''usage: dagda.py start [-h] [--server_host SERVER_HOST] [--server_port SERVER_PORT]
[--mongodb_host MONGODB_HOST] [--mongodb_port MONGODB_PORT]
[--falco_rules_file RULES_FILE]
The Dagda server.
Expand All @@ -89,4 +103,7 @@ def format_help(self):
-mp MONGODB_PORT, --mongodb_port MONGODB_PORT
port where the MongoDB is listening. By default, the
MongoDB port is set to 27017
--falco_rules_file sysdig/falco custom rules file (See 'Falco Rules' wiki
page [https://github.com/draios/falco/wiki/Falco-Rules]
for details)
'''
3 changes: 2 additions & 1 deletion dagda/dagda.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def main(parsed_args):
ds = DagdaServer(dagda_server_host=parsed_args.get_server_host(),
dagda_server_port=parsed_args.get_server_port(),
mongodb_host=parsed_args.get_mongodb_host(),
mongodb_port=parsed_args.get_mongodb_port())
mongodb_port=parsed_args.get_mongodb_port(),
falco_rules_filename=parsed_args.get_falco_rules_filename())
ds.run()

else:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ joblib==0.10.3
docker-py
Flask==0.11.1
flask-cors==3.0.2
PyYAML==3.12
12 changes: 6 additions & 6 deletions tests/cli/command/test_start_cli_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,30 @@
class StartCLIParserTestCase(unittest.TestCase):

def test_ok_empty_args(self):
args = generate_args(None, None, None, None)
args = generate_args(None, None, None, None, None)
status = StartCLIParser.verify_args(args)
self.assertEqual(status, 0)

def test_ok_server_ports(self):
args = generate_args(None, 5555, None, 27017)
args = generate_args(None, 5555, None, 27017, None)
status = StartCLIParser.verify_args(args)
self.assertEqual(status, 0)

def test_fail_server_port(self):
args = generate_args(None, 65536, None, None)
args = generate_args(None, 65536, None, None, None)
status = StartCLIParser.verify_args(args)
self.assertEqual(status, 1)

def test_fail_mongodb_port(self):
args = generate_args(None, None, None, 65536)
args = generate_args(None, None, None, 65536, None)
status = StartCLIParser.verify_args(args)
self.assertEqual(status, 2)

# -- Util methods

def generate_args(server_host, server_port, mongodb_host, mongodb_port):
def generate_args(server_host, server_port, mongodb_host, mongodb_port, falco_rules_file):
return AttrDict([('server_host', server_host), ('server_port', server_port), ('mongodb_host', mongodb_host),
('mongodb_port', mongodb_port)])
('mongodb_port', mongodb_port), ('falco_rules_file', falco_rules_file)])


# -- Util classes
Expand Down

0 comments on commit 36e43fc

Please sign in to comment.