-
Notifications
You must be signed in to change notification settings - Fork 917
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #390 from nestorsalceda/anchore-falco
Add integration between Falco and Anchore
- Loading branch information
Showing
9 changed files
with
399 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
FROM python:3-stretch | ||
|
||
RUN pip install pipenv | ||
|
||
WORKDIR /app | ||
|
||
ADD Pipfile /app/Pipfile | ||
ADD Pipfile.lock /app/Pipfile.lock | ||
RUN pipenv install --system --deploy | ||
|
||
ADD . /app | ||
|
||
CMD ["python", "main.py"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[[source]] | ||
url = "https://pypi.python.org/simple" | ||
verify_ssl = true | ||
name = "pypi" | ||
|
||
[dev-packages] | ||
doublex-expects = "==0.7.0rc2" | ||
doublex = "*" | ||
mamba = "*" | ||
expects = "*" | ||
|
||
[packages] | ||
requests = "*" | ||
|
||
[requires] | ||
python_version = "3.6" |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Create Falco rule from Anchore policy result | ||
|
||
This integration creates a rule for Sysdig Falco based on Anchore policy result. | ||
So that when we will try to run an image which has a ```stop``` final action result | ||
in Anchore, Falco will alert us. | ||
|
||
## Getting started | ||
|
||
### Prerequisites | ||
|
||
For running this integration you will need: | ||
|
||
* Python 3.6 | ||
* pipenv | ||
* An [anchore-engine](https://github.com/anchore/anchore-engine) running | ||
|
||
### Configuration | ||
|
||
This integration uses the [same environment variables that anchore-cli](https://github.com/anchore/anchore-cli#configuring-the-anchore-cli): | ||
|
||
* ANCHORE_CLI_USER: The user used to conect to anchore-engine. By default is ```admin``` | ||
* ANCHORE_CLI_PASS: The password used to connect to anchore-engine. | ||
* ANCHORE_CLI_URL: The url where anchore-engine listens. Make sure does not end with a slash. By default is ```http://localhost:8228/v1``` | ||
* ANCHORE_CLI_SSL_VERIFY: Flag for enabling if HTTP client verifies SSL. By default is ```true``` | ||
|
||
### Running | ||
|
||
This is a Python program which generates a Falco rule based on anchore-engine | ||
information: | ||
|
||
``` | ||
pipenv run python main.py | ||
``` | ||
|
||
And this will output something like: | ||
|
||
|
||
```yaml | ||
- macro: anchore_stop_policy_evaluation_containers | ||
condition: container.image.id in ("8626492fecd368469e92258dfcafe055f636cb9cbc321a5865a98a0a6c99b8dd", "e86d9bb526efa0b0401189d8df6e3856d0320a3d20045c87b4e49c8a8bdb22c1") | ||
|
||
- rule: Run Anchore Containers with Stop Policy Evaluation | ||
desc: Detect containers which does not receive a positive Policy Evaluation from Anchore Engine. | ||
|
||
condition: evt.type=execve and proc.vpid=1 and container and anchore_stop_policy_evaluation_containers | ||
output: A stop policy evaluation container from anchore has started (%container.info image=%container.image) | ||
priority: INFO | ||
tags: [container] | ||
``` | ||
You can save that output to ```/etc/falco/rules.d/anchore-integration-rules.yaml``` | ||
and Falco will start checking this rule. | ||
|
||
As long as information in anchore-engine can change, it's a good idea to run this | ||
integration **periodically** and keep the rule synchronized with anchore-engine | ||
policy evaluation result. | ||
|
||
## Tests | ||
|
||
As long as there are contract tests with anchore-engine, it needs a working | ||
anchore-engine and its environment variables. | ||
|
||
``` | ||
pipenv install -d | ||
pipenv run mamba --format=documentation | ||
``` | ||
## Docker support | ||
### Build the image | ||
``` | ||
docker build -t sysdig/anchore-falco . | ||
``` | ||
### Running the image | ||
An image exists on DockerHub, its name is ```sysdig/anchore-falco```. | ||
So you can run directly with Docker: | ||
``` | ||
docker run --rm -e ANCHORE_CLI_USER=<user-for-custom-anchore-engine> \ | ||
-e ANCHORE_CLI_PASS=<passsword-for-user-for-custom-anchore-engine> \ | ||
-e ANCHORE_CLI_URL=http://<custom-anchore-engine-host>:8228/v1 \ | ||
sysdig/anchore-falco | ||
``` | ||
And this will output the Falco rule based on *custom-anchore-engine-host*. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import string | ||
|
||
FALCO_RULE_TEMPLATE = string.Template(''' | ||
- macro: anchore_stop_policy_evaluation_containers | ||
condition: container.image.id in ($images) | ||
- rule: Run Anchore Containers with Stop Policy Evaluation | ||
desc: Detect containers which does not receive a positive Policy Evaluation from Anchore Engine. | ||
condition: evt.type=execve and proc.vpid=1 and container and anchore_stop_policy_evaluation_containers | ||
output: A stop policy evaluation container from anchore has started (%container.info image=%container.image) | ||
priority: INFO | ||
tags: [container] | ||
''') | ||
|
||
|
||
class CreateFalcoRuleFromAnchoreStopPolicyResults: | ||
def __init__(self, anchore_client): | ||
self._anchore_client = anchore_client | ||
|
||
def run(self): | ||
images = self._anchore_client.get_images_with_policy_result('stop') | ||
|
||
images = ['"{}"'.format(image) for image in images] | ||
return FALCO_RULE_TEMPLATE.substitute(images=', '.join(images)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import requests | ||
|
||
|
||
class AnchoreClient: | ||
def __init__(self, user, password, url, ssl_verify): | ||
self._user = user | ||
self._password = password | ||
self._url = url | ||
self._ssl_verify = ssl_verify | ||
|
||
def get_images_with_policy_result(self, policy_result): | ||
results = [] | ||
for image in self._get_all_images(): | ||
final_action = self._evaluate_image(image) | ||
|
||
if final_action == 'stop': | ||
results.append(image['image_id']) | ||
|
||
return results | ||
|
||
def _get_all_images(self): | ||
response = self._do_get_request(self._url + '/images') | ||
return [ | ||
{ | ||
'image_id': image['image_detail'][0]['imageId'], | ||
'image_digest': image['image_detail'][0]['imageDigest'], | ||
'full_tag': image['image_detail'][0]['fulltag'] | ||
} for image in response.json()] | ||
|
||
def _do_get_request(self, url): | ||
return requests.get(url, | ||
auth=(self._user, self._password), | ||
verify=self._ssl_verify, | ||
headers={'Content-Type': 'application/json'}) | ||
|
||
def _evaluate_image(self, image): | ||
response = self._do_get_request(self._url + '/images/{}/check?tag={}'.format(image['image_digest'], image['full_tag'])) | ||
if response.status_code == 200: | ||
return response.json()[0][image['image_digest']][image['full_tag']][0]['detail']['result']['final_action'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import os | ||
|
||
import actions, infrastructure | ||
|
||
|
||
def main(): | ||
anchore_client = infrastructure.AnchoreClient( | ||
os.environ.get('ANCHORE_CLI_USER', 'admin'), | ||
os.environ['ANCHORE_CLI_PASS'], | ||
os.environ.get('ANCHORE_CLI_URL', 'http://localhost:8228/v1'), | ||
os.environ.get('ANCHORE_CLI_SSL_VERIFY', True) | ||
) | ||
action = actions.CreateFalcoRuleFromAnchoreStopPolicyResults(anchore_client) | ||
|
||
result = action.run() | ||
|
||
print(result) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
21 changes: 21 additions & 0 deletions
21
integrations/anchore-falco/specs/create_falco_rule_from_anchore_policy_results_spec.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from mamba import description, it, before | ||
from expects import expect, contain | ||
|
||
from doublex import Stub, when | ||
|
||
import actions | ||
import infrastructure | ||
|
||
|
||
with description(actions.CreateFalcoRuleFromAnchoreStopPolicyResults) as self: | ||
with before.each: | ||
self.anchore_client = Stub(infrastructure.AnchoreClient) | ||
self.action = actions.CreateFalcoRuleFromAnchoreStopPolicyResults(self.anchore_client) | ||
|
||
with it('queries Anchore Server for images with Stop as policy results'): | ||
image_id = 'any image id' | ||
when(self.anchore_client).get_images_with_policy_result('stop').returns([image_id]) | ||
|
||
result = self.action.run() | ||
|
||
expect(result).to(contain(image_id)) |
Oops, something went wrong.