mirror of
https://github.com/falcosecurity/falco.git
synced 2025-06-29 08:07:24 +00:00
Merge pull request #390 from nestorsalceda/anchore-falco
Add integration between Falco and Anchore
This commit is contained in:
commit
ec0c109d2a
13
integrations/anchore-falco/Dockerfile
Normal file
13
integrations/anchore-falco/Dockerfile
Normal file
@ -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"]
|
16
integrations/anchore-falco/Pipfile
Normal file
16
integrations/anchore-falco/Pipfile
Normal file
@ -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"
|
156
integrations/anchore-falco/Pipfile.lock
generated
Normal file
156
integrations/anchore-falco/Pipfile.lock
generated
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "f2737a14e8f562cf355e13ae09f1eed0f80415effd2aa01b86125e94523da345"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.6"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.python.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
|
||||||
|
"sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
|
||||||
|
],
|
||||||
|
"version": "==2018.4.16"
|
||||||
|
},
|
||||||
|
"chardet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||||
|
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||||
|
],
|
||||||
|
"version": "==3.0.4"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
|
||||||
|
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
|
||||||
|
],
|
||||||
|
"version": "==2.7"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
|
||||||
|
"sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.19.1"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
|
||||||
|
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
|
||||||
|
],
|
||||||
|
"version": "==1.23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {
|
||||||
|
"args": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a785b8d837625e9b61c39108532d95b85274acd679693b71ebb5156848fcf814"
|
||||||
|
],
|
||||||
|
"version": "==0.1.0"
|
||||||
|
},
|
||||||
|
"clint": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:05224c32b1075563d0b16d0015faaf9da43aa214e4a2140e51f08789e7a4c5aa"
|
||||||
|
],
|
||||||
|
"version": "==0.5.1"
|
||||||
|
},
|
||||||
|
"coverage": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba",
|
||||||
|
"sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed",
|
||||||
|
"sha256:104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a",
|
||||||
|
"sha256:15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd",
|
||||||
|
"sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640",
|
||||||
|
"sha256:1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2",
|
||||||
|
"sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162",
|
||||||
|
"sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508",
|
||||||
|
"sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249",
|
||||||
|
"sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694",
|
||||||
|
"sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a",
|
||||||
|
"sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287",
|
||||||
|
"sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1",
|
||||||
|
"sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000",
|
||||||
|
"sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1",
|
||||||
|
"sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e",
|
||||||
|
"sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5",
|
||||||
|
"sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062",
|
||||||
|
"sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba",
|
||||||
|
"sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc",
|
||||||
|
"sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc",
|
||||||
|
"sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99",
|
||||||
|
"sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653",
|
||||||
|
"sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c",
|
||||||
|
"sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558",
|
||||||
|
"sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f",
|
||||||
|
"sha256:9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4",
|
||||||
|
"sha256:ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91",
|
||||||
|
"sha256:b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d",
|
||||||
|
"sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9",
|
||||||
|
"sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd",
|
||||||
|
"sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d",
|
||||||
|
"sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6",
|
||||||
|
"sha256:e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77",
|
||||||
|
"sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80",
|
||||||
|
"sha256:f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e"
|
||||||
|
],
|
||||||
|
"version": "==4.5.1"
|
||||||
|
},
|
||||||
|
"doublex": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:062af49d9e4148bc47b7512d3fdc8e145dea4671d074ffd54b2464a19d3757ab"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.8.4"
|
||||||
|
},
|
||||||
|
"doublex-expects": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5421bd92319c77ccc5a81d595d06e9c9f7f670de342b33e8007a81e70f9fade8"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.7.0rc2"
|
||||||
|
},
|
||||||
|
"expects": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:37538d7b0fa9c0d53e37d07b0e8c07d89754d3deec1f0f8ed1be27f4f10363dd"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.8.0"
|
||||||
|
},
|
||||||
|
"mamba": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:63e70a8666039cf143a255000e23f29be4ea4b5b8169f2b053f94eb73a2ea9e2"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.9.3"
|
||||||
|
},
|
||||||
|
"pyhamcrest": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6b672c02fdf7470df9674ab82263841ce8333fb143f32f021f6cb26f0e512420",
|
||||||
|
"sha256:7a4bdade0ed98c699d728191a058a60a44d2f9c213c51e2dd1e6fb42f2c6128a",
|
||||||
|
"sha256:8ffaa0a53da57e89de14ced7185ac746227a8894dbd5a3c718bf05ddbd1d56cd",
|
||||||
|
"sha256:bac0bea7358666ce52e3c6c85139632ed89f115e9af52d44b3c36e0bf8cf16a9",
|
||||||
|
"sha256:f30e9a310bcc1808de817a92e95169ffd16b60cbc5a016a49c8d0e8ababfae79"
|
||||||
|
],
|
||||||
|
"version": "==1.9.0"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
|
],
|
||||||
|
"version": "==1.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
89
integrations/anchore-falco/README.md
Normal file
89
integrations/anchore-falco/README.md
Normal file
@ -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*.
|
25
integrations/anchore-falco/actions.py
Normal file
25
integrations/anchore-falco/actions.py
Normal file
@ -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))
|
39
integrations/anchore-falco/infrastructure.py
Normal file
39
integrations/anchore-falco/infrastructure.py
Normal file
@ -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']
|
21
integrations/anchore-falco/main.py
Normal file
21
integrations/anchore-falco/main.py
Normal file
@ -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()
|
@ -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))
|
@ -0,0 +1,19 @@
|
|||||||
|
from mamba import description, it
|
||||||
|
from expects import expect, have_length, be_above
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
with description(infrastructure.AnchoreClient) as self:
|
||||||
|
with it('retrieves images with stop policy results'):
|
||||||
|
user = os.environ['ANCHORE_CLI_USER']
|
||||||
|
password = os.environ['ANCHORE_CLI_PASS']
|
||||||
|
url = os.environ['ANCHORE_CLI_URL']
|
||||||
|
|
||||||
|
client = infrastructure.AnchoreClient(user, password, url, True)
|
||||||
|
|
||||||
|
result = client.get_images_with_policy_result('stop')
|
||||||
|
|
||||||
|
expect(result).to(have_length(be_above(1)))
|
Loading…
Reference in New Issue
Block a user