diff --git a/integrations/kubernetes-response-engine/playbooks/README.md b/integrations/kubernetes-response-engine/playbooks/README.md index a9622fc5..e910fa0a 100644 --- a/integrations/kubernetes-response-engine/playbooks/README.md +++ b/integrations/kubernetes-response-engine/playbooks/README.md @@ -199,3 +199,18 @@ $ ./deploy_playbook -p capture -e CAPTURE_DURATION=300 -e AWS_S3_BUCKET=s3://xxx In this example, when we detect a shell in a container, we start to collect data for 300 seconds. This playbook requires permissions for creating a new pod from a Kubeless function. + +### Create a container in Phantom +This playbook creates a container in Phantom + +``` +./deploy_playbook -p phantom -t "falco.*.*" -e PHANTOM_USER=user -e PHANTOM_PASSWORD=xxxXxxxX -e PHANTOM_BASE_URL=https://..." +``` + +#### Parameters +* PHANTOM_USER: This is the user used to connect to Phantom +* PHANTOM_PASSWORD: This is the password used to connect to Phantom +* PHANTOM_BASE_URL: This is the base URL where your Phantom server lives on. Ensure there's no trailing slash. +* VERIFY_SSL: Verify SSL certificates for HTTPS requests. By default is enabled. + +In this example, when Falco raises any kind of alert, the alert will be created in Phantom. diff --git a/integrations/kubernetes-response-engine/playbooks/phantom.py b/integrations/kubernetes-response-engine/playbooks/phantom.py new file mode 100644 index 00000000..97d2e554 --- /dev/null +++ b/integrations/kubernetes-response-engine/playbooks/phantom.py @@ -0,0 +1,25 @@ +import sys +import os.path +sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)))) + +import os +import playbooks +from playbooks import infrastructure + + +def _to_bool(value): + return value.lower() in ('yes', 'true', '1') + + +playbook = playbooks.CreateContainerInPhantom( + infrastructure.PhantomClient( + os.environ['PHANTOM_USER'], + os.environ['PHANTOM_PASSWORD'], + os.environ['PHANTOM_BASE_URL'], + verify_ssl=_to_bool(os.environ.get('VERIFY_SSL', 'True')) + ) +) + + +def handler(event, context): + playbook.run(event['data']) diff --git a/integrations/kubernetes-response-engine/playbooks/playbooks/__init__.py b/integrations/kubernetes-response-engine/playbooks/playbooks/__init__.py index 0028b6ee..14ce1d5c 100644 --- a/integrations/kubernetes-response-engine/playbooks/playbooks/__init__.py +++ b/integrations/kubernetes-response-engine/playbooks/playbooks/__init__.py @@ -158,3 +158,42 @@ class StartSysdigCaptureForContainer: self._s3_bucket, self._aws_access_key_id, self._aws_secret_access_key) + + +class CreateContainerInPhantom: + def __init__(self, phantom_client): + self._phantom_client = phantom_client + + def run(self, alert): + container = self._build_container_from(alert) + self._phantom_client.create_container(container) + + return container + + def _build_container_from(self, alert): + return { + 'description': _output_from_alert(alert), + 'name': alert['rule'], + 'start_time': maya.parse(alert['time']).iso8601(), + 'severity': self._severity_from(alert['priority']), + 'label': 'events', + 'status': 'new', + 'data': { + 'container.id': alert['output_fields']['container.id'], + 'k8s.pod.name': alert['output_fields']['k8s.pod.name'], + } + } + + def _severity_from(self, priority): + return self._SEVERITIES.get(priority, 0) + + _SEVERITIES = { + 'Emergency': 'high', + 'Alert': 'high', + 'Critical': 'high', + 'Error': 'medium', + 'Warning': 'medium', + 'Notice': 'low', + 'Informational': 'low', + 'Debug': 'low', + } diff --git a/integrations/kubernetes-response-engine/playbooks/playbooks/infrastructure.py b/integrations/kubernetes-response-engine/playbooks/playbooks/infrastructure.py index 49364dd3..e18316b4 100644 --- a/integrations/kubernetes-response-engine/playbooks/playbooks/infrastructure.py +++ b/integrations/kubernetes-response-engine/playbooks/playbooks/infrastructure.py @@ -243,3 +243,25 @@ class DemistoClient: 'Accept': 'application/json', 'Authorization': self._api_key, } + + +class PhantomClient: + def __init__(self, user, password, base_url, verify_ssl=True): + self._user = user + self._password = password + self._base_url = base_url + self._verify_ssl = verify_ssl + + def create_container(self, container): + response = requests.post(self._base_url + '/rest/container', + data=json.dumps(container), + auth=(self._user, self._password), + verify=self._verify_ssl) + + response_as_json = response.json() + if 'success' in response_as_json: + result = container.copy() + result['id'] = response_as_json['id'] + return result + + raise RuntimeError(response_as_json['message']) diff --git a/integrations/kubernetes-response-engine/playbooks/specs/infrastructure/phantom_client_spec.py b/integrations/kubernetes-response-engine/playbooks/specs/infrastructure/phantom_client_spec.py new file mode 100644 index 00000000..e247a18a --- /dev/null +++ b/integrations/kubernetes-response-engine/playbooks/specs/infrastructure/phantom_client_spec.py @@ -0,0 +1,45 @@ +from mamba import description, it, before, context +from expects import expect, be_none, raise_error + +import os + +from playbooks import infrastructure + + +with description(infrastructure.PhantomClient) as self: + with before.each: + self.phantom_client = infrastructure.PhantomClient( + os.environ['PHANTOM_USER'], + os.environ['PHANTOM_PASSWORD'], + os.environ['PHANTOM_BASE_URL'], + verify_ssl=False + ) + + with it('creates a container in Phantom Server'): + container = { + 'name': 'My Container', + 'description': 'Useful description of this container.', + 'label': 'events', + 'run_automation': False, + 'severity': 'high', + 'status': 'new', + 'start_time': '2015-03-21T19:28:13.759Z', + } + + container = self.phantom_client.create_container(container) + + expect(container['id']).not_to(be_none) + + with context('when an error happens'): + with it('raises an error'): + container = { + 'description': 'Useful description of this container.', + 'label': 'events', + 'run_automation': False, + 'severity': 'high', + 'status': 'new', + 'start_time': '2015-03-21T19:28:13.759Z', + } + + expect(lambda: self.phantom_client.create_container(container))\ + .to(raise_error(RuntimeError)) diff --git a/integrations/kubernetes-response-engine/playbooks/specs/playbooks/create_container_in_phantom_playbook_spec.py b/integrations/kubernetes-response-engine/playbooks/specs/playbooks/create_container_in_phantom_playbook_spec.py new file mode 100644 index 00000000..8c0e0d7b --- /dev/null +++ b/integrations/kubernetes-response-engine/playbooks/specs/playbooks/create_container_in_phantom_playbook_spec.py @@ -0,0 +1,63 @@ +from mamba import description, it, before, context +from expects import expect, have_key + +from doublex import Spy +from doublex_expects import have_been_called_with + +from playbooks import infrastructure +import playbooks + + +with description(playbooks.CreateContainerInPhantom) as self: + with before.each: + self.phantom_client = Spy(infrastructure.PhantomClient) + self.playbook = playbooks.CreateContainerInPhantom(self.phantom_client) + + self.alert = { + "output": "10:22:15.576767292: Notice Unexpected setuid call by non-sudo, non-root program (user=bin cur_uid=2 parent=event_generator command=event_generator uid=root) k8s.pod=falco-event-generator-6fd89678f9-cdkvz container=1c76f49f40b4", + "output_fields": { + "container.id": "1c76f49f40b4", + "evt.arg.uid": "root", + "evt.time": 1527157335576767292, + "k8s.pod.name": "falco-event-generator-6fd89678f9-cdkvz", + "proc.cmdline": "event_generator ", + "proc.pname": "event_generator", + "user.name": "bin", + "user.uid": 2 + }, + "priority": "Notice", + "rule": "Non sudo setuid", + "time": "2018-05-24T10:22:15.576767292Z" + } + + self.container = self.playbook.run(self.alert) + + with it('creates the container in phantom'): + expect(self.phantom_client.create_container).to(have_been_called_with(self.container)) + + with it('includes falco output'): + falco_output = 'Unexpected setuid call by non-sudo, non-root program (user=bin cur_uid=2 parent=event_generator command=event_generator uid=root) k8s.pod=falco-event-generator-6fd89678f9-cdkvz container=1c76f49f40b4' + + expect(self.container).to(have_key('description', falco_output)) + + with it('includes severity'): + expect(self.container).to(have_key('severity', 'low')) + + with it('includes rule name'): + expect(self.container).to(have_key('name', 'Non sudo setuid')) + + with it('includes time when alert happened'): + expect(self.container).to(have_key('start_time', '2018-05-24T10:22:15.576767Z')) + + with it('includes label'): + expect(self.container).to(have_key('label', 'events')) + + with it('includes status'): + expect(self.container).to(have_key('status', 'new')) + + with context('when building additional data'): + with it('includes kubernetes pod name'): + expect(self.container['data']).to(have_key('k8s.pod.name', 'falco-event-generator-6fd89678f9-cdkvz')) + + with it('includes container id'): + expect(self.container['data']).to(have_key('container.id', '1c76f49f40b4'))