mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
310 Commits
0.32.1
...
fix/dev_ve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43e4d25848 | ||
|
|
67d592e83a | ||
|
|
356a4a0749 | ||
|
|
910b8ff858 | ||
|
|
83b12bab1d | ||
|
|
ecc1853d60 | ||
|
|
fbd6628693 | ||
|
|
ba61706557 | ||
|
|
234026e14b | ||
|
|
d03826379b | ||
|
|
3697d1fae2 | ||
|
|
e76c31b493 | ||
|
|
d95e36b526 | ||
|
|
15b57bd972 | ||
|
|
68f4d5bb59 | ||
|
|
138e373ace | ||
|
|
47fd90bb7f | ||
|
|
3a56804cff | ||
|
|
8f188ebe06 | ||
|
|
7ead21daac | ||
|
|
d3badeb77e | ||
|
|
588ab01bfd | ||
|
|
f08a5b4067 | ||
|
|
dea02f82e8 | ||
|
|
e3dbae3259 | ||
|
|
d0ceba83b4 | ||
|
|
161246fe1a | ||
|
|
240c0b870d | ||
|
|
136eacc17f | ||
|
|
c0c0246927 | ||
|
|
acf5c4ce5f | ||
|
|
4a4fa2592b | ||
|
|
d0467de0a7 | ||
|
|
c1be1496d3 | ||
|
|
fa1a5d58e6 | ||
|
|
62abefddf6 | ||
|
|
784fa8b374 | ||
|
|
10fe9fd84b | ||
|
|
3d7677ce5b | ||
|
|
0fd765f7c3 | ||
|
|
cca90b2f80 | ||
|
|
6c873418ce | ||
|
|
f12531a153 | ||
|
|
d5e3085b54 | ||
|
|
90d6d9080a | ||
|
|
7d28637f44 | ||
|
|
9d8f130f47 | ||
|
|
9ee0298c4d | ||
|
|
7da30ca661 | ||
|
|
12d709b8b1 | ||
|
|
57b26530b6 | ||
|
|
3629c4dc4a | ||
|
|
5e531870a9 | ||
|
|
c2dc0a7259 | ||
|
|
f684e144be | ||
|
|
a4218a4b4f | ||
|
|
48fbe0801d | ||
|
|
c47492ab6d | ||
|
|
4cb556aed2 | ||
|
|
7da3041cb3 | ||
|
|
48ce85f4da | ||
|
|
5f2bc6a2d3 | ||
|
|
88c7202fdc | ||
|
|
a98a1b2c4c | ||
|
|
77857a7236 | ||
|
|
e011b3b5e5 | ||
|
|
fd4d521a5f | ||
|
|
3f3386cfe0 | ||
|
|
11160f8463 | ||
|
|
79d875c28f | ||
|
|
7610ee53e5 | ||
|
|
3c02b40a21 | ||
|
|
e85a8c914f | ||
|
|
21c2b1f472 | ||
|
|
909f6d0961 | ||
|
|
83a83a5853 | ||
|
|
b4ea2f4da2 | ||
|
|
59ba2f9aab | ||
|
|
32ec3240b4 | ||
|
|
fbac2a9570 | ||
|
|
805f0cdd78 | ||
|
|
e68151eb07 | ||
|
|
ec7ddbbaf8 | ||
|
|
663c1d073a | ||
|
|
bbb821fb8e | ||
|
|
5781c53ddc | ||
|
|
545b58ee14 | ||
|
|
cf83ff5447 | ||
|
|
8d8e7622e1 | ||
|
|
fd097e94d7 | ||
|
|
6634c896b7 | ||
|
|
38c823533c | ||
|
|
3aa9267b48 | ||
|
|
725714726d | ||
|
|
c9fa585801 | ||
|
|
90e4634a79 | ||
|
|
b0b2f05eb5 | ||
|
|
8aea0935c9 | ||
|
|
9c240198a0 | ||
|
|
f6f763fe84 | ||
|
|
9b5f3ee99e | ||
|
|
89e8f70de0 | ||
|
|
b0f0105116 | ||
|
|
5f2267f716 | ||
|
|
b65157af5e | ||
|
|
b2b1feb1f2 | ||
|
|
b900e46dfe | ||
|
|
a98c9cdd20 | ||
|
|
2a427925a0 | ||
|
|
c0c37d87f5 | ||
|
|
f57c67cc96 | ||
|
|
7686c03a36 | ||
|
|
aa0abb4288 | ||
|
|
8b927fb010 | ||
|
|
a325086363 | ||
|
|
1930ec56c7 | ||
|
|
3902779409 | ||
|
|
7e37c72431 | ||
|
|
e068df514c | ||
|
|
9048d84ed4 | ||
|
|
00459f3447 | ||
|
|
0274959981 | ||
|
|
30b56d2960 | ||
|
|
d6b5789b7a | ||
|
|
af4524491d | ||
|
|
36a08aee13 | ||
|
|
39de011751 | ||
|
|
a83d38c6d7 | ||
|
|
86c3a9cd69 | ||
|
|
8473706526 | ||
|
|
4e622fc033 | ||
|
|
16dca8f905 | ||
|
|
2d5fc0b647 | ||
|
|
e5cd5eacf5 | ||
|
|
366bcfd7a3 | ||
|
|
c844eb9ef3 | ||
|
|
b71eb7e6ed | ||
|
|
c732e5d800 | ||
|
|
c0ea753262 | ||
|
|
e4008217b9 | ||
|
|
9c184af2a1 | ||
|
|
d11aec28d5 | ||
|
|
d17e173e35 | ||
|
|
25e9bd1c91 | ||
|
|
4bc9fc74c8 | ||
|
|
b65cc49221 | ||
|
|
ce769b1fbe | ||
|
|
65993ad1ed | ||
|
|
f4c6a81ed8 | ||
|
|
f9a152b24c | ||
|
|
ed025f1a86 | ||
|
|
8ba779de8c | ||
|
|
cf8b85ad86 | ||
|
|
9cf3d118f6 | ||
|
|
63bdc1119f | ||
|
|
9dc3eb2fc6 | ||
|
|
7bb319b21e | ||
|
|
3f7d61f150 | ||
|
|
cf9baea624 | ||
|
|
ccd3c896de | ||
|
|
11644ecafc | ||
|
|
23df49a47f | ||
|
|
3e1ef070b8 | ||
|
|
670b0733de | ||
|
|
752c3d8332 | ||
|
|
daba85b61c | ||
|
|
103d7e08b8 | ||
|
|
0f45cf49db | ||
|
|
7a5a4c32ee | ||
|
|
af95455bab | ||
|
|
4b75f213c6 | ||
|
|
0de617a7fb | ||
|
|
5745faeccc | ||
|
|
f9ee45b38e | ||
|
|
7d2f82fddc | ||
|
|
0956bac0de | ||
|
|
1b410ea2cc | ||
|
|
52402ac805 | ||
|
|
6e0971f1e1 | ||
|
|
934ae2f1a6 | ||
|
|
fab95f38a9 | ||
|
|
0be0191c53 | ||
|
|
5dcc329339 | ||
|
|
721aa30e80 | ||
|
|
565ddd70d3 | ||
|
|
c000695816 | ||
|
|
d67edeb39d | ||
|
|
1a22bfc654 | ||
|
|
bd865450ef | ||
|
|
98b8e390a1 | ||
|
|
6c1f908ca5 | ||
|
|
574a4b9f0a | ||
|
|
c05ad6fde4 | ||
|
|
e361069092 | ||
|
|
9c6ad6ce84 | ||
|
|
2d8efee73e | ||
|
|
28ff6ad3bd | ||
|
|
2f5461bed0 | ||
|
|
605dd2816d | ||
|
|
c5442ccb41 | ||
|
|
9646308651 | ||
|
|
e15d9f6f51 | ||
|
|
ce0dd918fb | ||
|
|
cc4ccc40d7 | ||
|
|
0e2a053151 | ||
|
|
97bf0338b9 | ||
|
|
6c12cc655e | ||
|
|
7387fffcef | ||
|
|
34ca78786a | ||
|
|
f2aba88a6c | ||
|
|
bc765f1b7d | ||
|
|
c2a8efc329 | ||
|
|
978f192c38 | ||
|
|
1120fb2564 | ||
|
|
1b8847c06b | ||
|
|
3839fdca1e | ||
|
|
f599fab439 | ||
|
|
2b7bcc87a7 | ||
|
|
0eacd41cd5 | ||
|
|
d9b6473db2 | ||
|
|
a6137e9475 | ||
|
|
dd49038b0d | ||
|
|
6efc5b42f7 | ||
|
|
7d3dacc6d7 | ||
|
|
a9d185f5e1 | ||
|
|
bd26bc09c2 | ||
|
|
97e3209222 | ||
|
|
6d30061576 | ||
|
|
2caadd1af5 | ||
|
|
b307853e39 | ||
|
|
8e8491f280 | ||
|
|
3d61d3427e | ||
|
|
928e10f0ce | ||
|
|
34ad5c43fb | ||
|
|
f7b662f936 | ||
|
|
45bf4db077 | ||
|
|
a8353307c7 | ||
|
|
6db7353264 | ||
|
|
d35dba30ed | ||
|
|
8872f256f6 | ||
|
|
c40a216434 | ||
|
|
3e3a380702 | ||
|
|
5e65e195ae | ||
|
|
02fce93d02 | ||
|
|
6051f2de81 | ||
|
|
9359db904b | ||
|
|
4c3b797003 | ||
|
|
8259a2cd5f | ||
|
|
e7502431a2 | ||
|
|
bec103de1a | ||
|
|
6c74aa1a29 | ||
|
|
af0b624a3a | ||
|
|
8760f04bf2 | ||
|
|
88494d1412 | ||
|
|
8e61e46016 | ||
|
|
3c7b6e037a | ||
|
|
49b7f0474f | ||
|
|
98c1e3d3f1 | ||
|
|
0828296abc | ||
|
|
6971ed2dce | ||
|
|
be10b1f8cb | ||
|
|
1efea20f57 | ||
|
|
fefd23f2f1 | ||
|
|
2a640daf13 | ||
|
|
0ab66c6fb5 | ||
|
|
1588f37788 | ||
|
|
66af8ad52b | ||
|
|
ff247f922d | ||
|
|
e9ba5d751f | ||
|
|
c81f3fc87e | ||
|
|
a37e2252b2 | ||
|
|
550cdbd176 | ||
|
|
f7f6d72ac0 | ||
|
|
cbe7cceb87 | ||
|
|
bb44d992ab | ||
|
|
0066ba49ea | ||
|
|
8497f25a43 | ||
|
|
6b7be38e41 | ||
|
|
9d443685ea | ||
|
|
928d3225b9 | ||
|
|
a531e8b3ed | ||
|
|
07fde46e7c | ||
|
|
136b528849 | ||
|
|
a46cbcffe8 | ||
|
|
577ba5904b | ||
|
|
1b8c8a86ec | ||
|
|
7317d80dd8 | ||
|
|
c8bc5758c3 | ||
|
|
ae43f30b0d | ||
|
|
fb579615a3 | ||
|
|
b759e77fda | ||
|
|
74b6186f7d | ||
|
|
baf5540c30 | ||
|
|
c3ddd7d5f1 | ||
|
|
b378c3a77d | ||
|
|
0cab9ba6ed | ||
|
|
8cb6fc532f | ||
|
|
35db0b4a24 | ||
|
|
4136a27de1 | ||
|
|
e73dbd4b42 | ||
|
|
b57a2d5a5f | ||
|
|
1bf5f864bc | ||
|
|
c40d1a5141 | ||
|
|
409ca4382e | ||
|
|
a71a635b7e | ||
|
|
07024a2e0f | ||
|
|
6feeaee0cd | ||
|
|
a7153f2fd8 | ||
|
|
c078f7c21d | ||
|
|
46f625c449 |
@@ -1,4 +1,2 @@
|
||||
approvers:
|
||||
- jonahjon
|
||||
reviewers:
|
||||
emeritus_approvers:
|
||||
- jonahjon
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
command: |
|
||||
mkdir -p /build-static/release
|
||||
cd /build-static/release
|
||||
cmake -DCPACK_GENERATOR=TGZ -DBUILD_BPF=Off -DBUILD_DRIVER=Off -DCMAKE_BUILD_TYPE=Release -DUSE_BUNDLED_DEPS=On -DMUSL_OPTIMIZED_BUILD=On -DFALCO_ETC_DIR=/etc/falco /source-static/falco
|
||||
cmake -DCPACK_GENERATOR=TGZ -DBUILD_BPF=Off -DBUILD_DRIVER=Off -DCMAKE_BUILD_TYPE=Release -DUSE_BUNDLED_DEPS=On -DUSE_BUNDLED_LIBELF=Off -DMUSL_OPTIMIZED_BUILD=On -DFALCO_ETC_DIR=/etc/falco /source-static/falco
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
@@ -316,56 +316,169 @@ jobs:
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
/source/falco/scripts/publish-deb -f /build/release/falco-${FALCO_VERSION}-x86_64.deb -f /build-arm64/release/falco-${FALCO_VERSION}-aarch64.deb -r deb-dev
|
||||
|
||||
"build-docker-dev":
|
||||
docker:
|
||||
- image: alpine:3.16
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- setup_remote_docker:
|
||||
version: 20.10.12
|
||||
docker_layer_caching: true
|
||||
- run:
|
||||
name: Install deps
|
||||
command: |
|
||||
apk update
|
||||
apk add make bash git docker docker-cli-buildx py3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
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/"$//')
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
|
||||
-t falcosecurity/falco-no-driver:x86_64-master \
|
||||
-t falcosecurity/falco:x86_64-master-slim \
|
||||
-t public.ecr.aws/falcosecurity/falco-no-driver:x86_64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco:x86_64-master-slim \
|
||||
docker/no-driver
|
||||
- run:
|
||||
name: Build and publish falco-dev
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
|
||||
-t falcosecurity/falco:x86_64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco:x86_64-master \
|
||||
docker/falco
|
||||
- run:
|
||||
name: Build and publish falco-driver-loader-dev
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-master \
|
||||
-t falcosecurity/falco-driver-loader:x86_64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master \
|
||||
docker/driver-loader
|
||||
|
||||
"build-docker-dev-arm64":
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202101-01
|
||||
docker_layer_caching: true
|
||||
resource_class: arm.medium
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp
|
||||
- run:
|
||||
name: Install deps
|
||||
command: |
|
||||
sudo apt update
|
||||
sudo apt install groff less python3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
name: Build and publish no-driver-dev
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /tmp/build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
|
||||
-t falcosecurity/falco-no-driver:aarch64-master \
|
||||
-t falcosecurity/falco:aarch64-master-slim \
|
||||
-t public.ecr.aws/falcosecurity/falco-no-driver:aarch64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco:aarch64-master-slim \
|
||||
docker/no-driver
|
||||
- run:
|
||||
name: Build and publish falco-dev
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /tmp/build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
|
||||
-t falcosecurity/falco:aarch64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco:aarch64-master \
|
||||
docker/falco
|
||||
- run:
|
||||
name: Build and publish falco-driver-loader-dev
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-master \
|
||||
-t falcosecurity/falco-driver-loader:aarch64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
|
||||
docker/driver-loader
|
||||
|
||||
# Publish docker packages
|
||||
"publish-docker-dev":
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
user: root
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 20.10.12
|
||||
- run:
|
||||
name: Prepare env
|
||||
name: Install deps
|
||||
command: |
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker context create falco-env
|
||||
docker buildx create falco-env --driver docker-container --use
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
sudo apt update
|
||||
sudo apt install groff less python3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to aws ECR
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
name: Build and publish no-driver-dev
|
||||
name: Upload no-driver-dev manifest to registries
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
docker buildx build --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} --platform "arm64,amd64" --push \
|
||||
-t falcosecurity/falco-no-driver:master \
|
||||
-t falcosecurity/falco:master-slim \
|
||||
-t public.ecr.aws/falcosecurity/falco-no-driver:master \
|
||||
-t public.ecr.aws/falcosecurity/falco:master-slim \
|
||||
docker/no-driver
|
||||
docker manifest create falcosecurity/falco-no-driver:master \
|
||||
falcosecurity/falco-no-driver:aarch64-master \
|
||||
falcosecurity/falco-no-driver:x86_64-master
|
||||
docker manifest push falcosecurity/falco-no-driver:master
|
||||
|
||||
docker manifest create falcosecurity/falco:master-slim \
|
||||
falcosecurity/falco:aarch64-master-slim \
|
||||
falcosecurity/falco:x86_64-master-slim
|
||||
docker manifest push falcosecurity/falco:master-slim
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:master \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-master \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-master
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:master
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:master-slim \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-master-slim \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-master-slim
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:master-slim
|
||||
- run:
|
||||
name: Build and publish dev
|
||||
name: Upload falco-dev manifest to registries
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
docker buildx build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} --platform "arm64,amd64" --push \
|
||||
-t falcosecurity/falco:master \
|
||||
-t public.ecr.aws/falcosecurity/falco:master \
|
||||
docker/falco
|
||||
docker manifest create falcosecurity/falco:master \
|
||||
falcosecurity/falco:aarch64-master \
|
||||
falcosecurity/falco:x86_64-master
|
||||
docker manifest push falcosecurity/falco:master
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:master \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-master \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-master
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:master
|
||||
- run:
|
||||
name: Build and publish dev falco-driver-loader-dev
|
||||
name: Upload falco-driver-loader-dev manifest to registries
|
||||
command: |
|
||||
docker buildx build --build-arg FALCO_IMAGE_TAG=master --platform "arm64,amd64" --push \
|
||||
-t falcosecurity/falco-driver-loader:master \
|
||||
-t public.ecr.aws/falcosecurity/falco-driver-loader:master \
|
||||
docker/driver-loader
|
||||
docker manifest create falcosecurity/falco-driver-loader:master \
|
||||
falcosecurity/falco-driver-loader:aarch64-master \
|
||||
falcosecurity/falco-driver-loader:x86_64-master
|
||||
docker manifest push falcosecurity/falco-driver-loader:master
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:master \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:master
|
||||
|
||||
# Publish the packages
|
||||
"publish-packages":
|
||||
@@ -417,62 +530,222 @@ jobs:
|
||||
command: |
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
/source/falco/scripts/publish-deb -f /build/release/falco-${FALCO_VERSION}-x86_64.deb -f /build-arm64/release/falco-${FALCO_VERSION}-aarch64.deb -r deb
|
||||
|
||||
"build-docker":
|
||||
docker:
|
||||
- image: alpine:3.16
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- setup_remote_docker:
|
||||
version: 20.10.12
|
||||
docker_layer_caching: true
|
||||
- run:
|
||||
name: Install deps
|
||||
command: |
|
||||
apk update
|
||||
apk add make bash git docker docker-cli-buildx py3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
name: Build and publish no-driver
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}" \
|
||||
-t falcosecurity/falco-no-driver:x86_64-latest \
|
||||
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim" \
|
||||
-t "falcosecurity/falco:x86_64-latest-slim" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-no-driver:x86_64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:x86_64-latest-slim" \
|
||||
docker/no-driver
|
||||
- run:
|
||||
name: Build and publish falco
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco:x86_64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:x86_64-latest" \
|
||||
docker/falco
|
||||
- run:
|
||||
name: Build and publish falco-driver-loader
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco-driver-loader:x86_64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-latest" \
|
||||
docker/driver-loader
|
||||
|
||||
"build-docker-arm64":
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202101-01
|
||||
docker_layer_caching: true
|
||||
resource_class: arm.medium
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp
|
||||
- run:
|
||||
name: Install deps
|
||||
command: |
|
||||
sudo apt update
|
||||
sudo apt install groff less python3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
name: Build and publish no-driver
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
|
||||
-t falcosecurity/falco-no-driver:aarch64-latest \
|
||||
-t falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
|
||||
-t "falcosecurity/falco:aarch64-latest-slim" \
|
||||
-t public.ecr.aws/falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
|
||||
-t "public.ecr.aws/falcosecurity/falco-no-driver:aarch64-latest" \
|
||||
-t public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
|
||||
-t "public.ecr.aws/falcosecurity/falco:aarch64-latest-slim" \
|
||||
docker/no-driver
|
||||
- run:
|
||||
name: Build and publish falco
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco:aarch64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:aarch64-latest" \
|
||||
docker/falco
|
||||
- run:
|
||||
name: Build and publish falco-driver-loader
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco-driver-loader:aarch64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-latest" \
|
||||
docker/driver-loader
|
||||
|
||||
# Publish docker packages
|
||||
"publish-docker":
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
user: root
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 20.10.12
|
||||
- run:
|
||||
name: Prepare env
|
||||
name: Install deps
|
||||
command: |
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker context create falco-env
|
||||
docker buildx create falco-env --driver docker-container --use
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
sudo apt update
|
||||
sudo apt install groff less python3-pip
|
||||
pip install awscli
|
||||
- run:
|
||||
name: Login to aws ECR
|
||||
name: Login to registries
|
||||
command: |
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
- run:
|
||||
name: Build and publish no-driver
|
||||
name: Upload no-driver manifest to registries
|
||||
command: |
|
||||
docker buildx build --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} --platform "arm64,amd64" --push \
|
||||
-t "falcosecurity/falco-no-driver:${CIRCLE_TAG}" \
|
||||
-t falcosecurity/falco-no-driver:latest \
|
||||
-t "falcosecurity/falco:${CIRCLE_TAG}-slim" \
|
||||
-t "falcosecurity/falco:latest-slim" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-no-driver:latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:latest-slim" \
|
||||
docker/no-driver
|
||||
docker manifest create falcosecurity/falco-no-driver:${CIRCLE_TAG} \
|
||||
falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
|
||||
falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push falcosecurity/falco-no-driver:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create falcosecurity/falco-no-driver:latest \
|
||||
falcosecurity/falco-no-driver:aarch64-latest \
|
||||
falcosecurity/falco-no-driver:x86_64-latest
|
||||
docker manifest push falcosecurity/falco-no-driver:latest
|
||||
|
||||
docker manifest create falcosecurity/falco:${CIRCLE_TAG}-slim \
|
||||
falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
|
||||
falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim
|
||||
docker manifest push falcosecurity/falco:${CIRCLE_TAG}-slim
|
||||
|
||||
docker manifest create falcosecurity/falco:latest-slim \
|
||||
falcosecurity/falco:aarch64-latest-slim \
|
||||
falcosecurity/falco:x86_64-latest-slim
|
||||
docker manifest push falcosecurity/falco:latest-slim
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:latest \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-latest \
|
||||
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-latest
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:latest
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:latest-slim \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-latest-slim \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-latest-slim
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:latest-slim
|
||||
- run:
|
||||
name: Build and publish falco
|
||||
name: Upload falco manifest to registries
|
||||
command: |
|
||||
docker buildx build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} --platform "arm64,amd64" --push \
|
||||
-t "falcosecurity/falco:${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco:latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:latest" \
|
||||
docker/falco
|
||||
docker manifest create falcosecurity/falco:${CIRCLE_TAG} \
|
||||
falcosecurity/falco:aarch64-${CIRCLE_TAG} \
|
||||
falcosecurity/falco:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push falcosecurity/falco:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create falcosecurity/falco:latest \
|
||||
falcosecurity/falco:aarch64-latest \
|
||||
falcosecurity/falco:x86_64-latest
|
||||
docker manifest push falcosecurity/falco:latest
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco:latest \
|
||||
public.ecr.aws/falcosecurity/falco:aarch64-latest \
|
||||
public.ecr.aws/falcosecurity/falco:x86_64-latest
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco:latest
|
||||
- run:
|
||||
name: Build and publish falco-driver-loader
|
||||
name: Upload falco-driver-loader manifest to registries
|
||||
command: |
|
||||
docker buildx build --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} --platform "arm64,amd64" --push \
|
||||
-t "falcosecurity/falco-driver-loader:${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco-driver-loader:latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG}" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:latest" \
|
||||
docker/driver-loader
|
||||
docker manifest create falcosecurity/falco-driver-loader:${CIRCLE_TAG} \
|
||||
falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG} \
|
||||
falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push falcosecurity/falco-driver-loader:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create falcosecurity/falco-driver-loader:latest \
|
||||
falcosecurity/falco-driver-loader:aarch64-latest \
|
||||
falcosecurity/falco-driver-loader:x86_64-latest
|
||||
docker manifest push falcosecurity/falco-driver-loader:latest
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG} \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG}
|
||||
|
||||
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:latest \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-latest \
|
||||
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-latest
|
||||
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:latest
|
||||
|
||||
workflows:
|
||||
version: 2.1
|
||||
@@ -481,6 +754,7 @@ workflows:
|
||||
- "build-musl"
|
||||
- "build-arm64"
|
||||
- "build-centos7"
|
||||
- "quality-static-analysis"
|
||||
- "tests-integration":
|
||||
requires:
|
||||
- "build-centos7"
|
||||
@@ -527,7 +801,7 @@ workflows:
|
||||
requires:
|
||||
- "tests-integration"
|
||||
- "tests-integration-arm64"
|
||||
- "publish-docker-dev":
|
||||
- "build-docker-dev":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
@@ -540,6 +814,31 @@ workflows:
|
||||
- "publish-packages-dev"
|
||||
- "publish-packages-deb-dev"
|
||||
- "tests-driver-loader-integration"
|
||||
- "build-docker-dev-arm64":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
filters:
|
||||
tags:
|
||||
ignore: /.*/
|
||||
branches:
|
||||
only: master
|
||||
requires:
|
||||
- "publish-packages-dev"
|
||||
- "publish-packages-deb-dev"
|
||||
- "tests-driver-loader-integration"
|
||||
- "publish-docker-dev":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
filters:
|
||||
tags:
|
||||
ignore: /.*/
|
||||
branches:
|
||||
only: master
|
||||
requires:
|
||||
- "build-docker-dev"
|
||||
- "build-docker-dev-arm64"
|
||||
# - "quality/static-analysis" # This is temporarily disabled: https://github.com/falcosecurity/falco/issues/1526
|
||||
release:
|
||||
jobs:
|
||||
@@ -595,7 +894,7 @@ workflows:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- "publish-docker":
|
||||
- "build-docker":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
@@ -607,3 +906,27 @@ workflows:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- "build-docker-arm64":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
requires:
|
||||
- "publish-packages"
|
||||
- "publish-packages-deb"
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- "publish-docker":
|
||||
context:
|
||||
- falco
|
||||
- test-infra
|
||||
requires:
|
||||
- "build-docker"
|
||||
- "build-docker-arm64"
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
aks
|
||||
creat
|
||||
chage
|
||||
ro
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -22,6 +22,8 @@
|
||||
|
||||
> /kind feature
|
||||
|
||||
> /kind release
|
||||
|
||||
> If contributing rules or changes to rules, please make sure to also uncomment one of the following line:
|
||||
|
||||
> /kind rule-update
|
||||
@@ -46,6 +48,8 @@ Please remove the leading whitespace before the `/kind <>` you uncommented.
|
||||
|
||||
> /area proposals
|
||||
|
||||
> /area CI
|
||||
|
||||
<!--
|
||||
Please remove the leading whitespace before the `/area <>` you uncommented.
|
||||
-->
|
||||
|
||||
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -14,6 +14,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Update base image
|
||||
run: sudo apt update -y
|
||||
@@ -47,6 +48,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Update base image
|
||||
run: sudo apt update -y
|
||||
@@ -80,6 +82,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Update base image
|
||||
run: sudo apt update -y
|
||||
@@ -113,6 +116,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Update base image
|
||||
run: sudo apt update -y
|
||||
@@ -151,6 +155,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
path: falco
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Link falco repo to /source/falco
|
||||
run: |
|
||||
|
||||
75
.github/workflows/codeql.yaml
vendored
Normal file
75
.github/workflows/codeql.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Update base image
|
||||
run: sudo apt update -y
|
||||
|
||||
- name: Install build dependencies
|
||||
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libyaml-dev libc-ares-dev libprotobuf-dev protobuf-compiler libjq-dev libyaml-cpp-dev libgrpc++-dev protobuf-compiler-grpc rpm libelf-dev cmake build-essential libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm git -y
|
||||
|
||||
- name: Prepare project
|
||||
run: |
|
||||
mkdir build
|
||||
pushd build
|
||||
cmake -DBUILD_BPF=On ..
|
||||
popd
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pushd build
|
||||
KERNELDIR=/lib/modules/$(uname -r)/build make -j4 all
|
||||
popd
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
2
.github/workflows/codespell.yml
vendored
2
.github/workflows/codespell.yml
vendored
@@ -11,4 +11,4 @@ jobs:
|
||||
skip: .git
|
||||
ignore_words_file: .codespellignore
|
||||
check_filenames: true
|
||||
check_hidden: true
|
||||
check_hidden: false
|
||||
|
||||
@@ -39,7 +39,7 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
* [Logz.io](https://logz.io/) - Logz.io is a cloud observability platform for modern engineering teams. The Logz.io platform consists of three products — Log Management, Infrastructure Monitoring, and Cloud SIEM — that work together to unify the jobs of monitoring, troubleshooting, and security. We empower engineers to deliver better software by offering the world's most popular open source observability tools — the ELK Stack, Grafana, and Jaeger — in a single, easy to use, and powerful platform purpose-built for monitoring distributed cloud environments. Cloud SIEM supports data from multiple sources, including Falco's alerts, and offers useful rules and dashboards content to visualize and manage incidents across your systems in a unified UI.
|
||||
* https://logz.io/blog/k8s-security-with-falco-and-cloud-siem/
|
||||
|
||||
* [MathWorks](https://mathworks.com) - MathWorks develops mathematical computing software for engineers and scientists. MathWorks uses Falco for Kubernetes threat detection, unexpected application behavior, and maps Falco rules to their cloud infrastructure's security kill chain model. MathWorks presented their Falco use case at [KubeCon + CloudNativeCon North America 2020](https://www.youtube.com/watch?v=L-5RYBTV010).
|
||||
* [MathWorks](https://mathworks.com) - MathWorks develops mathematical computing software for engineers and scientists. MathWorks uses Falco for Kubernetes threat detection, unexpected application behavior, and maps Falco rules to their cloud infrastructure's security kill chain model. MathWorks presented their Falco use case at [KubeCon + CloudNativeCon North America 2020](https://www.youtube.com/watch?v=L-5RYBTV010).
|
||||
|
||||
* [Pocteo](https://pocteo.co) - Pocteo helps with Kubernetes adoption in enterprises by providing a variety of services such as training, consulting, auditing and mentoring. We build CI/CD pipelines the GitOps way, as well as design and run k8s clusters. Pocteo uses Falco as a runtime monitoring system to secure clients' workloads against suspicious behavior and ensure k8s pods immutability. We also use Falco to collect, process and act on security events through a response engine and serverless functions.
|
||||
|
||||
@@ -70,12 +70,16 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call driver. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended Falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-defined infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc.
|
||||
|
||||
* [Xenit AB](https://xenit.se/contact/) Xenit is a growth company with services within cloud and digital transformation. We provide an open-source Kubernetes framework that we leverage to help our customers get their applications to production as quickly and as securely as possible. We use Falco's detection capabilities to identify anomalous behaviour within our clusters in both Azure and AWS.
|
||||
|
||||
## Projects that use Falco libs
|
||||
|
||||
* [R6/Phoenix](https://r6security.com/) is an attack surface protection company that uses moving target defense to provide fully automated, proactive and devops friendly security to its customers. There are a set of policies you can add to enable the moving target defense capabilities. Some of them are triggered by a combination of Falco's findings. You can kill, restart and rename pods according to the ever changing policies.
|
||||
|
||||
* [SysFlow](https://sysflow.io) SysFlow is a cloud-native system telemetry framework that focuses on data abstraction, behavioral analytics, and noise reduction. At its core, SysFlow exposes a compact open telemetry format that records workload behaviors by connecting event and flow representations of process control flows, file interactions, and network communications. The resulting abstraction encodes a graph structure that enables provenance reasoning on host and container environments, and fast retrieval of security-relevant information.
|
||||
|
||||
* [StackRox](https://stackrox.io) is the industry’s first Kubernetes-native security platform enabling organizations to build, deploy, and run cloud-native applications securely. The platform works with Kubernetes environments and integrates with DevOps and security tools, enabling teams to operationalize and secure their supply chain, infrastructure, and workloads. StackRox aims to harness containerized applications’ development speed while giving operations and security teams greater context and risk profiling. StackRox leverages cloud-native principles and declarative artifacts to automate DevSecOps best practices.
|
||||
|
||||
## Adding a name
|
||||
|
||||
If you would like to add your name to this file, submit a pull request with your change.
|
||||
|
||||
163
CHANGELOG.md
163
CHANGELOG.md
@@ -1,5 +1,168 @@
|
||||
# Change Log
|
||||
|
||||
## v0.33.1
|
||||
|
||||
Released on 2022-11-24
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* update(falco): fix container-gvisor and kubernetes-gvisor print options [[#2288](https://github.com/falcosecurity/falco/pull/2288)]
|
||||
* Update libs to 0.9.2, fixing potential CLBO on gVisor+Kubernetes and crash with eBPF when some CPUs are offline [[#2299](https://github.com/falcosecurity/falco/pull/2299)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
|
||||
## v0.33.0
|
||||
|
||||
Released on 2022-10-19
|
||||
|
||||
### Major Changes
|
||||
|
||||
|
||||
* new: add a `drop_pct` referred to the global number of events [[#2130](https://github.com/falcosecurity/falco/pull/2130)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new: print some info about eBPF and enabled sources when Falco starts [[#2133](https://github.com/falcosecurity/falco/pull/2133)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new(userspace): print architecture information [[#2147](https://github.com/falcosecurity/falco/pull/2147)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new(CI): add CodeQL security scanning to Falco. [[#2171](https://github.com/falcosecurity/falco/pull/2171)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new: configure syscall buffer dimension from Falco [[#2214](https://github.com/falcosecurity/falco/pull/2214)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new(cmdline): add development support for modern BPF probe [[#2221](https://github.com/falcosecurity/falco/pull/2221)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* new(falco-driver-loader): `DRIVERS_REPO` now supports the use of multiple download URLs (comma separated) [[#2165](https://github.com/falcosecurity/falco/pull/2165)] - [@IanRobertson-wpe](https://github.com/IanRobertson-wpe)
|
||||
* new(userspace/engine): support alternative plugin version requirements in checks [[#2190](https://github.com/falcosecurity/falco/pull/2190)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* new: support running multiple event sources in parallel [[#2182](https://github.com/falcosecurity/falco/pull/2182)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* new(userspace/falco): automatically create paths for grpc unix socket and gvisor endpoint. [[#2189](https://github.com/falcosecurity/falco/pull/2189)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* new(scripts): allow falco-driver-loader to properly distinguish any ubuntu flavor [[#2178](https://github.com/falcosecurity/falco/pull/2178)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* new: add option to enable event sources selectively [[#2085](https://github.com/falcosecurity/falco/pull/2085)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* docs(falco-driver-loader): add some comments in `falco-driver-loader` [[#2153](https://github.com/falcosecurity/falco/pull/2153)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* update(cmake): use latest libs tag `0.9.0` [[#2257](https://github.com/falcosecurity/falco/pull/2257)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* update(.circleci): re-enabled cppcheck [[#2186](https://github.com/falcosecurity/falco/pull/2186)] - [@leogr](https://github.com/leogr)
|
||||
* update(userspace/engine): improve falco files loading performance [[#2151](https://github.com/falcosecurity/falco/pull/2151)] - [@VadimZy](https://github.com/VadimZy)
|
||||
* update(cmake): use latest driver tag 3.0.1+driver [[#2251](https://github.com/falcosecurity/falco/pull/2251)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* update(userspace/falco)!: adapt stats writer for multiple parallel event sources [[#2182](https://github.com/falcosecurity/falco/pull/2182)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/engine): remove falco engine APIs that returned a required_engine_version [[#2096](https://github.com/falcosecurity/falco/pull/2096)] - [@mstemm](https://github.com/mstemm)
|
||||
* update(userspace/engine): add some small changes to rules matching that reduce cpu usage with high event volumes (> 1M syscalls/sec) [[#2210](https://github.com/falcosecurity/falco/pull/2210)] - [@mstemm](https://github.com/mstemm)
|
||||
* rules: added process IDs to default rules [[#2211](https://github.com/falcosecurity/falco/pull/2211)] - [@spyder-kyle](https://github.com/spyder-kyle)
|
||||
* update(scripts/debian): falco.service systemd unit is now cleaned-up during (re)install and removal via the DEB and RPM packages [[#2138](https://github.com/falcosecurity/falco/pull/2138)] - [@Happy-Dude](https://github.com/Happy-Dude)
|
||||
* update(userspace/falco): move on from deprecated libs API for printing event list [[#2253](https://github.com/falcosecurity/falco/pull/2253)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* chore(userspace/falco): improve cli helper and log options with debug level [[#2252](https://github.com/falcosecurity/falco/pull/2252)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update(userspace): minor pre-release improvements [[#2236](https://github.com/falcosecurity/falco/pull/2236)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update: bumped libs to fd46dd139a8e35692a7d40ab2f0ed2016df827cf. [[#2201](https://github.com/falcosecurity/falco/pull/2201)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* update!: gVisor sock default path changed from `/tmp/gvisor.sock` to `/run/falco/gvisor.sock` [[#2163](https://github.com/falcosecurity/falco/pull/2163)] - [@vjjmiras](https://github.com/vjjmiras)
|
||||
* update!: gRPC server sock default path changed from `/run/falco.sock.sock` to `/run/falco/falco.sock` [[#2163](https://github.com/falcosecurity/falco/pull/2163)] - [@vjjmiras](https://github.com/vjjmiras)
|
||||
* update(scripts/falco-driver-loader): minikube environment is now correctly detected [[#2191](https://github.com/falcosecurity/falco/pull/2191)] - [@alacuku](https://github.com/alacuku)
|
||||
* update(rules/falco_rules.yaml): `required_engine_version` changed to 13 [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* refactor(userspace/falco): re-design stats writer and make it thread-safe [[#2109](https://github.com/falcosecurity/falco/pull/2109)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/falco): make signal handlers thread safe [[#2091](https://github.com/falcosecurity/falco/pull/2091)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/engine): strengthen and document thread-safety guarantees of falco_engine::process_event [[#2082](https://github.com/falcosecurity/falco/pull/2082)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update(userspace/falco): make webserver threadiness configurable [[#2090](https://github.com/falcosecurity/falco/pull/2090)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/falco): reduce app actions dependency on app state and inspector [[#2097](https://github.com/falcosecurity/falco/pull/2097)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update(userspace/falco): use move semantics in falco logger [[#2095](https://github.com/falcosecurity/falco/pull/2095)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update: use `FALCO_HOSTNAME` env var to override the hostname value [[#2174](https://github.com/falcosecurity/falco/pull/2174)] - [@leogr](https://github.com/leogr)
|
||||
* update: bump libs and driver versions to 6599e2efebce30a95f27739d655d53f0d5f686e4 [[#2177](https://github.com/falcosecurity/falco/pull/2177)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/falco): make output rate limiter optional and output engine explicitly thread-safe [[#2139](https://github.com/falcosecurity/falco/pull/2139)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* update(falco.yaml)!: notification rate limiter disabled by default. [[#2139](https://github.com/falcosecurity/falco/pull/2139)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: compute the `drop ratio` in the right way [[#2128](https://github.com/falcosecurity/falco/pull/2128)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(falco_service): falco service needs to write under /sys/module/falco [[#2238](https://github.com/falcosecurity/falco/pull/2238)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(userspace): cleanup output of ruleset validation result [[#2248](https://github.com/falcosecurity/falco/pull/2248)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(userspace): properly print ignored syscalls messages when not in `-A` mode [[#2243](https://github.com/falcosecurity/falco/pull/2243)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(falco): clarify pid/tid and container info in gvisor [[#2223](https://github.com/falcosecurity/falco/pull/2223)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
* fix(userspace/engine): avoid reading duplicate exception values [[#2200](https://github.com/falcosecurity/falco/pull/2200)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix: hostname was not present when `json_output: true` [[#2174](https://github.com/falcosecurity/falco/pull/2174)] - [@leogr](https://github.com/leogr)
|
||||
|
||||
|
||||
### Rule Changes
|
||||
|
||||
* rule(macro: known_gke_mount_in_privileged_containers): add new macro [[#2198](https://github.com/falcosecurity/falco/pull/2198)] - [@hi120ki](https://github.com/hi120ki)
|
||||
* rule(Mount Launched in Privileged Container): add GKE default pod into allowlist in Mount Launched of Privileged Container rule [[#2198](https://github.com/falcosecurity/falco/pull/2198)] - [@hi120ki](https://github.com/hi120ki)
|
||||
* rule(list: known_binaries_to_read_environment_variables_from_proc_files): add new list [[#2193](https://github.com/falcosecurity/falco/pull/2193)] - [@hi120ki](https://github.com/hi120ki)
|
||||
* rule(Read environment variable from /proc files): add rule to detect an attempt to read process environment variables from /proc files [[#2193](https://github.com/falcosecurity/falco/pull/2193)] - [@hi120ki](https://github.com/hi120ki)
|
||||
* rule(macro: k8s_containers): add falco no-driver images [[#2234](https://github.com/falcosecurity/falco/pull/2234)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* rule(macro: open_file_failed): add new macro [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: directory_traversal): add new macro [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Directory traversal monitored file read): add new rule [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Modify Container Entrypoint): new rule created to detect CVE-2019-5736 [[#2188](https://github.com/falcosecurity/falco/pull/2188)] - [@darryk10](https://github.com/darryk10)
|
||||
* rule(Program run with disallowed http proxy env)!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Container Drift Detected (chmod))!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Container Drift Detected (open+create))!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Packet socket created in container)!: removed consider_packet_socket_communication macro [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_packet_socket_communication)!: remove unused macro [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Interpreted procs outbound network activity)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Interpreted procs inbound network activity)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Contact cloud metadata service from container)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_interpreted_outbound)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_interpreted_inbound)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_metadata_access)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Unexpected outbound connection destination)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Unexpected inbound connection source)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Read Shell Configuration File)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Schedule Cron Jobs)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Launch Suspicious Network Tool on Host)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Create Hidden Files or Directories)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Outbound or Inbound Traffic not to Authorized Server Process and Port)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Network Connection outside Local Subnet)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_all_outbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_all_inbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_shell_config_reads)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_all_cron_jobs)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_all_inbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_hidden_file_creation)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: allowed_port)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: enabled_rule_network_only_subnet)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_userfaultfd_activities)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(macro: consider_all_chmods)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Set Setuid or Setgid bit)!: removed consider_all_chmods macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Container Drift Detected (chmod))!: removed consider_all_chmods macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
* rule(Unprivileged Delegation of Page Faults Handling to a Userspace Process)!: removed consider_userfaultfd_activities macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum)
|
||||
|
||||
|
||||
### Non user-facing changes
|
||||
|
||||
* new(userspace): support `SCAP_FILTERED_EVENT` return code [[#2148](https://github.com/falcosecurity/falco/pull/2148)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* chore(test/utils): remove unused script [[#2157](https://github.com/falcosecurity/falco/pull/2157)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* Enrich pull request template [[#2162](https://github.com/falcosecurity/falco/pull/2162)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* vote: update(OWNERS): add Andrea Terzolo to owners [[#2185](https://github.com/falcosecurity/falco/pull/2185)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(CI): codespell should ignore `ro` word [[#2173](https://github.com/falcosecurity/falco/pull/2173)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* chore: bump plugin version [[#2256](https://github.com/falcosecurity/falco/pull/2256)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(userspace/falco): avoid using CPU when main thread waits for parallel event sources [[#2255](https://github.com/falcosecurity/falco/pull/2255)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(scripts): inject kmod script fails with some systemd versions [[#2250](https://github.com/falcosecurity/falco/pull/2250)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* chore(userspace/falco): make logging optional when terminating, restarting, and reopening outputs [[#2249](https://github.com/falcosecurity/falco/pull/2249)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* chore: bump libs version [[#2244](https://github.com/falcosecurity/falco/pull/2244)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* update(userspace): solve warnings and performance tips from cppcheck [[#2247](https://github.com/falcosecurity/falco/pull/2247)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(userspace/falco): make signal termination more robust with multi-threading [[#2235](https://github.com/falcosecurity/falco/pull/2235)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(userspace/falco): make termination and signal handlers more stable [[#2239](https://github.com/falcosecurity/falco/pull/2239)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(userspace): safely check string bounded access [[#2237](https://github.com/falcosecurity/falco/pull/2237)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* chore: bump libs/driver to the latest release branch commit [[#2232](https://github.com/falcosecurity/falco/pull/2232)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(userspace/falco): check plugin requirements when validating rule files [[#2233](https://github.com/falcosecurity/falco/pull/2233)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* fix(userspace): add explicit constructors and initializations [[#2229](https://github.com/falcosecurity/falco/pull/2229)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* Add StackRox to adopters [[#2187](https://github.com/falcosecurity/falco/pull/2187)] - [@Molter73](https://github.com/Molter73)
|
||||
* fix(process_events): check the return value of `open_live_inspector` [[#2215](https://github.com/falcosecurity/falco/pull/2215)] - [@Andreagit97](https://github.com/Andreagit97)
|
||||
* fix(userspace/engine): properly include stdexcept header to fix build. [[#2197](https://github.com/falcosecurity/falco/pull/2197)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* refactor(userspace/engine): split rule loader classes for a more testable design [[#2206](https://github.com/falcosecurity/falco/pull/2206)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* chore(OWNERS): cleanup inactive reviewer [[#2204](https://github.com/falcosecurity/falco/pull/2204)] - [@leogr](https://github.com/leogr)
|
||||
* fix(circleci): falco-driver-loader image build must be done starting from just-pushed falco master image. [[#2194](https://github.com/falcosecurity/falco/pull/2194)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* Support condition parse errors in rule loading results [[#2155](https://github.com/falcosecurity/falco/pull/2155)] - [@mstemm](https://github.com/mstemm)
|
||||
* docs: readme update [[#2183](https://github.com/falcosecurity/falco/pull/2183)] - [@leogr](https://github.com/leogr)
|
||||
* cleanup: rename legacy references [[#2180](https://github.com/falcosecurity/falco/pull/2180)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* refactor(userspace/engine): increase const coherence in falco engine [[#2081](https://github.com/falcosecurity/falco/pull/2081)] - [@jasondellaluce](https://github.com/jasondellaluce)
|
||||
* Rules result handle multiple files [[#2158](https://github.com/falcosecurity/falco/pull/2158)] - [@mstemm](https://github.com/mstemm)
|
||||
* fix: print full rule load errors/warnings without verbose/-v [[#2156](https://github.com/falcosecurity/falco/pull/2156)] - [@mstemm](https://github.com/mstemm)
|
||||
|
||||
|
||||
## v0.32.2
|
||||
|
||||
Released on 2022-08-09
|
||||
|
||||
### Major Changes
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: Added ARCH to bpf download URL [[#2142](https://github.com/falcosecurity/falco/pull/2142)] - [@eric-engberg](https://github.com/eric-engberg)
|
||||
|
||||
|
||||
## v0.32.1
|
||||
|
||||
Released on 2022-07-11
|
||||
|
||||
@@ -27,6 +27,14 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Modern BPF is not supported on not Linux systems and in MINIMAL_BUILD
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
option(BUILD_FALCO_MODERN_BPF "Build modern BPF support for Falco" OFF)
|
||||
if(BUILD_FALCO_MODERN_BPF)
|
||||
add_definitions(-DHAS_MODERN_BPF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# We shouldn't need to set this, see https://gitlab.kitware.com/cmake/cmake/-/issues/16419
|
||||
option(EP_UPDATE_DISCONNECTED "ExternalProject update disconnected" OFF)
|
||||
if (${EP_UPDATE_DISCONNECTED})
|
||||
@@ -56,16 +64,19 @@ if(NOT DEFINED FALCO_ETC_DIR)
|
||||
set(FALCO_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falco")
|
||||
endif()
|
||||
|
||||
if(NOT DRAIOS_DEBUG_FLAGS)
|
||||
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
|
||||
# This will be used to print the architecture for which Falco is compiled.
|
||||
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||
|
||||
if(NOT FALCO_EXTRA_DEBUG_FLAGS)
|
||||
set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG")
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "debug")
|
||||
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
|
||||
set(KBUILD_FLAGS "${FALCO_EXTRA_DEBUG_FLAGS} ${FALCO_EXTRA_FEATURE_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_BUILD_TYPE "release")
|
||||
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
|
||||
set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}")
|
||||
add_definitions(-DBUILD_TYPE_RELEASE)
|
||||
endif()
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
@@ -86,7 +97,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "release")
|
||||
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||
endif()
|
||||
|
||||
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${FALCO_EXTRA_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
|
||||
if(BUILD_WARNINGS_AS_ERRORS)
|
||||
set(CMAKE_SUPPRESSED_WARNINGS
|
||||
@@ -98,8 +109,8 @@ endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
|
||||
@@ -111,6 +122,11 @@ set(DRIVER_NAME "falco")
|
||||
set(DRIVER_DEVICE_NAME "falco")
|
||||
set(DRIVERS_REPO "https://download.falco.org/driver")
|
||||
|
||||
# If no path is provided, try to search the BPF probe in: `home/.falco/falco-bpf.o`
|
||||
# This is the same fallback that we had in the libraries: `SCAP_PROBE_BPF_FILEPATH`.
|
||||
set(FALCO_PROBE_BPF_FILEPATH ".${DRIVER_NAME}/${DRIVER_NAME}-bpf.o")
|
||||
add_definitions(-DFALCO_PROBE_BPF_FILEPATH="${FALCO_PROBE_BPF_FILEPATH}")
|
||||
|
||||
if(NOT DEFINED FALCO_COMPONENT_NAME)
|
||||
set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
@@ -165,9 +181,6 @@ include(cxxopts)
|
||||
# One TBB
|
||||
include(tbb)
|
||||
|
||||
#string-view-lite
|
||||
include(DownloadStringViewLite)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
include(zlib)
|
||||
include(cares)
|
||||
|
||||
12
OWNERS
12
OWNERS
@@ -1,18 +1,12 @@
|
||||
approvers:
|
||||
- fntlnz
|
||||
- kris-nova
|
||||
- leodido
|
||||
- mstemm
|
||||
- leogr
|
||||
- jasondellaluce
|
||||
- fededp
|
||||
- andreagit97
|
||||
reviewers:
|
||||
- fntlnz
|
||||
- kaizhe
|
||||
emeritus_approvers:
|
||||
- fntlnz
|
||||
- kris-nova
|
||||
- leodido
|
||||
- mfdii
|
||||
- mstemm
|
||||
- leogr
|
||||
- jasondellaluce
|
||||
- fededp
|
||||
|
||||
65
README.md
65
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
Want to talk? Join us on the [#falco](https://kubernetes.slack.com/messages/falco) channel in the [Kubernetes Slack](https://slack.k8s.io).
|
||||
|
||||
### Latest releases
|
||||
## Latest releases
|
||||
|
||||
Read the [change log](CHANGELOG.md).
|
||||
|
||||
@@ -51,12 +51,12 @@ Notes:
|
||||
|
||||
| | development | stable |
|
||||
|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| rpm | [][1] | [][2] |
|
||||
| deb | [][3] | [][4] |
|
||||
| binary | [][5] | [][6] |
|
||||
| rpm-arm64 | [][1] | [][2] |
|
||||
| deb-arm64 | [][3] | [][4] |
|
||||
| binary-arm64 | [][7] | [][8] |
|
||||
| rpm-x86_64 | [][1] | [][2] |
|
||||
| deb-x86_64 | [][3] | [][4] |
|
||||
| binary-x86_64 | [][5] | [][6] |
|
||||
| rpm-aarch64 | [][1] | [][2] |
|
||||
| deb-aarch64 | [][3] | [][4] |
|
||||
| binary-aarch64 | [][7] | [][8] |
|
||||
|
||||
---
|
||||
|
||||
@@ -66,7 +66,7 @@ Falco can also be extended to other data sources by using plugins.
|
||||
Falco has a rich set of security rules specifically built for Kubernetes, Linux, and cloud-native.
|
||||
If a rule is violated in a system, Falco will send an alert notifying the user of the violation and its severity.
|
||||
|
||||
### What can Falco detect?
|
||||
## What can Falco detect?
|
||||
|
||||
Falco can detect and alert on any behavior that involves making Linux system calls.
|
||||
Falco alerts can be triggered by the use of specific system calls, their arguments, and by properties of the calling process.
|
||||
@@ -80,11 +80,11 @@ For example, Falco can easily detect incidents including but not limited to:
|
||||
- A standard system binary, such as `ls`, is making an outbound network connection.
|
||||
- A privileged pod is started in a Kubernetes cluster.
|
||||
|
||||
### Installing Falco
|
||||
## Installing Falco
|
||||
|
||||
If you would like to run Falco in **production** please adhere to the [official installation guide](https://falco.org/docs/getting-started/installation/).
|
||||
|
||||
##### Kubernetes
|
||||
### Kubernetes
|
||||
|
||||
| Tool | Link | Note |
|
||||
|----------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||
@@ -93,22 +93,20 @@ If you would like to run Falco in **production** please adhere to the [official
|
||||
| Kind | [Tutorial](https://falco.org/docs/getting-started/third-party/#kind) | Running Falco with kind requires a driver on the host system. |
|
||||
| GKE | [Tutorial](https://falco.org/docs/getting-started/third-party/#gke) | We suggest using the eBPF driver for running Falco on GKE. |
|
||||
|
||||
### Developing
|
||||
## Developing
|
||||
|
||||
Falco is designed to be extensible such that it can be built into cloud-native applications and infrastructure.
|
||||
|
||||
Falco has a [gRPC](https://falco.org/docs/grpc/) endpoint and an API defined in [protobuf](https://github.com/falcosecurity/falco/blob/master/userspace/falco/outputs.proto).
|
||||
The Falco Project supports various SDKs for this endpoint.
|
||||
|
||||
##### SDKs
|
||||
### SDKs
|
||||
|
||||
| Language | Repository |
|
||||
|----------|---------------------------------------------------------|
|
||||
| Go | [client-go](https://github.com/falcosecurity/client-go) |
|
||||
| Rust | [client-rs](https://github.com/falcosecurity/client-rs) |
|
||||
| Python | [client-py](https://github.com/falcosecurity/client-py) |
|
||||
|
||||
### Plugins
|
||||
## Plugins
|
||||
|
||||
Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that extends it to potentially any cloud detection scenario. Plugins are shared libraries that conform to a documented API and allow for:
|
||||
|
||||
@@ -118,18 +116,18 @@ Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that exte
|
||||
The Falco Project maintains [various plugins](https://github.com/falcosecurity/plugins) and provides SDKs for plugin development.
|
||||
|
||||
|
||||
##### SDKs
|
||||
### SDKs
|
||||
|
||||
| Language | Repository |
|
||||
|----------|-------------------------------------------------------------------------------|
|
||||
| Go | [falcosecurity/plugin-sdk-go](https://github.com/falcosecurity/plugin-sdk-go) |
|
||||
|
||||
|
||||
### Documentation
|
||||
## Documentation
|
||||
|
||||
The [Official Documentation](https://falco.org/docs/) is the best resource to learn about Falco.
|
||||
|
||||
### Join the Community
|
||||
## Join the Community
|
||||
|
||||
To get involved with The Falco Project please visit [the community repository](https://github.com/falcosecurity/community) to find more.
|
||||
|
||||
@@ -139,23 +137,36 @@ How to reach out?
|
||||
- [Join the Falco mailing list](https://lists.cncf.io/g/cncf-falco-dev)
|
||||
- [Read the Falco documentation](https://falco.org/docs/)
|
||||
|
||||
## How to contribute
|
||||
|
||||
### Contributing
|
||||
|
||||
See the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md).
|
||||
|
||||
### Security Audit
|
||||
See the [contributing guide](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/falcosecurity/evolution/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Security Audit
|
||||
|
||||
A third party security audit was performed by Cure53, you can see the full report [here](./audits/SECURITY_AUDIT_2019_07.pdf).
|
||||
|
||||
### Reporting security vulnerabilities
|
||||
## Reporting security vulnerabilities
|
||||
|
||||
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/master/SECURITY.md).
|
||||
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/main/SECURITY.md).
|
||||
|
||||
### License Terms
|
||||
## License
|
||||
|
||||
Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
|
||||
|
||||
## Project Evolution
|
||||
|
||||
The [falcosecurity/evolution](https://github.com/falcosecurity/evolution) repository is the official space for the community to work together, discuss ideas, and document processes. It is also a place to make decisions. Check it out to find more helpful resources.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md)
|
||||
- [Code Of Conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md)
|
||||
- [Maintainers Guidelines](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS_GUIDELINES.md)
|
||||
- [Maintainers List](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS.md)
|
||||
- [Repositories Guidelines](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md)
|
||||
- [Repositories List](https://github.com/falcosecurity/evolution/blob/main/README.md#repositories)
|
||||
- [Adopters List](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md)
|
||||
|
||||
|
||||
[1]: https://download.falco.org/?prefix=packages/rpm-dev/
|
||||
[2]: https://download.falco.org/?prefix=packages/rpm/
|
||||
@@ -164,4 +175,4 @@ Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
|
||||
[5]: https://download.falco.org/?prefix=packages/bin-dev/x86_64/
|
||||
[6]: https://download.falco.org/?prefix=packages/bin/x86_64/
|
||||
[7]: https://download.falco.org/?prefix=packages/bin-dev/aarch64/
|
||||
[8]: https://download.falco.org/?prefix=packages/bin/aarch64/
|
||||
[8]: https://download.falco.org/?prefix=packages/bin/aarch64/
|
||||
|
||||
110
RELEASE.md
110
RELEASE.md
@@ -1,18 +1,77 @@
|
||||
# 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.
|
||||
## Overview
|
||||
|
||||
This document provides the process to create a new Falco release. In addition, it provides information about the versioning of the Falco components. At a high level each Falco release consists of the following main components:
|
||||
|
||||
- Falco binary (userspace)
|
||||
- Falco kernel driver object files (kernel space)
|
||||
- Option 1: Kernel module (`.ko` files)
|
||||
- Option 2: eBPF (`.o` files)
|
||||
- Falco config and primary rules `.yaml` files (userspace)
|
||||
- Falco plugins (userspace - optional)
|
||||
|
||||
One nice trait about releasing separate artifacts for userspace and kernel space is that Falco is amenable to supporting a large array of environments, that is, multiple kernel versions, distros and architectures (see `libs` [driver - kernel version support matrix](https://github.com/falcosecurity/libs#drivers-officially-supported-architectures)). The Falco project manages the release of both the Falco userspace binary and pre-compiled Falco kernel drivers for the most popular kernel versions and distros. The build and publish process is managed by the [test-infra](https://github.com/falcosecurity/test-infra) repo. The Falco userspace executable includes bundled dependencies, so that it can be run from anywhere.
|
||||
|
||||
The Falco project also publishes all sources for each component. In fact, sources are included in the Falco release in the same way as some plugins (k8saudit and cloudtrail) as well as the rules that are shipped together with Falco. This empowers the end user to audit the integrity of the project as well as build kernel drivers for custom kernels or not officially supported kernels / distros (see [driverkit](https://github.com/falcosecurity/driverkit) for more information). While the Falco project is deeply embedded into an ecosystem of supporting [Falco sub-projects](https://github.com/falcosecurity/evolution) that aim to make the deployment of Falco easy, user-friendly, extendible and cloud-native, core Falco is split across two repos, [falco](https://github.com/falcosecurity/falco) (this repo) and [libs](https://github.com/falcosecurity/libs). The `libs` repo contains >90% of Falco's core features and is the home of each of the kernel drivers and engines. More details are provided in the [Falco Components Versioning](#falco-components-versioning) section.
|
||||
|
||||
Finally, the release process follows a transparent process described in more detail in the following sections and the official [Falco docs](https://falco.org/) contain rich information around building, installing and using Falco.
|
||||
|
||||
|
||||
### Falco Binaries, Rules and Sources Artifacts - Quick Links
|
||||
|
||||
The Falco project publishes all sources and the Falco userspace binaries as GitHub releases. Rules are also released in the GitHub tree Falco release tag.
|
||||
|
||||
- [Falco Releases](https://github.com/falcosecurity/falco/releases)
|
||||
- `tgz`, `rpm` and `deb` Falco binary packages (contains sources, including driver sources, Falco rules as well as k8saudit and cloudtrail plugins)
|
||||
- `tgz`, `zip` source code
|
||||
- [Libs Releases](https://github.com/falcosecurity/libs/releases)
|
||||
- `tgz`, `zip` source code
|
||||
- Falco Rules (GitHub tree approach)
|
||||
- RELEASE="x.y.z", `https://github.com/falcosecurity/falco/tree/${RELEASE}/rules`
|
||||
|
||||
|
||||
Alternatively Falco binaries or plugins can be downloaded from the Falco Artifacts repo.
|
||||
|
||||
- [Falco Artifacts Repo Packages Root](https://download.falco.org/?prefix=packages/)
|
||||
- [Falco Artifacts Repo Plugins Root](https://download.falco.org/?prefix=plugins/)
|
||||
|
||||
|
||||
### Falco Drivers Artifacts Repo - Quick Links
|
||||
|
||||
|
||||
The Falco project publishes all drivers for each release for all popular kernel versions / distros and `x86_64` and `aarch64` architectures to the Falco project managed Artifacts repo. The Artifacts repo follows standard directory level conventions. The respective driver object file is prefixed by distro and named / versioned by kernel release - `$(uname -r)`. Pre-compiled drivers are released with a [best effort](https://github.com/falcosecurity/falco/blob/master/proposals/20200818-artifacts-storage.md#notice) notice. This is because gcc (`kmod`) and clang (`bpf`) compilers or for example the eBPF verifier are not perfect. More details around driver versioning and driver compatibility are provided in the [Falco Components Versioning](#falco-components-versioning) section. Short preview: If you use the standard Falco setup leveraging driver-loader, [driver-loader script](https://github.com/falcosecurity/falco/blob/master/scripts/falco-driver-loader) will fetch the kernel space artifact (object file) corresponding to the default `DRIVER_VERSION` Falco was shipped with.
|
||||
|
||||
- [Falco Artifacts Repo Drivers Root](https://download.falco.org/?prefix=driver/)
|
||||
- Option 1: Kernel module (`.ko` files) - all under same driver version directory
|
||||
- Option 2: eBPF (`.o` files) - all under same driver version directory
|
||||
|
||||
|
||||
### Timeline
|
||||
|
||||
Falco releases are due to happen 3 times per year. Our current schedule sees a new release by the end of January, May, and September each year. Hotfix releases can happen whenever it's needed.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Procedures
|
||||
|
||||
The release process is mostly automated requiring only a few manual steps to initiate and complete it.
|
||||
|
||||
Moreover, we need to assign owners for each release (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).
|
||||
|
||||
At a high level each Falco release needs to follow a pre-determined sequencing of releases and build order:
|
||||
|
||||
- [1 - 3] `libs` (+ `driver`) and `plugins` components releases
|
||||
- [4] Falco driver pre-compiled object files push to Falco's Artifacts repo
|
||||
- [5] Falco userspace binary + rules release
|
||||
|
||||
Finally, on the proposed due date the assignees for the upcoming release proceed with the processes described below.
|
||||
|
||||
## Pre-Release Checklist
|
||||
|
||||
Before cutting a release we need to do some homework in the Falco repository. This should take 5 minutes using the GitHub UI.
|
||||
Prior to cutting a release the following preparatory steps should take 5 minutes using the GitHub UI.
|
||||
|
||||
### 1. Release notes
|
||||
- Find the previous release date (`YYYY-MM-DD`) by looking at the [Falco releases](https://github.com/falcosecurity/falco/releases)
|
||||
@@ -68,9 +127,12 @@ Now assume `x.y.z` is the new version.
|
||||
|
||||
| Packages | Download |
|
||||
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| rpm | [](https://download.falco.org/packages/rpm/falco-x.y.z-x86_64.rpm) |
|
||||
| deb | [](https://download.falco.org/packages/deb/stable/falco-x.y.z-x86_64.deb) |
|
||||
| tgz | [](https://download.falco.org/packages/bin/x86_64/falco-x.y.z-x86_64.tar.gz) |
|
||||
| rpm-x86_64 | [](https://download.falco.org/packages/rpm/falco-x.y.z-x86_64.rpm) |
|
||||
| deb-x86_64 | [](https://download.falco.org/packages/deb/stable/falco-x.y.z-x86_64.deb) |
|
||||
| tgz-x86_64 | [](https://download.falco.org/packages/bin/x86_64/falco-x.y.z-x86_64.tar.gz) |
|
||||
| rpm-aarch64 | [](https://download.falco.org/packages/rpm/falco-x.y.z-aarch64.rpm) |
|
||||
| deb-aarch64 | [](https://download.falco.org/packages/deb/stable/falco-x.y.z-aarch64.deb) |
|
||||
| tgz-aarch64 | [](https://download.falco.org/packages/bin/aarch64/falco-x.y.z-aarch64.tar.gz) |
|
||||
|
||||
| Images |
|
||||
| --------------------------------------------------------------------------- |
|
||||
@@ -118,3 +180,39 @@ 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
|
||||
- IFF the on going release introduces a **new minor version**, [archive a snapshot of the Falco website](https://github.com/falcosecurity/falco-website/blob/master/release.md#documentation-versioning)
|
||||
|
||||
|
||||
## Falco Components Versioning
|
||||
|
||||
This section provides more details around the versioning of all components that make up core Falco. It can also be a useful guide for the uninitiated to be more informed about Falco's source. Because the `libs` repo contains >90% of Falco's core features and is the home of each of the kernel drivers and engines, the [libs release doc](https://github.com/falcosecurity/libs/blob/master/release.md) is an excellent additional resource. In addition, the [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) provides similar details around Falco's plugins. `SHA256` checksums are provided throughout Falco's source code to empower the end user to perform integrity checks. All Falco releases also contain the sources as part of the packages.
|
||||
|
||||
|
||||
### Falco repo (this repo)
|
||||
- Falco version is a git tag (`x.y.z`), see [Procedures](#procedures) section. Note that the Falco version is a sem-ver-like schema, but not fully compatible with sem-ver.
|
||||
- [FALCO_ENGINE_VERSION](https://github.com/falcosecurity/falco/blob/master/userspace/engine/falco_engine_version.h) is not sem-ver and must be bumped either when a backward incompatible change has been introduced to the rules files syntax or `falco --list -N | sha256sum` has changed. Breaking changes introduced in the Falco engine are not necessarily tied to the drivers or libs versions. The primary idea behind the hash is that when new filter / display fields (see currently supported [Falco fields](https://falco.org/docs/rules/supported-fields/)) are introduced a version bump indicates that this field was not available in previous engine versions. In case a new Falco rule uses new fields, the [Falco rules](https://github.com/falcosecurity/falco/blob/master/rules/falco_rules.yaml) file needs to bump this version as well via setting `required_engine_version` to the new version.
|
||||
- During development and release preparation, libs and driver reference commits are often bumped in Falco's cmake setup ([falcosecurity-libs cmake](https://github.com/falcosecurity/falco/blob/master/cmake/modules/falcosecurity-libs.cmake#L30) and [driver cmake](https://github.com/falcosecurity/falco/blob/master/cmake/modules/driver.cmake#L29)) in order to merge new Falco features. In practice they are mostly bumped at the same time referencing the same `libs` commit. However, for the official Falco build `FALCOSECURITY_LIBS_VERSION` flag that references the stable Libs version is used (read below).
|
||||
- Similarly, Falco plugins versions are bumped in Falco's cmake setup ([plugins cmake](https://github.com/falcosecurity/falco/blob/master/cmake/modules/plugins.cmake)) and those versions are the ones used for the Falco release.
|
||||
- At release time Plugin, Libs and Driver versions are compatible with Falco.
|
||||
- If you use the standard Falco setup leveraging driver-loader, [driver-loader script](https://github.com/falcosecurity/falco/blob/master/scripts/falco-driver-loader) will fetch the kernel space artifact (object file) corresponding to the default `DRIVER_VERSION` Falco was shipped with (read more below under Libs).
|
||||
|
||||
|
||||
```
|
||||
Falco version: x.y.z (sem-ver like)
|
||||
Libs version: x.y.z (sem-ver like)
|
||||
Plugin API: x.y.z (sem-ver like)
|
||||
Driver:
|
||||
API version: x.y.z (sem-ver)
|
||||
Schema version: x.y.z (sem-ver)
|
||||
Default driver: x.y.z+driver (sem-ver like, indirectly encodes compatibility range in addition to default version Falco is shipped with)
|
||||
```
|
||||
|
||||
|
||||
### Libs repo
|
||||
- Libs version is a git tag (`x.y.z`) and when building Falco the libs version is set via the `FALCOSECURITY_LIBS_VERSION` flag (see above).
|
||||
- Driver version in and of itself is not directly tied to the Falco binary as opposed to the libs version being part of the source code used to compile Falco's userspace binary. This is because of the strict separation between userspace and kernel space artifacts, so things become a bit more interesting here. This is why the concept of a `Default driver` has been introduced to still implicitly declare the compatible driver versions. For example, if the default driver version is `2.0.0+driver`, Falco works with all driver versions >= 2.0.0 and < 3.0.0. This is a consequence of how the driver version is constructed starting from the `Driver API version` and `Driver Schema version`. Driver API and Schema versions are explained in the respective [libs driver doc](https://github.com/falcosecurity/libs/blob/master/driver/README.VERSION.md) -> Falco's `driver-loader` will always fetch the default driver, therefore a Falco release is always "shipped" with the driver version corresponding to the default driver.
|
||||
- See [libs release doc](https://github.com/falcosecurity/libs/blob/master/release.md) for more information.
|
||||
|
||||
### Plugins repo
|
||||
|
||||
- Plugins version is a git tag (`x.y.z`)
|
||||
- See [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) for more information.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
if(CPACK_GENERATOR MATCHES "DEB")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "mkdir -p _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/debian/falco.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/debian/falco_inject_kmod.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
endif()
|
||||
|
||||
if(CPACK_GENERATOR MATCHES "RPM")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "mkdir -p _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/rpm/falco.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/rpm/falco_inject_kmod.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
endif()
|
||||
|
||||
if(CPACK_GENERATOR MATCHES "TGZ")
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2020 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(ExternalProject)
|
||||
|
||||
set(STRING_VIEW_LITE_PREFIX ${CMAKE_BINARY_DIR}/string-view-lite-prefix)
|
||||
set(STRING_VIEW_LITE_INCLUDE ${STRING_VIEW_LITE_PREFIX}/include)
|
||||
message(STATUS "Using bundled string-view-lite in ${STRING_VIEW_LITE_INCLUDE}")
|
||||
|
||||
ExternalProject_Add(
|
||||
string-view-lite
|
||||
PREFIX ${STRING_VIEW_LITE_PREFIX}
|
||||
GIT_REPOSITORY "https://github.com/martinmoene/string-view-lite.git"
|
||||
GIT_TAG "v1.4.0"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
UPDATE_COMMAND ""
|
||||
INSTALL_COMMAND
|
||||
${CMAKE_COMMAND} -E copy ${STRING_VIEW_LITE_PREFIX}/src/string-view-lite/include/nonstd/string_view.hpp
|
||||
${STRING_VIEW_LITE_INCLUDE}/nonstd/string_view.hpp)
|
||||
@@ -16,18 +16,32 @@ include(GetGitRevisionDescription)
|
||||
|
||||
# Create the falco version variable according to git index
|
||||
if(NOT FALCO_VERSION)
|
||||
string(STRIP "${FALCO_HASH}" FALCO_HASH)
|
||||
# Try to obtain the exact git tag
|
||||
git_get_exact_tag(FALCO_TAG)
|
||||
if(NOT FALCO_TAG)
|
||||
# Obtain the closest tag
|
||||
git_describe(FALCO_VERSION "--always" "--tags" "--abbrev=7")
|
||||
# Fallback version
|
||||
if(FALCO_VERSION MATCHES "NOTFOUND$")
|
||||
set(FALCO_VERSION "0.0.0")
|
||||
endif()
|
||||
# Format FALCO_VERSION to be semver with prerelease and build part
|
||||
string(REPLACE "-g" "+" FALCO_VERSION "${FALCO_VERSION}")
|
||||
# Fetch current hash
|
||||
get_git_head_revision(refspec FALCO_HASH)
|
||||
if(NOT FALCO_HASH OR FALCO_HASH MATCHES "NOTFOUND$")
|
||||
set(FALCO_VERSION "0.0.0")
|
||||
else()
|
||||
# Obtain the closest tag
|
||||
git_get_latest_tag(FALCO_LATEST_TAG)
|
||||
if(NOT FALCO_LATEST_TAG OR FALCO_LATEST_TAG MATCHES "NOTFOUND$")
|
||||
set(FALCO_VERSION "0.0.0")
|
||||
else()
|
||||
# Compute commit delta since tag
|
||||
git_get_delta_from_tag(FALCO_DELTA ${FALCO_LATEST_TAG} ${FALCO_HASH})
|
||||
if(NOT FALCO_DELTA OR FALCO_DELTA MATCHES "NOTFOUND$")
|
||||
set(FALCO_VERSION "0.0.0")
|
||||
else()
|
||||
# Cut hash to 7 bytes
|
||||
string(SUBSTRING ${FALCO_HASH} 0 7 FALCO_HASH)
|
||||
# Format FALCO_VERSION to be semver with prerelease and build part
|
||||
set(FALCO_VERSION
|
||||
"${FALCO_LATEST_TAG}-${FALCO_DELTA}+${FALCO_HASH}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# A tag has been found: use it as the Falco version
|
||||
set(FALCO_VERSION "${FALCO_TAG}")
|
||||
|
||||
@@ -86,29 +86,36 @@ function(get_git_head_revision _refspecvar _hashvar)
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
function(git_get_latest_tag _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
|
||||
# We use git describe --tags `git rev-list --tags --max-count=1`
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
rev-list
|
||||
--tags
|
||||
--max-count=1
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND tail -n1
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
tag_hash
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${tag_hash}-${res}-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
--tags
|
||||
${tag_hash}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
@@ -120,10 +127,108 @@ function(git_describe _var)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_delta_from_tag _var tag hash)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
|
||||
# Count commits in HEAD
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
rev-list
|
||||
--count
|
||||
${hash}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out_counter_head
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(${_var} "HEADCOUNT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Count commits in latest tag
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
rev-list
|
||||
--count
|
||||
${tag}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out_counter_tag
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(${_var} "TAGCOUNT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
expr
|
||||
${out_counter_head} - ${out_counter_tag}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out_delta
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(${_var} "DELTA-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(${_var} "${out_delta}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
|
||||
@@ -26,8 +26,8 @@ else()
|
||||
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "2.0.0+driver")
|
||||
set(DRIVER_CHECKSUM "SHA256=e616dfe27f95670a63150339ea2484937c5ce9b7e42d176de86c3f61481ae676")
|
||||
set(DRIVER_VERSION "dd443b67c6b04464cb8ee2771af8ada8777e7fac")
|
||||
set(DRIVER_CHECKSUM "SHA256=df373099d0f4cd4417a0103bb57f26c7412ffa86cde2bb2d579c6feba841626d")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
@@ -45,4 +45,4 @@ set(DRIVER_NAME "falco")
|
||||
set(DRIVER_PACKAGE_NAME "falco")
|
||||
set(DRIVER_COMPONENT_NAME "falco-driver")
|
||||
|
||||
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
|
||||
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
|
||||
|
||||
@@ -27,8 +27,8 @@ else()
|
||||
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
|
||||
if(NOT FALCOSECURITY_LIBS_VERSION)
|
||||
set(FALCOSECURITY_LIBS_VERSION "0.7.0")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=3adc1620c0e830554a54cdd486158dc2c0c40552e113785b70fbbc99edb7d96f")
|
||||
set(FALCOSECURITY_LIBS_VERSION "dd443b67c6b04464cb8ee2771af8ada8777e7fac")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=df373099d0f4cd4417a0103bb57f26c7412ffa86cde2bb2d579c6feba841626d")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
@@ -49,7 +49,6 @@ if(MUSL_OPTIMIZED_BUILD)
|
||||
add_definitions(-DMUSL_OPTIMIZED)
|
||||
endif()
|
||||
|
||||
set(SCAP_BPF_PROBE_ENV_VAR_NAME "FALCO_BPF_PROBE")
|
||||
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")
|
||||
|
||||
if(NOT LIBSCAP_DIR)
|
||||
@@ -61,6 +60,9 @@ set(LIBSINSP_DIR "${FALCOSECURITY_LIBS_SOURCE_DIR}")
|
||||
# configure gVisor support
|
||||
set(BUILD_LIBSCAP_GVISOR ${BUILD_FALCO_GVISOR} CACHE BOOL "")
|
||||
|
||||
# configure modern BPF support
|
||||
set(BUILD_LIBSCAP_MODERN_BPF ${BUILD_FALCO_MODERN_BPF} CACHE BOOL "")
|
||||
|
||||
# explicitly disable the tests/examples of this dependency
|
||||
set(CREATE_TEST_TARGETS OFF CACHE BOOL "")
|
||||
set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
|
||||
@@ -68,6 +70,8 @@ set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
|
||||
set(USE_BUNDLED_TBB ON CACHE BOOL "")
|
||||
set(USE_BUNDLED_B64 ON CACHE BOOL "")
|
||||
set(USE_BUNDLED_JSONCPP ON CACHE BOOL "")
|
||||
set(USE_BUNDLED_VALIJSON ON CACHE BOOL "")
|
||||
set(USE_BUNDLED_RE2 ON CACHE BOOL "")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${FALCOSECURITY_LIBS_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
@@ -83,4 +87,4 @@ endif()
|
||||
|
||||
include(driver)
|
||||
include(libscap)
|
||||
include(libsinsp)
|
||||
include(libsinsp)
|
||||
|
||||
@@ -19,11 +19,11 @@ if(NOT DEFINED PLUGINS_COMPONENT_NAME)
|
||||
set(PLUGINS_COMPONENT_NAME "${CMAKE_PROJECT_NAME}-plugins")
|
||||
endif()
|
||||
|
||||
set(PLUGIN_K8S_AUDIT_VERSION "0.3.0")
|
||||
set(PLUGIN_K8S_AUDIT_VERSION "0.4.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "214915fc2a61d147d64aaf4cb29c3fc6a513eda621dad1dfe77f2fd7099b31e1")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "ded0b5419f40084547620ccc48b19768e5e89457b85cfe8fbe496ca72267a3a4")
|
||||
else() # aarch64
|
||||
set(PLUGIN_K8S_AUDIT_HASH "d9b4610714df581043db76ecb4caf3a41aae5494cf61ab8740a3749bfac8457e")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "775cba666612114bc5b0c36f2e3c4557f5adbffcca2d77e72be87c6fcbf51ceb")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
@@ -39,18 +39,18 @@ install(FILES "${PROJECT_BINARY_DIR}/k8saudit-plugin-prefix/src/k8saudit-plugin/
|
||||
ExternalProject_Add(
|
||||
k8saudit-rules
|
||||
URL "https://download.falco.org/plugins/stable/k8saudit-rules-${PLUGIN_K8S_AUDIT_VERSION}.tar.gz"
|
||||
URL_HASH "SHA256=3913a8c6095794c7de6a97a2a64953a0fa4f87caab014d11b2c8f9221eb77591"
|
||||
URL_HASH "SHA256=53948fac0345e718d673142a992ac820135f771141dfaa9719c7575ac8ae6878"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
|
||||
|
||||
set(PLUGIN_CLOUDTRAIL_VERSION "0.5.0")
|
||||
set(PLUGIN_CLOUDTRAIL_VERSION "0.6.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "ca6c0d087b37090145ef0c92f10d1dd32bb2a08c7bae83cc6fb7a1ba712f3182")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "80e0c33f30c01a90efb7e9a671d978ff9679c462e3105020238abf31230e49a9")
|
||||
else() # aarch64
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "f6e12d3bd16ae0f504ed2bb56d13531d15b7d55beb1b63932cbe603cff941372")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "a3e739932e66d44be848a68857fa15f56134d5246a1b9ab912c81f91b68fb23f")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
@@ -66,18 +66,18 @@ install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-plugin-prefix/src/cloudtrail-plu
|
||||
ExternalProject_Add(
|
||||
cloudtrail-rules
|
||||
URL "https://download.falco.org/plugins/stable/cloudtrail-rules-${PLUGIN_CLOUDTRAIL_VERSION}.tar.gz"
|
||||
URL_HASH "SHA256=7f88fb6b530f8ee739b65d38a36c69cdc70398576299b90118bd7324dbdb5f46"
|
||||
URL_HASH "SHA256=e0dccb7b0f1d24b1e526a33ffd973ea5f2ac2879dbc999e119419ebfd24305ff"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-rules-prefix/src/cloudtrail-rules/aws_cloudtrail_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
|
||||
|
||||
set(PLUGIN_JSON_VERSION "0.5.0")
|
||||
set(PLUGIN_JSON_VERSION "0.6.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_JSON_HASH "b422c4f08bb54ccd384a87c5922e120d5731028c87742ef657cacf936447c202")
|
||||
set(PLUGIN_JSON_HASH "15fb7eddd978e8bb03f05412e9446e264e4548d7423b3d724b99d6d87a8c1b27")
|
||||
else() # aarch64
|
||||
set(PLUGIN_JSON_HASH "8358f04325d8a9e9675f38fae8d13a250fb132dcf6741fd0f9830e8c39f48aed")
|
||||
set(PLUGIN_JSON_HASH "4db23f35a750e10a5b7b54c9aa469a7587705e7faa22927e941b41f3c5533e9f")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
|
||||
@@ -25,11 +25,10 @@ else()
|
||||
"--force"
|
||||
"--inconclusive"
|
||||
"--inline-suppr" # allows to specify suppressions directly in source code
|
||||
"--project=${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" # use the compilation database as source
|
||||
"--quiet"
|
||||
"--xml" # we want to generate a report
|
||||
"--output-file=${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck/cppcheck.xml" # generate the report under the reports folder in the build folder
|
||||
"-i${CMAKE_CURRENT_BINARY_DIR}"# exclude the build folder
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
)
|
||||
endif() # CPPCHECK
|
||||
|
||||
|
||||
@@ -2,5 +2,4 @@ labels:
|
||||
- area/integration
|
||||
approvers:
|
||||
- leogr
|
||||
reviewers:
|
||||
- leogr
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ENV CMAKE_VERSION=${CMAKE_VERSION}
|
||||
|
||||
# build toolchain
|
||||
RUN yum -y install centos-release-scl && \
|
||||
INSTALL_PKGS="devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-toolchain devtoolset-7-libstdc++-devel devtoolset-7-elfutils-libelf-devel llvm-toolset-7.0 glibc-static autoconf automake libtool createrepo expect git which libcurl-devel zlib-devel rpm-build libyaml-devel" && \
|
||||
INSTALL_PKGS="devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-toolchain devtoolset-7-libstdc++-devel llvm-toolset-7.0 glibc-static autoconf automake libtool createrepo expect git which libcurl-devel rpm-build libyaml-devel" && \
|
||||
yum -y install --setopt=tsflags=nodocs $INSTALL_PKGS && \
|
||||
rpm -V $INSTALL_PKGS
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ shift
|
||||
|
||||
# Build type can be "debug" or "release", fallbacks to "release" by default
|
||||
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
|
||||
DRAIOS_DEBUG_FLAGS=
|
||||
FALCO_EXTRA_DEBUG_FLAGS=
|
||||
case "$BUILD_TYPE" in
|
||||
"debug")
|
||||
DRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
FALCO_EXTRA_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
;;
|
||||
*)
|
||||
BUILD_TYPE="release"
|
||||
@@ -37,7 +37,7 @@ case "$CMD" in
|
||||
-DBUILD_BPF="$BUILD_BPF" \
|
||||
-DBUILD_WARNINGS_AS_ERRORS="$BUILD_WARNINGS_AS_ERRORS" \
|
||||
-DFALCO_VERSION="$FALCO_VERSION" \
|
||||
-DDRAIOS_DEBUG_FLAGS="$DRAIOS_DEBUG_FLAGS" \
|
||||
-DFALCO_EXTRA_DEBUG_FLAGS="$FALCO_EXTRA_DEBUG_FLAGS" \
|
||||
-DUSE_BUNDLED_DEPS=ON \
|
||||
"$SOURCE_DIR/falco"
|
||||
exit "$(printf '%d\n' $?)"
|
||||
|
||||
89
falco.yaml
89
falco.yaml
@@ -73,7 +73,9 @@ watch_config_files: true
|
||||
# time zone, as governed by /etc/localtime.
|
||||
time_format_iso_8601: false
|
||||
|
||||
# Whether to output events in json or text
|
||||
# If "true", print falco alert messages and rules file
|
||||
# loading/validation results as json, which allows for easier
|
||||
# consumption by downstream programs. Default is "false".
|
||||
json_output: false
|
||||
|
||||
# When using json output, whether or not to include the "output" property
|
||||
@@ -148,6 +150,7 @@ syscall_event_drops:
|
||||
- alert
|
||||
rate: .03333
|
||||
max_burst: 1
|
||||
simulate_drops: false
|
||||
|
||||
# Falco uses a shared buffer between the kernel and userspace to receive
|
||||
# the events (eg., system call information) in userspace.
|
||||
@@ -167,6 +170,61 @@ syscall_event_drops:
|
||||
syscall_event_timeouts:
|
||||
max_consecutives: 1000
|
||||
|
||||
# --- [Description]
|
||||
#
|
||||
# This is an index that controls the dimension of the syscall buffers.
|
||||
# The syscall buffer is the shared space between Falco and its drivers where all the syscall events
|
||||
# are stored.
|
||||
# Falco uses a syscall buffer for every online CPU, and all these buffers share the same dimension.
|
||||
# So this parameter allows you to control the size of all the buffers!
|
||||
#
|
||||
# --- [Usage]
|
||||
#
|
||||
# You can choose between different indexes: from `1` to `10` (`0` is reserved for future uses).
|
||||
# Every index corresponds to a dimension in bytes:
|
||||
#
|
||||
# [(*), 1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 256 MB, 512 MB]
|
||||
# ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
|
||||
# | | | | | | | | | | |
|
||||
# 0 1 2 3 4 5 6 7 8 9 10
|
||||
#
|
||||
# As you can see the `0` index is reserved, while the index `1` corresponds to
|
||||
# `1 MB` and so on.
|
||||
#
|
||||
# These dimensions in bytes derive from the fact that the buffer size must be:
|
||||
# (1) a power of 2.
|
||||
# (2) a multiple of your system_page_dimension.
|
||||
# (3) greater than `2 * (system_page_dimension)`.
|
||||
#
|
||||
# According to these constraints is possible that sometimes you cannot use all the indexes, let's consider an
|
||||
# example to better understand it:
|
||||
# If you have a `page_size` of 1 MB the first available buffer size is 4 MB because 2 MB is exactly
|
||||
# `2 * (system_page_size)` -> `2 * 1 MB`, but this is not enough we need more than `2 * (system_page_size)`!
|
||||
# So from this example is clear that if you have a page size of 1 MB the first index that you can use is `3`.
|
||||
#
|
||||
# Please note: this is a very extreme case just to let you understand the mechanism, usually the page size is something
|
||||
# like 4 KB so you have no problem at all and you can use all the indexes (from `1` to `10`).
|
||||
#
|
||||
# To check your system page size use the Falco `--page-size` command line option. The output on a system with a page
|
||||
# size of 4096 Bytes (4 KB) should be the following:
|
||||
#
|
||||
# "Your system page size is: 4096 bytes."
|
||||
#
|
||||
# --- [Suggestions]
|
||||
#
|
||||
# Before the introduction of this param the buffer size was fixed to 8 MB (so index `4`, as you can see
|
||||
# in the default value below).
|
||||
# You can increase the buffer size when you face syscall drops. A size of 16 MB (so index `5`) can reduce
|
||||
# syscall drops in production-heavy systems without noticeable impact. Very large buffers however could
|
||||
# slow down the entire machine.
|
||||
# On the other side you can try to reduce the buffer size to speed up the system, but this could
|
||||
# increase the number of syscall drops!
|
||||
# As a final remark consider that the buffer size is mapped twice in the process' virtual memory so a buffer of 8 MB
|
||||
# will result in a 16 MB area in the process virtual memory.
|
||||
# Please pay attention when you use this parameter and change it only if the default size doesn't fit your use case.
|
||||
|
||||
syscall_buf_size_preset: 4
|
||||
|
||||
# Falco continuously monitors outputs performance. When an output channel does not allow
|
||||
# to deliver an alert within a given deadline, an error is reported indicating
|
||||
# which output is blocking notifications.
|
||||
@@ -185,19 +243,22 @@ syscall_event_timeouts:
|
||||
output_timeout: 2000
|
||||
|
||||
# A throttling mechanism implemented as a token bucket limits the
|
||||
# rate of falco notifications. This throttling is controlled by the following configuration
|
||||
# options:
|
||||
# rate of Falco notifications. One rate limiter is assigned to each event
|
||||
# source, so that alerts coming from one can't influence the throttling
|
||||
# mechanism of the others. This is controlled by the following options:
|
||||
# - rate: the number of tokens (i.e. right to send a notification)
|
||||
# gained per second. Defaults to 1.
|
||||
# gained per second. When 0, the throttling mechanism is disabled.
|
||||
# Defaults to 0.
|
||||
# - 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
|
||||
# With these defaults, the throttling mechanism is disabled.
|
||||
# For example, by setting rate to 1 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
|
||||
rate: 0
|
||||
max_burst: 1000
|
||||
|
||||
# Where security notifications should go.
|
||||
@@ -222,9 +283,10 @@ file_output:
|
||||
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 enabled).
|
||||
# Falco contains an embedded webserver that is used to implement an health
|
||||
# endpoint for checking if Falco is up and running. These config options control
|
||||
# the behavior of that webserver. By default, the webserver is enabled and
|
||||
# the endpoint is /healthz.
|
||||
#
|
||||
# The ssl_certificate is a combination SSL Certificate and corresponding
|
||||
# key contained in a single file. You can generate a key/cert as follows:
|
||||
@@ -232,11 +294,10 @@ stdout_output:
|
||||
# $ 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
|
||||
#
|
||||
# It also exposes a healthy endpoint that can be used to check if Falco is up and running
|
||||
# By default the endpoint is /healthz
|
||||
webserver:
|
||||
enabled: true
|
||||
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
|
||||
threadiness: 0
|
||||
listen_port: 8765
|
||||
k8s_healthz_endpoint: /healthz
|
||||
ssl_enabled: false
|
||||
@@ -291,7 +352,7 @@ http_output:
|
||||
# gRPC server using an unix socket
|
||||
grpc:
|
||||
enabled: false
|
||||
bind_address: "unix:///var/run/falco.sock"
|
||||
bind_address: "unix:///run/falco/falco.sock"
|
||||
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
|
||||
threadiness: 0
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ reviewers:
|
||||
- fntlnz
|
||||
- mfdii
|
||||
- kaizhe
|
||||
- mstemm
|
||||
- darryk10
|
||||
labels:
|
||||
- area/rules
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,9 @@ configure_file(debian/prerm.in debian/prerm)
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")
|
||||
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco_inject_kmod.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")
|
||||
|
||||
configure_file(rpm/postinstall.in rpm/postinstall)
|
||||
configure_file(rpm/postuninstall.in rpm/postuninstall)
|
||||
configure_file(rpm/preuninstall.in rpm/preuninstall)
|
||||
@@ -29,6 +32,9 @@ configure_file(rpm/preuninstall.in rpm/preuninstall)
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
|
||||
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco_inject_kmod.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
|
||||
|
||||
configure_file(falco-driver-loader falco-driver-loader @ONLY)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
After=falco_inject_kmod.service
|
||||
Requires=falco_inject_kmod.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
@@ -17,6 +18,7 @@ NoNewPrivileges=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
ReadWritePaths=/sys/module/falco
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
|
||||
|
||||
13
scripts/debian/falco_inject_kmod.service
Normal file
13
scripts/debian/falco_inject_kmod.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Before=falco.service
|
||||
Wants=falco.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/sbin/modprobe falco
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -41,3 +41,34 @@ case "$1" in
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
|
||||
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
# This will only remove masks created by d-s-h on package removal.
|
||||
deb-systemd-helper unmask 'falco.service' >/dev/null || true
|
||||
|
||||
# was-enabled defaults to true, so new installations run enable.
|
||||
if deb-systemd-helper --quiet was-enabled 'falco.service'; then
|
||||
# Enables the unit on first installation, creates new
|
||||
# symlinks on upgrades if the unit file has changed.
|
||||
deb-systemd-helper enable 'falco.service' >/dev/null || true
|
||||
else
|
||||
# Update the statefile to add new symlinks (if any), which need to be
|
||||
# cleaned up on purge. Also remove old symlinks.
|
||||
deb-systemd-helper update-state 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
if [ -n "$2" ]; then
|
||||
_dh_action=restart
|
||||
else
|
||||
_dh_action=start
|
||||
fi
|
||||
deb-systemd-invoke $_dh_action 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -15,3 +15,25 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
|
||||
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
|
||||
|
||||
set -e
|
||||
|
||||
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
|
||||
if [ "$1" = "remove" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper mask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "purge" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper purge 'falco.service' >/dev/null || true
|
||||
deb-systemd-helper unmask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
#
|
||||
set -e
|
||||
|
||||
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
|
||||
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
|
||||
# Currently running falco service uses the driver, so stop it before driver cleanup
|
||||
|
||||
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
|
||||
deb-systemd-invoke stop 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
/usr/bin/falco-driver-loader --clean
|
||||
|
||||
@@ -113,11 +113,14 @@ get_target_id() {
|
||||
elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
|
||||
# Older CentOS distros
|
||||
OS_ID=centos
|
||||
elif [ -f "${HOST_ROOT}/etc/VERSION" ]; then
|
||||
OS_ID=minikube
|
||||
else
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community"
|
||||
exit 1
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Overwrite the OS_ID if /etc/VERSION file is present.
|
||||
# Not sure if there is a better way to detect minikube.
|
||||
if [ -f "${HOST_ROOT}/etc/VERSION" ]; then
|
||||
OS_ID=minikube
|
||||
fi
|
||||
|
||||
case "${OS_ID}" in
|
||||
@@ -129,8 +132,13 @@ get_target_id() {
|
||||
fi
|
||||
;;
|
||||
("ubuntu")
|
||||
if [[ $KERNEL_RELEASE == *"aws"* ]]; then
|
||||
TARGET_ID="ubuntu-aws"
|
||||
# Extract the flavor from the kernelrelease
|
||||
# Examples:
|
||||
# 5.0.0-1028-aws-5.0 -> ubuntu-aws-5.0
|
||||
# 5.15.0-1009-aws -> ubuntu-aws
|
||||
if [[ $KERNEL_RELEASE =~ -([a-zA-Z]+)(-.*)?$ ]];
|
||||
then
|
||||
TARGET_ID="ubuntu-${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
|
||||
else
|
||||
TARGET_ID="ubuntu-generic"
|
||||
fi
|
||||
@@ -139,10 +147,23 @@ get_target_id() {
|
||||
KERNEL_RELEASE="${VERSION_ID}"
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
("minikube")
|
||||
TARGET_ID="${OS_ID}"
|
||||
# Extract the minikube version. Ex. With minikube version equal to "v1.26.0-1655407986-14197" the extracted version
|
||||
# will be "1.26.0"
|
||||
if [[ $(cat ${HOST_ROOT}/etc/VERSION) =~ ([0-9]+(\.[0-9]+){2}) ]]; then
|
||||
# kernel version for minikube is always in "1_minikubeversion" format. Ex "1_1.26.0".
|
||||
KERNEL_VERSION="1_${BASH_REMATCH[1]}"
|
||||
else
|
||||
echo "* Unable to extract minikube version from ${HOST_ROOT}/etc/VERSION"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
(*)
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
flatcar_relocate_tools() {
|
||||
@@ -232,16 +253,14 @@ load_kernel_module_compile() {
|
||||
}
|
||||
|
||||
load_kernel_module_download() {
|
||||
get_target_id
|
||||
|
||||
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
||||
local URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
|
||||
local URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
|
||||
|
||||
echo "* Trying to download a prebuilt ${DRIVER_NAME} module from ${URL}"
|
||||
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
|
||||
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
|
||||
echo "* Download succeeded"
|
||||
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
if insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}"; then
|
||||
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
if insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}"; then
|
||||
echo "* Success: ${DRIVER_NAME} module found and inserted"
|
||||
exit 0
|
||||
else
|
||||
@@ -261,6 +280,13 @@ print_clean_termination() {
|
||||
echo
|
||||
}
|
||||
|
||||
print_filename_components() {
|
||||
echo " - driver name: ${DRIVER_NAME}"
|
||||
echo " - target identifier: ${TARGET_ID}"
|
||||
echo " - kernel release: ${KERNEL_RELEASE}"
|
||||
echo " - kernel version: ${KERNEL_VERSION}"
|
||||
}
|
||||
|
||||
clean_kernel_module() {
|
||||
echo
|
||||
echo "================ Cleaning phase ================"
|
||||
@@ -346,19 +372,22 @@ load_kernel_module() {
|
||||
|
||||
echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"
|
||||
|
||||
get_target_id
|
||||
|
||||
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
||||
echo "* Filename '${FALCO_KERNEL_MODULE_FILENAME}' is composed of:"
|
||||
print_filename_components
|
||||
|
||||
if [ -f "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
|
||||
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
|
||||
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
|
||||
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
|
||||
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
|
||||
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [ -n "$ENABLE_DOWNLOAD" ]; then
|
||||
load_kernel_module_download
|
||||
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
|
||||
for url in "${urls[@]}"; do
|
||||
load_kernel_module_download $url
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
@@ -482,8 +511,8 @@ load_bpf_probe_compile() {
|
||||
|
||||
make -C "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf" > /dev/null
|
||||
|
||||
mkdir -p "${HOME}/.falco"
|
||||
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
||||
mkdir -p "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}"
|
||||
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
|
||||
|
||||
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
|
||||
rm -r /tmp/kernel
|
||||
@@ -493,47 +522,54 @@ load_bpf_probe_compile() {
|
||||
|
||||
load_bpf_probe_download() {
|
||||
local URL
|
||||
URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
|
||||
URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
|
||||
|
||||
echo "* Trying to download a prebuilt eBPF probe from ${URL}"
|
||||
|
||||
if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${URL}"; then
|
||||
if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${URL}"; then
|
||||
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe"
|
||||
return
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
load_bpf_probe() {
|
||||
echo "* Mounting debugfs"
|
||||
|
||||
if [ ! -d /sys/kernel/debug/tracing ]; then
|
||||
echo "* Mounting debugfs"
|
||||
mount -t debugfs nodev /sys/kernel/debug
|
||||
fi
|
||||
|
||||
get_target_id
|
||||
|
||||
BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"
|
||||
echo "* Filename '${BPF_PROBE_FILENAME}' is composed of:"
|
||||
print_filename_components
|
||||
|
||||
if [ -n "$ENABLE_DOWNLOAD" ]; then
|
||||
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
||||
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
|
||||
else
|
||||
load_bpf_probe_download
|
||||
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
|
||||
for url in "${urls[@]}"; do
|
||||
load_bpf_probe_download $url
|
||||
if [ $? -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
||||
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
|
||||
else
|
||||
load_bpf_probe_compile
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* eBPF probe located in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
||||
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
|
||||
echo "* eBPF probe located in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
|
||||
|
||||
ln -sf "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
|
||||
ln -sf "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
|
||||
&& echo "* Success: eBPF probe symlinked to ${HOME}/.falco/${DRIVER_NAME}-bpf.o"
|
||||
exit $?
|
||||
else
|
||||
@@ -559,7 +595,7 @@ print_usage() {
|
||||
echo " --source-only skip execution and allow sourcing in another script"
|
||||
echo ""
|
||||
echo "Environment variables:"
|
||||
echo " DRIVERS_REPO specify a different URL where to look for prebuilt Falco drivers"
|
||||
echo " DRIVERS_REPO specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
|
||||
echo " DRIVER_NAME specify a different name for the driver"
|
||||
echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
|
||||
echo ""
|
||||
@@ -592,10 +628,12 @@ if [[ -z "$MAX_RMMOD_WAIT" ]]; then
|
||||
MAX_RMMOD_WAIT=60
|
||||
fi
|
||||
|
||||
DRIVER_VERSION="@DRIVER_VERSION@"
|
||||
DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
|
||||
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
|
||||
FALCO_VERSION="@FALCO_VERSION@"
|
||||
|
||||
TARGET_ID="placeholder" # when no target id can be fetched, we try to build the driver from source anyway, using a placeholder name
|
||||
|
||||
DRIVER="module"
|
||||
if [ -v FALCO_BPF_PROBE ]; then
|
||||
DRIVER="bpf"
|
||||
@@ -662,13 +700,25 @@ if [ -z "$has_opts" ]; then
|
||||
fi
|
||||
|
||||
if [ -z "$source_only" ]; then
|
||||
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}"
|
||||
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}"
|
||||
|
||||
if [ "$(id -u)" != 0 ]; then
|
||||
>&2 echo "This program must be run as root (or with sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
get_target_id
|
||||
res=$?
|
||||
if [ $res != 0 ]; then
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
ENABLE_DOWNLOAD=
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway."
|
||||
else
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$clean" ]; then
|
||||
if [ -n "$has_opts" ]; then
|
||||
>&2 echo "Cannot use --clean with other options"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
After=falco_inject_kmod.service
|
||||
Requires=falco_inject_kmod.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
@@ -17,6 +18,7 @@ NoNewPrivileges=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
ReadWritePaths=/sys/module/falco
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
StandardOutput=null
|
||||
|
||||
13
scripts/rpm/falco_inject_kmod.service
Normal file
13
scripts/rpm/falco_inject_kmod.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Before=falco.service
|
||||
Wants=falco.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/sbin/modprobe falco
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -14,6 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
mod_version="@DRIVER_VERSION@"
|
||||
dkms add -m falco -v $mod_version --rpm_safe_upgrade
|
||||
@@ -29,3 +30,35 @@ else
|
||||
echo -e "Module build for the currently running kernel was skipped since the"
|
||||
echo -e "kernel source for this kernel does not seem to be installed."
|
||||
fi
|
||||
|
||||
# validate rpm macros by `rpm -qp --scripts <rpm>`
|
||||
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
|
||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
|
||||
|
||||
# systemd_post macro expands to
|
||||
# if postinst:
|
||||
# `systemd-update-helper install-system-units <service>`
|
||||
%systemd_post 'falco.service'
|
||||
|
||||
# post install mirrored from .deb
|
||||
if [ $1 -eq 1 ]; then
|
||||
# This will only remove masks created on package removal.
|
||||
/usr/bin/systemctl --system unmask 'falco.service' >/dev/null || true
|
||||
|
||||
# enable falco on installation
|
||||
# note: DEB postinstall script checks for changed symlinks
|
||||
/usr/bin/systemctl --system enable 'falco.service' >/dev/null || true
|
||||
|
||||
# start falco on installation
|
||||
/usr/bin/systemctl --system start 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
# post upgrade mirrored from .deb
|
||||
if [ $1 -gt 1 ]; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
/usr/bin/systemctl --system daemon-reload >/dev/null || true
|
||||
|
||||
# restart falco on upgrade if service is already running
|
||||
/usr/bin/systemctl --system condrestart 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -14,3 +14,20 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# post uninstall mirrored from .deb
|
||||
if [ -d /run/systemd/system ] && [ "$1" = 0 ]; then
|
||||
/usr/bin/systemctl --system daemon-reload >/dev/null || true
|
||||
/usr/bin/systemctl --system mask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
# validate rpm macros by `rpm -qp --scripts <rpm>`
|
||||
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
|
||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
|
||||
|
||||
# systemd_postun_with_restart macro expands to
|
||||
# if package upgrade, not uninstall:
|
||||
# `systemd-update-helper mark-restart-system-units <service>`
|
||||
%systemd_postun_with_restart 'falco.service'
|
||||
|
||||
@@ -14,5 +14,22 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
# pre uninstall mirrored from .deb
|
||||
# Currently running falco service uses the driver, so stop it before driver cleanup
|
||||
if [ -d /run/systemd/system ] && [ $1 -eq 0 ]; then
|
||||
# stop falco service before uninstall
|
||||
/usr/bin/systemctl --system stop 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
/usr/bin/falco-driver-loader --clean
|
||||
|
||||
# validate rpm macros by `rpm -qp --scripts <rpm>`
|
||||
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
|
||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
|
||||
|
||||
# systemd_preun macro expands to
|
||||
# if preuninstall:
|
||||
# `systemd-update-helper remove-system-units <service>`
|
||||
%systemd_preun 'falco.service'
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
stdout_output:
|
||||
enabled: true
|
||||
|
||||
plugins:
|
||||
- name: cloudtrail
|
||||
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||
init_config: ""
|
||||
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances.json"
|
||||
- name: test_source
|
||||
library_path: BUILD_DIR/test/plugins/libtest_source.so
|
||||
init_config: ""
|
||||
open_params: ""
|
||||
|
||||
# Optional
|
||||
load_plugins: [cloudtrail, test_source]
|
||||
@@ -19,6 +19,7 @@ trace_files: !mux
|
||||
compat_engine_v4_create_disallowed_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -30,6 +31,7 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_allowed_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -40,6 +42,7 @@ trace_files: !mux
|
||||
compat_engine_v4_create_privileged_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -50,6 +53,7 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_privileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -60,6 +64,7 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_unprivileged_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -69,6 +74,7 @@ trace_files: !mux
|
||||
compat_engine_v4_create_hostnetwork_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -79,6 +85,7 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_hostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -90,6 +97,7 @@ trace_files: !mux
|
||||
user_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -101,6 +109,7 @@ trace_files: !mux
|
||||
|
||||
user_in_allowed_set:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -113,6 +122,7 @@ trace_files: !mux
|
||||
create_disallowed_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -124,6 +134,7 @@ trace_files: !mux
|
||||
|
||||
create_allowed_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -134,6 +145,7 @@ trace_files: !mux
|
||||
create_privileged_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -145,6 +157,7 @@ trace_files: !mux
|
||||
create_privileged_no_secctx_1st_container_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -156,6 +169,7 @@ trace_files: !mux
|
||||
create_privileged_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -166,6 +180,7 @@ trace_files: !mux
|
||||
|
||||
create_privileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -175,6 +190,7 @@ trace_files: !mux
|
||||
|
||||
create_unprivileged_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -183,6 +199,7 @@ trace_files: !mux
|
||||
|
||||
create_unprivileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -193,6 +210,7 @@ trace_files: !mux
|
||||
create_sensitive_mount_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -204,6 +222,7 @@ trace_files: !mux
|
||||
create_sensitive_mount_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -214,6 +233,7 @@ trace_files: !mux
|
||||
|
||||
create_sensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -223,6 +243,7 @@ trace_files: !mux
|
||||
|
||||
create_unsensitive_mount_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -231,6 +252,7 @@ trace_files: !mux
|
||||
|
||||
create_unsensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -241,6 +263,7 @@ trace_files: !mux
|
||||
create_hostnetwork_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -251,6 +274,7 @@ trace_files: !mux
|
||||
|
||||
create_hostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -260,6 +284,7 @@ trace_files: !mux
|
||||
|
||||
create_nohostnetwork_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -268,6 +293,7 @@ trace_files: !mux
|
||||
|
||||
create_nohostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -278,6 +304,7 @@ trace_files: !mux
|
||||
create_nodeport_service:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -289,6 +316,7 @@ trace_files: !mux
|
||||
|
||||
create_nonodeport_service:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -299,6 +327,7 @@ trace_files: !mux
|
||||
create_configmap_private_creds:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -310,6 +339,7 @@ trace_files: !mux
|
||||
|
||||
create_configmap_no_private_creds:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -320,6 +350,7 @@ trace_files: !mux
|
||||
anonymous_user:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -331,6 +362,7 @@ trace_files: !mux
|
||||
pod_exec:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -342,6 +374,7 @@ trace_files: !mux
|
||||
pod_attach:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -353,6 +386,7 @@ trace_files: !mux
|
||||
namespace_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -364,6 +398,7 @@ trace_files: !mux
|
||||
|
||||
namespace_in_allowed_set:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -375,6 +410,7 @@ trace_files: !mux
|
||||
create_pod_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -386,6 +422,7 @@ trace_files: !mux
|
||||
create_pod_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -397,6 +434,7 @@ trace_files: !mux
|
||||
create_serviceaccount_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -408,6 +446,7 @@ trace_files: !mux
|
||||
create_serviceaccount_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -419,6 +458,7 @@ trace_files: !mux
|
||||
system_clusterrole_deleted:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -430,6 +470,7 @@ trace_files: !mux
|
||||
system_clusterrole_modified:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -441,6 +482,7 @@ trace_files: !mux
|
||||
attach_cluster_admin_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -452,6 +494,7 @@ trace_files: !mux
|
||||
create_cluster_role_wildcard_resources:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -463,6 +506,7 @@ trace_files: !mux
|
||||
create_cluster_role_wildcard_verbs:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -474,6 +518,7 @@ trace_files: !mux
|
||||
create_writable_cluster_role:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -485,6 +530,7 @@ trace_files: !mux
|
||||
create_pod_exec_cluster_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -496,6 +542,7 @@ trace_files: !mux
|
||||
create_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -507,6 +554,7 @@ trace_files: !mux
|
||||
delete_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -518,6 +566,7 @@ trace_files: !mux
|
||||
create_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -529,6 +578,7 @@ trace_files: !mux
|
||||
delete_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -540,6 +590,7 @@ trace_files: !mux
|
||||
create_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -551,6 +602,7 @@ trace_files: !mux
|
||||
delete_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -562,6 +614,7 @@ trace_files: !mux
|
||||
create_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -575,6 +628,7 @@ trace_files: !mux
|
||||
delete_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -586,6 +640,7 @@ trace_files: !mux
|
||||
create_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -597,6 +652,7 @@ trace_files: !mux
|
||||
delete_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -608,6 +664,7 @@ trace_files: !mux
|
||||
create_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -619,6 +676,7 @@ trace_files: !mux
|
||||
delete_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -630,6 +688,7 @@ trace_files: !mux
|
||||
create_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -641,6 +700,7 @@ trace_files: !mux
|
||||
delete_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -652,6 +712,7 @@ trace_files: !mux
|
||||
create_secret:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -664,6 +725,7 @@ trace_files: !mux
|
||||
create_service_account_token_secret:
|
||||
detect: False
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -673,6 +735,7 @@ trace_files: !mux
|
||||
create_kube_system_secret:
|
||||
detect: False
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -682,6 +745,7 @@ trace_files: !mux
|
||||
delete_secret:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -692,6 +756,7 @@ trace_files: !mux
|
||||
|
||||
fal_01_003:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -702,6 +767,7 @@ trace_files: !mux
|
||||
json_pointer_correct_parse:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ./rules/k8s_audit/single_rule_with_json_pointer.yaml
|
||||
detect_counts:
|
||||
|
||||
@@ -77,6 +77,10 @@ class FalcoTest(Test):
|
||||
else:
|
||||
self.stderr_not_contains = [self.stderr_not_contains]
|
||||
|
||||
self.validate_ok = self.params.get('validate_ok', '*', default='')
|
||||
self.validate_warnings = self.params.get('validate_warnings', '*', default='')
|
||||
self.validate_errors = self.params.get('validate_errors', '*', default='')
|
||||
|
||||
self.exit_status = self.params.get('exit_status', '*', default=0)
|
||||
self.should_detect = self.params.get('detect', '*', default=False)
|
||||
self.check_detection_counts = self.params.get('check_detection_counts', '*', default=True)
|
||||
@@ -93,6 +97,7 @@ class FalcoTest(Test):
|
||||
self.all_events = self.params.get('all_events', '*', default=False)
|
||||
self.priority = self.params.get('priority', '*', default='debug')
|
||||
self.addl_cmdline_opts = self.params.get('addl_cmdline_opts', '*', default='')
|
||||
self.enable_source = self.params.get('enable_source', '*', default='')
|
||||
self.rules_file = self.params.get(
|
||||
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||
|
||||
@@ -105,8 +110,18 @@ class FalcoTest(Test):
|
||||
if self.validate_rules_file == False:
|
||||
self.validate_rules_file = []
|
||||
else:
|
||||
# Always enable json output when validating rules
|
||||
# files. Makes parsing errors/warnings easier
|
||||
self.json_output = True
|
||||
if not isinstance(self.validate_rules_file, list):
|
||||
self.validate_rules_file = [self.validate_rules_file]
|
||||
|
||||
# can be either empty, a string, or a list
|
||||
if self.enable_source == '':
|
||||
self.enable_source = []
|
||||
else:
|
||||
if not isinstance(self.enable_source, list):
|
||||
self.enable_source = [self.enable_source]
|
||||
|
||||
self.rules_args = ""
|
||||
|
||||
@@ -153,13 +168,6 @@ class FalcoTest(Test):
|
||||
detect_counts[key] = value
|
||||
self.detect_counts = detect_counts
|
||||
|
||||
self.rules_warning = self.params.get(
|
||||
'rules_warning', '*', default=False)
|
||||
if self.rules_warning == False:
|
||||
self.rules_warning = set()
|
||||
else:
|
||||
self.rules_warning = set(self.rules_warning)
|
||||
|
||||
# Maps from rule name to set of evttypes
|
||||
self.rules_events = self.params.get('rules_events', '*', default=False)
|
||||
if self.rules_events == False:
|
||||
@@ -232,7 +240,7 @@ class FalcoTest(Test):
|
||||
self.grpcurl_res = None
|
||||
self.grpc_observer = None
|
||||
self.grpc_address = self.params.get(
|
||||
'address', 'grpc/*', default='/var/run/falco.sock')
|
||||
'address', 'grpc/*', default='/run/falco/falco.sock')
|
||||
if self.grpc_address.startswith("unix://"):
|
||||
self.is_grpc_using_unix_socket = True
|
||||
self.grpc_address = self.grpc_address[len("unix://"):]
|
||||
@@ -265,22 +273,6 @@ class FalcoTest(Test):
|
||||
if self.package != 'None':
|
||||
self.uninstall_package()
|
||||
|
||||
def check_rules_warnings(self, res):
|
||||
|
||||
found_warning = set()
|
||||
|
||||
for match in re.finditer('Rule ([^:]+): warning \(([^)]+)\):', res.stderr.decode("utf-8")):
|
||||
rule = match.group(1)
|
||||
warning = match.group(2)
|
||||
found_warning.add(rule)
|
||||
|
||||
self.log.debug("Expected warning rules: {}".format(self.rules_warning))
|
||||
self.log.debug("Actual warning rules: {}".format(found_warning))
|
||||
|
||||
if found_warning != self.rules_warning:
|
||||
self.fail("Expected rules with warnings {} does not match actual rules with warnings {}".format(
|
||||
self.rules_warning, found_warning))
|
||||
|
||||
def check_rules_events(self, res):
|
||||
|
||||
found_events = {}
|
||||
@@ -376,7 +368,68 @@ class FalcoTest(Test):
|
||||
|
||||
return True
|
||||
|
||||
def check_json_output(self, res):
|
||||
def get_validate_json(self, res):
|
||||
if self.validate_json is None:
|
||||
# The first line of stdout should be the validation result as json
|
||||
self.validate_json = json.loads(res.stdout.decode("utf-8").partition('\n')[0])
|
||||
return self.validate_json
|
||||
|
||||
def check_validate_ok(self, res):
|
||||
if self.validate_ok != '':
|
||||
vobj = self.get_validate_json(res)
|
||||
for expected in self.validate_ok:
|
||||
found = False
|
||||
for vres in vobj["falco_load_results"]:
|
||||
if vres["successful"] and os.path.basename(vres["name"]) == expected:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
self.fail("Validation json did not contain a successful result for file '{}'".format(expected))
|
||||
|
||||
def check_validate_warnings(self, res):
|
||||
if self.validate_warnings != '':
|
||||
vobj = self.get_validate_json(res)
|
||||
for warnobj in self.validate_warnings:
|
||||
found = False
|
||||
for vres in vobj["falco_load_results"]:
|
||||
for warning in vres["warnings"]:
|
||||
if warning["code"] == warnobj["code"]:
|
||||
if ("message" in warnobj and warning["message"] == warnobj["message"]) or ("message_contains" in warnobj and warnobj["message_contains"] in warning["message"]):
|
||||
for loc in warning["context"]["locations"]:
|
||||
if loc["item_type"] == warnobj["item_type"] and loc["item_name"] == warnobj["item_name"]:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if "message" in warnobj:
|
||||
self.fail("Validation json did not contain a warning '{}' for '{}' '{}' with message '{}'".format(
|
||||
warnobj["code"], warnobj["item_type"], warnobj["item_name"], warnobj["message"]))
|
||||
else:
|
||||
self.fail("Validation json did not contain a warning '{}' for '{}' '{}' with message containing '{}'".format(
|
||||
warnobj["code"], warnobj["item_type"], warnobj["item_name"], warnobj["message_contains"]))
|
||||
|
||||
def check_validate_errors(self, res):
|
||||
if self.validate_errors != '':
|
||||
vobj = self.get_validate_json(res)
|
||||
for errobj in self.validate_errors:
|
||||
found = False
|
||||
for vres in vobj["falco_load_results"]:
|
||||
for error in vres["errors"]:
|
||||
if error["code"] == errobj["code"]:
|
||||
if ("message" in errobj and error["message"] == errobj["message"]) or ("message_contains" in errobj and errobj["message_contains"] in error["message"]):
|
||||
for loc in error["context"]["locations"]:
|
||||
if loc["item_type"] == errobj["item_type"] and loc["item_name"] == errobj["item_name"]:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
if "message" in errobj:
|
||||
self.fail("Validation json did not contain a error '{}' for '{}' '{}' with message '{}'".format(
|
||||
errobj["code"], errobj["item_type"], errobj["item_name"], errobj["message"]))
|
||||
else:
|
||||
self.fail("Validation json did not contain a error '{}' for '{}' '{}' with message containing '{}'".format(
|
||||
errobj["code"], errobj["item_type"], errobj["item_name"], errobj["message_contains"]))
|
||||
|
||||
|
||||
def check_json_event_output(self, res):
|
||||
if self.json_output:
|
||||
# Just verify that any lines starting with '{' are valid json objects.
|
||||
# Doesn't do any deep inspection of the contents.
|
||||
@@ -578,15 +631,22 @@ class FalcoTest(Test):
|
||||
# This sets falco_binary_path as a side-effect.
|
||||
self.install_package()
|
||||
|
||||
self.validate_json = None
|
||||
|
||||
trace_arg = self.trace_file
|
||||
|
||||
if self.trace_file:
|
||||
trace_arg = "-e {}".format(self.trace_file)
|
||||
|
||||
extra_cmdline = ''
|
||||
for source in self.enable_source:
|
||||
extra_cmdline += ' --enable-source="{}"'.format(source)
|
||||
extra_cmdline += ' ' + self.addl_cmdline_opts
|
||||
|
||||
# Run falco
|
||||
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v {}'.format(
|
||||
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
|
||||
self.json_include_output_property, self.json_include_tags_property, self.priority, self.addl_cmdline_opts)
|
||||
self.json_include_output_property, self.json_include_tags_property, self.priority, extra_cmdline)
|
||||
|
||||
for tag in self.disable_tags:
|
||||
cmd += ' -T {}'.format(tag)
|
||||
@@ -603,7 +663,7 @@ class FalcoTest(Test):
|
||||
if self.time_iso_8601:
|
||||
cmd += ' -o time_format_iso_8601=true'
|
||||
|
||||
self.falco_proc = process.SubProcess(cmd)
|
||||
self.falco_proc = process.SubProcess(cmd, env=dict(os.environ, FALCO_HOSTNAME="test-falco-hostname"))
|
||||
|
||||
res = self.falco_proc.run(timeout=180, sig=9)
|
||||
|
||||
@@ -644,18 +704,22 @@ class FalcoTest(Test):
|
||||
self.error("Falco command \"{}\" exited with unexpected return value {} (!= {})".format(
|
||||
cmd, res.exit_status, self.exit_status))
|
||||
|
||||
self.check_validate_ok(res)
|
||||
self.check_validate_errors(res)
|
||||
self.check_validate_warnings(res)
|
||||
|
||||
# No need to check any outputs if the falco process exited abnormally.
|
||||
if res.exit_status != 0:
|
||||
return
|
||||
|
||||
self.check_rules_warnings(res)
|
||||
if len(self.rules_events) > 0:
|
||||
self.check_rules_events(res)
|
||||
if len(self.validate_rules_file) == 0 and self.check_detection_counts:
|
||||
self.check_detections(res)
|
||||
if len(self.detect_counts) > 0:
|
||||
self.check_detections_by_rule(res)
|
||||
self.check_json_output(res)
|
||||
if not self.validate_rules_file:
|
||||
self.check_json_event_output(res)
|
||||
self.check_outputs()
|
||||
self.check_output_strictly_contains(res)
|
||||
self.check_grpc()
|
||||
|
||||
@@ -20,22 +20,55 @@ trace_files: !mux
|
||||
builtin_rules_no_warnings:
|
||||
detect: False
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_warning: False
|
||||
|
||||
# The rules_events part of this test was mistakenly disabled when
|
||||
# generic events (e.g. k8s_audit support) was added (#1715).
|
||||
# The implementation no longer prints messages of the form:
|
||||
# "Event types for rule (<RULE>): (<EVENT TYPES>)
|
||||
# And without that output, none of the checks below rules_events
|
||||
# are considered.
|
||||
# XXX/mstemm add it back
|
||||
test_warnings:
|
||||
detect: False
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_file: rules/falco_rules_warnings.yaml
|
||||
rules_warning:
|
||||
- no_evttype
|
||||
- evttype_not_equals
|
||||
- leading_not
|
||||
- not_equals_at_end
|
||||
- not_at_end
|
||||
- not_equals_and_not
|
||||
- leading_in_not_equals_at_evttype
|
||||
- not_with_evttypes
|
||||
- not_with_evttypes_addl
|
||||
validate_rules_file: rules/falco_rules_warnings.yaml
|
||||
validate_warnings:
|
||||
- item_type: rule
|
||||
item_name: no_evttype
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: evttype_not_equals
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: leading_not
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: not_equals_at_end
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: not_at_end
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: not_equals_and_not
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: leading_in_not_equals_at_evttype
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: not_with_evttypes
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
- item_type: rule
|
||||
item_name: not_with_evttypes_addl
|
||||
code: LOAD_NO_EVTTYPE
|
||||
message: "Rule matches too many evt.type values. This has a significant performance penalty."
|
||||
rules_events:
|
||||
- no_warnings: [execve]
|
||||
- no_evttype: [all]
|
||||
@@ -251,159 +284,149 @@ trace_files: !mux
|
||||
|
||||
invalid_not_yaml:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rules content is not yaml
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Rules content is not yaml"
|
||||
validate_rules_file:
|
||||
- rules/invalid_not_yaml.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_not_array:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rules content is not yaml array of objects
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Rules content is not yaml array of objects"
|
||||
validate_rules_file:
|
||||
- rules/invalid_not_array.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_array_item_not_object:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Unexpected element type. Each element should be a yaml associative array.
|
||||
---
|
||||
- foo
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rules content item
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Unexpected element type. Each element should be a yaml associative array."
|
||||
validate_rules_file:
|
||||
- rules/invalid_array_item_not_object.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_engine_version_not_number:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Value of required_engine_version must be a number
|
||||
---
|
||||
- required_engine_version: not-a-number
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: required_engine_version
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Can't decode YAML scalar value"
|
||||
validate_rules_file:
|
||||
- rules/invalid_engine_version_not_number.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_yaml_parse_error:
|
||||
exit_status: 1
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_PARSE
|
||||
message: "yaml-cpp: error at line 1, column 11: illegal map value"
|
||||
validate_rules_file:
|
||||
- rules/invalid_yaml_parse_error.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_list_without_items:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List must have property items
|
||||
---
|
||||
- list: bad_list
|
||||
no_items: foo
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: list
|
||||
item_name: bad_list
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'items'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_list_without_items.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_macro_without_condition:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro must have property condition
|
||||
---
|
||||
- macro: bad_macro
|
||||
nope: 1
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: bad_macro
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'condition'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_macro_without_condition.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_rule_without_output:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule must have properties 'condition', 'output', 'desc', and 'priority'
|
||||
---
|
||||
- rule: no output rule
|
||||
desc: some desc
|
||||
condition: evt.type=fork
|
||||
priority: INFO
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: no output rule
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'output'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_rule_without_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_append_rule_without_condition:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Appended rule must have exceptions or condition property
|
||||
---
|
||||
- rule: no condition rule
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: no condition rule
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Appended rule must have exceptions or condition property"
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_rule_without_condition.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_append_macro_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro dangling append has 'append' key but no macro by that name already exists
|
||||
---
|
||||
- macro: dangling append
|
||||
condition: and evt.type=execve
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: dangling append
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Macro has 'append' key but no macro by that name already exists"
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_macro_dangling.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_list_append_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List my_list has 'append' key but no list by that name already exists
|
||||
---
|
||||
- list: my_list
|
||||
items: [not-cat]
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: list
|
||||
item_name: my_list
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "List has 'append' key but no list by that name already exists"
|
||||
validate_rules_file:
|
||||
- rules/list_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_rule_append_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule my_rule has 'append' key but no rule by that name already exists
|
||||
---
|
||||
- rule: my_rule
|
||||
condition: evt.type=open
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: my_rule
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Rule has 'append' key but no rule by that name already exists"
|
||||
validate_rules_file:
|
||||
- rules/rule_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_overwrite_macro:
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_macro.yaml: Ok
|
||||
.*invalid_overwrite_macro.yaml: 1 errors:
|
||||
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
|
||||
---
|
||||
- macro: some macro
|
||||
condition: foo
|
||||
append: false
|
||||
---
|
||||
validate_ok: [invalid_base_macro.yaml]
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Undefined macro 'foo' used in filter."
|
||||
validate_warnings:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_UNUSED_MACRO
|
||||
message: "Macro not referred to by any other rule/macro"
|
||||
validate_rules_file:
|
||||
- rules/invalid_base_macro.yaml
|
||||
- rules/invalid_overwrite_macro.yaml
|
||||
@@ -411,18 +434,17 @@ trace_files: !mux
|
||||
|
||||
invalid_append_macro:
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_macro.yaml: Ok
|
||||
.*invalid_append_macro.yaml: 1 errors:
|
||||
Compilation error when compiling "evt.type=execve foo": 17: unexpected token after 'execve', expecting 'or', 'and'
|
||||
---
|
||||
- macro: some macro
|
||||
condition: evt.type=execve
|
||||
|
||||
- macro: some macro
|
||||
condition: foo
|
||||
append: true
|
||||
---
|
||||
validate_ok: [invalid_base_macro.yaml]
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "unexpected token after 'execve', expecting 'or', 'and'"
|
||||
validate_warnings:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_UNUSED_MACRO
|
||||
message: "Macro not referred to by any other rule/macro"
|
||||
validate_rules_file:
|
||||
- rules/invalid_base_macro.yaml
|
||||
- rules/invalid_append_macro.yaml
|
||||
@@ -430,49 +452,34 @@ trace_files: !mux
|
||||
|
||||
invalid_overwrite_macro_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
|
||||
---
|
||||
- macro: some macro
|
||||
condition: foo
|
||||
append: false
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Undefined macro 'foo' used in filter."
|
||||
validate_rules_file:
|
||||
- rules/invalid_overwrite_macro_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_append_macro_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Compilation error when compiling "evt.type=execve foo": 17: unexpected token after 'execve', expecting 'or', 'and'
|
||||
---
|
||||
- macro: some macro
|
||||
condition: evt.type=execve
|
||||
|
||||
- macro: some macro
|
||||
condition: foo
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "unexpected token after 'execve', expecting 'or', 'and'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_macro_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_overwrite_rule:
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_rule.yaml: Ok
|
||||
.*invalid_overwrite_rule.yaml: 1 errors:
|
||||
Undefined macro 'bar' used in filter.
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: bar
|
||||
output: some output
|
||||
priority: INFO
|
||||
append: false
|
||||
---
|
||||
validate_ok: [invalid_base_rule.yaml]
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: some rule
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Undefined macro 'bar' used in filter."
|
||||
validate_rules_file:
|
||||
- rules/invalid_base_rule.yaml
|
||||
- rules/invalid_overwrite_rule.yaml
|
||||
@@ -480,24 +487,12 @@ trace_files: !mux
|
||||
|
||||
invalid_append_rule:
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_rule.yaml: Ok
|
||||
.*invalid_append_rule.yaml: 1 errors:
|
||||
Compilation error when compiling "evt.type=open bar": 15: unexpected token after 'open', expecting 'or', 'and'
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: evt.type=open
|
||||
output: some output
|
||||
priority: INFO
|
||||
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: bar
|
||||
output: some output
|
||||
priority: INFO
|
||||
append: true
|
||||
---
|
||||
validate_ok: [invalid_base_rule.yaml]
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: some rule
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "unexpected token after 'open', expecting 'or', 'and'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_base_rule.yaml
|
||||
- rules/invalid_append_rule.yaml
|
||||
@@ -505,96 +500,66 @@ trace_files: !mux
|
||||
|
||||
invalid_overwrite_rule_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Undefined macro 'bar' used in filter.
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: bar
|
||||
output: some output
|
||||
priority: INFO
|
||||
append: false
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: some rule
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Undefined macro 'bar' used in filter."
|
||||
validate_rules_file:
|
||||
- rules/invalid_overwrite_rule_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_append_rule_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
Compilation error when compiling "evt.type=open bar": 15: unexpected token after 'open', expecting 'or', 'and'
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: evt.type=open
|
||||
output: some output
|
||||
priority: INFO
|
||||
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
condition: bar
|
||||
output: some output
|
||||
priority: INFO
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: some rule
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "unexpected token after 'open', expecting 'or', 'and'"
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_rule_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_missing_rule_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule name is empty
|
||||
---
|
||||
- rule:
|
||||
desc: some desc
|
||||
condition: evt.type=execve
|
||||
output: some output
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Mapping for key 'rule' is empty"
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_rule_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_missing_list_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List name is empty
|
||||
---
|
||||
- list:
|
||||
items: [foo]
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: list
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Mapping for key 'list' is empty"
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_list_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_missing_macro_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro name is empty
|
||||
---
|
||||
- macro:
|
||||
condition: evt.type=execve
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: macro
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Mapping for key 'macro' is empty"
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_macro_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_rule_output:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'
|
||||
---
|
||||
- rule: rule_with_invalid_output
|
||||
desc: A rule with an invalid output field
|
||||
condition: evt.type=open
|
||||
output: "An open was seen %not_a_real_field"
|
||||
priority: WARNING
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: rule_with_invalid_output
|
||||
code: LOAD_ERR_COMPILE_OUTPUT
|
||||
message: "invalid formatting token not_a_real_field"
|
||||
validate_rules_file:
|
||||
- rules/invalid_rule_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -622,13 +587,13 @@ trace_files: !mux
|
||||
rules_file:
|
||||
- rules/single_rule_enabled_flag.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
|
||||
disabled_rule_using_false_enabled_flag_only:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/disabled_rule_using_enabled_flag_only.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
|
||||
enabled_rule_using_false_enabled_flag_only:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
@@ -754,7 +719,7 @@ trace_files: !mux
|
||||
- "Write below etc": 1
|
||||
- "System procs network activity": 1
|
||||
- "Mkdir binary dirs": 1
|
||||
- "System user interactive": 1
|
||||
- "System user interactive": 0
|
||||
- "DB program spawned process": 1
|
||||
- "Non sudo setuid": 1
|
||||
- "Create files below dev": 1
|
||||
@@ -1025,13 +990,6 @@ trace_files: !mux
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
list_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "List my_list has 'append' key but no list by that name already exists"
|
||||
rules_file:
|
||||
- rules/list_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
list_append:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
@@ -1045,13 +1003,6 @@ trace_files: !mux
|
||||
- rules/list_append_false.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
macro_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists"
|
||||
rules_file:
|
||||
- rules/macro_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
macro_append:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
@@ -1065,13 +1016,6 @@ trace_files: !mux
|
||||
- rules/macro_append_false.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists"
|
||||
rules_file:
|
||||
- rules/rule_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_append_skipped:
|
||||
detect: False
|
||||
priority: ERROR
|
||||
@@ -1149,13 +1093,21 @@ trace_files: !mux
|
||||
- rules/catchall_order.yaml
|
||||
detect_counts:
|
||||
- open_dev_null: 1
|
||||
dev_null: 0
|
||||
dev_null: 6
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
skip_unknown_noevt:
|
||||
validate_skip_unknown_noevt:
|
||||
validate_warnings:
|
||||
- item_type: rule
|
||||
item_name: "Contains Unknown Event And Skipping"
|
||||
code: LOAD_UNKNOWN_FIELD
|
||||
message: "filter_check called with nonexistent field proc.nobody"
|
||||
validate_rules_file:
|
||||
- rules/skip_unknown_evt.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
detect_skip_unknown_noevt:
|
||||
detect: False
|
||||
rules_warning:
|
||||
- Contains Unknown Event And Skipping
|
||||
rules_file:
|
||||
- rules/skip_unknown_evt.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -1168,41 +1120,34 @@ trace_files: !mux
|
||||
|
||||
skip_unknown_error:
|
||||
exit_status: 1
|
||||
stderr_contains: |+
|
||||
Could not load rules file.*skip_unknown_error.yaml: 1 errors:
|
||||
Rule Contains Unknown Event And Not Skipping: error filter_check called with nonexistent field proc.nobody
|
||||
---
|
||||
- rule: Contains Unknown Event And Not Skipping
|
||||
desc: Contains an unknown event
|
||||
condition: proc.nobody=cat
|
||||
output: Never
|
||||
skip-if-unknown-filter: false
|
||||
priority: INFO
|
||||
---
|
||||
rules_file:
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: "Contains Unknown Event And Not Skipping"
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "filter_check called with nonexistent field proc.nobody"
|
||||
validate_rules_file:
|
||||
- rules/skip_unknown_error.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
skip_unknown_unspec_error:
|
||||
exit_status: 1
|
||||
stderr_contains: |+
|
||||
Could not load rules file .*skip_unknown_unspec.yaml: 1 errors:
|
||||
Rule Contains Unknown Event And Unspecified: error filter_check called with nonexistent field proc.nobody
|
||||
---
|
||||
- rule: Contains Unknown Event And Unspecified
|
||||
desc: Contains an unknown event
|
||||
condition: proc.nobody=cat
|
||||
output: Never
|
||||
priority: INFO
|
||||
---
|
||||
rules_file:
|
||||
validate_errors:
|
||||
- item_type: rule
|
||||
item_name: "Contains Unknown Event And Unspecified"
|
||||
code: LOAD_ERR_COMPILE_CONDITION
|
||||
message: "filter_check called with nonexistent field proc.nobody"
|
||||
validate_rules_file:
|
||||
- rules/skip_unknown_unspec.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
engine_version_mismatch:
|
||||
exit_status: 1
|
||||
stderr_contains: Rules require engine version 9999999, but engine version is
|
||||
rules_file:
|
||||
validate_errors:
|
||||
- item_type: required_engine_version
|
||||
item_name: ""
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message_contains: "Rules require engine version 9999999, but engine version is"
|
||||
validate_rules_file:
|
||||
- rules/engine_version_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
|
||||
@@ -20,175 +20,99 @@ trace_files: !mux
|
||||
|
||||
rule_exception_no_fields:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: must have fields property with a list of fields
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'fields'"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_no_fields.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_no_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item must have name property
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'name'"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_no_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_no_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item must have name property
|
||||
---
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- values:
|
||||
- [nginx, /tmp/foo]
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Item has no mapping for key 'name'"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_no_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_unknown_fields:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: field name not.exist is not a supported filter field
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [not.exist]
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "'not.exist' is not a supported filter field"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_unknown_fields.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_comps_fields_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: fields and comps lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=]
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Fields and comps lists must have equal length"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_comps_fields_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_unknown_comp:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: comparison operator no-comp is not a supported comparison operator
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=, no-comp]
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "'no-comp' is not a supported comparison operator"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_unknown_comp.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_fields_values_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Exception item ex1: fields and values lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
values:
|
||||
- [nginx]
|
||||
priority: error
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Fields and values lists must have equal length"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_fields_values_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_fields_values_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Exception item ex1: fields and values lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- name: ex1
|
||||
values:
|
||||
- [nginx]
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex1
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Fields and values lists must have equal length"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_fields_values_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_item_not_in_rule:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception new item ex2: must have fields property with a list of fields
|
||||
---
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- name: ex2
|
||||
values:
|
||||
- [apache, /tmp]
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: ex2
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Rule exception must have fields property with a list of fields"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_not_in_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -325,7 +249,7 @@ trace_files: !mux
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_new_single_field_append.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
|
||||
rule_exception_new_second_field_append:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
@@ -335,18 +259,11 @@ trace_files: !mux
|
||||
|
||||
rule_exception_new_append_no_field:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception new item proc_cmdline: must have fields property with a list of fields
|
||||
---
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_cmdline
|
||||
comps: in
|
||||
values:
|
||||
- "cat /dev/null"
|
||||
append: true
|
||||
---
|
||||
validate_errors:
|
||||
- item_type: exception
|
||||
item_name: proc_cmdline
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Rule exception must have fields property with a list of fields"
|
||||
validate_rules_file:
|
||||
- rules/exceptions/rule_exception_new_no_field_append.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
@@ -35,6 +35,7 @@ trace_files: !mux
|
||||
stdout_contains: "ct.id"
|
||||
|
||||
detect_create_instance:
|
||||
enable_source: aws_cloudtrail
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
@@ -44,6 +45,7 @@ trace_files: !mux
|
||||
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||
|
||||
detect_create_instance_bigevent:
|
||||
enable_source: aws_cloudtrail
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
@@ -52,16 +54,9 @@ trace_files: !mux
|
||||
- 'Cloudtrail Create Instance': 1
|
||||
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances_bigevent.yaml
|
||||
|
||||
multiple_source_plugins:
|
||||
exit_status: 1
|
||||
stderr_contains: "Can not load multiple plugins with event sourcing capability: 'cloudtrail' already loaded."
|
||||
conf_file: BUILD_DIR/test/confs/plugins/multiple_source_plugins.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
|
||||
incompatible_extract_sources:
|
||||
exit_status: 1
|
||||
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any enabled event source"
|
||||
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any known event source"
|
||||
conf_file: BUILD_DIR/test/confs/plugins/incompatible_extract_sources.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
@@ -75,7 +70,7 @@ trace_files: !mux
|
||||
|
||||
incompat_plugin_api:
|
||||
exit_status: 1
|
||||
stderr_contains: "Plugin required API version '10000000.0.0' is not supported by the plugin API version of the framework '.*'"
|
||||
stderr_contains: "plugin required API version '10000000.0.0' not compatible with the framework's API version '.*'"
|
||||
conf_file: BUILD_DIR/test/confs/plugins/incompatible_plugin_api.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
@@ -89,27 +84,30 @@ trace_files: !mux
|
||||
|
||||
wrong_plugin_path:
|
||||
exit_status: 1
|
||||
stderr_contains: "error loading plugin.*No such file or directory. Exiting"
|
||||
stderr_contains: "cannot load plugin.*No such file or directory"
|
||||
conf_file: BUILD_DIR/test/confs/plugins/wrong_plugin_path.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_incompat_plugin_version.yaml
|
||||
|
||||
no_plugins_unknown_source:
|
||||
detect: False
|
||||
rules_file:
|
||||
exit_status: 0
|
||||
validate_warnings:
|
||||
- item_type: rule
|
||||
item_name: Cloudtrail Create Instance
|
||||
code: LOAD_UNKNOWN_SOURCE
|
||||
message: "Unknown source aws_cloudtrail, skipping"
|
||||
validate_rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_warning:
|
||||
- Cloudtrail Create Instance
|
||||
stderr_contains: "Rule Cloudtrail Create Instance: warning .unknown-source.: unknown source aws_cloudtrail, skipping"
|
||||
|
||||
no_plugins_unknown_source_rule_exception:
|
||||
detect: False
|
||||
rules_file:
|
||||
exit_status: 0
|
||||
validate_warnings:
|
||||
- item_type: rule
|
||||
item_name: Cloudtrail Create Instance
|
||||
code: LOAD_UNKNOWN_SOURCE
|
||||
message: "Unknown source aws_cloudtrail, skipping"
|
||||
validate_rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances_exceptions.yaml
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_warning:
|
||||
- Cloudtrail Create Instance
|
||||
stderr_contains: "Rule Cloudtrail Create Instance: warning .unknown-source.: unknown source aws_cloudtrail, skipping"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ traces: !mux
|
||||
|
||||
container-privileged:
|
||||
trace_file: traces-positive/container-privileged.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
detect_counts:
|
||||
@@ -37,6 +38,7 @@ traces: !mux
|
||||
|
||||
container-sensitive-mount:
|
||||
trace_file: traces-positive/container-sensitive-mount.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
detect_counts:
|
||||
@@ -51,6 +53,7 @@ traces: !mux
|
||||
|
||||
db-program-spawned-process:
|
||||
trace_file: traces-positive/db-program-spawned-process.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
detect_counts:
|
||||
@@ -59,7 +62,7 @@ traces: !mux
|
||||
falco-event-generator:
|
||||
trace_file: traces-positive/falco-event-generator.scap
|
||||
detect: True
|
||||
detect_level: [ERROR, WARNING, INFO, NOTICE, DEBUG]
|
||||
detect_level: [ERROR, WARNING, NOTICE, DEBUG]
|
||||
detect_counts:
|
||||
- "Write below binary dir": 1
|
||||
- "Read sensitive file untrusted": 3
|
||||
@@ -68,7 +71,7 @@ traces: !mux
|
||||
- "Write below etc": 1
|
||||
- "System procs network activity": 1
|
||||
- "Mkdir binary dirs": 1
|
||||
- "System user interactive": 1
|
||||
- "System user interactive": 0
|
||||
- "DB program spawned process": 1
|
||||
- "Non sudo setuid": 1
|
||||
- "Create files below dev": 1
|
||||
@@ -120,8 +123,10 @@ traces: !mux
|
||||
# falco-event-generator.scap so the rule is still being tested.
|
||||
run-shell-untrusted:
|
||||
trace_file: traces-positive/run-shell-untrusted.scap
|
||||
detect: False
|
||||
detect: True
|
||||
detect_level: DEBUG
|
||||
detect_counts:
|
||||
- "Run shell untrusted": 1
|
||||
|
||||
system-binaries-network-activity:
|
||||
trace_file: traces-positive/system-binaries-network-activity.scap
|
||||
@@ -132,6 +137,7 @@ traces: !mux
|
||||
|
||||
system-user-interactive:
|
||||
trace_file: traces-positive/system-user-interactive.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
detect_counts:
|
||||
@@ -139,6 +145,7 @@ traces: !mux
|
||||
|
||||
user-mgmt-binaries:
|
||||
trace_file: traces-positive/user-mgmt-binaries.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
detect_counts:
|
||||
@@ -169,6 +176,7 @@ traces: !mux
|
||||
# When a new version of the scap files is generated this should then become "traces-positive"
|
||||
docker-compose:
|
||||
trace_file: traces-negative/docker-compose.scap
|
||||
all_events: True
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
detect_counts:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{"output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}}
|
||||
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}}
|
||||
|
||||
@@ -19,7 +19,7 @@ limitations under the License.
|
||||
#include <string.h>
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = "1.0.0";
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static const char *pl_name_base = "test_extract";
|
||||
static char pl_name[1024];
|
||||
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = "1.0.0";
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static uint32_t pl_id = 999;
|
||||
static const char *pl_name = "test_source";
|
||||
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||
|
||||
@@ -1,19 +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.
|
||||
#
|
||||
- macro: my_macro
|
||||
condition: proc.name=not-cat
|
||||
append: true
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env 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.
|
||||
#
|
||||
|
||||
# Run sysdig excluding all events that aren't used by Falco and also
|
||||
# excluding other high-volume events that aren't essential. This
|
||||
# results in smaller trace files.
|
||||
|
||||
# The remaining arguments are taken from the command line.
|
||||
|
||||
exec sudo sysdig not evt.type in '(mprotect,brk,mq_timedreceive,mq_receive,mq_timedsend,mq_send,getrusage,procinfo,rt_sigprocmask,rt_sigaction,ioctl,clock_getres,clock_gettime,clock_nanosleep,clock_settime,close,epoll_create,epoll_create1,epoll_ctl,epoll_pwait,epoll_wait,eventfd,fcntl,fcntl64,fstat,fstat64,fstatat64,fstatfs,fstatfs64,futex,getitimer,gettimeofday,ioprio_get,ioprio_set,llseek,lseek,lstat,lstat64,mmap,mmap2,munmap,nanosleep,poll,ppoll,pread,pread64,preadv,procinfo,pselect6,pwrite,pwrite64,pwritev,read,readv,recv,recvfrom,recvmmsg,recvmsg,sched_yield,select,send,sendfile,sendfile64,sendmmsg,sendmsg,sendto,setitimer,settimeofday,shutdown,splice,stat,stat64,statfs,statfs64,switch,tee,timer_create,timer_delete,timerfd_create,timerfd_gettime,timerfd_settime,timer_getoverrun,timer_gettime,timer_settime,wait4,write,writev) and user.name!=ec2-user' "$@"
|
||||
@@ -22,6 +22,7 @@ set(
|
||||
engine/test_filter_macro_resolver.cpp
|
||||
engine/test_filter_evttype_resolver.cpp
|
||||
engine/test_filter_warning_resolver.cpp
|
||||
engine/test_plugin_requirements.cpp
|
||||
falco/test_configuration.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -14,27 +14,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#include "falco_utils.h"
|
||||
#include <nonstd/string_view.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("is_unix_scheme matches", "[utils]")
|
||||
{
|
||||
SECTION("rvalue")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("unix:///var/run/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme("unix:///run/falco/falco.sock");
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("std::string")
|
||||
{
|
||||
std::string url("unix:///var/run/falco.sock");
|
||||
std::string url("unix:///run/falco/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("char[]")
|
||||
{
|
||||
char url[] = "unix:///var/run/falco.sock";
|
||||
char url[] = "unix:///run/falco/falco.sock";
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
@@ -42,7 +41,7 @@ TEST_CASE("is_unix_scheme matches", "[utils]")
|
||||
|
||||
TEST_CASE("is_unix_scheme does not match", "[utils]")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("something:///var/run/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock");
|
||||
REQUIRE_FALSE(res);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ string to_string(set<uint16_t> s)
|
||||
return out;
|
||||
}
|
||||
|
||||
void compare_evttypes(ast::expr* f, set<uint16_t> &expected)
|
||||
void compare_evttypes(std::unique_ptr<ast::expr> f, set<uint16_t> &expected)
|
||||
{
|
||||
set<uint16_t> actual;
|
||||
filter_evttype_resolver().evttypes(f, actual);
|
||||
filter_evttype_resolver().evttypes(f.get(), actual);
|
||||
for(auto &etype : expected)
|
||||
{
|
||||
REQUIRE(actual.find(etype) != actual.end());
|
||||
@@ -49,7 +49,7 @@ void compare_evttypes(ast::expr* f, set<uint16_t> &expected)
|
||||
}
|
||||
}
|
||||
|
||||
ast::expr* compile(const string &fltstr)
|
||||
std::unique_ptr<ast::expr> compile(const string &fltstr)
|
||||
{
|
||||
return libsinsp::filter::parser(fltstr).parse();
|
||||
}
|
||||
@@ -57,12 +57,14 @@ ast::expr* compile(const string &fltstr)
|
||||
TEST_CASE("Should find event types from filter", "[rule_loader]")
|
||||
{
|
||||
set<uint16_t> openat_only{
|
||||
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
|
||||
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X };
|
||||
|
||||
set<uint16_t> close_only{
|
||||
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
|
||||
|
||||
set<uint16_t> openat_close{
|
||||
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
|
||||
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X,
|
||||
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
|
||||
|
||||
@@ -71,14 +73,15 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
|
||||
set<uint16_t> not_close;
|
||||
set<uint16_t> all_events;
|
||||
set<uint16_t> no_events;
|
||||
|
||||
for(uint32_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip "old" event versions that have been replaced
|
||||
// by newer event versions, or events that are unused.
|
||||
if(g_infotables.m_event_info[i].flags & (EF_OLD_VERSION | EF_UNUSED))
|
||||
// Skip events that are unused.
|
||||
if(sinsp::is_unused_event(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
all_events.insert(i);
|
||||
if(openat_only.find(i) == openat_only.end())
|
||||
{
|
||||
@@ -97,138 +100,138 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
|
||||
SECTION("evt_type_eq")
|
||||
{
|
||||
auto f = compile("evt.type=openat");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_in")
|
||||
{
|
||||
auto f = compile("evt.type in (openat, close)");
|
||||
compare_evttypes(f, openat_close);
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("evt_type_ne")
|
||||
{
|
||||
auto f = compile("evt.type!=openat");
|
||||
compare_evttypes(f, not_openat);
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_eq")
|
||||
{
|
||||
auto f = compile("not evt.type=openat");
|
||||
compare_evttypes(f, not_openat);
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_in")
|
||||
{
|
||||
auto f = compile("not evt.type in (openat, close)");
|
||||
compare_evttypes(f, not_openat_close);
|
||||
compare_evttypes(std::move(f), not_openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_ne")
|
||||
{
|
||||
auto f = compile("not evt.type != openat");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type=openat or evt.type=close");
|
||||
compare_evttypes(f, openat_close);
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type!=openat or evt.type!=close");
|
||||
compare_evttypes(f, all_events);
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("evt_type_or_ne")
|
||||
{
|
||||
auto f = compile("evt.type=close or evt.type!=openat");
|
||||
compare_evttypes(f, not_openat);
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and")
|
||||
{
|
||||
auto f = compile("evt.type=close and evt.type=openat");
|
||||
compare_evttypes(f, no_events);
|
||||
compare_evttypes(std::move(f), no_events);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_non_evt_type")
|
||||
{
|
||||
auto f = compile("evt.type=openat and proc.name=nginx");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_non_evt_type_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat and not proc.name=nginx");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_nested")
|
||||
{
|
||||
auto f = compile("evt.type=openat and (proc.name=nginx)");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_nested_multi")
|
||||
{
|
||||
auto f = compile("evt.type=openat and (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(f, no_events);
|
||||
compare_evttypes(std::move(f), no_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type")
|
||||
{
|
||||
auto f = compile("proc.name=nginx");
|
||||
compare_evttypes(f, all_events);
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type=openat or proc.name=nginx");
|
||||
compare_evttypes(f, all_events);
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_first")
|
||||
{
|
||||
auto f = compile("(evt.type=openat) or proc.name=nginx");
|
||||
compare_evttypes(f, all_events);
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_second")
|
||||
{
|
||||
auto f = compile("evt.type=openat or (proc.name=nginx)");
|
||||
compare_evttypes(f, all_events);
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_multi")
|
||||
{
|
||||
auto f = compile("evt.type=openat or (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(f, openat_close);
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_multi_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat or not (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(f, not_close);
|
||||
compare_evttypes(std::move(f), not_close);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_and_nested_multi_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat and not (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("ne_and_and")
|
||||
{
|
||||
auto f = compile("evt.type!=openat and evt.type!=close");
|
||||
compare_evttypes(f, not_openat_close);
|
||||
compare_evttypes(std::move(f), not_openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_not")
|
||||
{
|
||||
auto f = compile("not (not evt.type=openat)");
|
||||
compare_evttypes(f, openat_only);
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,25 +20,42 @@ limitations under the License.
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter::ast;
|
||||
|
||||
static pos_info create_pos(uint32_t idx, uint32_t line, uint32_t col)
|
||||
{
|
||||
pos_info ret;
|
||||
ret.idx = idx;
|
||||
ret.line = line;
|
||||
ret.col = col;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool operator==(const pos_info& p1, const pos_info& p2)
|
||||
{
|
||||
return (p1.idx == p2.idx) &&
|
||||
(p1.line == p2.line) &&
|
||||
(p1.col == p2.col);
|
||||
}
|
||||
|
||||
TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos = create_pos(12, 85, 27);
|
||||
|
||||
SECTION("in the general case")
|
||||
{
|
||||
shared_ptr<expr> macro(
|
||||
new unary_check_expr("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> macro = std::move(
|
||||
unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
expr* filter = new and_expr({
|
||||
new unary_check_expr("evt.name", "", "exists"),
|
||||
new not_expr(
|
||||
new value_expr(macro_name)
|
||||
),
|
||||
});
|
||||
expr* expected_filter = new and_expr({
|
||||
new unary_check_expr("evt.name", "", "exists"),
|
||||
new not_expr(clone(macro.get())),
|
||||
});
|
||||
std::vector<std::unique_ptr<expr>> filter_and;
|
||||
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(not_expr::create(value_expr::create(macro_name, macro_pos)));
|
||||
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_and;
|
||||
expected_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
expected_and.push_back(not_expr::create(clone(macro.get())));
|
||||
std::shared_ptr<expr> expected = std::move(and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(macro_name, macro);
|
||||
@@ -46,48 +63,45 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
REQUIRE(filter->is_equal(expected.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
|
||||
delete filter;
|
||||
delete expected_filter;
|
||||
REQUIRE(filter->is_equal(expected.get()));
|
||||
}
|
||||
|
||||
SECTION("with a single node")
|
||||
{
|
||||
shared_ptr<expr> macro(
|
||||
new unary_check_expr("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> macro = std::move(
|
||||
unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
expr* filter = new value_expr(macro_name);
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name, macro_pos));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(macro_name, macro);
|
||||
|
||||
// first run
|
||||
expr* old_filter_ptr = filter;
|
||||
expr* old_filter_ptr = filter.get();
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(filter != old_filter_ptr);
|
||||
REQUIRE(filter.get() != old_filter_ptr);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
|
||||
// second run
|
||||
old_filter_ptr = filter;
|
||||
old_filter_ptr = filter.get();
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(filter == old_filter_ptr);
|
||||
REQUIRE(filter.get() == old_filter_ptr);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
|
||||
delete filter;
|
||||
}
|
||||
|
||||
SECTION("with multiple macros")
|
||||
@@ -95,19 +109,23 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
shared_ptr<expr> a_macro(
|
||||
new unary_check_expr("one.field", "", "exists"));
|
||||
shared_ptr<expr> b_macro(
|
||||
new unary_check_expr("another.field", "", "exists"));
|
||||
pos_info a_macro_pos = create_pos(11, 75, 43);
|
||||
pos_info b_macro_pos = create_pos(91, 21, 9);
|
||||
|
||||
expr* filter = new or_expr({
|
||||
new value_expr(a_macro_name),
|
||||
new value_expr(b_macro_name),
|
||||
});
|
||||
expr* expected_filter = new or_expr({
|
||||
clone(a_macro.get()),
|
||||
clone(b_macro.get()),
|
||||
});
|
||||
std::shared_ptr<expr> a_macro = std::move(
|
||||
unary_check_expr::create("one.field", "", "exists"));
|
||||
std::shared_ptr<expr> b_macro = std::move(
|
||||
unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> filter_or;
|
||||
filter_or.push_back(value_expr::create(a_macro_name, a_macro_pos));
|
||||
filter_or.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> filter = std::move(or_expr::create(filter_or));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_or;
|
||||
expected_or.push_back(clone(a_macro.get()));
|
||||
expected_or.push_back(clone(b_macro.get()));
|
||||
std::shared_ptr<expr> expected_filter = std::move(or_expr::create(expected_or));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
@@ -116,21 +134,23 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 2);
|
||||
REQUIRE(resolver.get_resolved_macros().find(a_macro_name)
|
||||
!= resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_resolved_macros().find(b_macro_name)
|
||||
!= resolver.get_resolved_macros().end());
|
||||
auto a_resolved_itr = resolver.get_resolved_macros().find(a_macro_name);
|
||||
REQUIRE(a_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(a_resolved_itr->first == a_macro_name);
|
||||
REQUIRE(a_resolved_itr->second == a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = resolver.get_resolved_macros().find(b_macro_name);
|
||||
REQUIRE(b_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
REQUIRE(b_resolved_itr->first == b_macro_name);
|
||||
REQUIRE(b_resolved_itr->second == b_macro_pos);
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
|
||||
delete filter;
|
||||
delete expected_filter;
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
|
||||
SECTION("with nested macros")
|
||||
@@ -138,18 +158,23 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
shared_ptr<expr> a_macro(new and_expr({
|
||||
new unary_check_expr("one.field", "", "exists"),
|
||||
new value_expr(b_macro_name),
|
||||
}));
|
||||
shared_ptr<expr> b_macro(
|
||||
new unary_check_expr("another.field", "", "exists"));
|
||||
pos_info a_macro_pos = create_pos(47, 1, 76);
|
||||
pos_info b_macro_pos = create_pos(111, 65, 2);
|
||||
|
||||
expr* filter = new value_expr(a_macro_name);
|
||||
expr* expected_filter = new and_expr({
|
||||
new unary_check_expr("one.field", "", "exists"),
|
||||
new unary_check_expr("another.field", "", "exists"),
|
||||
});
|
||||
std::vector<std::unique_ptr<expr>> a_macro_and;
|
||||
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<expr> b_macro = std::move(
|
||||
unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name, a_macro_pos));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_and;
|
||||
expected_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
expected_and.push_back(unary_check_expr::create("another.field", "", "exists"));
|
||||
std::shared_ptr<expr> expected_filter = std::move(and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
@@ -158,44 +183,46 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 2);
|
||||
REQUIRE(resolver.get_resolved_macros().find(a_macro_name)
|
||||
!= resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_resolved_macros().find(b_macro_name)
|
||||
!= resolver.get_resolved_macros().end());
|
||||
auto a_resolved_itr = resolver.get_resolved_macros().find(a_macro_name);
|
||||
REQUIRE(a_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(a_resolved_itr->first == a_macro_name);
|
||||
REQUIRE(a_resolved_itr->second == a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = resolver.get_resolved_macros().find(b_macro_name);
|
||||
REQUIRE(b_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
REQUIRE(b_resolved_itr->first == b_macro_name);
|
||||
REQUIRE(b_resolved_itr->second == b_macro_pos);
|
||||
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
|
||||
delete filter;
|
||||
delete expected_filter;
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Should find unknown macros", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos = create_pos(9, 4, 2);
|
||||
|
||||
SECTION("in the general case")
|
||||
{
|
||||
expr* filter = new and_expr({
|
||||
new unary_check_expr("evt.name", "", "exists"),
|
||||
new not_expr(
|
||||
new value_expr(macro_name)
|
||||
),
|
||||
});
|
||||
std::vector<std::unique_ptr<expr>> filter_and;
|
||||
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(not_expr::create(value_expr::create(macro_name, macro_pos)));
|
||||
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_unknown_macros().begin() == macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
|
||||
delete filter;
|
||||
}
|
||||
|
||||
SECTION("with nested macros")
|
||||
@@ -203,13 +230,16 @@ TEST_CASE("Should find unknown macros", "[rule_loader]")
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
shared_ptr<expr> a_macro(new and_expr({
|
||||
new unary_check_expr("one.field", "", "exists"),
|
||||
new value_expr(b_macro_name),
|
||||
}));
|
||||
pos_info a_macro_pos = create_pos(32, 84, 9);
|
||||
pos_info b_macro_pos = create_pos(1, 0, 5);
|
||||
|
||||
expr* filter = new value_expr(a_macro_name);
|
||||
expr* expected_filter = clone(a_macro.get());
|
||||
std::vector<std::unique_ptr<expr>> a_macro_and;
|
||||
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name, a_macro_pos));
|
||||
auto expected_filter = clone(a_macro.get());
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
@@ -217,28 +247,31 @@ TEST_CASE("Should find unknown macros", "[rule_loader]")
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_resolved_macros().begin() == a_macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == a_macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == a_macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_unknown_macros().begin() == b_macro_name);
|
||||
REQUIRE(filter->is_equal(expected_filter));
|
||||
|
||||
delete filter;
|
||||
delete expected_filter;
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == b_macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == b_macro_pos);
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Should undefine macro", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
shared_ptr<expr> macro(new unary_check_expr("test.field", "", "exists"));
|
||||
expr* a_filter = new value_expr(macro_name);
|
||||
expr* b_filter = new value_expr(macro_name);
|
||||
pos_info macro_pos_1 = create_pos(12, 9, 3);
|
||||
pos_info macro_pos_2 = create_pos(9, 6, 3);
|
||||
|
||||
std::shared_ptr<expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> a_filter = std::move(value_expr::create(macro_name, macro_pos_1));
|
||||
std::shared_ptr<expr> b_filter = std::move(value_expr::create(macro_name, macro_pos_2));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
resolver.set_macro(macro_name, macro);
|
||||
REQUIRE(resolver.run(a_filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos_1);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(a_filter->is_equal(macro.get()));
|
||||
|
||||
@@ -246,30 +279,27 @@ TEST_CASE("Should undefine macro", "[rule_loader]")
|
||||
REQUIRE(resolver.run(b_filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_unknown_macros().begin() == macro_name);
|
||||
|
||||
delete a_filter;
|
||||
delete b_filter;
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == macro_pos_2);
|
||||
}
|
||||
|
||||
// checks that the macro AST is cloned and not shared across resolved filters
|
||||
TEST_CASE("Should clone macro AST", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
shared_ptr<unary_check_expr> macro(
|
||||
new unary_check_expr("test.field", "", "exists"));
|
||||
expr* filter = new value_expr(macro_name);
|
||||
pos_info macro_pos = create_pos(5, 2, 8888);
|
||||
std::shared_ptr<unary_check_expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name, macro_pos));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
|
||||
resolver.set_macro(macro_name, macro);
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
|
||||
macro.get()->field = "another.field";
|
||||
macro->field = "another.field";
|
||||
REQUIRE(!filter->is_equal(macro.get()));
|
||||
|
||||
delete filter;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,9 @@ limitations under the License.
|
||||
|
||||
static bool warns(const std::string& condition)
|
||||
{
|
||||
std::set<std::string> w;
|
||||
std::set<falco::load_result::warning_code> w;
|
||||
auto ast = libsinsp::filter::parser(condition).parse();
|
||||
filter_warning_resolver().run(ast, w);
|
||||
delete ast;
|
||||
filter_warning_resolver().run(ast.get(), w);
|
||||
return !w.empty();
|
||||
}
|
||||
|
||||
|
||||
269
tests/engine/test_plugin_requirements.cpp
Normal file
269
tests/engine/test_plugin_requirements.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 <memory>
|
||||
#include <catch.hpp>
|
||||
#include "falco_engine.h"
|
||||
|
||||
static void check_requirements(
|
||||
bool expect_success,
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const std::string& ruleset_content)
|
||||
{
|
||||
std::string err;
|
||||
std::unique_ptr<falco_engine> e(new falco_engine());
|
||||
falco::load_result::rules_contents_t c = {{"test", ruleset_content}};
|
||||
|
||||
auto res = e->load_rules(c.begin()->second, c.begin()->first);
|
||||
if (!res->successful())
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(res->as_string(false, c));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e->check_plugin_requirements(plugins, err))
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(err);
|
||||
}
|
||||
}
|
||||
else if (!expect_success)
|
||||
{
|
||||
FAIL("unexpected successful plugin requirements check");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must accept", "[rule_loader]")
|
||||
{
|
||||
SECTION("no requirement")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, "");
|
||||
}
|
||||
|
||||
SECTION("single plugin")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin newer version")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must reject", "[rule_loader]")
|
||||
{
|
||||
SECTION("no plugin loaded")
|
||||
{
|
||||
check_requirements(false, {}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong name")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong version")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with overlapping alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
@@ -27,30 +27,51 @@ static uint16_t other_non_default_ruleset = 2;
|
||||
static std::set<std::string> tags = {"some_tag", "some_other_tag"};
|
||||
static std::set<uint16_t> evttypes = { ppm_event_type::PPME_GENERIC_E };
|
||||
|
||||
static std::shared_ptr<libsinsp::filter::ast::expr> create_filter()
|
||||
static std::shared_ptr<gen_event_filter_factory> create_factory()
|
||||
{
|
||||
libsinsp::filter::parser parser("evt.type=open");
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ret(parser.parse());
|
||||
std::shared_ptr<gen_event_filter_factory> ret(new sinsp_filter_factory(NULL));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<filter_ruleset> create_ruleset()
|
||||
static std::shared_ptr<libsinsp::filter::ast::expr> create_ast(
|
||||
std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
libsinsp::filter::parser parser("evt.type=open");
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ret(parser.parse());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<gen_event_filter> create_filter(
|
||||
std::shared_ptr<gen_event_filter_factory> f,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ast)
|
||||
{
|
||||
sinsp_filter_compiler compiler(f, ast.get());
|
||||
std::shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
static std::shared_ptr<filter_ruleset> create_ruleset(
|
||||
std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
std::shared_ptr<gen_event_filter_factory> f(new sinsp_filter_factory(NULL));
|
||||
std::shared_ptr<filter_ruleset> ret(new evttype_index_ruleset(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
TEST_CASE("Should enable/disable on ruleset", "[rulesets]")
|
||||
{
|
||||
auto r = create_ruleset();
|
||||
auto filter = create_filter();
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
auto filter = create_filter(f, ast);
|
||||
falco_rule rule;
|
||||
rule.name = "one_rule";
|
||||
rule.source = falco_common::syscall_source;
|
||||
rule.tags = tags;
|
||||
|
||||
r->add(rule, filter);
|
||||
r->add(rule, filter, ast);
|
||||
|
||||
SECTION("Should enable/disable for exact match w/ default ruleset")
|
||||
{
|
||||
@@ -184,21 +205,23 @@ TEST_CASE("Should enable/disable on ruleset", "[rulesets]")
|
||||
|
||||
TEST_CASE("Should enable/disable on ruleset for incremental adding tags", "[rulesets]")
|
||||
{
|
||||
auto r = create_ruleset();
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
|
||||
auto rule1_filter = create_filter();
|
||||
auto rule1_filter = create_filter(f, ast);
|
||||
falco_rule rule1;
|
||||
rule1.name = "one_rule";
|
||||
rule1.source = falco_common::syscall_source;
|
||||
rule1.tags = {"rule1_tag"};
|
||||
r->add(rule1, rule1_filter);
|
||||
r->add(rule1, rule1_filter, ast);
|
||||
|
||||
auto rule2_filter = create_filter();
|
||||
auto rule2_filter = create_filter(f, ast);
|
||||
falco_rule rule2;
|
||||
rule2.name = "two_rule";
|
||||
rule2.source = falco_common::syscall_source;
|
||||
rule2.tags = {"rule2_tag"};
|
||||
r->add(rule2, rule2_filter);
|
||||
r->add(rule2, rule2_filter, ast);
|
||||
|
||||
std::set<std::string> want_tags;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
set(FALCO_ENGINE_SOURCE_FILES
|
||||
falco_common.cpp
|
||||
falco_engine.cpp
|
||||
falco_load_result.cpp
|
||||
falco_utils.cpp
|
||||
json_evt.cpp
|
||||
evttype_index_ruleset.cpp
|
||||
@@ -20,12 +21,13 @@ set(FALCO_ENGINE_SOURCE_FILES
|
||||
filter_macro_resolver.cpp
|
||||
filter_evttype_resolver.cpp
|
||||
filter_warning_resolver.cpp
|
||||
stats_manager.cpp
|
||||
rule_loader.cpp
|
||||
rule_reader.cpp
|
||||
stats_manager.cpp)
|
||||
rule_loader_reader.cpp
|
||||
rule_loader_collector.cpp
|
||||
rule_loader_compiler.cpp)
|
||||
|
||||
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
|
||||
add_dependencies(falco_engine njson string-view-lite)
|
||||
|
||||
if(USE_BUNDLED_DEPS)
|
||||
add_dependencies(falco_engine yamlcpp)
|
||||
@@ -37,7 +39,6 @@ if(MINIMAL_BUILD)
|
||||
PUBLIC
|
||||
"${NJSON_INCLUDE}"
|
||||
"${TBB_INCLUDE_DIR}"
|
||||
"${STRING_VIEW_LITE_INCLUDE}"
|
||||
"${LIBSCAP_INCLUDE_DIRS}"
|
||||
"${LIBSINSP_INCLUDE_DIRS}"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
@@ -48,7 +49,6 @@ else()
|
||||
PUBLIC
|
||||
"${NJSON_INCLUDE}"
|
||||
"${TBB_INCLUDE_DIR}"
|
||||
"${STRING_VIEW_LITE_INCLUDE}"
|
||||
"${LIBSCAP_INCLUDE_DIRS}"
|
||||
"${LIBSINSP_INCLUDE_DIRS}"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
|
||||
@@ -153,12 +153,11 @@ void evttype_index_ruleset::ruleset_filters::evttypes_for_ruleset(std::set<uint1
|
||||
|
||||
void evttype_index_ruleset::add(
|
||||
const falco_rule& rule,
|
||||
std::shared_ptr<gen_event_filter> filter,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> condition)
|
||||
{
|
||||
try
|
||||
{
|
||||
sinsp_filter_compiler compiler(m_filter_factory, condition.get());
|
||||
shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
std::shared_ptr<filter_wrapper> wrap(new filter_wrapper());
|
||||
wrap->rule = rule;
|
||||
wrap->filter = filter;
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
|
||||
void add(
|
||||
const falco_rule& rule,
|
||||
std::shared_ptr<gen_event_filter> filter,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> condition) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <sinsp.h>
|
||||
#include <plugin.h>
|
||||
@@ -25,7 +27,8 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "falco_utils.h"
|
||||
#include "falco_engine_version.h"
|
||||
#include "rule_reader.h"
|
||||
#include "rule_loader_reader.h"
|
||||
#include "rule_loader_compiler.h"
|
||||
|
||||
#include "formats.h"
|
||||
|
||||
@@ -36,9 +39,12 @@ limitations under the License.
|
||||
const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";
|
||||
|
||||
using namespace std;
|
||||
using namespace falco;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng)
|
||||
: m_next_ruleset_id(0),
|
||||
: m_syscall_source(NULL),
|
||||
m_syscall_source_idx(SIZE_MAX),
|
||||
m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
@@ -54,7 +60,7 @@ falco_engine::falco_engine(bool seed_rng)
|
||||
falco_engine::~falco_engine()
|
||||
{
|
||||
m_rules.clear();
|
||||
m_rule_loader.clear();
|
||||
m_rule_collector.clear();
|
||||
m_rule_stats_manager.clear();
|
||||
m_sources.clear();
|
||||
}
|
||||
@@ -64,7 +70,7 @@ uint32_t falco_engine::engine_version()
|
||||
return (uint32_t) FALCO_ENGINE_VERSION;
|
||||
}
|
||||
|
||||
falco_source* falco_engine::find_source(const std::string& name)
|
||||
const falco_source* falco_engine::find_source(const std::string& name) const
|
||||
{
|
||||
auto ret = m_sources.at(name);
|
||||
if(!ret)
|
||||
@@ -74,7 +80,7 @@ falco_source* falco_engine::find_source(const std::string& name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco_source* falco_engine::find_source(std::size_t index)
|
||||
const falco_source* falco_engine::find_source(std::size_t index) const
|
||||
{
|
||||
auto ret = m_sources.at(index);
|
||||
if(!ret)
|
||||
@@ -91,7 +97,7 @@ static std::string fieldclass_key(const gen_event_filter_factory::filter_fieldcl
|
||||
return fld_info.name + fld_info.shortdesc;
|
||||
}
|
||||
|
||||
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown)
|
||||
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const
|
||||
{
|
||||
// Maps from field class name + short desc to list of event
|
||||
// sources for which this field class can be used.
|
||||
@@ -99,14 +105,14 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
|
||||
// Do a first pass to group together classes that are
|
||||
// applicable to multiple event sources.
|
||||
for(auto &it : m_sources)
|
||||
for(const auto &it : m_sources)
|
||||
{
|
||||
if(source != "" && source != it.name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for(auto &fld_class : it.filter_factory->get_fields())
|
||||
for(const auto &fld_class : it.filter_factory->get_fields())
|
||||
{
|
||||
fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name);
|
||||
}
|
||||
@@ -118,7 +124,7 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
|
||||
// In the second pass, actually print info, skipping duplicate
|
||||
// field classes and also printing info on supported sources.
|
||||
for(auto &it : m_sources)
|
||||
for(const auto &it : m_sources)
|
||||
{
|
||||
if(source != "" && source != it.name)
|
||||
{
|
||||
@@ -165,80 +171,76 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
|
||||
{
|
||||
uint64_t dummy;
|
||||
static const std::string no_name = "N/A";
|
||||
|
||||
return load_rules(rules_content, verbose, all_events, dummy);
|
||||
std::unique_ptr<load_result> res = load_rules(rules_content, no_name);
|
||||
|
||||
interpret_load_result(res, no_name, rules_content, verbose);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
|
||||
std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_content, const std::string &name)
|
||||
{
|
||||
rule_loader::configuration cfg(rules_content, m_sources);
|
||||
rule_loader::configuration cfg(rules_content, m_sources, name);
|
||||
cfg.min_priority = m_min_priority;
|
||||
cfg.output_extra = m_extra;
|
||||
cfg.replace_output_container_info = m_replace_container_info;
|
||||
cfg.default_ruleset_id = m_default_ruleset_id;
|
||||
|
||||
std::ostringstream os;
|
||||
rule_reader reader;
|
||||
bool success = reader.load(cfg, m_rule_loader);
|
||||
if (success)
|
||||
rule_loader::reader reader;
|
||||
if (reader.read(cfg, m_rule_collector))
|
||||
{
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
src.ruleset = src.ruleset_factory->new_ruleset();
|
||||
}
|
||||
|
||||
rule_loader::compiler compiler;
|
||||
m_rules.clear();
|
||||
success = m_rule_loader.compile(cfg, m_rules);
|
||||
compiler.compile(cfg, m_rule_collector, m_rules);
|
||||
}
|
||||
if (!cfg.errors.empty())
|
||||
|
||||
if (cfg.res->successful())
|
||||
{
|
||||
os << cfg.errors.size() << " errors:" << std::endl;
|
||||
for(auto &err : cfg.errors)
|
||||
m_rule_stats_manager.clear();
|
||||
for (const auto &r : m_rules)
|
||||
{
|
||||
os << err << std::endl;
|
||||
m_rule_stats_manager.on_rule_loaded(r);
|
||||
}
|
||||
}
|
||||
if (!cfg.warnings.empty())
|
||||
{
|
||||
os << cfg.warnings.size() << " warnings:" << std::endl;
|
||||
for(auto &warn : cfg.warnings)
|
||||
{
|
||||
os << warn << std::endl;
|
||||
}
|
||||
}
|
||||
if(!success)
|
||||
{
|
||||
throw falco_exception(os.str());
|
||||
}
|
||||
if (verbose && os.str() != "") {
|
||||
// todo(jasondellaluce): introduce a logging callback in Falco
|
||||
fprintf(stderr, "When reading rules content: %s", os.str().c_str());
|
||||
}
|
||||
|
||||
return std::move(cfg.res);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
|
||||
void falco_engine::load_rules_file(const std::string &rules_filename, bool verbose, bool all_events)
|
||||
{
|
||||
uint64_t dummy;
|
||||
std::string rules_content;
|
||||
|
||||
return load_rules_file(rules_filename, verbose, all_events, dummy);
|
||||
read_file(rules_filename, rules_content);
|
||||
|
||||
std::unique_ptr<load_result> res = load_rules(rules_content, rules_filename);
|
||||
|
||||
interpret_load_result(res, rules_filename, rules_content, verbose);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version)
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_file(const string &rules_filename)
|
||||
{
|
||||
ifstream is;
|
||||
std::string rules_content;
|
||||
|
||||
is.open(rules_filename);
|
||||
if (!is.is_open())
|
||||
try {
|
||||
read_file(rules_filename, rules_content);
|
||||
}
|
||||
catch (falco_exception &e)
|
||||
{
|
||||
throw falco_exception("Could not open rules filename " +
|
||||
rules_filename + " " +
|
||||
"for reading");
|
||||
rule_loader::context ctx(rules_filename);
|
||||
|
||||
std::unique_ptr<rule_loader::result> res(new rule_loader::result(rules_filename));
|
||||
|
||||
res->add_error(load_result::LOAD_ERR_FILE_READ, e.what(), ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
string rules_content((istreambuf_iterator<char>(is)),
|
||||
istreambuf_iterator<char>());
|
||||
|
||||
load_rules(rules_content, verbose, all_events, required_engine_version);
|
||||
return load_rules(rules_content, rules_filename);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
|
||||
@@ -246,7 +248,7 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = false;
|
||||
|
||||
for(auto &it : m_sources)
|
||||
for(const auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -264,7 +266,7 @@ void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, cons
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = true;
|
||||
|
||||
for(auto &it : m_sources)
|
||||
for(const auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -281,7 +283,7 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
for(auto &it : m_sources)
|
||||
for(const auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -314,7 +316,7 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
uint64_t ret = 0;
|
||||
for (auto &src : m_sources)
|
||||
for (const auto &src : m_sources)
|
||||
{
|
||||
ret += src.ruleset->enabled_count(ruleset_id);
|
||||
}
|
||||
@@ -327,28 +329,49 @@ void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t>
|
||||
}
|
||||
|
||||
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
|
||||
const std::string &output)
|
||||
const std::string &output) const
|
||||
{
|
||||
return find_source(source)->formatter_factory->create_formatter(output);
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
|
||||
{
|
||||
falco_rule rule;
|
||||
if(should_drop_evt() || !find_source(source_idx)->ruleset->run(ev, rule, ruleset_id))
|
||||
// note: there are no thread-safety guarantees on the filter_ruleset::run()
|
||||
// method, but the thread-safety assumptions of falco_engine::process_event()
|
||||
// imply that concurrent invokers use different and non-switchable values of
|
||||
// source_idx, which means that at any time each filter_ruleset will only
|
||||
// be accessed by a single thread.
|
||||
|
||||
const falco_source *source;
|
||||
|
||||
if(source_idx == m_syscall_source_idx)
|
||||
{
|
||||
if(m_syscall_source == NULL)
|
||||
{
|
||||
m_syscall_source = find_source(m_syscall_source_idx);
|
||||
}
|
||||
|
||||
source = m_syscall_source;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = find_source(source_idx);
|
||||
}
|
||||
|
||||
if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
res->evt = ev;
|
||||
res->rule = rule.name;
|
||||
res->source = rule.source;
|
||||
res->format = rule.output;
|
||||
res->priority_num = rule.priority;
|
||||
res->tags = rule.tags;
|
||||
res->exception_fields = rule.exception_fields;
|
||||
m_rule_stats_manager.on_event(rule);
|
||||
res->rule = source->m_rule.name;
|
||||
res->source = source->m_rule.source;
|
||||
res->format = source->m_rule.output;
|
||||
res->priority_num = source->m_rule.priority;
|
||||
res->tags = source->m_rule.tags;
|
||||
res->exception_fields = source->m_rule.exception_fields;
|
||||
m_rule_stats_manager.on_event(source->m_rule);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -364,7 +387,14 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
// evttype_index_ruleset is the default ruleset implementation
|
||||
std::shared_ptr<filter_ruleset_factory> ruleset_factory(
|
||||
new evttype_index_ruleset_factory(filter_factory));
|
||||
return add_source(source, filter_factory, formatter_factory, ruleset_factory);
|
||||
size_t idx = add_source(source, filter_factory, formatter_factory, ruleset_factory);
|
||||
|
||||
if(source == falco_common::syscall_source)
|
||||
{
|
||||
m_syscall_source_idx = idx;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
std::size_t falco_engine::add_source(const std::string &source,
|
||||
@@ -381,7 +411,7 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
return m_sources.insert(src, source);
|
||||
}
|
||||
|
||||
void falco_engine::describe_rule(string *rule)
|
||||
void falco_engine::describe_rule(string *rule) const
|
||||
{
|
||||
static const char* rule_fmt = "%-50s %s\n";
|
||||
fprintf(stdout, rule_fmt, "Rule", "Description");
|
||||
@@ -402,7 +432,7 @@ void falco_engine::describe_rule(string *rule)
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::print_stats()
|
||||
void falco_engine::print_stats() const
|
||||
{
|
||||
string out;
|
||||
m_rule_stats_manager.format(m_rules, out);
|
||||
@@ -410,57 +440,110 @@ void falco_engine::print_stats()
|
||||
fprintf(stdout, "%s", out.c_str());
|
||||
}
|
||||
|
||||
bool falco_engine::is_source_valid(const std::string &source)
|
||||
bool falco_engine::is_source_valid(const std::string &source) const
|
||||
{
|
||||
return m_sources.at(source) != nullptr;
|
||||
}
|
||||
|
||||
bool falco_engine::check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
void falco_engine::read_file(const std::string& filename, std::string& contents)
|
||||
{
|
||||
ifstream is;
|
||||
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open " + filename + " for reading");
|
||||
}
|
||||
|
||||
contents.assign(istreambuf_iterator<char>(is),
|
||||
istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void falco_engine::interpret_load_result(std::unique_ptr<load_result>& res,
|
||||
const std::string& rules_filename,
|
||||
const std::string& rules_content,
|
||||
bool verbose)
|
||||
{
|
||||
falco::load_result::rules_contents_t rc = {{rules_filename, rules_content}};
|
||||
|
||||
if(!res->successful())
|
||||
{
|
||||
// The output here is always the full e.g. "verbose" output.
|
||||
throw falco_exception(res->as_string(true, rc).c_str());
|
||||
}
|
||||
|
||||
if(verbose && res->has_warnings())
|
||||
{
|
||||
// Here, verbose controls whether to additionally
|
||||
// "log" e.g. print to stderr. What's logged is always
|
||||
// non-verbose so it fits on a single line.
|
||||
// todo(jasondellaluce): introduce a logging callback in Falco
|
||||
fprintf(stderr, "%s\n", res->as_string(false, rc).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_plugin_requirement_alternatives(
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const rule_loader::plugin_version_info::requirement_alternatives& alternatives,
|
||||
std::string& err)
|
||||
{
|
||||
for (const auto &req : m_rule_loader.required_plugin_versions())
|
||||
for (const auto &req : alternatives)
|
||||
{
|
||||
bool found = false;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (req.first == plugin.name)
|
||||
if (req.name == plugin.name)
|
||||
{
|
||||
found = true;
|
||||
sinsp_version req_version(req.version);
|
||||
sinsp_version plugin_version(plugin.version);
|
||||
if(!plugin_version.m_valid)
|
||||
{
|
||||
err = "Plugin '" + req.first
|
||||
err = "Plugin '" + plugin.name
|
||||
+ "' has invalid version string '"
|
||||
+ plugin.version + "'";
|
||||
return false;
|
||||
}
|
||||
for (const auto &reqver: req.second)
|
||||
if (!plugin_version.check(req_version))
|
||||
{
|
||||
sinsp_version req_version(reqver);
|
||||
if (!plugin_version.check(req_version))
|
||||
{
|
||||
err = "Plugin '" + plugin.name
|
||||
+ "' version '" + plugin.version
|
||||
+ "' is not compatible with required plugin version '"
|
||||
+ reqver + "'";
|
||||
return false;
|
||||
}
|
||||
err = "Plugin '" + plugin.name
|
||||
+ "' version '" + plugin.version
|
||||
+ "' is not compatible with required plugin version '"
|
||||
+ req.version + "'";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool falco_engine::check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
std::string& err) const
|
||||
{
|
||||
err = "";
|
||||
for (const auto &alternatives : m_rule_collector.required_plugin_versions())
|
||||
{
|
||||
if (!check_plugin_requirement_alternatives(plugins, alternatives, err))
|
||||
{
|
||||
err = "Plugin '" + req.first + "' is required but not loaded";
|
||||
if (err.empty())
|
||||
{
|
||||
for (const auto& req : alternatives)
|
||||
{
|
||||
err += err.empty() ? "" : ", ";
|
||||
err += req.name + " (>= " + req.version + ")";
|
||||
}
|
||||
err = "Plugin requirement not satisfied, must load one of: " + err;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco_engine::complete_rule_loading()
|
||||
void falco_engine::complete_rule_loading() const
|
||||
{
|
||||
for (auto &src : m_sources)
|
||||
for (const auto &src : m_sources)
|
||||
{
|
||||
src.ruleset->on_loading_complete();
|
||||
}
|
||||
@@ -482,7 +565,7 @@ void falco_engine::set_extra(string &extra, bool replace_container_info)
|
||||
m_replace_container_info = replace_container_info;
|
||||
}
|
||||
|
||||
inline bool falco_engine::should_drop_evt()
|
||||
inline bool falco_engine::should_drop_evt() const
|
||||
{
|
||||
if(m_sampling_multiplier == 0)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -31,9 +32,11 @@ limitations under the License.
|
||||
#include "gen_filter.h"
|
||||
#include "filter_ruleset.h"
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.h"
|
||||
#include "stats_manager.h"
|
||||
#include "falco_common.h"
|
||||
#include "falco_source.h"
|
||||
#include "falco_load_result.h"
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
@@ -55,7 +58,7 @@ public:
|
||||
|
||||
// Print to stdout (using printf) a description of each field supported by this engine.
|
||||
// If source is non-empty, only fields for the provided source are printed.
|
||||
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown);
|
||||
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const;
|
||||
|
||||
//
|
||||
// Load rules either directly or from a filename.
|
||||
@@ -64,11 +67,10 @@ public:
|
||||
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
|
||||
|
||||
//
|
||||
// Identical to above, but also returns the required engine version for the file/content.
|
||||
// (If no required engine version is specified, returns 0).
|
||||
//
|
||||
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version);
|
||||
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
|
||||
// Identical to above, but returns a result object instead of
|
||||
// throwing exceptions on error.
|
||||
std::unique_ptr<falco::load_result> load_rules_file(const std::string &rules_filename);
|
||||
std::unique_ptr<falco::load_result> load_rules(const std::string &rules_content, const std::string &name);
|
||||
|
||||
//
|
||||
// Enable/Disable any rules matching the provided substring.
|
||||
@@ -98,7 +100,7 @@ public:
|
||||
// Internally, this can be used to release unused resources before starting
|
||||
// processing events with process_event().
|
||||
//
|
||||
void complete_rule_loading();
|
||||
void complete_rule_loading() const;
|
||||
|
||||
// Only load rules having this priority or more severe.
|
||||
void set_min_priority(falco_common::priority_type priority);
|
||||
@@ -121,12 +123,12 @@ public:
|
||||
// Print details on the given rule. If rule is NULL, print
|
||||
// details on all rules.
|
||||
//
|
||||
void describe_rule(std::string *rule);
|
||||
void describe_rule(std::string *rule) const;
|
||||
|
||||
//
|
||||
// Print statistics on how many events matched each rule.
|
||||
//
|
||||
void print_stats();
|
||||
void print_stats() const;
|
||||
|
||||
//
|
||||
// Set the sampling ratio, which can affect which events are
|
||||
@@ -165,18 +167,33 @@ public:
|
||||
//
|
||||
// Given an event, check it against the set of rules in the
|
||||
// engine and if a matching rule is found, return details on
|
||||
// the rule that matched. If no rule matched, returns NULL.
|
||||
// the rule that matched. If no rule matched, returns nullptr.
|
||||
//
|
||||
// When ruleset_id is provided, use the enabled/disabled status
|
||||
// associated with the provided ruleset. This is only useful
|
||||
// when you have previously called enable_rule/enable_rule_by_tag
|
||||
// with a ruleset string.
|
||||
// This method should be invoked only after having initialized and
|
||||
// configured the engine. In particular, invoking this with a source_idx
|
||||
// not previosly-returned by a call to add_source() would cause a
|
||||
// falco_exception to be thrown.
|
||||
//
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
// This method is thread-safe only with the assumption that every invoker
|
||||
// uses a different source_idx. Moreover, each invoker must not switch
|
||||
// source_idx in subsequent invocations of this method.
|
||||
// Considering that each invoker is related to a unique event source, it
|
||||
// is safe to assume that each invoker will pass a different event
|
||||
// to this method too, since two distinct sources cannot possibly produce
|
||||
// the same event. Lastly, filterchecks and formatters (and their factories)
|
||||
// that are used to populate the conditions for a given event-source
|
||||
// ruleset must not be reused across rulesets of other event sources.
|
||||
// These assumptions guarantee thread-safety because internally the engine
|
||||
// is partitioned by event sources. However, each ruleset assigned to each
|
||||
// event source is not thread-safe of its own, so invoking this method
|
||||
// concurrently with the same source_idx would inherently cause data races
|
||||
// and lead to undefined behavior.
|
||||
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset
|
||||
// Wrapper assuming the default ruleset.
|
||||
//
|
||||
// This inherits the same thread-safety guarantees.
|
||||
//
|
||||
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev);
|
||||
|
||||
@@ -188,7 +205,7 @@ public:
|
||||
std::size_t add_source(const std::string &source,
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory,
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory);
|
||||
|
||||
|
||||
//
|
||||
// Equivalent to above, but allows specifying a ruleset factory
|
||||
// for the newly added source.
|
||||
@@ -200,7 +217,7 @@ public:
|
||||
|
||||
// Return whether or not there is a valid filter/formatter
|
||||
// factory for this source.
|
||||
bool is_source_valid(const std::string &source);
|
||||
bool is_source_valid(const std::string &source) const;
|
||||
|
||||
//
|
||||
// Given an event source and ruleset, fill in a bitset
|
||||
@@ -216,10 +233,10 @@ public:
|
||||
// event.
|
||||
//
|
||||
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
|
||||
const std::string &output);
|
||||
const std::string &output) const;
|
||||
|
||||
// The rule loader definition is aliased as it is exactly what we need
|
||||
typedef rule_loader::plugin_version_info plugin_version_requirement;
|
||||
typedef rule_loader::plugin_version_info::requirement plugin_version_requirement;
|
||||
|
||||
//
|
||||
// Returns true if the provided list of plugins satisfies all the
|
||||
@@ -230,22 +247,39 @@ public:
|
||||
//
|
||||
bool check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
std::string& err);
|
||||
std::string& err) const;
|
||||
|
||||
private:
|
||||
|
||||
// Throws falco_exception if the file can not be read
|
||||
void read_file(const std::string& filename, std::string& contents);
|
||||
|
||||
// For load_rules methods that throw exceptions on error,
|
||||
// interpret a load_result and throw an exception if needed.
|
||||
void interpret_load_result(std::unique_ptr<falco::load_result>& res,
|
||||
const std::string& rules_filename,
|
||||
const std::string& rules_content,
|
||||
bool verbose);
|
||||
|
||||
indexed_vector<falco_source> m_sources;
|
||||
|
||||
falco_source* find_source(std::size_t index);
|
||||
falco_source* find_source(const std::string& name);
|
||||
const falco_source* find_source(std::size_t index) const;
|
||||
const falco_source* find_source(const std::string& name) const;
|
||||
|
||||
// To allow the engine to be extremely fast for syscalls (can
|
||||
// be > 1M events/sec), we save the syscall source/source_idx
|
||||
// separately and check it explicitly in process_event()
|
||||
const falco_source* m_syscall_source;
|
||||
std::atomic<size_t> m_syscall_source_idx;
|
||||
|
||||
//
|
||||
// Determine whether the given event should be matched at all
|
||||
// against the set of rules, given the current sampling
|
||||
// ratio/multiplier.
|
||||
//
|
||||
inline bool should_drop_evt();
|
||||
inline bool should_drop_evt() const;
|
||||
|
||||
rule_loader m_rule_loader;
|
||||
rule_loader::collector m_rule_collector;
|
||||
indexed_vector<falco_rule> m_rules;
|
||||
stats_manager m_rule_stats_manager;
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
|
||||
// The version of rules/filter fields/etc supported by this Falco
|
||||
// engine.
|
||||
#define FALCO_ENGINE_VERSION (13)
|
||||
#define FALCO_ENGINE_VERSION (15)
|
||||
|
||||
// This is the result of running "falco --list -N | sha256sum" and
|
||||
// represents the fields supported by this version of Falco. It's used
|
||||
// at build time to detect a changed set of fields.
|
||||
#define FALCO_FIELDS_CHECKSUM "94290ff98e5affc85b2287b09a3f4054918f14e90db1ac4bfd6d5ce4e910329c"
|
||||
#define FALCO_FIELDS_CHECKSUM "674c6cf2bc1c105038c8676f018fa3d1431d86597df428453441f5d859cad284"
|
||||
|
||||
104
userspace/engine/falco_load_result.cpp
Normal file
104
userspace/engine/falco_load_result.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 "falco_load_result.h"
|
||||
|
||||
static const std::string error_codes[] = {
|
||||
"LOAD_ERR_FILE_READ",
|
||||
"LOAD_ERR_YAML_PARSE",
|
||||
"LOAD_ERR_YAML_VALIDATE",
|
||||
"LOAD_ERR_COMPILE_CONDITION",
|
||||
"LOAD_ERR_COMPILE_OUTPUT",
|
||||
"LOAD_ERR_VALIDATE"
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::error_code_str(error_code ec)
|
||||
{
|
||||
return error_codes[ec];
|
||||
}
|
||||
|
||||
static const std::string error_strings[] = {
|
||||
"File read error",
|
||||
"YAML parse error",
|
||||
"Error validating internal structure of YAML file",
|
||||
"Error compiling condition",
|
||||
"Error compiling output",
|
||||
"Error validating rule/macro/list/exception objects"
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::error_str(error_code ec)
|
||||
{
|
||||
return error_strings[ec];
|
||||
}
|
||||
|
||||
static const std::string error_descs[] = {
|
||||
"This occurs when falco can not read a given file. Check permissions and whether the file exists.",
|
||||
"This occurs when the rules content is not valid YAML.",
|
||||
"This occurs when the internal structure of the YAML file is incorrect. Examples include not consisting of a sequence of maps, a given rule/macro/list item not having required keys, values not having the right type (e.g. the items property of a list not being a sequence), etc.",
|
||||
"This occurs when a condition string can not be compiled to a filter object.",
|
||||
"This occurs when an output string can not be compiled to an output object.",
|
||||
"This occurs when a rule/macro/list item is incorrect. Examples include a condition field referring to an undefined macro, falco engine/plugin version mismatches, items with append without any existing item, exception fields/comps having different lengths, etc."
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::error_desc(error_code ec)
|
||||
{
|
||||
return error_strings[ec];
|
||||
}
|
||||
|
||||
static const std::string warning_codes[] = {
|
||||
"LOAD_UNKNOWN_SOURCE",
|
||||
"LOAD_UNSAFE_NA_CHECK",
|
||||
"LOAD_NO_EVTTYPE",
|
||||
"LOAD_UNKNOWN_FIELD",
|
||||
"LOAD_UNUSED_MACRO",
|
||||
"LOAD_UNUSED_LIST",
|
||||
"LOAD_UNKNOWN_ITEM"
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::warning_code_str(warning_code wc)
|
||||
{
|
||||
return warning_codes[wc];
|
||||
}
|
||||
|
||||
static const std::string warning_strings[] = {
|
||||
"Unknown event source",
|
||||
"Unsafe <NA> comparison in condition",
|
||||
"Condition has no event-type restriction",
|
||||
"Unknown field in condition",
|
||||
"Unused macro",
|
||||
"Unused list",
|
||||
"Unknown rules file item"
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::warning_str(warning_code wc)
|
||||
{
|
||||
return warning_strings[wc];
|
||||
}
|
||||
|
||||
static const std::string warning_descs[] = {
|
||||
"A rule has a unknown event source. This can occur when reading rules content without having a corresponding plugin loaded, etc. The rule will be silently ignored.",
|
||||
"Comparing a field value with <NA> is unsafe and can lead to unpredictable behavior of the rule condition. If you need to check for the existence of a field, consider using the 'exists' operator instead.",
|
||||
"A rule condition matches too many evt.type values. This has a significant performance penalty. Make the condition more specific by adding an evt.type field or further restricting the number of evt.type values in the condition.",
|
||||
"A rule condition refers to a field that does not exist. This is normally an error, but if a rule has a skip-if-unknown-filter property, the error is downgraded to a warning.",
|
||||
"A macro is defined in the rules content but is not used by any other macro or rule.",
|
||||
"A list is defined in the rules content but is not used by any other list, macro, or rule.",
|
||||
"An unknown top-level object is in the rules content. It will be ignored."
|
||||
};
|
||||
|
||||
const std::string& falco::load_result::warning_desc(warning_code wc)
|
||||
{
|
||||
return warning_descs[wc];
|
||||
}
|
||||
112
userspace/engine/falco_load_result.h
Normal file
112
userspace/engine/falco_load_result.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace falco
|
||||
{
|
||||
|
||||
// Represents the result of loading a rules file.
|
||||
class load_result {
|
||||
public:
|
||||
|
||||
enum error_code {
|
||||
LOAD_ERR_FILE_READ = 0,
|
||||
LOAD_ERR_YAML_PARSE,
|
||||
LOAD_ERR_YAML_VALIDATE,
|
||||
LOAD_ERR_COMPILE_CONDITION,
|
||||
LOAD_ERR_COMPILE_OUTPUT,
|
||||
LOAD_ERR_VALIDATE
|
||||
};
|
||||
|
||||
// The error code as a string
|
||||
static const std::string& error_code_str(error_code ec);
|
||||
|
||||
// A short string representation of the error
|
||||
static const std::string& error_str(error_code ec);
|
||||
|
||||
// A longer description of what the error represents and the
|
||||
// impact.
|
||||
static const std::string& error_desc(error_code ec);
|
||||
|
||||
enum warning_code {
|
||||
LOAD_UNKNOWN_SOURCE = 0,
|
||||
LOAD_UNSAFE_NA_CHECK,
|
||||
LOAD_NO_EVTTYPE,
|
||||
LOAD_UNKNOWN_FIELD,
|
||||
LOAD_UNUSED_MACRO,
|
||||
LOAD_UNUSED_LIST,
|
||||
LOAD_UNKNOWN_ITEM
|
||||
};
|
||||
|
||||
virtual ~load_result() = default;
|
||||
|
||||
// The warning code as a string
|
||||
static const std::string& warning_code_str(warning_code ec);
|
||||
|
||||
// A short string representation of the warning
|
||||
static const std::string& warning_str(warning_code ec);
|
||||
|
||||
// A longer description of what the warning represents and the
|
||||
// impact.
|
||||
static const std::string& warning_desc(warning_code ec);
|
||||
|
||||
// If true, the rules were loaded successfully and can be used
|
||||
// against events. If false, there were one or more
|
||||
// errors--use one of the as_xxx methods to return information
|
||||
// about why the rules could not be loaded.
|
||||
virtual bool successful() = 0;
|
||||
|
||||
// If true, there were one or more warnings. successful() and
|
||||
// has_warnings() can both be true if there were only warnings.
|
||||
virtual bool has_warnings() = 0;
|
||||
|
||||
// This represents a set of rules contents as a mapping from
|
||||
// rules content name (usually filename) to rules content. The
|
||||
// rules content is actually a reference to the actual string
|
||||
// to avoid copies. Using reference_wrapper allows the
|
||||
// reference to be held in the stl map (bare references can't
|
||||
// be copied/assigned, but reference_wrappers can).
|
||||
//
|
||||
// It's used in the as_string/as_json() methods below.
|
||||
typedef std::map<std::string, std::reference_wrapper<const std::string>> rules_contents_t;
|
||||
|
||||
// This contains a human-readable version of the result,
|
||||
// suitable for display to end users.
|
||||
//
|
||||
// The provided rules_contents_t should map from content name
|
||||
// to rules content (reference) for each rules_content that has
|
||||
// been passed to rule_loader::compile() or
|
||||
// rule_reader::load().
|
||||
//
|
||||
// When verbose is true, the returned value has full details
|
||||
// on the result including document locations/context.
|
||||
//
|
||||
// When verbose is false, the returned value is a short string
|
||||
// with the success value and a list of
|
||||
// errors/warnings. Suitable for simple one-line display.
|
||||
virtual const std::string& as_string(bool verbose, const rules_contents_t& contents) = 0;
|
||||
|
||||
// This contains the full result structure as json, suitable
|
||||
// for automated parsing/interpretation downstream.
|
||||
virtual const nlohmann::json& as_json(const rules_contents_t& contents) = 0;
|
||||
};
|
||||
|
||||
} // namespace falco
|
||||
@@ -26,6 +26,12 @@ limitations under the License.
|
||||
*/
|
||||
struct falco_rule
|
||||
{
|
||||
falco_rule(): id(0), priority(falco_common::PRIORITY_DEBUG) {}
|
||||
falco_rule(falco_rule&&) = default;
|
||||
falco_rule& operator = (falco_rule&&) = default;
|
||||
falco_rule(const falco_rule&) = default;
|
||||
falco_rule& operator = (const falco_rule&) = default;
|
||||
|
||||
std::size_t id;
|
||||
std::string source;
|
||||
std::string name;
|
||||
|
||||
@@ -26,13 +26,36 @@ limitations under the License.
|
||||
*/
|
||||
struct falco_source
|
||||
{
|
||||
falco_source() = default;
|
||||
falco_source(falco_source&&) = default;
|
||||
falco_source& operator = (falco_source&&) = default;
|
||||
falco_source(const falco_source& s):
|
||||
name(s.name),
|
||||
ruleset(s.ruleset),
|
||||
ruleset_factory(s.ruleset_factory),
|
||||
filter_factory(s.filter_factory),
|
||||
formatter_factory(s.formatter_factory) { };
|
||||
falco_source& operator = (const falco_source& s)
|
||||
{
|
||||
name = s.name;
|
||||
ruleset = s.ruleset;
|
||||
ruleset_factory = s.ruleset_factory;
|
||||
filter_factory = s.filter_factory;
|
||||
formatter_factory = s.formatter_factory;
|
||||
return *this;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::shared_ptr<filter_ruleset> ruleset;
|
||||
std::shared_ptr<filter_ruleset_factory> ruleset_factory;
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory;
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory;
|
||||
|
||||
inline bool is_field_defined(std::string field) const
|
||||
// Used by the filter_ruleset interface. Filled in when a rule
|
||||
// matches an event.
|
||||
mutable falco_rule m_rule;
|
||||
|
||||
inline bool is_field_defined(const std::string& field) const
|
||||
{
|
||||
auto *chk = filter_factory->new_filtercheck(field.c_str());
|
||||
if (chk)
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
#include <iomanip>
|
||||
|
||||
#include "falco_utils.h"
|
||||
#include "utils.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
namespace falco
|
||||
@@ -75,9 +76,9 @@ void readfile(const std::string& filename, std::string& data)
|
||||
}
|
||||
namespace network
|
||||
{
|
||||
bool is_unix_scheme(nonstd::string_view url)
|
||||
bool is_unix_scheme(const std::string& url)
|
||||
{
|
||||
return url.starts_with(UNIX_SCHEME);
|
||||
return sinsp_utils::startswith(url, UNIX_SCHEME);
|
||||
}
|
||||
} // namespace network
|
||||
} // namespace utils
|
||||
|
||||
@@ -24,7 +24,6 @@ limitations under the License.
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <nonstd/string_view.hpp>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
@@ -49,7 +48,7 @@ uint32_t hardware_concurrency();
|
||||
namespace network
|
||||
{
|
||||
static const std::string UNIX_SCHEME("unix://");
|
||||
bool is_unix_scheme(nonstd::string_view url);
|
||||
bool is_unix_scheme(const std::string& url);
|
||||
} // namespace network
|
||||
} // namespace utils
|
||||
} // namespace falco
|
||||
|
||||
@@ -17,41 +17,40 @@ limitations under the License.
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include <sinsp.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
extern sinsp_evttables g_infotables;
|
||||
|
||||
static bool is_evttype_operator(const string& op)
|
||||
static bool is_evttype_operator(const std::string& op)
|
||||
{
|
||||
return op == "==" || op == "=" || op == "!=" || op == "in";
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::inversion(set<uint16_t>& types)
|
||||
|
||||
size_t falco_event_types::get_ppm_event_max()
|
||||
{
|
||||
set<uint16_t> all_types;
|
||||
return PPM_EVENT_MAX;
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
|
||||
{
|
||||
falco_event_types all_types;
|
||||
evttypes("", all_types);
|
||||
if (types != all_types) // we don't invert the "all types" set
|
||||
{
|
||||
set<uint16_t> diff = types;
|
||||
types.clear();
|
||||
set_difference(
|
||||
all_types.begin(), all_types.end(), diff.begin(), diff.end(),
|
||||
inserter(types, types.begin()));
|
||||
types = all_types.diff(types);
|
||||
}
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& out)
|
||||
void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falco_event_types& out)
|
||||
{
|
||||
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
|
||||
// those are PPM_GENERIC_E/PPME_GENERIC_X
|
||||
const struct ppm_event_info* etable = g_infotables.m_event_info;
|
||||
for(uint16_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip "old" event versions, unused events, or events not matching
|
||||
// the requested evtname
|
||||
if(!(etable[i].flags & (EF_OLD_VERSION | EF_UNUSED))
|
||||
&& (evtname.empty() || string(etable[i].name) == evtname))
|
||||
// Skip unused events or events not matching the requested evtname
|
||||
if(!sinsp::is_unused_event(i) && (evtname.empty() || std::string(etable[i].name) == evtname))
|
||||
{
|
||||
out.insert(i);
|
||||
}
|
||||
@@ -60,42 +59,38 @@ void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& o
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
ast::expr* filter,
|
||||
set<uint16_t>& out) const
|
||||
std::set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter->accept(&v);
|
||||
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;});
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
shared_ptr<ast::expr> filter,
|
||||
set<uint16_t>& out) const
|
||||
std::set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter.get()->accept(&v);
|
||||
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;} );
|
||||
}
|
||||
|
||||
// "and" nodes evttypes are the intersection of the evttypes of their children.
|
||||
// we initialize the set with "all event types"
|
||||
void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
|
||||
{
|
||||
set<uint16_t> types, inters;
|
||||
falco_event_types types;
|
||||
evttypes("", types);
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
inters.clear();
|
||||
falco_event_types inters;
|
||||
c->accept(this);
|
||||
set_intersection(
|
||||
types.begin(), types.end(),
|
||||
m_last_node_evttypes.begin(), m_last_node_evttypes.end(),
|
||||
inserter(inters, inters.begin()));
|
||||
types = inters;
|
||||
types = types.intersect(m_last_node_evttypes);
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
@@ -103,12 +98,12 @@ void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
|
||||
// "or" nodes evttypes are the union of the evttypes their children
|
||||
void filter_evttype_resolver::visitor::visit(ast::or_expr* e)
|
||||
{
|
||||
set<uint16_t> types;
|
||||
falco_event_types types;
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
c->accept(this);
|
||||
types.insert(m_last_node_evttypes.begin(), m_last_node_evttypes.end());
|
||||
types.merge(m_last_node_evttypes);
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,128 @@ limitations under the License.
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
class falco_event_types
|
||||
{
|
||||
private:
|
||||
using vec_t = std::vector<uint8_t>;
|
||||
vec_t m_types{};
|
||||
|
||||
static inline void check_range(uint16_t e)
|
||||
{
|
||||
static const auto enum_max = get_ppm_event_max();
|
||||
if(e > enum_max)
|
||||
{
|
||||
throw std::range_error("invalid event type");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
falco_event_types(falco_event_types&&) = default;
|
||||
falco_event_types(const falco_event_types&) = default;
|
||||
falco_event_types& operator=(falco_event_types&&) = default;
|
||||
falco_event_types& operator=(const falco_event_types&) = default;
|
||||
|
||||
static size_t get_ppm_event_max();
|
||||
|
||||
inline falco_event_types():
|
||||
m_types(get_ppm_event_max() + 1, 0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void insert(uint16_t e)
|
||||
{
|
||||
check_range(e);
|
||||
m_types[e] = 1;
|
||||
}
|
||||
|
||||
void merge(const falco_event_types& other)
|
||||
{
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
m_types[i] |= other.m_types[i];
|
||||
}
|
||||
}
|
||||
|
||||
void merge(const std::set<uint16_t>& other)
|
||||
{
|
||||
for(const auto& e : other)
|
||||
{
|
||||
insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool contains(uint16_t e) const
|
||||
{
|
||||
check_range(e);
|
||||
return m_types[e] != 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(auto& v : m_types)
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool equals(const falco_event_types& other) const
|
||||
{
|
||||
return m_types == other.m_types;
|
||||
}
|
||||
|
||||
falco_event_types diff(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 0)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco_event_types intersect(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 1)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void for_each(std::function<bool(uint16_t)> consumer) const
|
||||
{
|
||||
for(uint16_t i = 0; i < m_types.size(); ++i)
|
||||
{
|
||||
if(m_types[i] != 0)
|
||||
{
|
||||
if(!consumer(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Helper class for finding event types
|
||||
@@ -35,9 +157,12 @@ public:
|
||||
string is passed, all the available evttypes are collected
|
||||
\param out The set to be filled with the evttypes
|
||||
*/
|
||||
inline void evttypes(std::string evtname, std::set<uint16_t>& out) const
|
||||
inline void evttypes(const std::string& evtname, falco_event_types& out) const
|
||||
{
|
||||
visitor().evttypes(evtname, out);
|
||||
falco_event_types evt_types;
|
||||
visitor().evttypes(evtname, evt_types);
|
||||
evt_types.for_each([&out](uint16_t val)
|
||||
{out.insert(val); return true; });
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -63,8 +188,14 @@ public:
|
||||
private:
|
||||
struct visitor : public libsinsp::filter::ast::expr_visitor
|
||||
{
|
||||
visitor(): m_expect_value(false) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = default;
|
||||
visitor& operator = (const visitor&) = default;
|
||||
|
||||
bool m_expect_value;
|
||||
std::set<uint16_t> m_last_node_evttypes;
|
||||
falco_event_types m_last_node_evttypes;
|
||||
|
||||
void visit(libsinsp::filter::ast::and_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::or_expr* e) override;
|
||||
@@ -73,7 +204,7 @@ private:
|
||||
void visit(libsinsp::filter::ast::list_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
void inversion(std::set<uint16_t>& types);
|
||||
void evttypes(std::string evtname, std::set<uint16_t>& out);
|
||||
void inversion(falco_event_types& types);
|
||||
void evttypes(const std::string& evtname, falco_event_types& out);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -21,37 +21,31 @@ using namespace libsinsp::filter;
|
||||
|
||||
bool filter_macro_resolver::run(libsinsp::filter::ast::expr*& filter)
|
||||
{
|
||||
visitor v;
|
||||
m_unknown_macros.clear();
|
||||
m_resolved_macros.clear();
|
||||
v.m_unknown_macros = &m_unknown_macros;
|
||||
v.m_resolved_macros = &m_resolved_macros;
|
||||
v.m_macros = &m_macros;
|
||||
v.m_last_node_changed = false;
|
||||
v.m_last_node = filter;
|
||||
|
||||
visitor v(m_unknown_macros, m_resolved_macros, m_macros);
|
||||
v.m_node_substitute = nullptr;
|
||||
filter->accept(&v);
|
||||
if (v.m_last_node_changed)
|
||||
if (v.m_node_substitute)
|
||||
{
|
||||
delete filter;
|
||||
filter = v.m_last_node;
|
||||
filter = v.m_node_substitute.release();
|
||||
}
|
||||
return !m_resolved_macros.empty();
|
||||
}
|
||||
|
||||
bool filter_macro_resolver::run(std::shared_ptr<libsinsp::filter::ast::expr>& filter)
|
||||
{
|
||||
visitor v;
|
||||
m_unknown_macros.clear();
|
||||
m_resolved_macros.clear();
|
||||
v.m_unknown_macros = &m_unknown_macros;
|
||||
v.m_resolved_macros = &m_resolved_macros;
|
||||
v.m_macros = &m_macros;
|
||||
v.m_last_node_changed = false;
|
||||
v.m_last_node = filter.get();
|
||||
|
||||
visitor v(m_unknown_macros, m_resolved_macros, m_macros);
|
||||
v.m_node_substitute = nullptr;
|
||||
filter->accept(&v);
|
||||
if (v.m_last_node_changed)
|
||||
if (v.m_node_substitute)
|
||||
{
|
||||
filter.reset(v.m_last_node);
|
||||
filter = std::move(v.m_node_substitute);
|
||||
}
|
||||
return !m_resolved_macros.empty();
|
||||
}
|
||||
@@ -63,12 +57,12 @@ void filter_macro_resolver::set_macro(
|
||||
m_macros[name] = macro;
|
||||
}
|
||||
|
||||
const set<string>& filter_macro_resolver::get_unknown_macros() const
|
||||
const filter_macro_resolver::macro_info_map& filter_macro_resolver::get_unknown_macros() const
|
||||
{
|
||||
return m_unknown_macros;
|
||||
}
|
||||
|
||||
const set<string>& filter_macro_resolver::get_resolved_macros() const
|
||||
const filter_macro_resolver::macro_info_map& filter_macro_resolver::get_resolved_macros() const
|
||||
{
|
||||
return m_resolved_macros;
|
||||
}
|
||||
@@ -78,14 +72,12 @@ void filter_macro_resolver::visitor::visit(ast::and_expr* e)
|
||||
for (size_t i = 0; i < e->children.size(); i++)
|
||||
{
|
||||
e->children[i]->accept(this);
|
||||
if (m_last_node_changed)
|
||||
if (m_node_substitute)
|
||||
{
|
||||
delete e->children[i];
|
||||
e->children[i] = m_last_node;
|
||||
e->children[i] = std::move(m_node_substitute);
|
||||
}
|
||||
}
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::or_expr* e)
|
||||
@@ -93,46 +85,39 @@ void filter_macro_resolver::visitor::visit(ast::or_expr* e)
|
||||
for (size_t i = 0; i < e->children.size(); i++)
|
||||
{
|
||||
e->children[i]->accept(this);
|
||||
if (m_last_node_changed)
|
||||
if (m_node_substitute)
|
||||
{
|
||||
delete e->children[i];
|
||||
e->children[i] = m_last_node;
|
||||
e->children[i] = std::move(m_node_substitute);
|
||||
}
|
||||
}
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::not_expr* e)
|
||||
{
|
||||
e->child->accept(this);
|
||||
if (m_last_node_changed)
|
||||
if (m_node_substitute)
|
||||
{
|
||||
delete e->child;
|
||||
e->child = m_last_node;
|
||||
e->child = std::move(m_node_substitute);
|
||||
}
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::list_expr* e)
|
||||
{
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::binary_check_expr* e)
|
||||
{
|
||||
// avoid exploring checks, so that we can be sure that each
|
||||
// value_expr* node visited is a macro identifier
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::unary_check_expr* e)
|
||||
{
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_node_substitute = nullptr;
|
||||
}
|
||||
|
||||
void filter_macro_resolver::visitor::visit(ast::value_expr* e)
|
||||
@@ -140,18 +125,23 @@ void filter_macro_resolver::visitor::visit(ast::value_expr* e)
|
||||
// we are supposed to get here only in case
|
||||
// of identier-only children from either a 'not',
|
||||
// an 'and' or an 'or'.
|
||||
auto macro = m_macros->find(e->value);
|
||||
if (macro != m_macros->end() && macro->second) // skip null-ptr macros
|
||||
auto macro = m_macros.find(e->value);
|
||||
if (macro != m_macros.end() && macro->second) // skip null-ptr macros
|
||||
{
|
||||
ast::expr* new_node = ast::clone(macro->second.get());
|
||||
new_node->accept(this); // this sets m_last_node
|
||||
m_last_node_changed = true;
|
||||
m_resolved_macros->insert(e->value);
|
||||
m_node_substitute = nullptr;
|
||||
auto new_node = ast::clone(macro->second.get());
|
||||
new_node->accept(this);
|
||||
// new_node might already have set a non-NULL m_node_substitute.
|
||||
// if not, the right substituted is the newly-cloned node.
|
||||
if (!m_node_substitute)
|
||||
{
|
||||
m_node_substitute = std::move(new_node);
|
||||
}
|
||||
m_resolved_macros[e->value] = e->get_pos();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_last_node = e;
|
||||
m_last_node_changed = false;
|
||||
m_unknown_macros->insert(e->value);
|
||||
m_node_substitute = nullptr;
|
||||
m_unknown_macros[e->value] = e->get_pos();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ limitations under the License.
|
||||
|
||||
#include <filter/parser.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
/*!
|
||||
@@ -40,7 +40,7 @@ class filter_macro_resolver
|
||||
\return true if at least one of the defined macros is resolved
|
||||
*/
|
||||
bool run(libsinsp::filter::ast::expr*& filter);
|
||||
|
||||
|
||||
/*!
|
||||
\brief Version of run() that works with shared pointers
|
||||
*/
|
||||
@@ -58,12 +58,17 @@ class filter_macro_resolver
|
||||
std::string name,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> macro);
|
||||
|
||||
/*!
|
||||
\brief used in get_{resolved,unknown}_macros
|
||||
*/
|
||||
typedef std::unordered_map<std::string,libsinsp::filter::ast::pos_info> macro_info_map;
|
||||
|
||||
/*!
|
||||
\brief Returns a set containing the names of all the macros
|
||||
substituted during the last invocation of run(). Should be
|
||||
non-empty if the last invocation of run() returned true.
|
||||
*/
|
||||
const std::set<std::string>& get_resolved_macros() const;
|
||||
const macro_info_map& get_resolved_macros() const;
|
||||
|
||||
/*!
|
||||
\brief Returns a set containing the names of all the macros
|
||||
@@ -71,21 +76,28 @@ class filter_macro_resolver
|
||||
A macro remains unresolved if it is found inside the processed
|
||||
filter but it was not defined with set_macro();
|
||||
*/
|
||||
const std::set<std::string>& get_unknown_macros() const;
|
||||
|
||||
const macro_info_map& get_unknown_macros() const;
|
||||
|
||||
private:
|
||||
typedef std::map<
|
||||
typedef std::unordered_map<
|
||||
std::string,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr>
|
||||
> macro_defs;
|
||||
|
||||
struct visitor : public libsinsp::filter::ast::expr_visitor
|
||||
{
|
||||
bool m_last_node_changed;
|
||||
libsinsp::filter::ast::expr* m_last_node;
|
||||
std::set<std::string>* m_unknown_macros;
|
||||
std::set<std::string>* m_resolved_macros;
|
||||
macro_defs* m_macros;
|
||||
visitor(macro_info_map& unknown_macros, macro_info_map& resolved_macros, macro_defs& macros)
|
||||
: m_unknown_macros(unknown_macros), m_resolved_macros(resolved_macros), m_macros(macros) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = delete;
|
||||
visitor& operator = (const visitor&) = delete;
|
||||
|
||||
std::unique_ptr<libsinsp::filter::ast::expr> m_node_substitute;
|
||||
macro_info_map& m_unknown_macros;
|
||||
macro_info_map& m_resolved_macros;
|
||||
|
||||
macro_defs& m_macros;
|
||||
|
||||
void visit(libsinsp::filter::ast::and_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::or_expr* e) override;
|
||||
@@ -96,7 +108,7 @@ class filter_macro_resolver
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
};
|
||||
|
||||
std::set<std::string> m_unknown_macros;
|
||||
std::set<std::string> m_resolved_macros;
|
||||
macro_info_map m_unknown_macros;
|
||||
macro_info_map m_resolved_macros;
|
||||
macro_defs m_macros;
|
||||
};
|
||||
|
||||
@@ -32,16 +32,20 @@ public:
|
||||
virtual ~filter_ruleset() = default;
|
||||
|
||||
/*!
|
||||
\brief Adds a rule and its filtering condition inside the manager.
|
||||
An exception is thrown is case of error. This method only adds the rule
|
||||
inside the internal collection, but does not enable it for any ruleset.
|
||||
The rule must be enabled for one or more rulesets with the enable() or
|
||||
enable_tags() methods.
|
||||
\brief Adds a rule and its filtering filter + condition inside the manager.
|
||||
This method only adds the rule inside the internal collection,
|
||||
but does not enable it for any ruleset. The rule must be enabled
|
||||
for one or more rulesets with the enable() or enable_tags() methods.
|
||||
The ast representation of the rule's condition is provided to allow
|
||||
the filter_ruleset object to parse the ast to obtain event types
|
||||
or do other analysis/indexing of the condition.
|
||||
\param rule The rule to be added
|
||||
\param the filter representing the rule's filtering condition.
|
||||
\param condition The AST representing the rule's filtering condition
|
||||
*/
|
||||
virtual void add(
|
||||
const falco_rule& rule,
|
||||
std::shared_ptr<gen_event_filter> filter,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> condition) = 0;
|
||||
|
||||
/*!
|
||||
@@ -151,5 +155,7 @@ public:
|
||||
class filter_ruleset_factory
|
||||
{
|
||||
public:
|
||||
virtual ~filter_ruleset_factory() = default;
|
||||
|
||||
virtual std::shared_ptr<filter_ruleset> new_ruleset() = 0;
|
||||
};
|
||||
|
||||
@@ -17,8 +17,9 @@ limitations under the License.
|
||||
#include <sinsp.h>
|
||||
#include "filter_warning_resolver.h"
|
||||
|
||||
using namespace falco;
|
||||
|
||||
static const char* no_value = "<NA>";
|
||||
static const char* warn_unsafe_na_check = "unsafe-na-check";
|
||||
|
||||
static inline bool is_unsafe_field(const string& f)
|
||||
{
|
||||
@@ -34,7 +35,7 @@ static inline bool is_equality_operator(const string& op)
|
||||
|
||||
bool filter_warning_resolver::run(
|
||||
libsinsp::filter::ast::expr* filter,
|
||||
std::set<string>& warnings) const
|
||||
std::set<load_result::warning_code>& warnings) const
|
||||
{
|
||||
visitor v;
|
||||
auto size = warnings.size();
|
||||
@@ -44,22 +45,6 @@ bool filter_warning_resolver::run(
|
||||
return warnings.size() > size;
|
||||
}
|
||||
|
||||
// todo(jasondellaluce): use an hard-coded map once we support more warnings
|
||||
bool filter_warning_resolver::format(
|
||||
const std::string& code,
|
||||
std::string& out) const
|
||||
{
|
||||
if (code == warn_unsafe_na_check)
|
||||
{
|
||||
out = "comparing a field value with <NA> is unsafe and can lead to "
|
||||
"unpredictable behavior of the rule condition. If you need to "
|
||||
" check for the existence of a field, consider using the "
|
||||
"'exists' operator instead.";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void filter_warning_resolver::visitor::visit(
|
||||
libsinsp::filter::ast::binary_check_expr* e)
|
||||
{
|
||||
@@ -76,7 +61,7 @@ void filter_warning_resolver::visitor::visit(
|
||||
{
|
||||
if (m_is_equality_check && e->value == no_value)
|
||||
{
|
||||
m_warnings->insert(warn_unsafe_na_check);
|
||||
m_warnings->insert(load_result::LOAD_UNSAFE_NA_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +71,6 @@ void filter_warning_resolver::visitor::visit(
|
||||
if (m_is_equality_check
|
||||
&& std::find(e->values.begin(), e->values.end(), no_value) != e->values.end())
|
||||
{
|
||||
m_warnings->insert(warn_unsafe_na_check);
|
||||
m_warnings->insert(load_result::LOAD_UNSAFE_NA_CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ limitations under the License.
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "falco_common.h"
|
||||
#include "falco_load_result.h"
|
||||
|
||||
/*!
|
||||
\brief Searches for bad practices in filter conditions and
|
||||
@@ -42,40 +43,19 @@ public:
|
||||
*/
|
||||
bool run(
|
||||
libsinsp::filter::ast::expr* filter,
|
||||
std::set<std::string>& warnings) const;
|
||||
|
||||
/*!
|
||||
\brief Given a warning code retrieved through run(), returns
|
||||
a verbose message describing the problem of the warning.
|
||||
\param code The warning code string
|
||||
\param out The string to be filled-out with the warning message
|
||||
\return true if the warning code is recognized, false otherwise
|
||||
*/
|
||||
bool format(const std::string& code, std::string& out) const;
|
||||
|
||||
/*!
|
||||
\brief Given a warning code retrieved through run(), returns
|
||||
a verbose message describing the problem of the warning.
|
||||
\param code The warning code string
|
||||
\return The warning message string
|
||||
\throw falco_exception if the warning code is not recognized
|
||||
|
||||
*/
|
||||
inline std::string format(const std::string& code) const
|
||||
{
|
||||
std::string v;
|
||||
if (!format(code, v))
|
||||
{
|
||||
throw falco_exception("unrecognized warning code: " + code);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
std::set<falco::load_result::warning_code>& warnings) const;
|
||||
|
||||
private:
|
||||
struct visitor : public libsinsp::filter::ast::base_expr_visitor
|
||||
{
|
||||
visitor(): m_is_equality_check(false), m_warnings(nullptr) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = delete;
|
||||
visitor& operator = (const visitor&) = delete;
|
||||
|
||||
bool m_is_equality_check;
|
||||
std::set<std::string>* m_warnings;
|
||||
std::set<falco::load_result::warning_code>* m_warnings;
|
||||
|
||||
void visit(libsinsp::filter::ast::value_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::list_expr* e) override;
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
falco_formats::falco_formats(std::shared_ptr<falco_engine> engine,
|
||||
falco_formats::falco_formats(std::shared_ptr<const falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property)
|
||||
: m_falco_engine(engine),
|
||||
@@ -34,7 +34,8 @@ falco_formats::~falco_formats()
|
||||
}
|
||||
|
||||
string falco_formats::format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags)
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const
|
||||
{
|
||||
string line;
|
||||
|
||||
@@ -83,6 +84,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
|
||||
event["rule"] = rule;
|
||||
event["priority"] = level;
|
||||
event["source"] = source;
|
||||
event["hostname"] = hostname;
|
||||
|
||||
if(m_json_include_output_property)
|
||||
{
|
||||
@@ -130,7 +132,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
|
||||
}
|
||||
|
||||
map<string, string> falco_formats::get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format)
|
||||
const std::string &format) const
|
||||
{
|
||||
std::shared_ptr<gen_event_formatter> formatter;
|
||||
|
||||
|
||||
@@ -24,19 +24,20 @@ limitations under the License.
|
||||
class falco_formats
|
||||
{
|
||||
public:
|
||||
falco_formats(std::shared_ptr<falco_engine> engine,
|
||||
falco_formats(std::shared_ptr<const falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property);
|
||||
virtual ~falco_formats();
|
||||
|
||||
std::string format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags);
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const;
|
||||
|
||||
map<string, string> get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format);
|
||||
const std::string &format) const ;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<falco_engine> m_falco_engine;
|
||||
std::shared_ptr<const falco_engine> m_falco_engine;
|
||||
bool m_json_include_output_property;
|
||||
bool m_json_include_tags_property;
|
||||
};
|
||||
|
||||
@@ -28,7 +28,12 @@ template <typename T>
|
||||
class indexed_vector
|
||||
{
|
||||
public:
|
||||
indexed_vector() = default;
|
||||
virtual ~indexed_vector() = default;
|
||||
indexed_vector(indexed_vector&&) = default;
|
||||
indexed_vector& operator = (indexed_vector&&) = default;
|
||||
indexed_vector(const indexed_vector&) = default;
|
||||
indexed_vector& operator = (const indexed_vector&) = default;
|
||||
|
||||
/*!
|
||||
\brief Returns the number of elements
|
||||
@@ -68,7 +73,7 @@ public:
|
||||
\param index String index of the element to be added in the vector
|
||||
\return The numeric index assigned to the element
|
||||
*/
|
||||
virtual inline size_t insert(T& entry, const std::string& index)
|
||||
virtual inline size_t insert(const T& entry, const std::string& index)
|
||||
{
|
||||
size_t id;
|
||||
auto prev = m_index.find(index);
|
||||
@@ -89,7 +94,7 @@ public:
|
||||
*/
|
||||
virtual inline T* at(size_t id) const
|
||||
{
|
||||
if (id <= m_entries.size())
|
||||
if (id < m_entries.size())
|
||||
{
|
||||
return (T* const) &m_entries[id];
|
||||
}
|
||||
@@ -131,5 +136,5 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<T> m_entries;
|
||||
std::map<std::string, size_t> m_index;
|
||||
std::unordered_map<std::string, size_t> m_index;
|
||||
};
|
||||
|
||||
@@ -515,16 +515,17 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
|
||||
|
||||
size_t idx_len = 0;
|
||||
|
||||
for(auto &info : m_info.m_fields)
|
||||
for(const auto &info : get_info().m_fields)
|
||||
{
|
||||
if(m_aliases.find(info.m_name) == m_aliases.end())
|
||||
auto iter = get_aliases().find(info.m_name);
|
||||
if( iter == get_aliases().end())
|
||||
{
|
||||
throw falco_exception("Could not find alias for field name " + info.m_name);
|
||||
}
|
||||
|
||||
m_uses_paths = info.m_uses_paths;
|
||||
|
||||
auto &al = m_aliases[info.m_name];
|
||||
auto &al = iter->second;
|
||||
|
||||
// What follows the match must not be alphanumeric or a dot
|
||||
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
|
||||
@@ -692,11 +693,6 @@ size_t json_event_filter_check::parsed_size()
|
||||
}
|
||||
}
|
||||
|
||||
json_event_filter_check::check_info &json_event_filter_check::get_info()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
void json_event_filter_check::add_extracted_value(const std::string &str)
|
||||
{
|
||||
m_evalues.first.emplace_back(json_event_value(str));
|
||||
@@ -793,9 +789,9 @@ std::string jevt_filter_check::s_jevt_rawtime_field = "jevt.rawtime";
|
||||
std::string jevt_filter_check::s_jevt_value_field = "jevt.value";
|
||||
std::string jevt_filter_check::s_jevt_obj_field = "jevt.obj";
|
||||
|
||||
jevt_filter_check::jevt_filter_check()
|
||||
const jevt_filter_check::check_info &jevt_filter_check::get_info() const
|
||||
{
|
||||
m_info = {"jevt",
|
||||
static const check_info info = {"jevt",
|
||||
"generic ways to access json events",
|
||||
"",
|
||||
{{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
|
||||
@@ -803,6 +799,11 @@ jevt_filter_check::jevt_filter_check()
|
||||
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
|
||||
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
|
||||
{s_jevt_obj_field, "The entire json object, stringified"}}};
|
||||
return info;
|
||||
}
|
||||
|
||||
jevt_filter_check::jevt_filter_check()
|
||||
{
|
||||
}
|
||||
|
||||
jevt_filter_check::~jevt_filter_check()
|
||||
@@ -1282,71 +1283,77 @@ bool k8s_audit_filter_check::extract_any_privileged(const json &j,
|
||||
return true;
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
const json_event_filter_check::check_info &k8s_audit_filter_check::get_info() const
|
||||
{
|
||||
m_info = {"ka",
|
||||
"Access K8s Audit Log Events",
|
||||
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
|
||||
{{"ka.auditid", "The unique id of the audit event"},
|
||||
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
|
||||
{"ka.auth.decision", "The authorization decision"},
|
||||
{"ka.auth.reason", "The authorization reason"},
|
||||
{"ka.user.name", "The user name performing the request"},
|
||||
{"ka.user.groups", "The groups to which the user belongs"},
|
||||
{"ka.impuser.name", "The impersonated user name"},
|
||||
{"ka.verb", "The action being performed"},
|
||||
{"ka.uri", "The request URI as sent from client to server"},
|
||||
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.target.name", "The target object name"},
|
||||
{"ka.target.namespace", "The target object namespace"},
|
||||
{"ka.target.resource", "The target object resource"},
|
||||
{"ka.target.subresource", "The target object subresource"},
|
||||
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
|
||||
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
|
||||
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
|
||||
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
|
||||
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
|
||||
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
|
||||
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
|
||||
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
|
||||
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
|
||||
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
|
||||
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
|
||||
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
|
||||
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
|
||||
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
|
||||
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.service.type", "When the request object refers to a service, the service type"},
|
||||
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
|
||||
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
|
||||
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.resp.name", "The response object name"},
|
||||
{"ka.response.code", "The response code"},
|
||||
{"ka.response.reason", "The response reason (usually present only for failures)"},
|
||||
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
|
||||
static const json_event_filter_check::check_info
|
||||
info = {"ka",
|
||||
"Access K8s Audit Log Events",
|
||||
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
|
||||
{{"ka.auditid", "The unique id of the audit event"},
|
||||
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
|
||||
{"ka.auth.decision", "The authorization decision"},
|
||||
{"ka.auth.reason", "The authorization reason"},
|
||||
{"ka.user.name", "The user name performing the request"},
|
||||
{"ka.user.groups", "The groups to which the user belongs"},
|
||||
{"ka.impuser.name", "The impersonated user name"},
|
||||
{"ka.verb", "The action being performed"},
|
||||
{"ka.uri", "The request URI as sent from client to server"},
|
||||
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.target.name", "The target object name"},
|
||||
{"ka.target.namespace", "The target object namespace"},
|
||||
{"ka.target.resource", "The target object resource"},
|
||||
{"ka.target.subresource", "The target object subresource"},
|
||||
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
|
||||
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
|
||||
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
|
||||
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
|
||||
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
|
||||
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
|
||||
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
|
||||
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
|
||||
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
|
||||
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
|
||||
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
|
||||
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
|
||||
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
|
||||
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
|
||||
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.service.type", "When the request object refers to a service, the service type"},
|
||||
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
|
||||
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
|
||||
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.resp.name", "The response object name"},
|
||||
{"ka.response.code", "The response code"},
|
||||
{"ka.response.reason", "The response reason (usually present only for failures)"},
|
||||
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
|
||||
return info;
|
||||
|
||||
{
|
||||
m_aliases = {
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, k8s_audit_filter_check::alias> &k8s_audit_filter_check::get_aliases() const
|
||||
{
|
||||
static const std::unordered_map<std::string, k8s_audit_filter_check::alias>
|
||||
aliases = {
|
||||
{"ka.auditid", {{"/auditID"_json_pointer}}},
|
||||
{"ka.stage", {{"/stage"_json_pointer}}},
|
||||
{"ka.auth.decision", {{"/annotations/authorization.k8s.io~1decision"_json_pointer}}},
|
||||
@@ -1404,7 +1411,11 @@ k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
{"ka.response.code", {{"/responseStatus/code"_json_pointer}}},
|
||||
{"ka.response.reason", {{"/responseStatus/reason"_json_pointer}}},
|
||||
{"ka.useragent", {{"/userAgent"_json_pointer}}}};
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
{
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::~k8s_audit_filter_check()
|
||||
@@ -1475,14 +1486,14 @@ std::list<gen_event_filter_factory::filter_fieldclass_info> json_event_filter_fa
|
||||
|
||||
for(auto &chk: m_defined_checks)
|
||||
{
|
||||
json_event_filter_check::check_info &info = chk->get_info();
|
||||
const json_event_filter_check::check_info &info = chk->get_info();
|
||||
gen_event_filter_factory::filter_fieldclass_info cinfo;
|
||||
|
||||
cinfo.name = info.m_name;
|
||||
cinfo.desc = info.m_desc;
|
||||
cinfo.shortdesc = info.m_shortdesc;
|
||||
|
||||
for(auto &field : info.m_fields)
|
||||
for(const auto &field : info.m_fields)
|
||||
{
|
||||
gen_event_filter_factory::filter_field_info info;
|
||||
info.name = field.m_name;
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
};
|
||||
|
||||
json_event_filter_check();
|
||||
virtual ~json_event_filter_check();
|
||||
virtual ~json_event_filter_check() = 0;
|
||||
|
||||
virtual int32_t parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering);
|
||||
void add_filter_value(const char *str, uint32_t len, uint32_t i = 0);
|
||||
@@ -197,7 +197,7 @@ public:
|
||||
// brackets (e.g. ka.image[foo])
|
||||
size_t parsed_size();
|
||||
|
||||
check_info &get_info();
|
||||
virtual const check_info &get_info() const = 0;
|
||||
|
||||
//
|
||||
// Allocate a new check of the same type. Must be overridden.
|
||||
@@ -260,9 +260,9 @@ protected:
|
||||
//
|
||||
// The version of parse_field_name in this base class will
|
||||
// check a field specification against all the aliases.
|
||||
std::map<std::string, struct alias> m_aliases;
|
||||
virtual const std::unordered_map<std::string, alias> &get_aliases() const = 0;
|
||||
|
||||
check_info m_info;
|
||||
//check_info m_info;
|
||||
|
||||
// The actual field name parsed in parse_field_name.
|
||||
std::string m_field;
|
||||
@@ -315,11 +315,18 @@ public:
|
||||
|
||||
int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) final;
|
||||
|
||||
json_event_filter_check *allocate_new();
|
||||
json_event_filter_check *allocate_new() override;
|
||||
const check_info &get_info() const override;
|
||||
|
||||
protected:
|
||||
|
||||
bool extract_values(json_event *jevt) final;
|
||||
const std::unordered_map<std::string, alias> &get_aliases() const override
|
||||
{
|
||||
static std::unordered_map<std::string, alias> a;
|
||||
return a;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@@ -340,9 +347,12 @@ public:
|
||||
k8s_audit_filter_check();
|
||||
virtual ~k8s_audit_filter_check();
|
||||
|
||||
json_event_filter_check *allocate_new();
|
||||
json_event_filter_check *allocate_new() override;
|
||||
|
||||
// Extract all images/image repositories from the provided containers
|
||||
const check_info &get_info() const override;
|
||||
const std::unordered_map<std::string, alias> &get_aliases() const override;
|
||||
|
||||
// Extract all images/image repositories from the provided containers
|
||||
static bool extract_images(const nlohmann::json &j,
|
||||
json_event_filter_check &jchk);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,49 +16,247 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "falco_rule.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "falco_source.h"
|
||||
#include "falco_load_result.h"
|
||||
#include "indexed_vector.h"
|
||||
|
||||
|
||||
/*!
|
||||
\brief Ruleset loader of the falco engine
|
||||
*/
|
||||
class rule_loader
|
||||
namespace rule_loader
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
\brief Represents a section of text from which a certain info
|
||||
struct has been decoded
|
||||
*/
|
||||
struct context
|
||||
class context
|
||||
{
|
||||
std::string content;
|
||||
public:
|
||||
// The kinds of items that can be in rules
|
||||
// content. These generally map to yaml items but a
|
||||
// few are more specific (e.g. "within condition
|
||||
// expression", "value for yaml node", etc.)
|
||||
enum item_type {
|
||||
VALUE_FOR = 0,
|
||||
EXCEPTIONS,
|
||||
EXCEPTION,
|
||||
EXCEPTION_VALUES,
|
||||
EXCEPTION_VALUE,
|
||||
RULES_CONTENT,
|
||||
RULES_CONTENT_ITEM,
|
||||
REQUIRED_ENGINE_VERSION,
|
||||
REQUIRED_PLUGIN_VERSIONS,
|
||||
REQUIRED_PLUGIN_VERSIONS_ENTRY,
|
||||
REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE,
|
||||
LIST,
|
||||
LIST_ITEM,
|
||||
MACRO,
|
||||
MACRO_CONDITION,
|
||||
RULE,
|
||||
RULE_CONDITION,
|
||||
CONDITION_EXPRESSION,
|
||||
RULE_OUTPUT,
|
||||
RULE_OUTPUT_EXPRESSION,
|
||||
RULE_PRIORITY
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Wraps an error by adding info about the text section
|
||||
*/
|
||||
inline std::string error(std::string err) const
|
||||
{
|
||||
std::string cnt = content;
|
||||
err += "\n---\n";
|
||||
err += trim(cnt);
|
||||
err += "\n---";
|
||||
return err;
|
||||
}
|
||||
static const std::string& item_type_as_string(enum item_type it);
|
||||
|
||||
/*!
|
||||
\brief Appends another text section info to this one
|
||||
*/
|
||||
inline void append(context& m)
|
||||
static const size_t default_snippet_width = 160;
|
||||
|
||||
struct position
|
||||
{
|
||||
content += "\n\n";
|
||||
content += m.content;
|
||||
}
|
||||
position() : pos(0), line(0), column(0) {};
|
||||
explicit position(const YAML::Mark& mark) : pos(mark.pos), line(mark.line), column(mark.column) {};
|
||||
~position() = default;
|
||||
position(position&&) = default;
|
||||
position& operator = (position&&) = default;
|
||||
position(const position&) = default;
|
||||
position& operator = (const position&) = default;
|
||||
|
||||
int pos;
|
||||
int line;
|
||||
int column;
|
||||
};
|
||||
|
||||
struct location
|
||||
{
|
||||
location(): item_type(context::item_type::VALUE_FOR) {}
|
||||
location(
|
||||
const std::string& n,
|
||||
const position& p,
|
||||
context::item_type i,
|
||||
const std::string& in):
|
||||
name(n), pos(p), item_type(i), item_name(in) {}
|
||||
location(location&&) = default;
|
||||
location& operator = (location&&) = default;
|
||||
location(const location&) = default;
|
||||
location& operator = (const location&) = default;
|
||||
|
||||
// A name for the content this location refers
|
||||
// to. Will generally be a filename, can also
|
||||
// refer to a rule/macro condition when the
|
||||
// location points into a condition string.
|
||||
std::string name;
|
||||
|
||||
// The original location in the document
|
||||
position pos;
|
||||
|
||||
// The kind of item at this location
|
||||
// (e.g. "list", "macro", "rule", "exception", etc)
|
||||
context::item_type item_type;
|
||||
|
||||
// The name of this item (e.g. "Write Below Etc",
|
||||
// etc).
|
||||
std::string item_name;
|
||||
};
|
||||
|
||||
explicit context(const std::string& name);
|
||||
context(const YAML::Node& item,
|
||||
item_type item_type,
|
||||
const std::string& item_name,
|
||||
const context& parent);
|
||||
|
||||
// Build a context from a condition expression +
|
||||
// parser position. This does not use the original
|
||||
// yaml content because:
|
||||
// - YAML block indicators will remove whitespace/newlines/wrapping
|
||||
// from the YAML node containing the condition expression.
|
||||
// - When compiling, the condition expression has expanded
|
||||
// macro and list references with their values.
|
||||
context(const libsinsp::filter::ast::pos_info& pos,
|
||||
const std::string& condition,
|
||||
const context& parent);
|
||||
|
||||
virtual ~context() = default;
|
||||
|
||||
context(context&&) = default;
|
||||
context& operator = (context&&) = default;
|
||||
context(const context&) = default;
|
||||
context& operator = (const context&) = default;
|
||||
|
||||
// Return the content name (generally filename) for
|
||||
// this context
|
||||
const std::string& name() const;
|
||||
|
||||
// Return a snippet of the provided rules content
|
||||
// corresponding to this context.
|
||||
// Uses the provided rules_contents to look up the original
|
||||
// rules content for a given location name.
|
||||
// (If this context has a non-empty alt_content, it
|
||||
// will be used to create the snippet, ignoring the
|
||||
// provided rules_contents).
|
||||
std::string snippet(const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width = default_snippet_width) const;
|
||||
|
||||
std::string as_string();
|
||||
nlohmann::json as_json();
|
||||
|
||||
private:
|
||||
void init(const std::string& name,
|
||||
const position& pos,
|
||||
const item_type item_type,
|
||||
const std::string& item_name,
|
||||
const context& parent);
|
||||
|
||||
// A chain of locations from the current item, its
|
||||
// parent, possibly older ancestors.
|
||||
std::vector<location> m_locs;
|
||||
|
||||
// If non-empty, this content will be used when
|
||||
// creating snippets. Used for contexts involving
|
||||
// condition expressions.
|
||||
std::string alt_content;
|
||||
};
|
||||
|
||||
struct warning
|
||||
{
|
||||
warning(): wc(falco::load_result::warning_code::LOAD_UNKNOWN_SOURCE), ctx("no-filename-given") {}
|
||||
warning(
|
||||
falco::load_result::warning_code w,
|
||||
const std::string& m,
|
||||
const context& c): wc(w), msg(m), ctx(c) {}
|
||||
warning(warning&&) = default;
|
||||
warning& operator = (warning&&) = default;
|
||||
warning(const warning&) = default;
|
||||
warning& operator = (const warning&) = default;
|
||||
|
||||
falco::load_result::warning_code wc;
|
||||
std::string msg;
|
||||
context ctx;
|
||||
};
|
||||
|
||||
struct error
|
||||
{
|
||||
error(): ec(falco::load_result::error_code::LOAD_ERR_FILE_READ), ctx("no-filename-given") {}
|
||||
error(
|
||||
falco::load_result::error_code e,
|
||||
const std::string& m,
|
||||
const context& c): ec(e), msg(m), ctx(c) {}
|
||||
error(error&&) = default;
|
||||
error& operator = (error&&) = default;
|
||||
error(const error&) = default;
|
||||
error& operator = (const error&) = default;
|
||||
|
||||
falco::load_result::error_code ec;
|
||||
std::string msg;
|
||||
context ctx;
|
||||
};
|
||||
|
||||
class rule_load_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx);
|
||||
virtual ~rule_load_exception();
|
||||
rule_load_exception(rule_load_exception&&) = default;
|
||||
rule_load_exception& operator = (rule_load_exception&&) = default;
|
||||
rule_load_exception(const rule_load_exception&) = default;
|
||||
rule_load_exception& operator = (const rule_load_exception&) = default;
|
||||
|
||||
const char* what();
|
||||
|
||||
falco::load_result::error_code ec;
|
||||
std::string msg;
|
||||
context ctx;
|
||||
|
||||
std::string errstr;
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Contains the result of loading rule definitions
|
||||
*/
|
||||
class result : public falco::load_result
|
||||
{
|
||||
public:
|
||||
result(const std::string &name);
|
||||
virtual ~result() = default;
|
||||
result(result&&) = default;
|
||||
result& operator = (result&&) = default;
|
||||
result(const result&) = default;
|
||||
result& operator = (const result&) = default;
|
||||
|
||||
virtual bool successful() override;
|
||||
virtual bool has_warnings() override;
|
||||
|
||||
virtual const std::string& as_string(bool verbose, const falco::load_result::rules_contents_t& contents) override;
|
||||
virtual const nlohmann::json& as_json(const falco::load_result::rules_contents_t& contents) override;
|
||||
|
||||
void add_error(falco::load_result::error_code ec,
|
||||
const std::string& msg,
|
||||
const context& ctx);
|
||||
|
||||
void add_warning(falco::load_result::warning_code ec,
|
||||
const std::string& msg,
|
||||
const context& ctx);
|
||||
protected:
|
||||
|
||||
const std::string& as_summary_string();
|
||||
const std::string& as_verbose_string(const falco::load_result::rules_contents_t& contents);
|
||||
std::string name;
|
||||
bool success;
|
||||
|
||||
std::vector<error> errors;
|
||||
std::vector<warning> warnings;
|
||||
|
||||
std::string res_summary_string;
|
||||
std::string res_verbose_string;
|
||||
nlohmann::json res_json;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -68,13 +266,23 @@ public:
|
||||
{
|
||||
explicit configuration(
|
||||
const std::string& cont,
|
||||
const indexed_vector<falco_source>& srcs)
|
||||
: content(cont), sources(srcs) {}
|
||||
const indexed_vector<falco_source>& srcs,
|
||||
const std::string& name)
|
||||
: content(cont), sources(srcs), name(name),
|
||||
default_ruleset_id(0), replace_output_container_info(false),
|
||||
min_priority(falco_common::PRIORITY_DEBUG)
|
||||
{
|
||||
res.reset(new result(name));
|
||||
}
|
||||
configuration(configuration&&) = default;
|
||||
configuration& operator = (configuration&&) = default;
|
||||
configuration(const configuration&) = delete;
|
||||
configuration& operator = (const configuration&) = delete;
|
||||
|
||||
const std::string& content;
|
||||
const indexed_vector<falco_source>& sources;
|
||||
std::vector<std::string> errors;
|
||||
std::vector<std::string> warnings;
|
||||
std::string name;
|
||||
std::unique_ptr<result> res;
|
||||
std::string output_extra;
|
||||
uint16_t default_ruleset_id;
|
||||
bool replace_output_container_info;
|
||||
@@ -86,6 +294,14 @@ public:
|
||||
*/
|
||||
struct engine_version_info
|
||||
{
|
||||
engine_version_info(context &ctx);
|
||||
~engine_version_info() = default;
|
||||
engine_version_info(engine_version_info&&) = default;
|
||||
engine_version_info& operator = (engine_version_info&&) = default;
|
||||
engine_version_info(const engine_version_info&) = default;
|
||||
engine_version_info& operator = (const engine_version_info&) = default;
|
||||
|
||||
context ctx;
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
@@ -94,15 +310,49 @@ public:
|
||||
*/
|
||||
struct plugin_version_info
|
||||
{
|
||||
std::string name;
|
||||
std::string version;
|
||||
struct requirement
|
||||
{
|
||||
requirement() = default;
|
||||
requirement(const std::string& n, const std::string& v):
|
||||
name(n), version(v) { }
|
||||
requirement(requirement&&) = default;
|
||||
requirement& operator = (requirement&&) = default;
|
||||
requirement(const requirement&) = default;
|
||||
requirement& operator = (const requirement&) = default;
|
||||
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
|
||||
typedef std::vector<requirement> requirement_alternatives;
|
||||
|
||||
// This differs from the other _info structs by having
|
||||
// a default constructor. This allows it to be used
|
||||
// by falco_engine, which aliases the type.
|
||||
plugin_version_info();
|
||||
plugin_version_info(context &ctx);
|
||||
~plugin_version_info() = default;
|
||||
plugin_version_info(plugin_version_info&&) = default;
|
||||
plugin_version_info& operator = (plugin_version_info&&) = default;
|
||||
plugin_version_info(const plugin_version_info&) = default;
|
||||
plugin_version_info& operator = (const plugin_version_info&) = default;
|
||||
|
||||
context ctx;
|
||||
requirement_alternatives alternatives;
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Represents infos about a list
|
||||
\brief Represents infos about a list
|
||||
*/
|
||||
struct list_info
|
||||
{
|
||||
list_info(context &ctx);
|
||||
~list_info() = default;
|
||||
list_info(list_info&&) = default;
|
||||
list_info& operator = (list_info&&) = default;
|
||||
list_info(const list_info&) = default;
|
||||
list_info& operator = (const list_info&) = default;
|
||||
|
||||
context ctx;
|
||||
bool used;
|
||||
size_t index;
|
||||
@@ -112,11 +362,19 @@ public:
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Represents infos about a macro
|
||||
\brief Represents infos about a macro
|
||||
*/
|
||||
struct macro_info
|
||||
{
|
||||
macro_info(context &ctx);
|
||||
~macro_info() = default;
|
||||
macro_info(macro_info&&) = default;
|
||||
macro_info& operator = (macro_info&&) = default;
|
||||
macro_info(const macro_info&) = default;
|
||||
macro_info& operator = (const macro_info&) = default;
|
||||
|
||||
context ctx;
|
||||
context cond_ctx;
|
||||
bool used;
|
||||
size_t index;
|
||||
size_t visibility;
|
||||
@@ -130,6 +388,13 @@ public:
|
||||
*/
|
||||
struct rule_exception_info
|
||||
{
|
||||
rule_exception_info(context &ctx);
|
||||
~rule_exception_info() = default;
|
||||
rule_exception_info(rule_exception_info&&) = default;
|
||||
rule_exception_info& operator = (rule_exception_info&&) = default;
|
||||
rule_exception_info(const rule_exception_info&) = default;
|
||||
rule_exception_info& operator = (const rule_exception_info&) = default;
|
||||
|
||||
/*!
|
||||
\brief This is necessary due to the dynamic-typed nature of
|
||||
exceptions. Each of fields, comps, and values, can either be a
|
||||
@@ -137,6 +402,14 @@ public:
|
||||
this easier to implement in C++, that is not non-dynamic-typed.
|
||||
*/
|
||||
struct entry {
|
||||
entry(): is_list(false) {}
|
||||
explicit entry(const std::string& i): is_list(false), item(i) {}
|
||||
explicit entry(const std::vector<entry>& v): is_list(true), items(v) {}
|
||||
entry(entry&&) = default;
|
||||
entry& operator = (entry&&) = default;
|
||||
entry(const entry&) = default;
|
||||
entry& operator = (const entry&) = default;
|
||||
|
||||
bool is_list;
|
||||
std::string item;
|
||||
std::vector<entry> items;
|
||||
@@ -148,6 +421,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
context ctx;
|
||||
std::string name;
|
||||
entry fields;
|
||||
entry comps;
|
||||
@@ -155,11 +429,20 @@ public:
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Represents infos about a rule
|
||||
\brief Represents infos about a rule
|
||||
*/
|
||||
struct rule_info
|
||||
{
|
||||
rule_info(context &ctx);
|
||||
~rule_info() = default;
|
||||
rule_info(rule_info&&) = default;
|
||||
rule_info& operator = (rule_info&&) = default;
|
||||
rule_info(const rule_info&) = default;
|
||||
rule_info& operator = (const rule_info&) = default;
|
||||
|
||||
context ctx;
|
||||
context cond_ctx;
|
||||
context output_ctx;
|
||||
size_t index;
|
||||
size_t visibility;
|
||||
std::string name;
|
||||
@@ -174,67 +457,4 @@ public:
|
||||
bool warn_evttypes;
|
||||
bool skip_if_unknown_filter;
|
||||
};
|
||||
|
||||
virtual ~rule_loader() = default;
|
||||
|
||||
/*!
|
||||
\brief Erases all the internal state and definitions
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
/*!
|
||||
\brief Uses the internal state to compile a list of falco_rules
|
||||
*/
|
||||
virtual bool compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
|
||||
|
||||
/*!
|
||||
\brief Returns the set of all required versions for each plugin according
|
||||
to the internal definitions.
|
||||
*/
|
||||
virtual const std::map<std::string, std::set<std::string>> required_plugin_versions() const;
|
||||
|
||||
/*!
|
||||
\brief Defines an info block. If a similar info block is found
|
||||
in the internal state (e.g. another rule with same name), then
|
||||
the previous definition gets overwritten
|
||||
*/
|
||||
virtual void define(configuration& cfg, engine_version_info& info);
|
||||
virtual void define(configuration& cfg, plugin_version_info& info);
|
||||
virtual void define(configuration& cfg, list_info& info);
|
||||
virtual void define(configuration& cfg, macro_info& info);
|
||||
virtual void define(configuration& cfg, rule_info& info);
|
||||
|
||||
/*!
|
||||
\brief Appends an info block to an existing one. An exception
|
||||
is thrown if no existing definition can be matched with the appended
|
||||
one
|
||||
*/
|
||||
virtual void append(configuration& cfg, list_info& info);
|
||||
virtual void append(configuration& cfg, macro_info& info);
|
||||
virtual void append(configuration& cfg, rule_info& info);
|
||||
|
||||
/*!
|
||||
\brief Updates the 'enabled' flag of an existing definition
|
||||
*/
|
||||
virtual void enable(configuration& cfg, rule_info& info);
|
||||
|
||||
private:
|
||||
void compile_list_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& out) const;
|
||||
void compile_macros_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const;
|
||||
void compile_rule_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
|
||||
uint32_t m_cur_index;
|
||||
indexed_vector<rule_info> m_rule_infos;
|
||||
indexed_vector<macro_info> m_macro_infos;
|
||||
indexed_vector<list_info> m_list_infos;
|
||||
std::map<std::string, std::set<std::string>> m_required_plugin_versions;
|
||||
};
|
||||
|
||||
280
userspace/engine/rule_loader_collector.cpp
Normal file
280
userspace/engine/rule_loader_collector.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 <string>
|
||||
#include <version.h>
|
||||
|
||||
#include "falco_engine.h"
|
||||
#include "rule_loader_collector.h"
|
||||
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, (err), (ctx)); } }
|
||||
|
||||
|
||||
static inline bool is_operator_defined(const std::string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators();
|
||||
return find(ops.begin(), ops.end(), op) != ops.end();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void define_info(indexed_vector<T>& infos, T& info, uint32_t id)
|
||||
{
|
||||
auto prev = infos.at(info.name);
|
||||
if (prev)
|
||||
{
|
||||
info.index = prev->index;
|
||||
info.visibility = id;
|
||||
*prev = info;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.index = id;
|
||||
info.visibility = id;
|
||||
infos.insert(info, info.name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void append_info(T* prev, T& info, uint32_t id)
|
||||
{
|
||||
prev->visibility = id;
|
||||
}
|
||||
|
||||
static void validate_exception_info(
|
||||
const falco_source& source,
|
||||
rule_loader::rule_exception_info &ex)
|
||||
{
|
||||
if (ex.fields.is_list)
|
||||
{
|
||||
if (!ex.comps.is_valid())
|
||||
{
|
||||
ex.comps.is_list = true;
|
||||
for (size_t i = 0; i < ex.fields.items.size(); i++)
|
||||
{
|
||||
ex.comps.items.push_back(rule_loader::rule_exception_info::entry("="));
|
||||
}
|
||||
}
|
||||
THROW(ex.fields.items.size() != ex.comps.items.size(),
|
||||
"Fields and comps lists must have equal length",
|
||||
ex.ctx);
|
||||
for (auto &v : ex.comps.items)
|
||||
{
|
||||
THROW(!is_operator_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported comparison operator",
|
||||
ex.ctx);
|
||||
}
|
||||
for (auto &v : ex.fields.items)
|
||||
{
|
||||
THROW(!source.is_field_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ex.comps.is_valid())
|
||||
{
|
||||
ex.comps.is_list = false;
|
||||
ex.comps.item = "in";
|
||||
}
|
||||
THROW(ex.comps.is_list,
|
||||
"Fields and comps must both be strings",
|
||||
ex.ctx);
|
||||
THROW((ex.comps.item != "in" && ex.comps.item != "pmatch" && ex.comps.item != "intersects"),
|
||||
"When fields is a single value, comps must be one of (in, pmatch, intersects)",
|
||||
ex.ctx);
|
||||
THROW(!source.is_field_defined(ex.fields.item),
|
||||
std::string("'") + ex.fields.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void rule_loader::collector::clear()
|
||||
{
|
||||
m_cur_index = 0;
|
||||
m_rule_infos.clear();
|
||||
m_list_infos.clear();
|
||||
m_macro_infos.clear();
|
||||
m_required_plugin_versions.clear();
|
||||
}
|
||||
|
||||
const std::vector<rule_loader::plugin_version_info::requirement_alternatives>& rule_loader::collector::required_plugin_versions() const
|
||||
{
|
||||
return m_required_plugin_versions;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::list_info>& rule_loader::collector::lists() const
|
||||
{
|
||||
return m_list_infos;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::macro_info>& rule_loader::collector::macros() const
|
||||
{
|
||||
return m_macro_infos;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::rule_info>& rule_loader::collector::rules() const
|
||||
{
|
||||
return m_rule_infos;
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, engine_version_info& info)
|
||||
{
|
||||
auto v = falco_engine::engine_version();
|
||||
THROW(v < info.version, "Rules require engine version "
|
||||
+ std::to_string(info.version) + ", but engine version is " + std::to_string(v),
|
||||
info.ctx);
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, plugin_version_info& info)
|
||||
{
|
||||
std::unordered_set<std::string> plugin_names;
|
||||
for (const auto& req : info.alternatives)
|
||||
{
|
||||
sinsp_version plugin_version(req.version);
|
||||
THROW(!plugin_version.m_valid,
|
||||
"Invalid required version '" + req.version
|
||||
+ "' for plugin '" + req.name + "'",
|
||||
info.ctx);
|
||||
THROW(plugin_names.find(req.name) != plugin_names.end(),
|
||||
"Defined multiple alternative version requirements for plugin '"
|
||||
+ req.name + "'",
|
||||
info.ctx);
|
||||
plugin_names.insert(req.name);
|
||||
}
|
||||
m_required_plugin_versions.push_back(info.alternatives);
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, list_info& info)
|
||||
{
|
||||
define_info(m_list_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::append(configuration& cfg, list_info& info)
|
||||
{
|
||||
auto prev = m_list_infos.at(info.name);
|
||||
THROW(!prev,
|
||||
"List has 'append' key but no list by that name already exists",
|
||||
info.ctx);
|
||||
prev->items.insert(prev->items.end(), info.items.begin(), info.items.end());
|
||||
append_info(prev, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, macro_info& info)
|
||||
{
|
||||
define_info(m_macro_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::append(configuration& cfg, macro_info& info)
|
||||
{
|
||||
auto prev = m_macro_infos.at(info.name);
|
||||
THROW(!prev,
|
||||
"Macro has 'append' key but no macro by that name already exists",
|
||||
info.ctx);
|
||||
prev->cond += " ";
|
||||
prev->cond += info.cond;
|
||||
append_info(prev, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto source = cfg.sources.at(info.source);
|
||||
if (!source)
|
||||
{
|
||||
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_SOURCE,
|
||||
"Unknown source " + info.source + ", skipping",
|
||||
info.ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(prev && prev->source != info.source,
|
||||
"Rule has been re-defined with a different source",
|
||||
info.ctx);
|
||||
|
||||
for (auto &ex : info.exceptions)
|
||||
{
|
||||
THROW(!ex.fields.is_valid(),
|
||||
"Rule exception item must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
}
|
||||
|
||||
define_info(m_rule_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::append(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
|
||||
THROW(!prev,
|
||||
"Rule has 'append' key but no rule by that name already exists",
|
||||
info.ctx);
|
||||
THROW(info.cond.empty() && info.exceptions.empty(),
|
||||
"Appended rule must have exceptions or condition property",
|
||||
info.ctx);
|
||||
|
||||
auto source = cfg.sources.at(prev->source);
|
||||
// note: this is not supposed to happen
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + prev->source,
|
||||
info.ctx);
|
||||
|
||||
if (!info.cond.empty())
|
||||
{
|
||||
prev->cond += " ";
|
||||
prev->cond += info.cond;
|
||||
}
|
||||
|
||||
for (auto &ex : info.exceptions)
|
||||
{
|
||||
auto prev_ex = find_if(prev->exceptions.begin(), prev->exceptions.end(),
|
||||
[&ex](const rule_loader::rule_exception_info& i)
|
||||
{ return i.name == ex.name; });
|
||||
if (prev_ex == prev->exceptions.end())
|
||||
{
|
||||
THROW(!ex.fields.is_valid(),
|
||||
"Rule exception must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
THROW(ex.values.empty(),
|
||||
"Rule exception must have values property with a list of values",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
prev->exceptions.push_back(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW(ex.fields.is_valid(),
|
||||
"Can not append exception fields to existing exception, only values",
|
||||
ex.ctx);
|
||||
THROW(ex.comps.is_valid(),
|
||||
"Can not append exception comps to existing exception, only values",
|
||||
ex.ctx);
|
||||
prev_ex->values.insert(
|
||||
prev_ex->values.end(), ex.values.begin(), ex.values.end());
|
||||
}
|
||||
}
|
||||
append_info(prev, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::enable(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(!prev,
|
||||
"Rule has 'enabled' key but no rule by that name already exists",
|
||||
info.ctx);
|
||||
prev->enabled = info.enabled;
|
||||
}
|
||||
97
userspace/engine/rule_loader_collector.h
Normal file
97
userspace/engine/rule_loader_collector.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "rule_loader.h"
|
||||
#include "indexed_vector.h"
|
||||
|
||||
namespace rule_loader
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Collector for the ruleset loader of the falco engine
|
||||
*/
|
||||
class collector
|
||||
{
|
||||
public:
|
||||
collector(): m_cur_index(0) { }
|
||||
virtual ~collector() = default;
|
||||
collector(collector&&) = default;
|
||||
collector& operator = (collector&&) = default;
|
||||
collector(const collector&) = delete;
|
||||
collector& operator = (const collector&) = delete;
|
||||
|
||||
/*!
|
||||
\brief Erases all the internal state and definitions
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
/*!
|
||||
\brief Returns the set of all defined required plugin versions
|
||||
*/
|
||||
virtual const std::vector<plugin_version_info::requirement_alternatives>& required_plugin_versions() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined lists
|
||||
*/
|
||||
virtual const indexed_vector<list_info>& lists() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined macros
|
||||
*/
|
||||
virtual const indexed_vector<macro_info>& macros() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined rules
|
||||
*/
|
||||
virtual const indexed_vector<rule_info>& rules() const;
|
||||
|
||||
/*!
|
||||
\brief Defines an info block. If a similar info block is found
|
||||
in the internal state (e.g. another rule with same name), then
|
||||
the previous definition gets overwritten
|
||||
*/
|
||||
virtual void define(configuration& cfg, engine_version_info& info);
|
||||
virtual void define(configuration& cfg, plugin_version_info& info);
|
||||
virtual void define(configuration& cfg, list_info& info);
|
||||
virtual void define(configuration& cfg, macro_info& info);
|
||||
virtual void define(configuration& cfg, rule_info& info);
|
||||
|
||||
/*!
|
||||
\brief Appends an info block to an existing one. An exception
|
||||
is thrown if no existing definition can be matched with the appended
|
||||
one
|
||||
*/
|
||||
virtual void append(configuration& cfg, list_info& info);
|
||||
virtual void append(configuration& cfg, macro_info& info);
|
||||
virtual void append(configuration& cfg, rule_info& info);
|
||||
|
||||
/*!
|
||||
\brief Updates the 'enabled' flag of an existing definition
|
||||
*/
|
||||
virtual void enable(configuration& cfg, rule_info& info);
|
||||
|
||||
private:
|
||||
uint32_t m_cur_index;
|
||||
indexed_vector<rule_info> m_rule_infos;
|
||||
indexed_vector<macro_info> m_macro_infos;
|
||||
indexed_vector<list_info> m_list_infos;
|
||||
std::vector<plugin_version_info::requirement_alternatives> m_required_plugin_versions;
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
557
userspace/engine/rule_loader_compiler.cpp
Normal file
557
userspace/engine/rule_loader_compiler.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "rule_loader_compiler.h"
|
||||
#include "filter_macro_resolver.h"
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include "filter_warning_resolver.h"
|
||||
|
||||
#define MAX_VISIBILITY ((uint32_t) -1)
|
||||
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, (err), (ctx)); } }
|
||||
|
||||
static std::string s_container_info_fmt = "%container.info";
|
||||
static std::string s_default_extra_fmt = "%container.name (id=%container.id)";
|
||||
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
// todo(jasondellaluce): this breaks string escaping in lists and exceptions
|
||||
static void quote_item(std::string& e)
|
||||
{
|
||||
if (e.find(" ") != std::string::npos && e[0] != '"' && e[0] != '\'')
|
||||
{
|
||||
e = '"' + e + '"';
|
||||
}
|
||||
}
|
||||
|
||||
static void paren_item(std::string& e)
|
||||
{
|
||||
if(e[0] != '(')
|
||||
{
|
||||
e = '(' + e + ')';
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_operator_defined(const std::string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators();
|
||||
return find(ops.begin(), ops.end(), op) != ops.end();
|
||||
}
|
||||
|
||||
static inline bool is_operator_for_list(const std::string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators(true);
|
||||
return find(ops.begin(), ops.end(), op) != ops.end();
|
||||
}
|
||||
|
||||
static bool is_format_valid(const falco_source& source, std::string fmt, std::string& err)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::shared_ptr<gen_event_formatter> formatter;
|
||||
formatter = source.formatter_factory->create_formatter(fmt);
|
||||
return true;
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
err = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_rule_exception_infos(
|
||||
const std::vector<rule_loader::rule_exception_info>& exceptions,
|
||||
std::set<std::string>& exception_fields,
|
||||
std::string& condition)
|
||||
{
|
||||
std::string tmp;
|
||||
for (auto &ex : exceptions)
|
||||
{
|
||||
std::string icond;
|
||||
if(!ex.fields.is_list)
|
||||
{
|
||||
for (auto &val : ex.values)
|
||||
{
|
||||
THROW(val.is_list,
|
||||
"Expected values array to contain a list of strings",
|
||||
ex.ctx)
|
||||
icond += icond.empty()
|
||||
? ("(" + ex.fields.item + " "
|
||||
+ ex.comps.item + " (")
|
||||
: ", ";
|
||||
exception_fields.insert(ex.fields.item);
|
||||
tmp = val.item;
|
||||
quote_item(tmp);
|
||||
icond += tmp;
|
||||
}
|
||||
icond += icond.empty() ? "" : "))";
|
||||
}
|
||||
else
|
||||
{
|
||||
icond = "(";
|
||||
for (auto &values : ex.values)
|
||||
{
|
||||
THROW(ex.fields.items.size() != values.items.size(),
|
||||
"Fields and values lists must have equal length",
|
||||
ex.ctx);
|
||||
icond += icond == "(" ? "" : " or ";
|
||||
icond += "(";
|
||||
uint32_t k = 0;
|
||||
std::string istr;
|
||||
for (auto &field : ex.fields.items)
|
||||
{
|
||||
icond += k == 0 ? "" : " and ";
|
||||
if (values.items[k].is_list)
|
||||
{
|
||||
istr = "(";
|
||||
for (auto &v : values.items[k].items)
|
||||
{
|
||||
tmp = v.item;
|
||||
quote_item(tmp);
|
||||
istr += istr == "(" ? "" : ", ";
|
||||
istr += tmp;
|
||||
}
|
||||
istr += ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
istr = values.items[k].item;
|
||||
if(is_operator_for_list(ex.comps.items[k].item))
|
||||
{
|
||||
paren_item(istr);
|
||||
}
|
||||
else
|
||||
{
|
||||
quote_item(istr);
|
||||
}
|
||||
}
|
||||
icond += " " + field.item;
|
||||
icond += " " + ex.comps.items[k].item + " " + istr;
|
||||
exception_fields.insert(field.item);
|
||||
k++;
|
||||
}
|
||||
icond += ")";
|
||||
}
|
||||
icond += ")";
|
||||
if (icond == "()")
|
||||
{
|
||||
icond = "";
|
||||
}
|
||||
}
|
||||
condition += icond.empty() ? "" : " and not " + icond;
|
||||
}
|
||||
}
|
||||
|
||||
// todo(jasondellaluce): this breaks string escaping in lists
|
||||
static bool resolve_list(std::string& cnd, const rule_loader::list_info& list)
|
||||
{
|
||||
static std::string blanks = " \t\n\r";
|
||||
static std::string delims = blanks + "(),=";
|
||||
std::string new_cnd;
|
||||
size_t start, end;
|
||||
bool used = false;
|
||||
start = cnd.find(list.name);
|
||||
while (start != std::string::npos)
|
||||
{
|
||||
// the characters surrounding the name must
|
||||
// be delims of beginning/end of string
|
||||
end = start + list.name.length();
|
||||
if ((start == 0 || delims.find(cnd[start - 1]) != std::string::npos)
|
||||
&& (end >= cnd.length() || delims.find(cnd[end]) != std::string::npos))
|
||||
{
|
||||
// shift pointers to consume all whitespaces
|
||||
while (start > 0
|
||||
&& blanks.find(cnd[start - 1]) != std::string::npos)
|
||||
{
|
||||
start--;
|
||||
}
|
||||
while (end < cnd.length()
|
||||
&& blanks.find(cnd[end]) != std::string::npos)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
// create substitution string by concatenating all values
|
||||
std::string sub = "";
|
||||
for (auto &v : list.items)
|
||||
{
|
||||
if (!sub.empty())
|
||||
{
|
||||
sub += ", ";
|
||||
}
|
||||
sub += v;
|
||||
}
|
||||
// if substituted list is empty, we need to
|
||||
// remove a comma from the left or the right
|
||||
if (sub.empty())
|
||||
{
|
||||
if (start > 0 && cnd[start - 1] == ',')
|
||||
{
|
||||
start--;
|
||||
}
|
||||
else if (end < cnd.length() && cnd[end] == ',')
|
||||
{
|
||||
end++;
|
||||
}
|
||||
}
|
||||
// compose new string with substitution
|
||||
new_cnd = "";
|
||||
if (start > 0)
|
||||
{
|
||||
new_cnd += cnd.substr(0, start) + " ";
|
||||
}
|
||||
new_cnd += sub + " ";
|
||||
if (end <= cnd.length())
|
||||
{
|
||||
new_cnd += cnd.substr(end);
|
||||
}
|
||||
cnd = new_cnd;
|
||||
start += sub.length() + 1;
|
||||
used = true;
|
||||
}
|
||||
start = cnd.find(list.name, start + 1);
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
static void resolve_macros(
|
||||
indexed_vector<rule_loader::macro_info>& macros,
|
||||
std::shared_ptr<ast::expr>& ast,
|
||||
const std::string& condition,
|
||||
uint32_t visibility,
|
||||
const rule_loader::context &ctx)
|
||||
{
|
||||
filter_macro_resolver macro_resolver;
|
||||
for (auto &m : macros)
|
||||
{
|
||||
if (m.index < visibility)
|
||||
{
|
||||
macro_resolver.set_macro(m.name, m.cond_ast);
|
||||
}
|
||||
}
|
||||
macro_resolver.run(ast);
|
||||
|
||||
// Note: only complaining about the first unknown macro
|
||||
const filter_macro_resolver::macro_info_map& unresolved_macros = macro_resolver.get_unknown_macros();
|
||||
if(!unresolved_macros.empty())
|
||||
{
|
||||
auto it = unresolved_macros.begin();
|
||||
const rule_loader::context cond_ctx(it->second, condition, ctx);
|
||||
|
||||
THROW(true,
|
||||
std::string("Undefined macro '")
|
||||
+ it->first
|
||||
+ "' used in filter.",
|
||||
cond_ctx);
|
||||
}
|
||||
|
||||
for (auto &it : macro_resolver.get_resolved_macros())
|
||||
{
|
||||
macros.at(it.first)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is no visibility order between filter conditions and lists
|
||||
static std::shared_ptr<ast::expr> parse_condition(
|
||||
std::string condition,
|
||||
indexed_vector<rule_loader::list_info>& lists,
|
||||
const rule_loader::context &ctx)
|
||||
{
|
||||
for (auto &l : lists)
|
||||
{
|
||||
if (resolve_list(condition, l))
|
||||
{
|
||||
l.used = true;
|
||||
}
|
||||
}
|
||||
libsinsp::filter::parser p(condition);
|
||||
p.set_max_depth(1000);
|
||||
try
|
||||
{
|
||||
std::shared_ptr<ast::expr> res_ptr(p.parse());
|
||||
return res_ptr;
|
||||
}
|
||||
catch (const sinsp_exception& e)
|
||||
{
|
||||
rule_loader::context parsectx(p.get_pos(), condition, ctx);
|
||||
|
||||
throw rule_loader::rule_load_exception(
|
||||
falco::load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
parsectx);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_output_substitutions(
|
||||
rule_loader::configuration& cfg,
|
||||
std::string& out)
|
||||
{
|
||||
if (out.find(s_container_info_fmt) != std::string::npos)
|
||||
{
|
||||
if (cfg.replace_output_container_info)
|
||||
{
|
||||
out = replace(out, s_container_info_fmt, cfg.output_extra);
|
||||
return;
|
||||
}
|
||||
out = replace(out, s_container_info_fmt, s_default_extra_fmt);
|
||||
}
|
||||
out += cfg.output_extra.empty() ? "" : " " + cfg.output_extra;
|
||||
}
|
||||
|
||||
void rule_loader::compiler::compile_list_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& out) const
|
||||
{
|
||||
std::string tmp;
|
||||
std::vector<std::string> used;
|
||||
for (auto &list : col.lists())
|
||||
{
|
||||
list_info v = list;
|
||||
v.items.clear();
|
||||
for (auto &item : list.items)
|
||||
{
|
||||
const auto ref = col.lists().at(item);
|
||||
if (ref && ref->index < list.visibility)
|
||||
{
|
||||
used.push_back(ref->name);
|
||||
for (auto val : ref->items)
|
||||
{
|
||||
quote_item(val);
|
||||
v.items.push_back(val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = item;
|
||||
quote_item(tmp);
|
||||
v.items.push_back(tmp);
|
||||
}
|
||||
}
|
||||
v.used = false;
|
||||
out.insert(v, v.name);
|
||||
}
|
||||
for (auto &v : used)
|
||||
{
|
||||
out.at(v)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is a visibility ordering between macros
|
||||
void rule_loader::compiler::compile_macros_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const
|
||||
{
|
||||
for (auto &m : col.macros())
|
||||
{
|
||||
macro_info entry = m;
|
||||
entry.cond_ast = parse_condition(m.cond, lists, m.cond_ctx);
|
||||
entry.used = false;
|
||||
out.insert(entry, m.name);
|
||||
}
|
||||
|
||||
for (auto &m : out)
|
||||
{
|
||||
resolve_macros(out, m.cond_ast, m.cond, m.visibility, m.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rule_loader::compiler::compile_rule_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
std::string err, condition;
|
||||
std::set<falco::load_result::load_result::warning_code> warn_codes;
|
||||
filter_warning_resolver warn_resolver;
|
||||
for (auto &r : col.rules())
|
||||
{
|
||||
// skip the rule if below the minimum priority
|
||||
if (r.priority > cfg.min_priority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto source = cfg.sources.at(r.source);
|
||||
// note: this is not supposed to happen
|
||||
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + r.source,
|
||||
r.ctx);
|
||||
|
||||
// build filter AST by parsing the condition, building exceptions,
|
||||
// and resolving lists and macros
|
||||
falco_rule rule;
|
||||
|
||||
condition = r.cond;
|
||||
if (!r.exceptions.empty())
|
||||
{
|
||||
build_rule_exception_infos(
|
||||
r.exceptions, rule.exception_fields, condition);
|
||||
}
|
||||
auto ast = parse_condition(condition, lists, r.cond_ctx);
|
||||
resolve_macros(macros, ast, condition, MAX_VISIBILITY, r.ctx);
|
||||
|
||||
// check for warnings in the filtering condition
|
||||
warn_codes.clear();
|
||||
if (warn_resolver.run(ast.get(), warn_codes))
|
||||
{
|
||||
for (auto &w : warn_codes)
|
||||
{
|
||||
cfg.res->add_warning(w, "", r.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// build rule output message
|
||||
rule.output = r.output;
|
||||
if (r.source == falco_common::syscall_source)
|
||||
{
|
||||
apply_output_substitutions(cfg, rule.output);
|
||||
}
|
||||
|
||||
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
|
||||
{
|
||||
throw rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_OUTPUT,
|
||||
err,
|
||||
r.output_ctx);
|
||||
}
|
||||
|
||||
// construct rule definition and compile it to a filter
|
||||
rule.name = r.name;
|
||||
rule.source = r.source;
|
||||
rule.description = r.desc;
|
||||
rule.priority = r.priority;
|
||||
rule.tags = r.tags;
|
||||
|
||||
auto rule_id = out.insert(rule, rule.name);
|
||||
out.at(rule_id)->id = rule_id;
|
||||
|
||||
// This also compiles the filter, and might throw a
|
||||
// falco_exception with details on the compilation
|
||||
// failure.
|
||||
sinsp_filter_compiler compiler(cfg.sources.at(r.source)->filter_factory, ast.get());
|
||||
try {
|
||||
shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
source->ruleset->add(*out.at(rule_id), filter, ast);
|
||||
}
|
||||
catch (const sinsp_exception& e)
|
||||
{
|
||||
// Allow errors containing "nonexistent field" if
|
||||
// skip_if_unknown_filter is true
|
||||
std::string err = e.what();
|
||||
|
||||
if (err.find("nonexistent field") != std::string::npos &&
|
||||
r.skip_if_unknown_filter)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNKNOWN_FIELD,
|
||||
e.what(),
|
||||
r.cond_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
rule_loader::context ctx(compiler.get_pos(),
|
||||
condition,
|
||||
r.cond_ctx);
|
||||
|
||||
throw rule_loader::rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// By default rules are enabled/disabled for the default ruleset
|
||||
if(r.enabled)
|
||||
{
|
||||
source->ruleset->enable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
|
||||
// populate set of event types and emit an special warning
|
||||
std::set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||
if(rule.source == falco_common::syscall_source)
|
||||
{
|
||||
evttypes.clear();
|
||||
filter_evttype_resolver().evttypes(ast, evttypes);
|
||||
if ((evttypes.empty() || evttypes.size() > 100)
|
||||
&& r.warn_evttypes)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_NO_EVTTYPE,
|
||||
"Rule matches too many evt.type values. This has a significant performance penalty.",
|
||||
r.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_loader::compiler::compile(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
indexed_vector<list_info> lists;
|
||||
indexed_vector<macro_info> macros;
|
||||
|
||||
// expand all lists, macros, and rules
|
||||
try
|
||||
{
|
||||
compile_list_infos(cfg, col, lists);
|
||||
compile_macros_infos(cfg, col, lists, macros);
|
||||
compile_rule_infos(cfg, col, lists, macros, out);
|
||||
}
|
||||
catch(rule_load_exception &e)
|
||||
{
|
||||
cfg.res->add_error(e.ec, e.msg, e.ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// print info on any dangling lists or macros that were not used anywhere
|
||||
for (auto &m : macros)
|
||||
{
|
||||
if (!m.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNUSED_MACRO,
|
||||
"Macro not referred to by any other rule/macro",
|
||||
m.ctx);
|
||||
}
|
||||
}
|
||||
for (auto &l : lists)
|
||||
{
|
||||
if (!l.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::LOAD_UNUSED_LIST,
|
||||
"List not referred to by any other rule/macro",
|
||||
l.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
userspace/engine/rule_loader_compiler.h
Normal file
69
userspace/engine/rule_loader_compiler.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.h"
|
||||
#include "indexed_vector.h"
|
||||
#include "falco_rule.h"
|
||||
|
||||
namespace rule_loader
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Compiler for the ruleset loader of the falco engine
|
||||
*/
|
||||
class compiler
|
||||
{
|
||||
public:
|
||||
compiler() = default;
|
||||
virtual ~compiler() = default;
|
||||
compiler(compiler&&) = default;
|
||||
compiler& operator = (compiler&&) = default;
|
||||
compiler(const compiler&) = default;
|
||||
compiler& operator = (const compiler&) = default;
|
||||
|
||||
/*!
|
||||
\brief Compiles a list of falco rules
|
||||
*/
|
||||
virtual void compile(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
|
||||
private:
|
||||
void compile_list_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& out) const;
|
||||
|
||||
void compile_macros_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const;
|
||||
|
||||
void compile_rule_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
|
||||
487
userspace/engine/rule_loader_reader.cpp
Normal file
487
userspace/engine/rule_loader_reader.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rule_loader_reader.h"
|
||||
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_YAML_VALIDATE, (err), (ctx)); } }
|
||||
|
||||
|
||||
// Don't call this directly, call decode_val/decode_optional_val instead.
|
||||
template <typename T>
|
||||
static void decode_val_generic(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx, bool optional)
|
||||
{
|
||||
const YAML::Node& val = item[key];
|
||||
|
||||
if(!val.IsDefined() && optional)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
THROW(val.IsNull(), std::string("Mapping for key '") + key + "' is empty", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
|
||||
THROW(!val.IsScalar(), "Value is not a scalar value", valctx);
|
||||
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
|
||||
|
||||
THROW(!YAML::convert<T>::decode(val, out), "Can't decode YAML scalar value", valctx);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_val(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = false;
|
||||
|
||||
decode_val_generic(item, key, out, ctx, optional);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_optional_val(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = true;
|
||||
|
||||
decode_val_generic(item, key, out, ctx, optional);
|
||||
}
|
||||
|
||||
// Don't call this directly, call decode_items/decode_tags instead.
|
||||
template <typename T>
|
||||
static void decode_seq(const YAML::Node& item, const char *key,
|
||||
std::function<void(T)> inserter,
|
||||
const rule_loader::context &ctx, bool optional)
|
||||
{
|
||||
const YAML::Node& val = item[key];
|
||||
|
||||
if(!val.IsDefined() && optional)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
|
||||
THROW(!val.IsSequence(), "Value is not a sequence", valctx);
|
||||
|
||||
T value;
|
||||
for(const YAML::Node& v : val)
|
||||
{
|
||||
rule_loader::context ictx(v, rule_loader::context::LIST_ITEM, "", valctx);
|
||||
THROW(!v.IsScalar(), "sequence value is not scalar", ictx);
|
||||
THROW(!YAML::convert<T>::decode(v, value), "Can't decode YAML sequence value", ictx);
|
||||
inserter(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_items(const YAML::Node& item, vector<T>& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = false;
|
||||
|
||||
std::function<void(T)> inserter = [&out] (T value) {
|
||||
out.push_back(value);
|
||||
};
|
||||
|
||||
decode_seq(item, "items", inserter, ctx, optional);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_tags(const YAML::Node& item, set<T>& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = true;
|
||||
|
||||
std::function<void(T)> inserter = [&out] (T value) {
|
||||
out.insert(value);
|
||||
};
|
||||
|
||||
decode_seq(item, "tags", inserter, ctx, optional);
|
||||
}
|
||||
|
||||
// Don't call this directly, call decode_exception_{fields,comps,values} instead
|
||||
static void decode_exception_info_entry(
|
||||
const YAML::Node& item,
|
||||
const char *key,
|
||||
rule_loader::rule_exception_info::entry& out,
|
||||
const rule_loader::context& ctx,
|
||||
bool optional)
|
||||
{
|
||||
const YAML::Node& val = (key == NULL ? item : item[key]);
|
||||
|
||||
if(!val.IsDefined() && optional)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, (key == NULL ? "" : key), ctx);
|
||||
|
||||
if (val.IsScalar())
|
||||
{
|
||||
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
|
||||
out.is_list = false;
|
||||
THROW(!YAML::convert<string>::decode(val, out.item), "Could not decode scalar value", valctx);
|
||||
}
|
||||
if (val.IsSequence())
|
||||
{
|
||||
out.is_list = true;
|
||||
for(const YAML::Node& v : val)
|
||||
{
|
||||
rule_loader::rule_exception_info::entry tmp;
|
||||
rule_loader::context lctx(v, rule_loader::context::EXCEPTION, "", valctx);
|
||||
|
||||
// Optional is always false once you get past the outer values
|
||||
optional = false;
|
||||
decode_exception_info_entry(v, NULL, tmp, lctx, optional);
|
||||
out.items.push_back(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_exception_fields(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_exception_info::entry& out,
|
||||
const rule_loader::context& ctx,
|
||||
bool optional)
|
||||
{
|
||||
decode_exception_info_entry(item, "fields", out, ctx, optional);
|
||||
}
|
||||
|
||||
static void decode_exception_comps(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_exception_info::entry& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = true;
|
||||
|
||||
decode_exception_info_entry(item, "comps", out, ctx, optional);
|
||||
}
|
||||
|
||||
static void decode_exception_values(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_exception_info::entry& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = false;
|
||||
|
||||
decode_exception_info_entry(item, NULL, out, ctx, optional);
|
||||
}
|
||||
|
||||
static void read_rule_exceptions(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_info& v,
|
||||
const rule_loader::context& parent,
|
||||
bool append)
|
||||
{
|
||||
const YAML::Node& exs = item["exceptions"];
|
||||
|
||||
// No exceptions property, or an exceptions property with
|
||||
// nothing in it, are allowed
|
||||
if(!exs.IsDefined() || exs.IsNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rule_loader::context exes_ctx(exs, rule_loader::context::EXCEPTIONS, "", parent);
|
||||
|
||||
THROW(!exs.IsSequence(), "Rule exceptions must be a sequence", exes_ctx);
|
||||
|
||||
for (auto &ex : exs)
|
||||
{
|
||||
// Make a temp context to verify simple properties
|
||||
// about the exception.
|
||||
std::string name;
|
||||
rule_loader::context tmp(ex, rule_loader::context::EXCEPTION, "", exes_ctx);
|
||||
|
||||
THROW(!ex.IsMap(), "Rule exception must be a mapping", tmp);
|
||||
decode_val(ex, "name", name, tmp);
|
||||
|
||||
// Now use a real context including the exception name.
|
||||
rule_loader::context ex_ctx(ex, rule_loader::context::EXCEPTION, name, parent);
|
||||
rule_loader::rule_exception_info v_ex(ex_ctx);
|
||||
v_ex.name = name;
|
||||
|
||||
// note: the legacy lua loader used to throw a "xxx must strings" error
|
||||
|
||||
// fields are optional when append is true
|
||||
decode_exception_fields(ex, v_ex.fields, ex_ctx, append);
|
||||
decode_exception_comps(ex, v_ex.comps, ex_ctx);
|
||||
const YAML::Node& exvals = ex["values"];
|
||||
if (exvals.IsDefined())
|
||||
{
|
||||
rule_loader::context vals_ctx(exvals, rule_loader::context::EXCEPTION_VALUES, "", ex_ctx);
|
||||
THROW(!exvals.IsSequence(),
|
||||
"Rule exception values must be a sequence", vals_ctx);
|
||||
for (auto &val : exvals)
|
||||
{
|
||||
rule_loader::context vctx(val, rule_loader::context::EXCEPTION_VALUE, "", vals_ctx);
|
||||
rule_loader::rule_exception_info::entry v_ex_val;
|
||||
|
||||
decode_exception_values(val, v_ex_val, vctx);
|
||||
v_ex.values.push_back(v_ex_val);
|
||||
}
|
||||
}
|
||||
v.exceptions.push_back(v_ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_item(
|
||||
rule_loader::configuration& cfg,
|
||||
rule_loader::collector& collector,
|
||||
const YAML::Node& item,
|
||||
const rule_loader::context& parent)
|
||||
{
|
||||
rule_loader::context tmp(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
|
||||
THROW(!item.IsMap(), "Unexpected element type. "
|
||||
"Each element should be a yaml associative array.", tmp);
|
||||
|
||||
if (item["required_engine_version"].IsDefined())
|
||||
{
|
||||
rule_loader::context ctx(item, rule_loader::context::REQUIRED_ENGINE_VERSION, "", parent);
|
||||
rule_loader::engine_version_info v(ctx);
|
||||
|
||||
decode_val(item, "required_engine_version", v.version, ctx);
|
||||
collector.define(cfg, v);
|
||||
}
|
||||
else if(item["required_plugin_versions"].IsDefined())
|
||||
{
|
||||
const YAML::Node& req_plugin_vers = item["required_plugin_versions"];
|
||||
rule_loader::context ctx(req_plugin_vers, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent);
|
||||
|
||||
THROW(!req_plugin_vers.IsSequence(),
|
||||
"Value of required_plugin_versions must be a sequence",
|
||||
ctx);
|
||||
|
||||
for(const YAML::Node& plugin : req_plugin_vers)
|
||||
{
|
||||
rule_loader::plugin_version_info::requirement r;
|
||||
|
||||
// Use a temp context until we can get a name
|
||||
rule_loader::context tmp(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, "", ctx);
|
||||
THROW(!plugin.IsMap(), "Plugin version must be a mapping", tmp);
|
||||
decode_val(plugin, "name", r.name, tmp);
|
||||
rule_loader::context pctx(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, r.name, ctx);
|
||||
rule_loader::plugin_version_info v(pctx);
|
||||
decode_val(plugin, "version", r.version, pctx);
|
||||
v.alternatives.push_back(r);
|
||||
|
||||
const YAML::Node& alternatives = plugin["alternatives"];
|
||||
if(alternatives.IsDefined())
|
||||
{
|
||||
THROW(!alternatives.IsSequence(),
|
||||
"Value of plugin version alternatives must be a sequence",
|
||||
pctx);
|
||||
for (const auto &req : alternatives)
|
||||
{
|
||||
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, "", pctx);
|
||||
THROW(!req.IsMap(), "Plugin version alternative must be a mapping", tmp);
|
||||
decode_val(req, "name", r.name, tmp);
|
||||
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, r.name, pctx);
|
||||
decode_val(req, "version", r.version, tmp);
|
||||
v.alternatives.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
collector.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["list"].IsDefined())
|
||||
{
|
||||
std::string name;
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::LIST, "", parent);
|
||||
decode_val(item, "list", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::LIST, name, parent);
|
||||
rule_loader::list_info v(ctx);
|
||||
|
||||
bool append = false;
|
||||
decode_val(item, "list", v.name, ctx);
|
||||
decode_items(item, v.items, ctx);
|
||||
|
||||
decode_optional_val(item, "append", append, ctx);
|
||||
|
||||
if(append)
|
||||
{
|
||||
collector.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["macro"].IsDefined())
|
||||
{
|
||||
std::string name;
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::MACRO, "", parent);
|
||||
decode_val(item, "macro", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::MACRO, name, parent);
|
||||
rule_loader::macro_info v(ctx);
|
||||
v.name = name;
|
||||
|
||||
bool append = false;
|
||||
decode_val(item, "condition", v.cond, ctx);
|
||||
|
||||
// Now set the proper context for the condition now that we know it exists
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::MACRO_CONDITION, "", ctx);
|
||||
|
||||
decode_optional_val(item, "append", append, ctx);
|
||||
|
||||
if(append)
|
||||
{
|
||||
collector.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["rule"].IsDefined())
|
||||
{
|
||||
std::string name;
|
||||
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::RULE, "", parent);
|
||||
decode_val(item, "rule", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::RULE, name, parent);
|
||||
rule_loader::rule_info v(ctx);
|
||||
v.name = name;
|
||||
|
||||
bool append = false;
|
||||
v.enabled = true;
|
||||
v.warn_evttypes = true;
|
||||
v.skip_if_unknown_filter = false;
|
||||
|
||||
decode_optional_val(item, "append", append, ctx);
|
||||
|
||||
if(append)
|
||||
{
|
||||
decode_optional_val(item, "condition", v.cond, ctx);
|
||||
if(item["condition"].IsDefined())
|
||||
{
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
|
||||
}
|
||||
read_rule_exceptions(item, v, ctx, append);
|
||||
collector.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the rule does *not* have any of
|
||||
// condition/output/desc/priority, it *must*
|
||||
// have an enabled property. Use the enabled
|
||||
// property to set the enabled status of an
|
||||
// earlier rule.
|
||||
if (!item["condition"].IsDefined() &&
|
||||
!item["output"].IsDefined() &&
|
||||
!item["desc"].IsDefined() &&
|
||||
!item["priority"].IsDefined())
|
||||
{
|
||||
decode_val(item, "enabled", v.enabled, ctx);
|
||||
collector.enable(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
string priority;
|
||||
|
||||
// All of these are required
|
||||
decode_val(item, "condition", v.cond, ctx);
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
|
||||
|
||||
decode_val(item, "output", v.output, ctx);
|
||||
v.output_ctx = rule_loader::context(item["output"], rule_loader::context::RULE_OUTPUT, "", ctx);
|
||||
|
||||
decode_val(item, "desc", v.desc, ctx);
|
||||
decode_val(item, "priority", priority, ctx);
|
||||
|
||||
v.output = trim(v.output);
|
||||
v.source = falco_common::syscall_source;
|
||||
rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx);
|
||||
THROW(!falco_common::parse_priority(priority, v.priority),
|
||||
"Invalid priority", prictx);
|
||||
decode_optional_val(item, "source", v.source, ctx);
|
||||
decode_optional_val(item, "enabled", v.enabled, ctx);
|
||||
decode_optional_val(item, "warn_evttypes", v.warn_evttypes, ctx);
|
||||
decode_optional_val(item, "skip-if-unknown-filter", v.skip_if_unknown_filter, ctx);
|
||||
decode_tags(item, v.tags, ctx);
|
||||
read_rule_exceptions(item, v, ctx, append);
|
||||
collector.define(cfg, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rule_loader::context ctx(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
|
||||
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector)
|
||||
{
|
||||
std::vector<YAML::Node> docs;
|
||||
try
|
||||
{
|
||||
docs = YAML::LoadAll(cfg.content);
|
||||
}
|
||||
catch(const exception& e)
|
||||
{
|
||||
rule_loader::context ctx(cfg.name);
|
||||
cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, e.what(), ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto doc = docs.begin(); doc != docs.end(); doc++)
|
||||
{
|
||||
if (doc->IsDefined() && !doc->IsNull())
|
||||
{
|
||||
rule_loader::context ctx(cfg.name);
|
||||
|
||||
try {
|
||||
THROW(!doc->IsMap() && !doc->IsSequence(),
|
||||
"Rules content is not yaml",
|
||||
ctx);
|
||||
|
||||
THROW(!doc->IsSequence(),
|
||||
"Rules content is not yaml array of objects",
|
||||
ctx);
|
||||
|
||||
for (auto it = doc->begin(); it != doc->end(); it++)
|
||||
{
|
||||
if (!it->IsNull())
|
||||
{
|
||||
read_item(cfg, collector, *it, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (rule_loader::rule_load_exception &e)
|
||||
{
|
||||
cfg.res->add_error(e.ec, e.msg, e.ctx);
|
||||
|
||||
// Although we *could* continue on to the next doc,
|
||||
// as it's effectively a new rules file, for
|
||||
// consistency we stop at the first error.
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -16,22 +16,30 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.h"
|
||||
|
||||
namespace rule_loader
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Reads the contents of a ruleset
|
||||
*/
|
||||
class rule_reader
|
||||
class reader
|
||||
{
|
||||
public:
|
||||
virtual ~rule_reader() = default;
|
||||
reader() = default;
|
||||
virtual ~reader() = default;
|
||||
reader(reader&&) = default;
|
||||
reader& operator = (reader&&) = default;
|
||||
reader(const reader&) = default;
|
||||
reader& operator = (const reader&) = default;
|
||||
|
||||
/*!
|
||||
\brief Reads the contents of a ruleset and uses a loader to store
|
||||
\brief Reads the contents of a ruleset and uses a collector to store
|
||||
thew new definitions
|
||||
*/
|
||||
virtual bool load(rule_loader::configuration& cfg, rule_loader& loader);
|
||||
virtual bool read(configuration& cfg, collector& loader);
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
@@ -1,329 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 "rule_reader.h"
|
||||
|
||||
#define THROW(cond, err) { if (cond) { throw falco_exception(err); } }
|
||||
|
||||
static rule_loader::context yaml_get_context(
|
||||
const string& content,
|
||||
const vector<YAML::Node>& docs,
|
||||
vector<YAML::Node>::iterator doc,
|
||||
YAML::iterator node)
|
||||
{
|
||||
rule_loader::context m;
|
||||
YAML::Node item = *node++;
|
||||
YAML::Node cur_doc = *doc++;
|
||||
// include the "- " sequence mark
|
||||
size_t from = item.Mark().pos - 2;
|
||||
size_t to = 0;
|
||||
if (node != cur_doc.end())
|
||||
{
|
||||
// end of item is beginning of next item
|
||||
to = node->Mark().pos - 2;
|
||||
}
|
||||
else if (doc != docs.end())
|
||||
{
|
||||
// end of item is beginning of next doc
|
||||
to = doc->Mark().pos - 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// end of item is end of file contents
|
||||
to = content.length();
|
||||
}
|
||||
m.content = content.substr(from, to - from);
|
||||
m.content = trim(m.content);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool decode_val(const YAML::Node& v, T& out)
|
||||
{
|
||||
return v.IsDefined() && v.IsScalar() && YAML::convert<T>::decode(v, out);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool decode_seq(const YAML::Node& item, vector<T>& out)
|
||||
{
|
||||
if (item.IsDefined() && item.IsSequence())
|
||||
{
|
||||
T value;
|
||||
for(const YAML::Node& v : item)
|
||||
{
|
||||
THROW(!v.IsScalar() || !YAML::convert<T>::decode(v, value),
|
||||
"Can't decode YAML sequence value: " + YAML::Dump(v));
|
||||
out.push_back(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool decode_seq(const YAML::Node& item, set<T>& out)
|
||||
{
|
||||
if (item.IsDefined() && item.IsSequence())
|
||||
{
|
||||
T value;
|
||||
for(const YAML::Node& v : item)
|
||||
{
|
||||
THROW(!v.IsScalar() || !YAML::convert<T>::decode(v, value),
|
||||
"Can't decode YAML sequence value: " + YAML::Dump(v));
|
||||
out.insert(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool decode_exception_info_entry(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_exception_info::entry& out)
|
||||
{
|
||||
if (item.IsDefined())
|
||||
{
|
||||
if (item.IsScalar())
|
||||
{
|
||||
out.is_list = false;
|
||||
if (YAML::convert<string>::decode(item, out.item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (item.IsSequence())
|
||||
{
|
||||
out.is_list = true;
|
||||
rule_loader::rule_exception_info::entry tmp;
|
||||
for(const YAML::Node& v : item)
|
||||
{
|
||||
if (!decode_exception_info_entry(v, tmp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
out.items.push_back(tmp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void read_rule_exceptions(
|
||||
const YAML::Node& item,
|
||||
rule_loader::rule_info& v)
|
||||
{
|
||||
// An exceptions property with nothing in it is allowed
|
||||
if(item.IsNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
THROW(!item.IsSequence(), "Rule exceptions must be a sequence");
|
||||
for (auto &ex : item)
|
||||
{
|
||||
rule_loader::rule_exception_info v_ex;
|
||||
THROW(!decode_val(ex["name"], v_ex.name) || v_ex.name.empty(),
|
||||
"Rule exception item must have name property");
|
||||
// note: the legacy lua loader used to throw a "xxx must strings" error
|
||||
decode_exception_info_entry(ex["fields"], v_ex.fields);
|
||||
decode_exception_info_entry(ex["comps"], v_ex.comps);
|
||||
if (ex["values"].IsDefined())
|
||||
{
|
||||
THROW(!ex["values"].IsSequence(),
|
||||
"Rule exception values must be a sequence");
|
||||
for (auto &val : ex["values"])
|
||||
{
|
||||
rule_loader::rule_exception_info::entry v_ex_val;
|
||||
decode_exception_info_entry(val, v_ex_val);
|
||||
v_ex.values.push_back(v_ex_val);
|
||||
}
|
||||
}
|
||||
v.exceptions.push_back(v_ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_item(
|
||||
rule_loader::configuration& cfg,
|
||||
rule_loader& loader,
|
||||
const YAML::Node& item,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
if (item["required_engine_version"].IsDefined())
|
||||
{
|
||||
rule_loader::engine_version_info v;
|
||||
THROW(!decode_val(item["required_engine_version"], v.version),
|
||||
"Value of required_engine_version must be a number");
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
else if(item["required_plugin_versions"].IsDefined())
|
||||
{
|
||||
THROW(!item["required_plugin_versions"].IsSequence(),
|
||||
"Value of required_plugin_versions must be a sequence");
|
||||
|
||||
for(const YAML::Node& plugin : item["required_plugin_versions"])
|
||||
{
|
||||
rule_loader::plugin_version_info v;
|
||||
THROW(!decode_val(plugin["name"], v.name) || v.name.empty(),
|
||||
"required_plugin_versions item must have name property");
|
||||
THROW(!decode_val(plugin["version"], v.version) || v.version.empty(),
|
||||
"required_plugin_versions item must have version property");
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["list"].IsDefined())
|
||||
{
|
||||
rule_loader::list_info v;
|
||||
v.ctx = ctx;
|
||||
bool append = false;
|
||||
THROW(!decode_val(item["list"], v.name) || v.name.empty(),
|
||||
"List name is empty");
|
||||
THROW(!decode_seq(item["items"], v.items),
|
||||
"List must have property items");
|
||||
if(decode_val(item["append"], append) && append)
|
||||
{
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["macro"].IsDefined())
|
||||
{
|
||||
rule_loader::macro_info v;
|
||||
v.ctx = ctx;
|
||||
bool append = false;
|
||||
THROW(!decode_val(item["macro"], v.name) || v.name.empty(),
|
||||
"Macro name is empty");
|
||||
THROW(!decode_val(item["condition"], v.cond) || v.cond.empty(),
|
||||
"Macro must have property condition");
|
||||
if(decode_val(item["append"], append) && append)
|
||||
{
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["rule"].IsDefined())
|
||||
{
|
||||
rule_loader::rule_info v;
|
||||
v.ctx = ctx;
|
||||
bool append = false;
|
||||
v.enabled = true;
|
||||
v.warn_evttypes = true;
|
||||
v.skip_if_unknown_filter = false;
|
||||
THROW(!decode_val(item["rule"], v.name) || v.name.empty(),
|
||||
"Rule name is empty");
|
||||
if(decode_val(item["append"], append) && append)
|
||||
{
|
||||
decode_val(item["condition"], v.cond);
|
||||
if (item["exceptions"].IsDefined())
|
||||
{
|
||||
read_rule_exceptions(item["exceptions"], v);
|
||||
}
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
string priority;
|
||||
bool has_enabled = decode_val(item["enabled"], v.enabled);
|
||||
bool has_defs = decode_val(item["condition"], v.cond)
|
||||
&& decode_val(item["output"], v.output)
|
||||
&& decode_val(item["desc"], v.desc)
|
||||
&& decode_val(item["priority"], priority);
|
||||
if (!has_defs)
|
||||
{
|
||||
THROW(!has_enabled, "Rule must have properties 'condition', 'output', 'desc', and 'priority'");
|
||||
loader.enable(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
v.output = trim(v.output);
|
||||
v.source = falco_common::syscall_source;
|
||||
THROW(!falco_common::parse_priority(priority, v.priority),
|
||||
"Invalid priority");
|
||||
decode_val(item["source"], v.source);
|
||||
decode_val(item["warn_evttypes"], v.warn_evttypes);
|
||||
decode_val(item["skip-if-unknown-filter"], v.skip_if_unknown_filter);
|
||||
decode_seq(item["tags"], v.tags);
|
||||
if (item["exceptions"].IsDefined())
|
||||
{
|
||||
read_rule_exceptions(item["exceptions"], v);
|
||||
}
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg.warnings.push_back("Unknown top level object");
|
||||
}
|
||||
}
|
||||
|
||||
bool rule_reader::load(rule_loader::configuration& cfg, rule_loader& loader)
|
||||
{
|
||||
std::vector<YAML::Node> docs;
|
||||
try
|
||||
{
|
||||
docs = YAML::LoadAll(cfg.content);
|
||||
}
|
||||
catch(const exception& e)
|
||||
{
|
||||
cfg.errors.push_back("Could not load YAML file: " + string(e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto doc = docs.begin(); doc != docs.end(); doc++)
|
||||
{
|
||||
if (doc->IsDefined() && !doc->IsNull())
|
||||
{
|
||||
if(!doc->IsMap() && !doc->IsSequence())
|
||||
{
|
||||
cfg.errors.push_back("Rules content is not yaml");
|
||||
return false;
|
||||
}
|
||||
if(!doc->IsSequence())
|
||||
{
|
||||
cfg.errors.push_back(
|
||||
"Rules content is not yaml array of objects");
|
||||
return false;
|
||||
}
|
||||
for (auto it = doc->begin(); it != doc->end(); it++)
|
||||
{
|
||||
if (!it->IsNull())
|
||||
{
|
||||
auto ctx = yaml_get_context(cfg.content, docs, doc, it);
|
||||
YAML::Node item = *it;
|
||||
try
|
||||
{
|
||||
THROW(!item.IsMap(), "Unexpected element type. "
|
||||
"Each element should be a yaml associative array.");
|
||||
read_item(cfg, loader, item, ctx);
|
||||
}
|
||||
catch(const exception& e)
|
||||
{
|
||||
cfg.errors.push_back(ctx.error(e.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -45,37 +45,48 @@ void stats_manager::format(
|
||||
out += "Rule counts by severity:\n";
|
||||
for (size_t i = 0; i < m_by_priority.size(); i++)
|
||||
{
|
||||
if (m_by_priority[i] > 0)
|
||||
auto val = m_by_priority[i].get()->load();
|
||||
if (val > 0)
|
||||
{
|
||||
falco_common::format_priority(
|
||||
(falco_common::priority_type) i, fmt, true);
|
||||
transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
|
||||
out += " " + fmt;
|
||||
out += ": " + to_string(m_by_priority[i]) + "\n";
|
||||
out += " " + fmt + ": " + to_string(val) + "\n";
|
||||
}
|
||||
}
|
||||
out += "Triggered rules by rule name:\n";
|
||||
for (size_t i = 0; i < m_by_rule_id.size(); i++)
|
||||
{
|
||||
if (m_by_rule_id[i] > 0)
|
||||
auto val = m_by_rule_id[i].get()->load();
|
||||
if (val > 0)
|
||||
{
|
||||
out += " " + rules.at(i)->name;
|
||||
out += ": " + to_string(m_by_rule_id[i]) + "\n";
|
||||
out += " " + rules.at(i)->name + ": " + to_string(val) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stats_manager::on_rule_loaded(const falco_rule& rule)
|
||||
{
|
||||
while (m_by_rule_id.size() <= rule.id)
|
||||
{
|
||||
m_by_rule_id.emplace_back();
|
||||
m_by_rule_id[m_by_rule_id.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
while (m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_priority.emplace_back();
|
||||
m_by_priority[m_by_priority.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
}
|
||||
|
||||
void stats_manager::on_event(const falco_rule& rule)
|
||||
{
|
||||
if (m_by_rule_id.size() <= rule.id)
|
||||
if (m_by_rule_id.size() <= rule.id
|
||||
|| m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_rule_id.resize(rule.id + 1, (uint64_t) 0);
|
||||
throw falco_exception("rule id or priority out of bounds");
|
||||
}
|
||||
if (m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_priority.resize((size_t) rule.priority + 1, (uint64_t) 0);
|
||||
}
|
||||
m_total++;
|
||||
m_by_rule_id[rule.id]++;
|
||||
m_by_priority[(size_t) rule.priority]++;
|
||||
m_total.fetch_add(1, std::memory_order_relaxed);
|
||||
m_by_rule_id[rule.id]->fetch_add(1, std::memory_order_relaxed);
|
||||
m_by_priority[(size_t) rule.priority]->fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
@@ -18,17 +18,26 @@ limitations under the License.
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include "falco_rule.h"
|
||||
#include "indexed_vector.h"
|
||||
|
||||
/*!
|
||||
\brief Manager for the internal statistics of the rule engine
|
||||
\brief Manager for the internal statistics of the rule engine.
|
||||
The on_event() is thread-safe and non-blocking, and it can be used
|
||||
concurrently across many callers in parallel.
|
||||
All the other methods are not thread safe.
|
||||
*/
|
||||
class stats_manager
|
||||
{
|
||||
public:
|
||||
stats_manager();
|
||||
virtual ~stats_manager();
|
||||
stats_manager(stats_manager&&) = default;
|
||||
stats_manager& operator = (stats_manager&&) = default;
|
||||
stats_manager(const stats_manager&) = default;
|
||||
stats_manager& operator = (const stats_manager&) = default;
|
||||
|
||||
/*!
|
||||
\brief Erases the internal state and statistics data
|
||||
@@ -36,19 +45,29 @@ public:
|
||||
virtual void clear();
|
||||
|
||||
/*!
|
||||
\brief Callback for when a given rule matches an event
|
||||
\brief Callback for when a new rule is loaded in the engine.
|
||||
Rules must be passed through this method before submitting them as
|
||||
an argument of on_event().
|
||||
*/
|
||||
virtual void on_rule_loaded(const falco_rule& rule);
|
||||
|
||||
/*!
|
||||
\brief Callback for when a given rule matches an event.
|
||||
This method is thread-safe.
|
||||
\throws falco_exception if rule has not been passed to
|
||||
on_rule_loaded() first
|
||||
*/
|
||||
virtual void on_event(const falco_rule& rule);
|
||||
|
||||
/*!
|
||||
\brief Formats the internal statistics into the out string
|
||||
\brief Formats the internal statistics into the out string.
|
||||
*/
|
||||
virtual void format(
|
||||
const indexed_vector<falco_rule>& rules,
|
||||
std::string& out) const;
|
||||
|
||||
private:
|
||||
uint64_t m_total;
|
||||
std::vector<uint64_t> m_by_priority;
|
||||
std::vector<uint64_t> m_by_rule_id;
|
||||
atomic<uint64_t> m_total;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_priority;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_rule_id;
|
||||
};
|
||||
@@ -20,7 +20,7 @@ set(
|
||||
app_actions/create_signal_handlers.cpp
|
||||
app_actions/daemonize.cpp
|
||||
app_actions/init_falco_engine.cpp
|
||||
app_actions/init_inspector.cpp
|
||||
app_actions/init_inspectors.cpp
|
||||
app_actions/init_clients.cpp
|
||||
app_actions/init_outputs.cpp
|
||||
app_actions/list_fields.cpp
|
||||
@@ -35,10 +35,16 @@ set(
|
||||
app_actions/print_ignored_events.cpp
|
||||
app_actions/print_plugin_info.cpp
|
||||
app_actions/print_support.cpp
|
||||
app_actions/print_syscall_events.cpp
|
||||
app_actions/print_version.cpp
|
||||
app_actions/print_page_size.cpp
|
||||
app_actions/compute_syscall_buffer_size.cpp
|
||||
app_actions/select_event_sources.cpp
|
||||
app_actions/start_grpc_server.cpp
|
||||
app_actions/start_webserver.cpp
|
||||
app_actions/validate_rules_files.cpp
|
||||
app_actions/create_requested_paths.cpp
|
||||
app_actions/configure_interesting_sets.cpp
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
@@ -47,7 +53,7 @@ set(
|
||||
outputs_stdout.cpp
|
||||
outputs_syslog.cpp
|
||||
event_drops.cpp
|
||||
statsfilewriter.cpp
|
||||
stats_writer.cpp
|
||||
falco.cpp
|
||||
)
|
||||
|
||||
@@ -56,17 +62,16 @@ set(
|
||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${PROJECT_BINARY_DIR}/driver/src"
|
||||
"${STRING_VIEW_LITE_INCLUDE}"
|
||||
"${CXXOPTS_INCLUDE_DIR}"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include"
|
||||
)
|
||||
|
||||
list(APPEND FALCO_INCLUDE_DIRECTORIES "${FALCO_EXTRA_INCLUDE_DIRS}")
|
||||
|
||||
set(
|
||||
FALCO_DEPENDENCIES
|
||||
string-view-lite
|
||||
b64
|
||||
cxxopts
|
||||
)
|
||||
|
||||
71
userspace/falco/app_actions/compute_syscall_buffer_size.cpp
Normal file
71
userspace/falco/app_actions/compute_syscall_buffer_size.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
/* These indexes could change over the Falco releases. */
|
||||
#define MIN_INDEX 1
|
||||
#define MAX_INDEX 10
|
||||
#define DEFAULT_BYTE_SIZE 1 << 23
|
||||
|
||||
application::run_result application::configure_syscall_buffer_size()
|
||||
{
|
||||
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
|
||||
* the syscall source is not enabled.
|
||||
*/
|
||||
if(is_capture_mode() || m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end() || is_gvisor_enabled())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
uint16_t index = m_state->config->m_syscall_buf_size_preset;
|
||||
if(index < MIN_INDEX || index > MAX_INDEX)
|
||||
{
|
||||
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
|
||||
}
|
||||
|
||||
/* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */
|
||||
std::vector<uint32_t> vect{0, 1 << 20, 1 << 21, 1 << 22, DEFAULT_BYTE_SIZE, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29};
|
||||
|
||||
uint64_t chosen_size = vect[index];
|
||||
|
||||
/* If the page size is not valid we return here. */
|
||||
long page_size = getpagesize();
|
||||
if(page_size <= 0)
|
||||
{
|
||||
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
|
||||
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
/* Check if the chosen size is a multiple of the page size. */
|
||||
if(chosen_size % page_size != 0)
|
||||
{
|
||||
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not a multiple of your system page size '" + std::to_string(page_size) + "'. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
|
||||
}
|
||||
|
||||
/* Check if the chosen size is greater than `2 * page_size`. */
|
||||
if((chosen_size / page_size) <= 2)
|
||||
{
|
||||
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
|
||||
}
|
||||
|
||||
m_state->syscall_buffer_bytes_size = chosen_size;
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
61
userspace/falco/app_actions/configure_interesting_sets.cpp
Normal file
61
userspace/falco/app_actions/configure_interesting_sets.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
void application::configure_interesting_sets()
|
||||
{
|
||||
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
|
||||
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
|
||||
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
|
||||
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
|
||||
*/
|
||||
|
||||
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
|
||||
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
|
||||
* plus syscalls for Falco default rules.
|
||||
*/
|
||||
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
|
||||
m_state->ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest */
|
||||
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(ev)
|
||||
&& !sinsp::is_unused_event(ev)
|
||||
&& !sinsp::is_unknown_event(ev))
|
||||
{
|
||||
/* So far we only covered syscalls, so we add other kinds of
|
||||
interesting events. In this case, we are also interested in
|
||||
metaevents and in the procexit tracepoint event. */
|
||||
if (sinsp::is_metaevent(ev) || ev == PPME_PROCEXIT_1_E)
|
||||
{
|
||||
m_state->ppm_event_info_of_interest.insert(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In this case we get the tracepoints for the `libsinsp` state and we remove
|
||||
* the `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
* for our state/events enrichment.
|
||||
*/
|
||||
m_state->tp_of_interest = inspector->enforce_sinsp_state_tp();
|
||||
m_state->tp_of_interest.erase(SCHED_SWITCH);
|
||||
}
|
||||
111
userspace/falco/app_actions/create_requested_paths.cpp
Normal file
111
userspace/falco/app_actions/create_requested_paths.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright (C) 2022 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 "application.h"
|
||||
#include "falco_utils.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef CPPPATH_SEP
|
||||
#ifdef _MSC_VER
|
||||
#define CPPPATH_SEP "\\"
|
||||
#else
|
||||
#define CPPPATH_SEP "/"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
application::run_result application::create_requested_paths()
|
||||
{
|
||||
if(!m_options.gvisor_config.empty())
|
||||
{
|
||||
// This is bad: parsing gvisor config to get endpoint
|
||||
// to be able to auto-create the path to the file for the user.
|
||||
std::ifstream reader(m_options.gvisor_config);
|
||||
if (reader.fail())
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
|
||||
}
|
||||
|
||||
nlohmann::json parsed_json;
|
||||
std::string gvisor_socket;
|
||||
try
|
||||
{
|
||||
parsed_json = nlohmann::json::parse(reader);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot parse JSON: " + e.what());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
gvisor_socket = parsed_json["trace_session"]["sinks"][0]["config"]["endpoint"];
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
|
||||
}
|
||||
|
||||
int ret = create_dir(gvisor_socket);
|
||||
if (ret != 0)
|
||||
{
|
||||
return run_result::fatal(gvisor_socket + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
|
||||
{
|
||||
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
|
||||
{
|
||||
auto server_path = m_state->config->m_grpc_bind_address.substr(
|
||||
falco::utils::network::UNIX_SCHEME.length()
|
||||
);
|
||||
int ret = create_dir(server_path);
|
||||
if(ret != 0)
|
||||
{
|
||||
return run_result::fatal(server_path + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: eventually other files written by Falco whose destination is
|
||||
// customizable by users, must be handled here.
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
int application::create_dir(const std::string &path)
|
||||
{
|
||||
// Properly reset errno
|
||||
errno = 0;
|
||||
|
||||
istringstream f(path);
|
||||
string path_until_token;
|
||||
string s;
|
||||
// Create all the subfolder stopping at last token (f.eof());
|
||||
// Examples:
|
||||
// "/tmp/foo/bar" -> "", "tmp", "foo" -> mkdir("/") + mkdir("/tmp/") + midir("/tmp/foo/")
|
||||
// "tmp/foo/bar" -> "tmp", "foo" -> mkdir("tmp/") + midir("tmp/foo/")
|
||||
while (getline(f, s, *CPPPATH_SEP) && !f.eof()) {
|
||||
path_until_token += s + CPPPATH_SEP;
|
||||
int ret = mkdir(path_until_token.c_str(), 0600);
|
||||
if (ret != 0 && errno != EEXIST)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -30,23 +30,24 @@ using namespace falco::app;
|
||||
// provided application, and in unregister_signal_handlers it will be
|
||||
// rebound back to the dummy application.
|
||||
|
||||
static application dummy;
|
||||
static std::reference_wrapper<application> s_app = dummy;
|
||||
static int inot_fd;
|
||||
|
||||
static void signal_callback(int signal)
|
||||
static void terminate_signal_handler(int signal)
|
||||
{
|
||||
s_app.get().terminate();
|
||||
ASSERT(falco::app::g_terminate.is_lock_free());
|
||||
falco::app::g_terminate.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
static void reopen_outputs(int signal)
|
||||
static void reopen_outputs_signal_handler(int signal)
|
||||
{
|
||||
s_app.get().reopen_outputs();
|
||||
ASSERT(falco::app::g_reopen_outputs.is_lock_free());
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
static void restart_falco(int signal)
|
||||
static void restart_signal_handler(int signal)
|
||||
{
|
||||
s_app.get().restart();
|
||||
ASSERT(falco::app::g_restart.is_lock_free());
|
||||
falco::app::g_restart.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
@@ -71,42 +72,52 @@ bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
|
||||
application::run_result application::create_signal_handlers()
|
||||
{
|
||||
run_result ret;
|
||||
if(! create_handler(SIGINT, ::signal_callback, ret) ||
|
||||
! create_handler(SIGTERM, ::signal_callback, ret) ||
|
||||
! create_handler(SIGUSR1, ::reopen_outputs, ret) ||
|
||||
! create_handler(SIGHUP, ::restart_falco, ret))
|
||||
falco::app::g_terminate.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_restart.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
|
||||
if (!g_terminate.is_lock_free()
|
||||
|| !g_restart.is_lock_free()
|
||||
|| !g_reopen_outputs.is_lock_free())
|
||||
{
|
||||
return ret;
|
||||
falco_logger::log(LOG_WARNING, "Bundled atomics implementation is not lock-free, signal handlers may be unstable\n");
|
||||
}
|
||||
|
||||
run_result ret;
|
||||
if(! create_handler(SIGINT, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGTERM, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) ||
|
||||
! create_handler(SIGHUP, ::restart_signal_handler, ret))
|
||||
{
|
||||
// we use the if just to make sure we return at the first failed statement
|
||||
}
|
||||
|
||||
s_app = *this;
|
||||
return ret;
|
||||
}
|
||||
|
||||
application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
if (m_state->config->m_watch_config_files)
|
||||
if (m_state->config->m_watch_config_files)
|
||||
{
|
||||
inot_fd = inotify_init();
|
||||
if (inot_fd == -1)
|
||||
{
|
||||
return run_result::fatal("Could not create inotify handler.");
|
||||
return run_result::fatal("Could not create inotify handler");
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = restart_falco;
|
||||
sa.sa_handler = restart_signal_handler;
|
||||
if (sigaction(SIGIO, &sa, NULL) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler.");
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler");
|
||||
}
|
||||
|
||||
/* Set owner process that is to receive "I/O possible" signal */
|
||||
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting owner on inotify handler.");
|
||||
return run_result::fatal("Failed to setting owner on inotify handler");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -116,14 +127,14 @@ application::run_result application::attach_inotify_signals()
|
||||
int flags = fcntl(inot_fd, F_GETFL);
|
||||
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting flags on inotify handler.");
|
||||
return run_result::fatal("Failed to setting flags on inotify handler");
|
||||
}
|
||||
|
||||
// Watch conf file
|
||||
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch conf file.");
|
||||
return run_result::fatal("Failed to watch conf file");
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");
|
||||
|
||||
@@ -135,7 +146,7 @@ application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule file: " + rule);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +".\n");
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
|
||||
}
|
||||
|
||||
// Watch specified rules folders, if any:
|
||||
@@ -149,7 +160,7 @@ application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule folder: " + fld);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder.\n");
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
|
||||
}
|
||||
}
|
||||
return run_result::ok();
|
||||
@@ -167,7 +178,5 @@ bool application::unregister_signal_handlers(std::string &errstr)
|
||||
errstr = ret.errstr;
|
||||
return false;
|
||||
}
|
||||
|
||||
s_app = dummy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ application::run_result application::daemonize()
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
// error
|
||||
return run_result::fatal("Could not fork.");
|
||||
return run_result::fatal("Could not fork");
|
||||
} else if (pid > 0) {
|
||||
// parent. Write child pid to pidfile and exit
|
||||
std::ofstream pidfile;
|
||||
@@ -54,7 +54,7 @@ application::run_result application::daemonize()
|
||||
// Become own process group.
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
return run_result::fatal("Could not set session id.");
|
||||
return run_result::fatal("Could not set session id");
|
||||
}
|
||||
|
||||
// Set umask so no files are world anything or group writable.
|
||||
@@ -62,7 +62,7 @@ application::run_result application::daemonize()
|
||||
|
||||
// Change working directory to '/'
|
||||
if ((chdir("/")) < 0) {
|
||||
return run_result::fatal("Could not change working directory to '/'.");
|
||||
return run_result::fatal("Could not change working directory to '/'");
|
||||
}
|
||||
|
||||
// Close stdin, stdout, stderr and reopen to /dev/null
|
||||
|
||||
@@ -22,15 +22,17 @@ application::run_result application::init_clients()
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
// k8s and mesos clients are useful only if syscall source is enabled
|
||||
if (!is_syscall_source_enabled())
|
||||
if (m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
auto inspector = m_state->source_infos.at(falco_common::syscall_source)->inspector;
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + to_string(m_state->config->m_metadata_download_max_mb) + " MB\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + to_string(m_state->config->m_metadata_download_chunk_wait_us) + " μs\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(m_state->config->m_metadata_download_watch_freq_sec) + " seconds\n");
|
||||
m_state->inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
|
||||
inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
|
||||
|
||||
//
|
||||
// Run k8s, if required
|
||||
@@ -53,7 +55,7 @@ application::run_result application::init_clients()
|
||||
*k8s_api_cert_ptr = k8s_cert_env;
|
||||
}
|
||||
}
|
||||
m_state->inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
|
||||
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -65,12 +67,12 @@ application::run_result application::init_clients()
|
||||
// passes a pointer but the inspector does
|
||||
// *not* own it and does not use it after
|
||||
// init_mesos_client() returns.
|
||||
m_state->inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
|
||||
inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
|
||||
}
|
||||
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
|
||||
{
|
||||
std::string mesos_api_copy = mesos_api_env;
|
||||
m_state->inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
|
||||
inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user