mirror of
https://github.com/falcosecurity/falco.git
synced 2025-06-28 07:37:32 +00:00
K8s audit evts (#450)
* Add new json/webserver libs, embedded webserver Add two new external libraries: - nlohmann-json is a better json library that has stronger use of c++ features like type deduction, better conversion from stl structures, etc. We'll use it to hold generic json objects instead of jsoncpp. - civetweb is an embeddable webserver that will allow us to accept posted json data. New files webserver.{cpp,h} start an embedded webserver that listens for POSTS on a configurable url and passes the json data to the falco engine. New falco config items are under webserver: - enabled: true|false. Whether to start the embedded webserver or not. - listen_port. Port that webserver listens on - k8s_audit_endpoint: uri on which to accept POSTed k8s audit events. (This commit doesn't compile entirely on its own, but we're grouping these related changes into one commit for clarity). * Don't use relative paths to find lua code You can look directly below PROJECT_SOURCE_DIR. * Reorganize compiler lua code The lua compiler code is generic enough to work on more than just sinsp-based rules, so move the parts of the compiler related to event types and filterchecks out into a standalone lua file sinsp_rule_utils.lua. The checks for event types/filterchecks are now done from rule_loader, and are dependent on a "source" attribute of the rule being "sinsp". We'll be adding additional types of events next that come from sources other than system calls. * Manage separate syscall/k8s audit rulesets Add the ability to manage separate sets of rules (syscall and k8s_audit). Stop using the sinsp_evttype_filter object from the sysdig repo, replacing it with falco_ruleset/falco_sinsp_ruleset from ruleset.{cpp,h}. It has the same methods to add rules, associate them with rulesets, and (for syscall) quickly find the relevant rules for a given syscall/event type. At the falco engine level, there are new parallel interfaces for both types of rules (syscall and k8s_audit) to: - add a rule: add_k8s_audit_filter/add_sinsp_filter - match an event against rules, possibly returning a result: process_sinsp_event/process_k8s_audit_event At the rule loading level, the mechanics of creating filterchecks objects is handled two factories (sinsp_filter_factory and json_event_filter_factory), both of which are held by the engine. * Handle multiple rule types when parsing rules Modify the steps of parsing a rule's filter expression to handle multiple types of rules. Notable changes: - In the rule loader/ast traversal, pass a filter api object down, which is passed back up in the lua parser api calls like nest(), bool_op(), rel_expr(), etc. - The filter api object is either the sinsp factory or k8s audit factory, depending on the rule type. - When the rule is complete, the complete filter is passed to the engine using either add_sinsp_filter()/add_k8s_audit_filter(). * Add multiple output formatting types Add support for multiple output formatters. Notable changes: - The falco engine is passed along to falco_formats to gain access to the engine's factories. - When creating a formatter, the source of the rule is passed along with the format string, which controls which kind of output formatter is created. Also clean up exception handling a bit so all lua callbacks catch all exceptions and convert them into lua errors. * Add support for json, k8s audit filter fields With some corresponding changes in sysdig, you can now create general purpose filter fields and events, which can be tied together with nesting, expressions, and relational operators. The classes here represent an instance of these fields devoted to generic json objects as well as k8s audit events. Notable changes: - json_event: holds a json object, used by all of the below - json_event_filter_check: Has the ability to extract values out of a json_event object and has the ability to define macros that associate a field like "group.field" with a json pointer expression that extracts a single property's value out of the json object. The basic field definition also allows creating an index e.g. group.field[index], where a std::function is responsible for performing the indexing. This class has virtual void methods so it must be overridden. - jevt_filter_check: subclass of json_event_filter_check and defines the following fields: - jevt.time/jevt.rawtime: extracts the time from the underlying json object. - jevt.value[<json pointer>]: general purpose way to extract any json value out of the underlying object. <json pointer> is a json pointer expression - jevt.obj: Return the entire object, stringified. - k8s_audit_filter_check: implements fields that extract values from k8s audit events. Most of the implementation is in the form of macros like ka.user.name, ka.uri, ka.target.name, etc. that just use json pointers to extact the appropriate value from a k8s audit event. More advanced fields like ka.uri.param, ka.req.container.image use indexing to extract individual values out of maps or arrays. - json_event_filter_factory: used by things like the lua parser api, output formatter, etc to create the necessary objects and return them. - json_event_formatter: given a format string, create the necessary fields that will be used to create a resolved string when given a json_event object. * Add ability to list fields Similar to sysdig's -l option, add --list (<source>) to list the fields supported by falco. With no source specified, will print all fields. Source can be "syscall" for inspector fields e.g. what is supported by sysdig, or "k8s_audit" to list fields supported only by the k8s audit support in falco. * Initial set of k8s audit rules Add an initial set of k8s audit rules. They're broken into 3 classes of rules: - Suspicious activity: this includes things like: - A disallowed k8s user performing an operation - A disallowed container being used in a pod. - A pod created with a privileged pod. - A pod created with a sensitive mount. - A pod using host networking - Creating a NodePort Service - A configmap containing private credentials - A request being made by an unauthenticated user. - Attach/exec to a pod. (We eventually want to also do privileged pods, but that will require some state management that we don't currently have). - Creating a new namespace outside of an allowed set - Creating a pod in either of the kube-system/kube-public namespaces - Creating a serviceaccount in either of the kube-system/kube-public namespaces - Modifying any role starting with "system:" - Creating a clusterrolebinding to the cluster-admin role - Creating a role that wildcards verbs or resources - Creating a role with writable permissions/pod exec permissions. - Resource tracking. This includes noting when a deployment, service, - configmap, cluster role, service account, etc are created or destroyed. - Audit tracking: This tracks all audit events. To support these rules, add macros/new indexing functions as needed to support the required fields and ways to index the results. * Add ability to read trace files of k8s audit evts Expand the use of the -e flag to cover both .scap files containing system calls as well as jsonl files containing k8s audit events: If a trace file is specified, first try to read it using the inspector. If that throws an exception, try to read the first line as json. If both fail, return an error. Based on the results of the open, the main loop either calls do_inspect(), looping over system events, or read_k8s_audit_trace_file(), reading each line as json and passing it to the engine and outputs. * Example showing how to enable k8s audit logs. An example of how to enable k8s audit logging for minikube. * Add unit tests for k8s audit support Initial unit test support for k8s audit events. A new multiplex file falco_k8s_audit_tests.yaml defines the tests. Traces (jsonl files) are in trace_files/k8s_audit and new rules files are in test/rules/k8s_audit. Current test cases include: - User outside allowed set - Creating disallowed pod. - Creating a pod explicitly on the allowed list - Creating a pod w/ a privileged container (or second container), or a pod with no privileged container. - Creating a pod w/ a sensitive mount container (or second container), or a pod with no sensitive mount. - Cases for a trace w/o the relevant property + the container being trusted, and hostnetwork tests. - Tests that create a Service w/ and w/o a NodePort type. - Tests for configmaps: tries each disallowed string, ensuring each is detected, and the other has a configmap with no disallowed string, ensuring it is not detected. - The anonymous user creating a namespace. - Tests for all kactivity rules e.g. those that create/delete resources as compared to suspicious activity. - Exec/Attach to Pod - Creating a namespace outside of an allowed set - Creating a pod/serviceaccount in kube-system/kube-public namespaces - Deleting/modifying a system cluster role - Creating a binding to the cluster-admin role - Creating a cluster role binding that wildcards verbs or resources - Creating a cluster role with write/pod exec privileges * Don't manually install gcc 4.8 gcc 4.8 should already be installed by default on the vm we use for travis.
This commit is contained in:
parent
ff4f7ca13b
commit
1f28f85bdf
@ -15,7 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
language: c
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
env:
|
||||
- BUILD_TYPE=Debug
|
||||
- BUILD_TYPE=Release
|
||||
@ -23,10 +24,8 @@ sudo: required
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update
|
||||
install:
|
||||
- sudo apt-get --force-yes install g++-4.8
|
||||
- sudo apt-get install rpm linux-headers-$(uname -r) libelf-dev
|
||||
- git clone https://github.com/draios/sysdig.git ../sysdig
|
||||
- sudo apt-get install -y python-pip libvirt-dev jq dkms
|
||||
@ -42,8 +41,6 @@ before_script:
|
||||
- export KERNELDIR=/lib/modules/$(uname -r)/build
|
||||
script:
|
||||
- set -e
|
||||
- export CC="gcc-4.8"
|
||||
- export CXX="g++-4.8"
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DDRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
|
@ -136,6 +136,32 @@ set(JSONCPP_SRC "${SYSDIG_DIR}/userspace/libsinsp/third-party/jsoncpp")
|
||||
set(JSONCPP_INCLUDE "${JSONCPP_SRC}")
|
||||
set(JSONCPP_LIB_SRC "${JSONCPP_SRC}/jsoncpp.cpp")
|
||||
|
||||
#
|
||||
# nlohmann-json
|
||||
#
|
||||
option(USE_BUNDLED_NJSON "Enable building of the bundled nlohmann-json" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_NJSON)
|
||||
find_path(NJSON_INCLUDE json.hpp PATH_SUFFIXES nlohmann)
|
||||
if(NJSON_INCLUDE)
|
||||
message(STATUS "Found nlohmann-json: include: ${NJSON_INCLUDE}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system nlohmann-json")
|
||||
endif()
|
||||
else()
|
||||
# No distinction needed for windows. The implementation is
|
||||
# solely in json.hpp.
|
||||
set(NJSON_SRC "${PROJECT_BINARY_DIR}/njson-prefix/src/njson")
|
||||
message(STATUS "Using bundled nlohmann-json in '${NJSON_SRC}'")
|
||||
set(NJSON_INCLUDE "${NJSON_SRC}/single_include")
|
||||
ExternalProject_Add(njson
|
||||
URL "http://download.draios.com/dependencies/njson-3.3.0.tar.gz"
|
||||
URL_MD5 "e26760e848656a5da400662e6c5d999a"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# curses
|
||||
#
|
||||
@ -438,6 +464,40 @@ else()
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# civetweb
|
||||
#
|
||||
option(USE_BUNDLED_CIVETWEB "Enable building of the bundled civetweb" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_CIVETWEB)
|
||||
find_library(CIVETWEB_LIB NAMES civetweb)
|
||||
if(CIVETWEB_LIB)
|
||||
message(STATUS "Found civetweb: lib: ${CIVETWEB_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system civetweb")
|
||||
endif()
|
||||
else()
|
||||
set(CIVETWEB_SRC "${PROJECT_BINARY_DIR}/civetweb-prefix/src/civetweb/")
|
||||
set(CIVETWEB_LIB "${CIVETWEB_SRC}/install/lib/libcivetweb.a")
|
||||
set(CIVETWEB_INCLUDE_DIR "${CIVETWEB_SRC}/install/include")
|
||||
message(STATUS "Using bundled civetweb in '${CIVETWEB_SRC}'")
|
||||
set(CIVETWEB_DEPENDENCIES "")
|
||||
if(USE_BUNDLED_OPENSSL)
|
||||
list(APPEND CIVETWEB_DEPENDENCIES "openssl")
|
||||
endif()
|
||||
ExternalProject_Add(civetweb
|
||||
DEPENDS ${CIVETWEB_DEPENDENCIES}
|
||||
URL "http://s3.amazonaws.com/download.draios.com/dependencies/civetweb-1.11.tar.gz"
|
||||
URL_MD5 "b6d2175650a27924bccb747cbe084cd4"
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory ${CIVETWEB_SRC}/install/lib
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CIVETWEB_SRC}/install/include
|
||||
BUILD_IN_SOURCE 1
|
||||
BUILD_COMMAND ${CMD_MAKE} COPT="-DNO_FILES" WITH_CPP=1
|
||||
INSTALL_COMMAND ${CMD_MAKE} install-lib install-headers PREFIX=${CIVETWEB_SRC}/install WITH_CPP=1)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
install(FILES falco.yaml
|
||||
DESTINATION "${FALCO_ETC_DIR}")
|
||||
|
||||
|
23
examples/k8s_audit_config/README.md
Normal file
23
examples/k8s_audit_config/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Introduction
|
||||
|
||||
The files in this directory can be used to configure k8s audit logging. The relevant files are:
|
||||
|
||||
* [audit-policy.yaml](./audit-policy.yaml): The k8s audit log configuration we used to create the rules in [k8s_audit_rules.yaml](../../rules/k8s_audit_rules.yaml). You may find it useful as a reference when creating your own K8s Audit Log configuration.
|
||||
* [webhook-config.yaml](./webhook-config.yaml): A webhook configuration that sends audit events to localhost, port 8765. You may find it useful as a starting point when deciding how to route audit events to the embedded webserver within falco.
|
||||
|
||||
This file is only needed when using Minikube, which doesn't currently
|
||||
have the ability to provide an audit config/webhook config directly
|
||||
from the minikube commandline. See [this issue](https://github.com/kubernetes/minikube/issues/2741) for more details.
|
||||
|
||||
* [apiserver-config.patch.sh](./apiserver-config.patch.sh): A script that changes the configuration file `/etc/kubernetes/manifests/kube-apiserver.yaml` to add necessary config options and mounts for the kube-apiserver container that runs within the minikube vm.
|
||||
|
||||
A way to use these files with minikube to enable audit logging would be to run the following commands, from this directory:
|
||||
|
||||
```
|
||||
minikube start --kubernetes-version v1.11.0 --mount --mount-string $PWD:/tmp/k8s_audit_config --feature-gates AdvancedAuditing=true
|
||||
ssh -i $(minikube ssh-key) docker@$(minikube ip) sudo bash /tmp/k8s_audit_config/apiserver-config.patch.sh
|
||||
ssh -i $(minikube ssh-key) -R 8765:localhost:8765 docker@$(minikube ip)
|
||||
```
|
||||
|
||||
K8s audit events will then be sent to localhost on the host (not minikube vm) machine, port 8765.
|
||||
|
40
examples/k8s_audit_config/apiserver-config.patch.sh
Normal file
40
examples/k8s_audit_config/apiserver-config.patch.sh
Normal file
@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
|
||||
IFS=''
|
||||
|
||||
FILENAME="/etc/kubernetes/manifests/kube-apiserver.yaml"
|
||||
|
||||
if grep audit-webhook-config-file $FILENAME ; then
|
||||
echo audit-webhook patch already applied
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TMPFILE="/tmp/kube-apiserver.yaml.patched"
|
||||
rm -f "$TMPFILE"
|
||||
|
||||
while read LINE
|
||||
do
|
||||
echo "$LINE" >> "$TMPFILE"
|
||||
case "$LINE" in
|
||||
*"- kube-apiserver"*)
|
||||
echo " - --audit-log-path=/tmp/k8s_audit_config/audit.log" >> "$TMPFILE"
|
||||
echo " - --audit-policy-file=/tmp/k8s_audit_config/audit-policy.yaml" >> "$TMPFILE"
|
||||
echo " - --audit-webhook-config-file=/tmp/k8s_audit_config/webhook-config.yaml" >> "$TMPFILE"
|
||||
echo " - --audit-webhook-batch-max-wait=5s" >> "$TMPFILE"
|
||||
;;
|
||||
*"volumeMounts:"*)
|
||||
echo " - mountPath: /tmp/k8s_audit_config/" >> "$TMPFILE"
|
||||
echo " name: data" >> "$TMPFILE"
|
||||
;;
|
||||
*"volumes:"*)
|
||||
echo " - hostPath:" >> "$TMPFILE"
|
||||
echo " path: /tmp/k8s_audit_config" >> "$TMPFILE"
|
||||
echo " name: data" >> "$TMPFILE"
|
||||
;;
|
||||
|
||||
esac
|
||||
done < "$FILENAME"
|
||||
|
||||
cp "$FILENAME" "/tmp/kube-apiserver.yaml.original"
|
||||
cp "$TMPFILE" "$FILENAME"
|
||||
|
76
examples/k8s_audit_config/audit-policy.yaml
Normal file
76
examples/k8s_audit_config/audit-policy.yaml
Normal file
@ -0,0 +1,76 @@
|
||||
apiVersion: audit.k8s.io/v1beta1 # This is required.
|
||||
kind: Policy
|
||||
# Don't generate audit events for all requests in RequestReceived stage.
|
||||
omitStages:
|
||||
- "RequestReceived"
|
||||
rules:
|
||||
# Log pod changes at RequestResponse level
|
||||
- level: RequestResponse
|
||||
resources:
|
||||
- group: ""
|
||||
# Resource "pods" doesn't match requests to any subresource of pods,
|
||||
# which is consistent with the RBAC policy.
|
||||
resources: ["pods", "deployments"]
|
||||
|
||||
- level: RequestResponse
|
||||
resources:
|
||||
- group: "rbac.authorization.k8s.io"
|
||||
# Resource "pods" doesn't match requests to any subresource of pods,
|
||||
# which is consistent with the RBAC policy.
|
||||
resources: ["clusterroles", "clusterrolebindings"]
|
||||
|
||||
# Log "pods/log", "pods/status" at Metadata level
|
||||
- level: Metadata
|
||||
resources:
|
||||
- group: ""
|
||||
resources: ["pods/log", "pods/status"]
|
||||
|
||||
# Don't log requests to a configmap called "controller-leader"
|
||||
- level: None
|
||||
resources:
|
||||
- group: ""
|
||||
resources: ["configmaps"]
|
||||
resourceNames: ["controller-leader"]
|
||||
|
||||
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
|
||||
- level: None
|
||||
users: ["system:kube-proxy"]
|
||||
verbs: ["watch"]
|
||||
resources:
|
||||
- group: "" # core API group
|
||||
resources: ["endpoints", "services"]
|
||||
|
||||
# Don't log authenticated requests to certain non-resource URL paths.
|
||||
- level: None
|
||||
userGroups: ["system:authenticated"]
|
||||
nonResourceURLs:
|
||||
- "/api*" # Wildcard matching.
|
||||
- "/version"
|
||||
|
||||
# Log the request body of configmap changes in kube-system.
|
||||
- level: Request
|
||||
resources:
|
||||
- group: "" # core API group
|
||||
resources: ["configmaps"]
|
||||
# This rule only applies to resources in the "kube-system" namespace.
|
||||
# The empty string "" can be used to select non-namespaced resources.
|
||||
namespaces: ["kube-system"]
|
||||
|
||||
# Log configmap and secret changes in all other namespaces at the RequestResponse level.
|
||||
- level: RequestResponse
|
||||
resources:
|
||||
- group: "" # core API group
|
||||
resources: ["secrets", "configmaps"]
|
||||
|
||||
# Log all other resources in core and extensions at the Request level.
|
||||
- level: Request
|
||||
resources:
|
||||
- group: "" # core API group
|
||||
- group: "extensions" # Version of group should NOT be included.
|
||||
|
||||
# A catch-all rule to log all other requests at the Metadata level.
|
||||
- level: Metadata
|
||||
# Long-running requests like watches that fall under this rule will not
|
||||
# generate an audit event in RequestReceived.
|
||||
omitStages:
|
||||
- "RequestReceived"
|
14
examples/k8s_audit_config/webhook-config.yaml
Normal file
14
examples/k8s_audit_config/webhook-config.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: falco
|
||||
cluster:
|
||||
server: http://127.0.0.1:8765/k8s_audit
|
||||
contexts:
|
||||
- context:
|
||||
cluster: falco
|
||||
user: ""
|
||||
name: default-context
|
||||
current-context: default-context
|
||||
preferences: {}
|
||||
users: []
|
@ -100,6 +100,15 @@ 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 disabled).
|
||||
# enabled: false
|
||||
webserver:
|
||||
enabled: true
|
||||
listen_port: 8765
|
||||
k8s_audit_endpoint: /k8s_audit
|
||||
|
||||
# Possible additional things you might want to do with program output:
|
||||
# - send to a slack webhook:
|
||||
# program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX"
|
||||
|
@ -23,6 +23,7 @@ if(NOT DEFINED FALCO_RULES_DEST_FILENAME)
|
||||
set(FALCO_RULES_DEST_FILENAME "falco_rules.yaml")
|
||||
set(FALCO_LOCAL_RULES_DEST_FILENAME "falco_rules.local.yaml")
|
||||
set(FALCO_APP_RULES_DEST_FILENAME "application_rules.yaml")
|
||||
set(FALCO_K8S_AUDIT_RULES_DEST_FILENAME "k8s_audit_rules.yaml")
|
||||
endif()
|
||||
|
||||
if(DEFINED FALCO_COMPONENT)
|
||||
@ -47,6 +48,10 @@ install(FILES falco_rules.local.yaml
|
||||
DESTINATION "${FALCO_ETC_DIR}"
|
||||
RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}")
|
||||
|
||||
install(FILES k8s_audit_rules.yaml
|
||||
DESTINATION "${FALCO_ETC_DIR}"
|
||||
RENAME "${FALCO_K8S_AUDIT_RULES_DEST_FILENAME}")
|
||||
|
||||
install(FILES application_rules.yaml
|
||||
DESTINATION "/etc/falco/rules.available"
|
||||
RENAME "${FALCO_APP_RULES_DEST_FILENAME}")
|
||||
|
418
rules/k8s_audit_rules.yaml
Normal file
418
rules/k8s_audit_rules.yaml
Normal file
@ -0,0 +1,418 @@
|
||||
# Generally only consider audit events once the response has completed
|
||||
- list: k8s_audit_stages
|
||||
items: ["ResponseComplete"]
|
||||
|
||||
# Generally exclude users starting with "system:"
|
||||
- macro: non_system_user
|
||||
condition: (not ka.user.name startswith "system:")
|
||||
|
||||
# This macro selects the set of Audit Events used by the below rules.
|
||||
- macro: kevt
|
||||
condition: (jevt.value[/stage] in (k8s_audit_stages))
|
||||
|
||||
- macro: kevt_started
|
||||
condition: (jevt.value[/stage]=ResponseStarted)
|
||||
|
||||
# If you wish to restrict activity to a specific set of users, override/append to this list.
|
||||
- list: allowed_k8s_users
|
||||
items: ["minikube", "minikube-user"]
|
||||
|
||||
- rule: Disallowed K8s User
|
||||
desc: Detect any k8s operation by users outside of an allowed set of users.
|
||||
condition: kevt and non_system_user and not ka.user.name in (allowed_k8s_users)
|
||||
output: K8s Operation performed by user not in allowed list of users (user=%ka.user.name target=%ka.target.name/%ka.target.resource verb=%ka.verb uri=%ka.uri resp=%ka.response.code)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# In a local/user rules file, you could override this macro to
|
||||
# explicitly enumerate the container images that you want to run in
|
||||
# your environment. In this main falco rules file, there isn't any way
|
||||
# to know all the containers that can run, so any container is
|
||||
# alllowed, by using a filter that is guaranteed to evaluate to true
|
||||
# (the event time existing). In the overridden macro, the condition
|
||||
# would look something like (ka.req.container.image.repository=my-repo/my-image)
|
||||
- macro: allowed_k8s_containers
|
||||
condition: (jevt.rawtime exists)
|
||||
|
||||
- macro: response_successful
|
||||
condition: (ka.response.code startswith 2)
|
||||
|
||||
- macro: create
|
||||
condition: ka.verb=create
|
||||
|
||||
- macro: modify
|
||||
condition: (ka.verb in (create,update,patch))
|
||||
|
||||
- macro: delete
|
||||
condition: ka.verb=delete
|
||||
|
||||
- macro: pod
|
||||
condition: ka.target.resource=pods and not ka.target.subresource exists
|
||||
|
||||
- macro: pod_subresource
|
||||
condition: ka.target.resource=pods and ka.target.subresource exists
|
||||
|
||||
- macro: deployment
|
||||
condition: ka.target.resource=deployments
|
||||
|
||||
- macro: service
|
||||
condition: ka.target.resource=services
|
||||
|
||||
- macro: configmap
|
||||
condition: ka.target.resource=configmaps
|
||||
|
||||
- macro: namespace
|
||||
condition: ka.target.resource=namespaces
|
||||
|
||||
- macro: serviceaccount
|
||||
condition: ka.target.resource=serviceaccounts
|
||||
|
||||
- macro: clusterrole
|
||||
condition: ka.target.resource=clusterroles
|
||||
|
||||
- macro: clusterrolebinding
|
||||
condition: ka.target.resource=clusterrolebindings
|
||||
|
||||
- macro: role
|
||||
condition: ka.target.resource=roles
|
||||
|
||||
- macro: health_endpoint
|
||||
condition: ka.uri=/healthz
|
||||
|
||||
- rule: Create Disallowed Pod
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a container image outside of a list of allowed images.
|
||||
condition: kevt and pod and create and not allowed_k8s_containers
|
||||
output: Pod started with container not in allowed list (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace image=%ka.req.container.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- list: trusted_k8s_containers
|
||||
items: [sysdig/agent, sysdig/falco, quay.io/coreos/flannel, calico/node, rook/toolbox,
|
||||
gcr.io/google_containers/hyperkube, gcr.io/google_containers/kube-proxy,
|
||||
openshift3/ose-sti-builder,
|
||||
registry.access.redhat.com/openshift3/logging-fluentd,
|
||||
registry.access.redhat.com/openshift3/logging-elasticsearch,
|
||||
registry.access.redhat.com/openshift3/metrics-cassandra,
|
||||
registry.access.redhat.com/openshift3/ose-sti-builder,
|
||||
registry.access.redhat.com/openshift3/ose-docker-builder,
|
||||
registry.access.redhat.com/openshift3/image-inspector,
|
||||
cloudnativelabs/kube-router, istio/proxy,
|
||||
datadog/docker-dd-agent, datadog/agent,
|
||||
docker/ucp-agent,
|
||||
gliderlabs/logspout]
|
||||
|
||||
- rule: Create Privileged Pod
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a privileged container
|
||||
condition: kevt and pod and create and ka.req.container.privileged=true and not ka.req.container.image.repository in (trusted_k8s_containers)
|
||||
output: Pod started with privileged container (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace image=%ka.req.container.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- macro: sensitive_vol_mount
|
||||
condition: >
|
||||
(ka.req.volume.hostpath[/proc*]=true or
|
||||
ka.req.volume.hostpath[/var/run/docker.sock]=true or
|
||||
ka.req.volume.hostpath[/]=true or
|
||||
ka.req.volume.hostpath[/etc]=true or
|
||||
ka.req.volume.hostpath[/root*]=true)
|
||||
|
||||
- rule: Create Sensitive Mount Pod
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a volume from a sensitive host directory (i.e. /proc).
|
||||
Exceptions are made for known trusted images.
|
||||
condition: kevt and pod and create and sensitive_vol_mount and not ka.req.container.image.repository in (trusted_k8s_containers)
|
||||
output: Pod started with sensitive mount (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace image=%ka.req.container.image mounts=%jevt.value[/requestObject/spec/volumes])
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Corresponds to K8s CIS Benchmark 1.7.4
|
||||
- rule: Create HostNetwork Pod
|
||||
desc: Detect an attempt to start a pod using the host network.
|
||||
condition: kevt and pod and create and ka.req.container.host_network=true and not ka.req.container.image.repository in (trusted_k8s_containers)
|
||||
output: Pod started using host network (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace image=%ka.req.container.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: Create NodePort Service
|
||||
desc: >
|
||||
Detect an attempt to start a service with a NodePort service type
|
||||
condition: kevt and service and create and ka.req.service.type=NodePort
|
||||
output: NodePort Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace ports=%ka.req.service.ports)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- macro: contains_private_credentials
|
||||
condition: >
|
||||
(ka.req.configmap.obj contains "aws_access_key_id" or
|
||||
ka.req.configmap.obj contains "aws-access-key-id" or
|
||||
ka.req.configmap.obj contains "aws_s3_access_key_id" or
|
||||
ka.req.configmap.obj contains "aws-s3-access-key-id" or
|
||||
ka.req.configmap.obj contains "password" or
|
||||
ka.req.configmap.obj contains "passphrase")
|
||||
|
||||
- rule: Create/Modify Configmap With Private Credentials
|
||||
desc: >
|
||||
Detect creating/modifying a configmap containing a private credential (aws key, password, etc.)
|
||||
condition: kevt and configmap and modify and contains_private_credentials
|
||||
output: K8s configmap with private credential (user=%ka.user.name verb=%ka.verb configmap=%ka.req.configmap.name config=%ka.req.configmap.obj)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Corresponds to K8s CIS Benchmark, 1.1.1.
|
||||
- rule: Anonymous Request Allowed
|
||||
desc: >
|
||||
Detect any request made by the anonymous user that was allowed
|
||||
condition: kevt and ka.user.name=system:anonymous and ka.auth.decision!=reject and not health_endpoint
|
||||
output: Request by anonymous user allowed (user=%ka.user.name verb=%ka.verb uri=%ka.uri reason=%ka.auth.reason))
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Roughly corresponds to K8s CIS Benchmark, 1.1.12. In this case,
|
||||
# notifies an attempt to exec/attach to a privileged container.
|
||||
|
||||
# Ideally, we'd add a more stringent rule that detects attaches/execs
|
||||
# to a privileged pod, but that requires the engine for k8s audit
|
||||
# events to be stateful, so it could know if a container named in an
|
||||
# attach request was created privileged or not. For now, we have a
|
||||
# less severe rule that detects attaches/execs to any pod.
|
||||
|
||||
- rule: Attach/Exec Pod
|
||||
desc: >
|
||||
Detect any attempt to attach/exec to a pod
|
||||
condition: kevt_started and pod_subresource and create and ka.target.subresource in (exec,attach)
|
||||
output: Attach/Exec to pod (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace action=%ka.target.subresource command=%ka.uri.param[command])
|
||||
priority: NOTICE
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# In a local/user rules fie, you can append to this list to add additional allowed namespaces
|
||||
- list: allowed_namespaces
|
||||
items: [kube-system, kube-public, default]
|
||||
|
||||
- rule: Create Disallowed Namespace
|
||||
desc: Detect any attempt to create a namespace outside of a set of known namespaces
|
||||
condition: kevt and namespace and create and not ka.target.name in (allowed_namespaces)
|
||||
output: Disallowed namespace created (user=%ka.user.name ns=%ka.target.name)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Detect any new pod created in the kube-system namespace
|
||||
- rule: Pod Created in Kube Namespace
|
||||
desc: Detect any attempt to create a pod in the kube-system or kube-public namespaces
|
||||
condition: kevt and pod and create and ka.target.namespace in (kube-system, kube-public)
|
||||
output: Pod created in kube namespace (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace image=%ka.req.container.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Detect creating a service account in the kube-system/kube-public namespace
|
||||
- rule: Service Account Created in Kube Namespace
|
||||
desc: Detect any attempt to create a serviceaccount in the kube-system or kube-public namespaces
|
||||
condition: kevt and serviceaccount and create and ka.target.namespace in (kube-system, kube-public)
|
||||
output: Service account created in kube namespace (user=%ka.user.name serviceaccount=%ka.target.name ns=%ka.target.namespace)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Detect any modify/delete to any ClusterRole starting with
|
||||
# "system:". "system:coredns" is excluded as changes are expected in
|
||||
# normal operation.
|
||||
- rule: System ClusterRole Modified/Deleted
|
||||
desc: Detect any attempt to modify/delete a ClusterRole/Role starting with system
|
||||
condition: kevt and (role or clusterrole) and (modify or delete) and (ka.target.name startswith "system:") and ka.target.name!="system:coredns"
|
||||
output: System ClusterRole/Role modified or deleted (user=%ka.user.name role=%ka.target.name ns=%ka.target.namespace action=%ka.verb)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# Detect any attempt to create a ClusterRoleBinding to the cluster-admin user
|
||||
# (exapand this to any built-in cluster role that does "sensitive" things)
|
||||
- rule: Attach to cluster-admin Role
|
||||
desc: Detect any attempt to create a ClusterRoleBinding to the cluster-admin user
|
||||
condition: kevt and clusterrolebinding and create and ka.req.binding.role=cluster-admin
|
||||
output: Cluster Role Binding to cluster-admin role (user=%ka.user.name subject=%ka.req.binding.subjects)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: ClusterRole With Wildcard Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole with wildcard resources or verbs
|
||||
condition: kevt and (role or clusterrole) and create and (ka.req.role.rules.resources contains '"*"' or ka.req.role.rules.verbs contains '"*"')
|
||||
output: Created Role/ClusterRole with wildcard (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- macro: writable_verbs
|
||||
condition: >
|
||||
(ka.req.role.rules.verbs contains create or
|
||||
ka.req.role.rules.verbs contains update or
|
||||
ka.req.role.rules.verbs contains patch or
|
||||
ka.req.role.rules.verbs contains delete or
|
||||
ka.req.role.rules.verbs contains deletecollection)
|
||||
|
||||
- rule: ClusterRole With Write Privileges Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole that can perform write-related actions
|
||||
condition: kevt and (role or clusterrole) and create and writable_verbs
|
||||
output: Created Role/ClusterRole with write privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: NOTICE
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: ClusterRole With Pod Exec Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole that can exec to pods
|
||||
condition: kevt and (role or clusterrole) and create and ka.req.role.rules.resources contains "pods/exec"
|
||||
output: Created Role/ClusterRole with pod exec privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# The rules below this point are less discriminatory and generally
|
||||
# represent a stream of activity for a cluster. If you wish to disable
|
||||
# these events, modify the following macro.
|
||||
- macro: consider_activity_events
|
||||
condition: (jevt.rawtime exists)
|
||||
|
||||
- macro: kactivity
|
||||
condition: (kevt and consider_activity_events)
|
||||
|
||||
- rule: K8s Deployment Created
|
||||
desc: Detect any attempt to create a deployment
|
||||
condition: (kactivity and create and deployment and response_successful)
|
||||
output: K8s Deployment Created (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Deployment Deleted
|
||||
desc: Detect any attempt to delete a deployment
|
||||
condition: (kactivity and delete and deployment and response_successful)
|
||||
output: K8s Deployment Deleted (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Service Created
|
||||
desc: Detect any attempt to create a service
|
||||
condition: (kactivity and create and service and response_successful)
|
||||
output: K8s Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Service Deleted
|
||||
desc: Detect any attempt to delete a service
|
||||
condition: (kactivity and delete and service and response_successful)
|
||||
output: K8s Service Deleted (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s ConfigMap Created
|
||||
desc: Detect any attempt to create a configmap
|
||||
condition: (kactivity and create and configmap and response_successful)
|
||||
output: K8s ConfigMap Created (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s ConfigMap Deleted
|
||||
desc: Detect any attempt to delete a configmap
|
||||
condition: (kactivity and delete and configmap and response_successful)
|
||||
output: K8s ConfigMap Deleted (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Namespace Created
|
||||
desc: Detect any attempt to create a namespace
|
||||
condition: (kactivity and create and namespace and response_successful)
|
||||
output: K8s Namespace Created (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Namespace Deleted
|
||||
desc: Detect any attempt to delete a namespace
|
||||
condition: (kactivity and non_system_user and delete and namespace and response_successful)
|
||||
output: K8s Namespace Deleted (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Serviceaccount Created
|
||||
desc: Detect any attempt to create a service account
|
||||
condition: (kactivity and create and serviceaccount and response_successful)
|
||||
output: K8s Serviceaccount Created (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Serviceaccount Deleted
|
||||
desc: Detect any attempt to delete a service account
|
||||
condition: (kactivity and delete and serviceaccount and response_successful)
|
||||
output: K8s Serviceaccount Deleted (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Role/Clusterrole Created
|
||||
desc: Detect any attempt to create a cluster role/role
|
||||
condition: (kactivity and create and (clusterrole or role) and response_successful)
|
||||
output: K8s Cluster Role Created (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Role/Clusterrole Deleted
|
||||
desc: Detect any attempt to delete a cluster role/role
|
||||
condition: (kactivity and delete and (clusterrole or role) and response_successful)
|
||||
output: K8s Cluster Role Deleted (user=%ka.user.name role=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Role/Clusterrolebinding Created
|
||||
desc: Detect any attempt to create a clusterrolebinding
|
||||
condition: (kactivity and create and clusterrolebinding and response_successful)
|
||||
output: K8s Cluster Role Binding Created (user=%ka.user.name binding=%ka.target.name subjects=%ka.req.binding.subjects role=%ka.req.binding.role resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason foo=%ka.req.binding.subject.has_name[cluster-admin])
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
- rule: K8s Role/Clusterrolebinding Deleted
|
||||
desc: Detect any attempt to delete a clusterrolebinding
|
||||
condition: (kactivity and delete and clusterrolebinding and response_successful)
|
||||
output: K8s Cluster Role Binding Deleted (user=%ka.user.name binding=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
|
||||
# This rule generally matches all events, and as a result is disabled
|
||||
# by default. If you wish to enable these events, modify the
|
||||
# following macro.
|
||||
# condition: (jevt.rawtime exists)
|
||||
- macro: consider_all_events
|
||||
condition: (not jevt.rawtime exists)
|
||||
|
||||
- macro: kall
|
||||
condition: (kevt and consider_all_events)
|
||||
|
||||
- rule: All K8s Audit Events
|
||||
desc: Match all K8s Audit Events
|
||||
condition: kall
|
||||
output: K8s Audit Event received (user=%ka.user.name verb=%ka.verb uri=%ka.uri obj=%jevt.obj)
|
||||
priority: DEBUG
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
465
test/falco_k8s_audit_tests.yaml
Normal file
465
test/falco_k8s_audit_tests.yaml
Normal file
@ -0,0 +1,465 @@
|
||||
#
|
||||
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
|
||||
#
|
||||
# This file is part of falco.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
trace_files: !mux
|
||||
|
||||
user_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_namespace_foo.yaml
|
||||
detect_counts:
|
||||
- Disallowed K8s User: 1
|
||||
trace_file: trace_files/k8s_audit/some-user_creates_namespace_foo.json
|
||||
|
||||
user_in_allowed_set:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_namespace_foo.yaml
|
||||
- ./rules/k8s_audit/allow_user_some-user.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
trace_file: trace_files/k8s_audit/some-user_creates_namespace_foo.json
|
||||
|
||||
create_disallowed_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_only_apache_container.yaml
|
||||
detect_counts:
|
||||
- Create Disallowed Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unprivileged.json
|
||||
|
||||
create_allowed_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unprivileged.json
|
||||
|
||||
create_privileged_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Create Privileged Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_privileged.json
|
||||
|
||||
create_privileged_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Create Privileged Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_privileged_2nd_container.json
|
||||
|
||||
create_privileged_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_privileged.json
|
||||
|
||||
create_unprivileged_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unprivileged.json
|
||||
|
||||
create_unprivileged_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unprivileged.json
|
||||
|
||||
create_sensitive_mount_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Create Sensitive Mount Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_sensitive_mount.json
|
||||
|
||||
create_sensitive_mount_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Create Sensitive Mount Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_sensitive_mount_2nd_container.json
|
||||
|
||||
create_sensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_sensitive_mount.json
|
||||
|
||||
create_unsensitive_mount_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unsensitive_mount.json
|
||||
|
||||
create_unsensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_unsensitive_mount.json
|
||||
|
||||
create_hostnetwork_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Create HostNetwork Pod: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_hostnetwork.json
|
||||
|
||||
create_hostnetwork_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_hostnetwork.json
|
||||
|
||||
create_nohostnetwork_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_nohostnetwork.json
|
||||
|
||||
create_nohostnetwork_trusted_pod:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/trust_nginx_container.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_pod_nohostnetwork.json
|
||||
|
||||
create_nodeport_service:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
detect_counts:
|
||||
- Create NodePort Service: 1
|
||||
trace_file: trace_files/k8s_audit/create_nginx_service_nodeport.json
|
||||
|
||||
create_nonodeport_service:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
trace_file: trace_files/k8s_audit/create_nginx_service_nonodeport.json
|
||||
|
||||
create_configmap_private_creds:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
detect_counts:
|
||||
- Create/Modify Configmap With Private Credentials: 6
|
||||
trace_file: trace_files/k8s_audit/create_configmap_sensitive_values.json
|
||||
|
||||
create_configmap_no_private_creds:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
trace_file: trace_files/k8s_audit/create_configmap_no_sensitive_values.json
|
||||
|
||||
anonymous_user:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Anonymous Request Allowed: 1
|
||||
trace_file: trace_files/k8s_audit/anonymous_creates_namespace_foo.json
|
||||
|
||||
pod_exec:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Attach/Exec Pod: 1
|
||||
trace_file: trace_files/k8s_audit/exec_pod.json
|
||||
|
||||
pod_attach:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Attach/Exec Pod: 1
|
||||
trace_file: trace_files/k8s_audit/attach_pod.json
|
||||
|
||||
namespace_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_user_some-user.yaml
|
||||
detect_counts:
|
||||
- Create Disallowed Namespace: 1
|
||||
trace_file: trace_files/k8s_audit/some-user_creates_namespace_foo.json
|
||||
|
||||
namespace_in_allowed_set:
|
||||
detect: False
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_namespace_foo.yaml
|
||||
- ./rules/k8s_audit/disallow_kactivity.yaml
|
||||
trace_file: trace_files/k8s_audit/minikube_creates_namespace_foo.json
|
||||
|
||||
create_pod_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Pod Created in Kube Namespace: 1
|
||||
trace_file: trace_files/k8s_audit/create_pod_kube_system_namespace.json
|
||||
|
||||
create_pod_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Pod Created in Kube Namespace: 1
|
||||
trace_file: trace_files/k8s_audit/create_pod_kube_public_namespace.json
|
||||
|
||||
create_serviceaccount_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Service Account Created in Kube Namespace: 1
|
||||
trace_file: trace_files/k8s_audit/create_serviceaccount_kube_system_namespace.json
|
||||
|
||||
create_serviceaccount_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Service Account Created in Kube Namespace: 1
|
||||
trace_file: trace_files/k8s_audit/create_serviceaccount_kube_public_namespace.json
|
||||
|
||||
system_clusterrole_deleted:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- System ClusterRole Modified/Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_cluster_role_kube_aggregator.json
|
||||
|
||||
system_clusterrole_modified:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- System ClusterRole Modified/Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/modify_cluster_role_node_problem_detector.json
|
||||
|
||||
attach_cluster_admin_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- Attach to cluster-admin Role: 1
|
||||
trace_file: trace_files/k8s_audit/attach_cluster_admin_role.json
|
||||
|
||||
create_cluster_role_wildcard_resources:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- ClusterRole With Wildcard Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_cluster_role_wildcard_resources.json
|
||||
|
||||
create_cluster_role_wildcard_verbs:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- ClusterRole With Wildcard Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_cluster_role_wildcard_verbs.json
|
||||
|
||||
create_writable_cluster_role:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- ClusterRole With Write Privileges Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_cluster_role_write_privileges.json
|
||||
|
||||
create_pod_exec_cluster_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- ClusterRole With Pod Exec Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_cluster_role_pod_exec.json
|
||||
|
||||
create_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Deployment Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_deployment.json
|
||||
|
||||
delete_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Deployment Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_deployment.json
|
||||
|
||||
create_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Service Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_service.json
|
||||
|
||||
delete_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Service Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_service.json
|
||||
|
||||
create_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s ConfigMap Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_configmap.json
|
||||
|
||||
delete_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s ConfigMap Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_configmap.json
|
||||
|
||||
create_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
- ./rules/k8s_audit/allow_namespace_foo.yaml
|
||||
- ./rules/k8s_audit/allow_user_some-user.yaml
|
||||
detect_counts:
|
||||
- K8s Namespace Created: 1
|
||||
trace_file: trace_files/k8s_audit/some-user_creates_namespace_foo.json
|
||||
|
||||
delete_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Namespace Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_namespace_foo.json
|
||||
|
||||
create_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Serviceaccount Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_serviceaccount.json
|
||||
|
||||
delete_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Serviceaccount Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_serviceaccount.json
|
||||
|
||||
create_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Role/Clusterrole Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_clusterrole.json
|
||||
|
||||
delete_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Role/Clusterrole Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_clusterrole.json
|
||||
|
||||
create_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Role/Clusterrolebinding Created: 1
|
||||
trace_file: trace_files/k8s_audit/create_clusterrolebinding.json
|
||||
|
||||
delete_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
- ../rules/k8s_audit_rules.yaml
|
||||
detect_counts:
|
||||
- K8s Role/Clusterrolebinding Deleted: 1
|
||||
trace_file: trace_files/k8s_audit/delete_clusterrolebinding.json
|
3
test/rules/k8s_audit/allow_namespace_foo.yaml
Normal file
3
test/rules/k8s_audit/allow_namespace_foo.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
- list: allowed_namespaces
|
||||
items: [foo]
|
||||
append: true
|
3
test/rules/k8s_audit/allow_nginx_container.yaml
Normal file
3
test/rules/k8s_audit/allow_nginx_container.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
- macro: allowed_k8s_containers
|
||||
condition: (ka.req.container.image.repository=nginx)
|
||||
|
3
test/rules/k8s_audit/allow_only_apache_container.yaml
Normal file
3
test/rules/k8s_audit/allow_only_apache_container.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
- macro: allowed_k8s_containers
|
||||
condition: (ka.req.container.image.repository=apache)
|
||||
|
3
test/rules/k8s_audit/allow_user_some-user.yaml
Normal file
3
test/rules/k8s_audit/allow_user_some-user.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
- list: allowed_k8s_users
|
||||
items: [some-user]
|
||||
append: true
|
2
test/rules/k8s_audit/disallow_kactivity.yaml
Normal file
2
test/rules/k8s_audit/disallow_kactivity.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
- macro: consider_activity_events
|
||||
condition: (not jevt.rawtime exists)
|
3
test/rules/k8s_audit/trust_nginx_container.yaml
Normal file
3
test/rules/k8s_audit/trust_nginx_container.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
- list: trusted_k8s_containers
|
||||
items: [nginx]
|
||||
append: true
|
@ -74,7 +74,7 @@ function run_tests() {
|
||||
rm -rf /tmp/falco_outputs
|
||||
mkdir /tmp/falco_outputs
|
||||
TEST_RC=0
|
||||
for mult in $SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml; do
|
||||
for mult in $SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml $SCRIPTDIR/falco_k8s_audit_tests.yaml; do
|
||||
CMD="avocado run --multiplex $mult --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py"
|
||||
echo "Running: $CMD"
|
||||
$CMD
|
||||
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-25T13:58:49Z"},"level":"Request","timestamp":"2018-10-25T13:58:49Z","auditID":"841d3e6d-90d2-43df-8da4-684738bee3d5","stage":"ResponseComplete","requestURI":"/api/v1/namespaces","verb":"create","user":{"username":"system:anonymous","groups":["system:masters","system:authenticated"]},"sourceIPs":["192.168.99.1"],"objectRef":{"resource":"namespaces","name":"foo","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"foo","creationTimestamp":null},"spec":{},"status":{"phase":"Active"}},"requestReceivedTimestamp":"2018-10-25T13:58:49.730588Z","stageTimestamp":"2018-10-25T13:58:49.736141Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"c97244c7-4aec-4714-946a-8204d00ecdca","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:26:34Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader-binding","resource":"clusterrolebindings"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T20:09:26Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader-binding"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"cluster-admin"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"minikube"}]},"requestReceivedTimestamp":"2018-10-26T14:26:34.238974Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterrolebindings","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T14:26:34Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader-binding","resourceVersion":"270440","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/some-reader-binding","uid":"23cf124e-d92b-11e8-a2e6-080027728ac4"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"cluster-admin"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"minikube"}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:26:34.242498Z","timestamp":"2018-10-26T14:26:34Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
1
test/trace_files/k8s_audit/attach_pod.json
Normal file
1
test/trace_files/k8s_audit/attach_pod.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"14933b4e-7b09-4964-af09-b7987367e1ba","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:42:46Z"},"objectRef":{"apiVersion":"v1","name":"nginx-deployment-7998647bdf-phvq7","namespace":"default","resource":"pods","subresource":"attach"},"requestReceivedTimestamp":"2018-10-26T13:42:46.764713Z","requestURI":"/api/v1/namespaces/default/pods/nginx-deployment-7998647bdf-phvq7/attach?container=nginx1&stderr=true&stdin=true&stdout=true","responseStatus":{"code":101,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseStarted","stageTimestamp":"2018-10-26T13:42:46.808261Z","timestamp":"2018-10-26T13:42:46Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"412a7809-4016-4228-a670-182be4bd18ec","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:35:50Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T17:41:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader"},"rules":[{"apiGroups":[""],"resources":["pods/exec"],"verbs":["get"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"requestReceivedTimestamp":"2018-10-26T14:35:50.621493Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T14:35:50Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader","resourceVersion":"271128","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","uid":"6f7056c9-d92c-11e8-a2e6-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["pods/exec"],"verbs":["get"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:35:50.627514Z","timestamp":"2018-10-26T14:35:50Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"d8964c73-0042-4f35-b253-4711092185a1","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:21:53Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T17:41:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader"},"rules":[{"apiGroups":[""],"resources":["*"],"verbs":["get","list"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"requestReceivedTimestamp":"2018-10-26T14:21:53.885708Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T14:21:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader","resourceVersion":"270097","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","uid":"7cb48d08-d92a-11e8-a2e6-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["*"],"verbs":["get","list"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:21:53.889736Z","timestamp":"2018-10-26T14:21:53Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"0dc97dd4-aa34-4071-b8cc-e1a1036c9ae9","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:24:52Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T17:41:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["*"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"requestReceivedTimestamp":"2018-10-26T14:24:52.737769Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T14:24:52Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader","resourceVersion":"270313","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","uid":"e74f3721-d92a-11e8-a2e6-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["*"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:24:52.741516Z","timestamp":"2018-10-26T14:24:52Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"25cd4bc5-50c9-4ce5-aa9a-b9ab6065929d","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:32:32Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T17:41:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["get"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["delete"]}]},"requestReceivedTimestamp":"2018-10-26T14:32:32.179779Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T14:32:32Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader","resourceVersion":"270886","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","uid":"f9287d7e-d92b-11e8-a2e6-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["get"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["delete"]}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:32:32.182250Z","timestamp":"2018-10-26T14:32:32Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
1
test/trace_files/k8s_audit/create_clusterrole.json
Normal file
1
test/trace_files/k8s_audit/create_clusterrole.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"b8dfba33-a6e6-4f3c-b2cb-d47de1c4e74b","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:25:18Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T17:41:53Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["get","list"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"requestReceivedTimestamp":"2018-10-26T13:25:18.266512Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T13:25:18Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader","resourceVersion":"266028","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","uid":"94c25719-d922-11e8-a2e6-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["get","list"]},{"nonResourceURLs":["/api","/api/*","/apis","/apis/*","/healthz","/openapi","/openapi/*","/swagger-2.0.0.pb-v1","/swagger.json","/swaggerapi","/swaggerapi/*","/version","/version/"],"verbs":["get"]}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:25:18.275010Z","timestamp":"2018-10-26T13:25:18Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"e040d0ab-473c-442c-9707-88553173e962","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:25:18Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader-binding","resource":"clusterrolebindings"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-02T20:09:26Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader-binding"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"system:basic-user"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"minikube"}]},"requestReceivedTimestamp":"2018-10-26T13:25:18.279128Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterrolebindings","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-26T13:25:18Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"some-reader-binding","resourceVersion":"266029","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/some-reader-binding","uid":"94c41db6-d922-11e8-a2e6-080027728ac4"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"system:basic-user"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"minikube"}]},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:25:18.286399Z","timestamp":"2018-10-26T13:25:18Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
1
test/trace_files/k8s_audit/create_configmap.json
Normal file
1
test/trace_files/k8s_audit/create_configmap.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"155b887c-c27c-4fdb-9667-999f74512d0a","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:13:03Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T13:13:03.539180Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T13:13:03Z","name":"my-config","namespace":"default","resourceVersion":"265142","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"ded3abb9-d920-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:13:03.544952Z","timestamp":"2018-10-26T13:13:03Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-26T09:55:02Z"},"level":"RequestResponse","timestamp":"2018-10-26T09:55:02Z","auditID":"87bac0ec-53ca-429b-a646-47a7e72ec075","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/configmaps","verb":"create","user":{"username":"minikube-user","groups":["system:masters","system:authenticated"]},"sourceIPs":["192.168.99.1"],"objectRef":{"resource":"configmaps","namespace":"default","name":"my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985","creationTimestamp":"2016-02-18T18:52:05Z"},"data":{"ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"}},"responseObject":{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"34ffc893-d905-11e8-a2e6-080027728ac4","resourceVersion":"260014","creationTimestamp":"2018-10-26T09:55:02Z"},"data":{"ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"}},"requestReceivedTimestamp":"2018-10-26T09:55:02.203917Z","stageTimestamp":"2018-10-26T09:55:02.209473Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -0,0 +1,6 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"9a2bb12c-b9f7-4d34-809c-671e8c7e1e06","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:51:20Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"aws_access_key_id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:51:20.713631Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"aws_access_key_id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:51:20Z","name":"my-config","namespace":"default","resourceVersion":"259736","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b0fb2adf-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:51:20.717013Z","timestamp":"2018-10-26T09:51:20Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"7139c58a-6787-4cb2-8fd4-c68c308fc16d","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:51:35Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"aws-access-key-id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:51:35.560447Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"aws-access-key-id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:51:35Z","name":"my-config","namespace":"default","resourceVersion":"259757","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b9d485f5-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:51:35.562843Z","timestamp":"2018-10-26T09:51:35Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"d222e22f-0947-4af0-853e-c66f6bd0de33","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:51:53Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"aws_s3_access_key_id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:51:53.345483Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"aws_s3_access_key_id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:51:53Z","name":"my-config","namespace":"default","resourceVersion":"259779","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"c46e4724-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:51:53.350181Z","timestamp":"2018-10-26T09:51:53Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"7e93bc59-e3da-4d53-95bc-e3faaf54ffdb","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:52:07Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"aws-s3-access-key-id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:52:07.604912Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"aws-s3-access-key-id = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:52:07Z","name":"my-config","namespace":"default","resourceVersion":"259799","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"ccee22d8-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:52:07.608730Z","timestamp":"2018-10-26T09:52:07Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"6beb5311-472e-48ce-80a6-4c6388fc17da","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:52:20Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"password = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:52:20.656719Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"password = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:52:20Z","name":"my-config","namespace":"default","resourceVersion":"259816","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"d4b5acb5-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:52:20.659835Z","timestamp":"2018-10-26T09:52:20Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"efafb264-39ca-444f-b70e-0180f47f5628","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:52:32Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"},"requestObject":{"apiVersion":"v1","data":{"access.properties":"passphrase = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2016-02-18T18:52:05Z","name":"my-config","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"b4952dc3-d670-11e5-8cd0-68f728db1985"}},"requestReceivedTimestamp":"2018-10-26T09:52:32.985422Z","requestURI":"/api/v1/namespaces/default/configmaps","responseObject":{"apiVersion":"v1","data":{"access.properties":"passphrase = MY-ID\naws_secret_access_key = MY-KEY\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\n"},"kind":"ConfigMap","metadata":{"creationTimestamp":"2018-10-26T09:52:32Z","name":"my-config","namespace":"default","resourceVersion":"259832","selfLink":"/api/v1/namespaces/default/configmaps/my-config","uid":"dc0edea8-d904-11e8-a2e6-080027728ac4"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:52:32.993095Z","timestamp":"2018-10-26T09:52:32Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
1
test/trace_files/k8s_audit/create_deployment.json
Normal file
1
test/trace_files/k8s_audit/create_deployment.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"c5be68f4-80b8-4159-adc4-76ddb195334e","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:07:36Z"},"objectRef":{"apiGroup":"extensions","apiVersion":"v1beta1","name":"nginx-deployment","namespace":"default","resource":"deployments"},"requestObject":{"apiVersion":"extensions/v1beta1","kind":"Deployment","metadata":{"creationTimestamp":null,"labels":{"app":"demo","name":"nginx-deployment"},"name":"nginx-deployment","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":1},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{}},"requestReceivedTimestamp":"2018-10-26T13:07:36.905144Z","requestURI":"/apis/extensions/v1beta1/namespaces/default/deployments","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:07:36.911442Z","timestamp":"2018-10-26T13:07:36Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"24142dcc-7ad8-4fb8-8d3b-106ea474759d","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T09:09:28Z"},"objectRef":{"apiVersion":"v1","namespace":"default","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-754c877bcd-","labels":{"app":"nginx","pod-template-hash":"3107433678"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-754c877bcd","uid":"d7ab5eb7-d8fe-11e8-a2e6-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","hostNetwork":true,"restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}},"requestReceivedTimestamp":"2018-10-26T09:09:28.664442Z","requestURI":"/api/v1/namespaces/default/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-26T09:09:28Z","generateName":"nginx-deployment-754c877bcd-","labels":{"app":"nginx","pod-template-hash":"3107433678"},"name":"nginx-deployment-754c877bcd-zh5qx","namespace":"default","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-754c877bcd","uid":"d7ab5eb7-d8fe-11e8-a2e6-080027728ac4"}],"resourceVersion":"256667","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-754c877bcd-zh5qx","uid":"d7afa813-d8fe-11e8-a2e6-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]}],"dnsPolicy":"ClusterFirst","hostNetwork":true,"restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"name":"default-token-g2sp7","secret":{"defaultMode":420,"secretName":"default-token-g2sp7"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:09:28.675492Z","timestamp":"2018-10-26T09:09:28Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-26T09:13:36Z"},"level":"RequestResponse","timestamp":"2018-10-26T09:13:36Z","auditID":"1354641c-135e-4adb-8052-43e125ea8690","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/pods","verb":"create","user":{"username":"system:serviceaccount:kube-system:replicaset-controller","uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"]},"sourceIPs":["::1"],"objectRef":{"resource":"pods","namespace":"default","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Pod","apiVersion":"v1","metadata":{"generateName":"nginx-deployment-7998647bdf-","creationTimestamp":null,"labels":{"app":"nginx","pod-template-hash":"3554203689"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"6b5785c8-d8ff-11e8-a2e6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"containers":[{"name":"nginx1","image":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"},"status":{}},"responseObject":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-deployment-7998647bdf-4j7t7","generateName":"nginx-deployment-7998647bdf-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-7998647bdf-4j7t7","uid":"6b5d73bb-d8ff-11e8-a2e6-080027728ac4","resourceVersion":"256987","creationTimestamp":"2018-10-26T09:13:36Z","labels":{"app":"nginx","pod-template-hash":"3554203689"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"6b5785c8-d8ff-11e8-a2e6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"default-token-g2sp7","secret":{"secretName":"default-token-g2sp7","defaultMode":420}}],"containers":[{"name":"nginx1","image":"nginx","resources":{},"volumeMounts":[{"name":"default-token-g2sp7","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"requestReceivedTimestamp":"2018-10-26T09:13:36.411557Z","stageTimestamp":"2018-10-26T09:13:36.445965Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""}}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-25T14:09:12Z"},"level":"RequestResponse","timestamp":"2018-10-25T14:09:12Z","auditID":"a362d22b-db3c-4590-9505-23782f12925f","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/pods","verb":"create","user":{"username":"system:serviceaccount:kube-system:replicaset-controller","uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"]},"sourceIPs":["::1"],"objectRef":{"resource":"pods","namespace":"default","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Pod","apiVersion":"v1","metadata":{"generateName":"nginx-deployment-5cdcc99dbf-","creationTimestamp":null,"labels":{"app":"nginx","pod-template-hash":"1787755869"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-5cdcc99dbf","uid":"8c800470-d85f-11e8-88b6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"containers":[{"name":"nginx","image":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always","securityContext":{"privileged":true}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"},"status":{}},"responseObject":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-deployment-5cdcc99dbf-rgw6z","generateName":"nginx-deployment-5cdcc99dbf-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-5cdcc99dbf-rgw6z","uid":"8c845395-d85f-11e8-88b6-080027728ac4","resourceVersion":"237252","creationTimestamp":"2018-10-25T14:09:12Z","labels":{"app":"nginx","pod-template-hash":"1787755869"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-5cdcc99dbf","uid":"8c800470-d85f-11e8-88b6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"default-token-g2sp7","secret":{"secretName":"default-token-g2sp7","defaultMode":420}}],"containers":[{"name":"nginx","image":"nginx","resources":{},"volumeMounts":[{"name":"default-token-g2sp7","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always","securityContext":{"privileged":true}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"requestReceivedTimestamp":"2018-10-25T14:09:12.572676Z","stageTimestamp":"2018-10-25T14:09:12.581541Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""}}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"f83ecd50-5bf4-4fe7-a419-dea22852ca49","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-25T17:53:07Z"},"objectRef":{"apiVersion":"v1","namespace":"default","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-544b59f8b8-","labels":{"app":"nginx","pod-template-hash":"1006159464"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-544b59f8b8","uid":"d40b40e1-d87e-11e8-a473-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"securityContext":{"privileged":false},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"},{"image":"mysql:latest","imagePullPolicy":"Always","name":"mysql","resources":{},"securityContext":{"privileged":true},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}},"requestReceivedTimestamp":"2018-10-25T17:53:06.995407Z","requestURI":"/api/v1/namespaces/default/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-25T17:53:06Z","generateName":"nginx-deployment-544b59f8b8-","labels":{"app":"nginx","pod-template-hash":"1006159464"},"name":"nginx-deployment-544b59f8b8-ffkxm","namespace":"default","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-544b59f8b8","uid":"d40b40e1-d87e-11e8-a473-080027728ac4"}],"resourceVersion":"246302","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-544b59f8b8-ffkxm","uid":"d40dfcd7-d87e-11e8-a473-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"securityContext":{"privileged":false},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]},{"image":"mysql:latest","imagePullPolicy":"Always","name":"mysql","resources":{},"securityContext":{"privileged":true},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"name":"default-token-g2sp7","secret":{"defaultMode":420,"secretName":"default-token-g2sp7"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-25T17:53:07.006845Z","timestamp":"2018-10-25T17:53:06Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"e456c9cf-9abe-4fa1-8526-e014da96821b","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-25T17:36:11Z"},"objectRef":{"apiVersion":"v1","namespace":"default","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-7d5b5dd9cf-","labels":{"app":"nginx","pod-template-hash":"3816188579"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7d5b5dd9cf","uid":"76dd668b-d87c-11e8-88b6-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/host/etc","name":"etc"}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30,"volumes":[{"hostPath":{"path":"/etc","type":""},"name":"etc"}]},"status":{}},"requestReceivedTimestamp":"2018-10-25T17:36:11.686139Z","requestURI":"/api/v1/namespaces/default/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-25T17:36:11Z","generateName":"nginx-deployment-7d5b5dd9cf-","labels":{"app":"nginx","pod-template-hash":"3816188579"},"name":"nginx-deployment-7d5b5dd9cf-t8ngb","namespace":"default","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7d5b5dd9cf","uid":"76dd668b-d87c-11e8-88b6-080027728ac4"}],"resourceVersion":"245060","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-7d5b5dd9cf-t8ngb","uid":"76e27404-d87c-11e8-88b6-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/host/etc","name":"etc"},{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"hostPath":{"path":"/etc","type":""},"name":"etc"},{"name":"default-token-g2sp7","secret":{"defaultMode":420,"secretName":"default-token-g2sp7"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-25T17:36:11.693676Z","timestamp":"2018-10-25T17:36:11Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"9a52c28e-d6f2-480e-a11e-686fb583a0ff","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-25T17:55:18Z"},"objectRef":{"apiVersion":"v1","namespace":"default","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-f7f486546-","labels":{"app":"nginx","pod-template-hash":"939042102"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-f7f486546","uid":"22a5bd9c-d87f-11e8-a473-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"},{"image":"mysql:latest","imagePullPolicy":"Always","name":"mysql","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/host/etc","name":"etc"}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30,"volumes":[{"hostPath":{"path":"/etc","type":""},"name":"etc"}]},"status":{}},"requestReceivedTimestamp":"2018-10-25T17:55:18.886267Z","requestURI":"/api/v1/namespaces/default/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-25T17:55:18Z","generateName":"nginx-deployment-f7f486546-","labels":{"app":"nginx","pod-template-hash":"939042102"},"name":"nginx-deployment-f7f486546-hzhsw","namespace":"default","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-f7f486546","uid":"22a5bd9c-d87f-11e8-a473-080027728ac4"}],"resourceVersion":"246482","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-f7f486546-hzhsw","uid":"22aed08a-d87f-11e8-a473-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]},{"image":"mysql:latest","imagePullPolicy":"Always","name":"mysql","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/host/etc","name":"etc"},{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-g2sp7","readOnly":true}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"hostPath":{"path":"/etc","type":""},"name":"etc"},{"name":"default-token-g2sp7","secret":{"defaultMode":420,"secretName":"default-token-g2sp7"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-25T17:55:18.926631Z","timestamp":"2018-10-25T17:55:18Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-25T14:09:49Z"},"level":"RequestResponse","timestamp":"2018-10-25T14:09:49Z","auditID":"7c8b2603-6a87-4764-b166-49dd7fa46f4c","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/pods","verb":"create","user":{"username":"system:serviceaccount:kube-system:replicaset-controller","uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"]},"sourceIPs":["::1"],"objectRef":{"resource":"pods","namespace":"default","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Pod","apiVersion":"v1","metadata":{"generateName":"nginx-deployment-78f5d695bd-","creationTimestamp":null,"labels":{"app":"nginx","pod-template-hash":"3491825168"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-78f5d695bd","uid":"a2a78691-d85f-11e8-88b6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"containers":[{"name":"nginx","image":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"},"status":{}},"responseObject":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-deployment-78f5d695bd-nxqz5","generateName":"nginx-deployment-78f5d695bd-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-78f5d695bd-nxqz5","uid":"a2ad81ba-d85f-11e8-88b6-080027728ac4","resourceVersion":"237324","creationTimestamp":"2018-10-25T14:09:49Z","labels":{"app":"nginx","pod-template-hash":"3491825168"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-78f5d695bd","uid":"a2a78691-d85f-11e8-88b6-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"default-token-g2sp7","secret":{"secretName":"default-token-g2sp7","defaultMode":420}}],"containers":[{"name":"nginx","image":"nginx","resources":{},"volumeMounts":[{"name":"default-token-g2sp7","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"requestReceivedTimestamp":"2018-10-25T14:09:49.750328Z","stageTimestamp":"2018-10-25T14:09:49.761315Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""}}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-25T17:44:08Z"},"level":"RequestResponse","timestamp":"2018-10-25T17:44:08Z","auditID":"8ae98948-66b2-4963-9b99-e2b7e2f499f1","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/pods/nginx-deployment-6dfd8b84dc-6dkjc","verb":"get","user":{"username":"system:node:minikube","groups":["system:nodes","system:authenticated"]},"sourceIPs":["127.0.0.1"],"objectRef":{"resource":"pods","namespace":"default","name":"nginx-deployment-6dfd8b84dc-6dkjc","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"responseObject":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-deployment-6dfd8b84dc-6dkjc","generateName":"nginx-deployment-6dfd8b84dc-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-deployment-6dfd8b84dc-6dkjc","uid":"90a9d57f-d87d-11e8-a473-080027728ac4","resourceVersion":"245564","creationTimestamp":"2018-10-25T17:44:04Z","labels":{"app":"nginx","pod-template-hash":"2898464087"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"nginx-deployment-6dfd8b84dc","uid":"90a4bba5-d87d-11e8-a473-080027728ac4","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"etc","hostPath":{"path":"/usr/local","type":""}},{"name":"default-token-g2sp7","secret":{"secretName":"default-token-g2sp7","defaultMode":420}}],"containers":[{"name":"nginx","image":"nginx","resources":{},"volumeMounts":[{"name":"etc","mountPath":"/host/etc"},{"name":"default-token-g2sp7","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"minikube","securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}]},"status":{"phase":"Pending","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2018-10-25T17:44:04Z"},{"type":"Ready","status":"False","lastProbeTime":null,"lastTransitionTime":"2018-10-25T17:44:04Z","reason":"ContainersNotReady","message":"containers with unready status: [nginx]"},{"type":"ContainersReady","status":"False","lastProbeTime":null,"lastTransitionTime":null,"reason":"ContainersNotReady","message":"containers with unready status: [nginx]"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2018-10-25T17:44:04Z"}],"hostIP":"10.0.2.15","startTime":"2018-10-25T17:44:04Z","containerStatuses":[{"name":"nginx","state":{"waiting":{"reason":"ContainerCreating"}},"lastState":{},"ready":false,"restartCount":0,"image":"nginx","imageID":""}],"qosClass":"BestEffort"}},"requestReceivedTimestamp":"2018-10-25T17:44:08.581856Z","stageTimestamp":"2018-10-25T17:44:08.583534Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -0,0 +1,2 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"ceba9ec7-50b5-4bde-8b27-5909df9b35c9","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T09:31:36Z"},"objectRef":{"apiVersion":"v1","name":"nginx","namespace":"default","resource":"services"},"requestObject":{"apiVersion":"v1","kind":"Service","metadata":{"creationTimestamp":null,"labels":{"app":"nginx"},"name":"nginx","namespace":"default"},"spec":{"externalTrafficPolicy":"Cluster","ports":[{"nodePort":30080,"port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"nginx"},"sessionAffinity":"None","type":"NodePort"},"status":{"loadBalancer":{}}},"requestReceivedTimestamp":"2018-10-26T09:31:36.115986Z","requestURI":"/api/v1/namespaces/default/services","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T09:31:36.134295Z","timestamp":"2018-10-26T09:31:36Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
||||
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-26T09:38:36Z"},"level":"Request","timestamp":"2018-10-26T09:38:36Z","auditID":"abcc3bac-bb66-475f-9be9-61afae0e18e3","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/default/services","verb":"create","user":{"username":"minikube-user","groups":["system:masters","system:authenticated"]},"sourceIPs":["192.168.99.1"],"objectRef":{"resource":"services","namespace":"default","name":"nginx","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Service","apiVersion":"v1","metadata":{"name":"nginx","namespace":"default","creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"ports":[{"protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"nginx"},"type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}},"requestReceivedTimestamp":"2018-10-26T09:38:36.480694Z","stageTimestamp":"2018-10-26T09:38:36.493490Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"fc32bb0a-28ce-4013-b89f-97af003ea5dc","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:49:19Z"},"objectRef":{"apiVersion":"v1","namespace":"kube-public","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-7998647bdf-","labels":{"app":"nginx","pod-template-hash":"3554203689"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"efbad48a-d925-11e8-a2e6-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}},"requestReceivedTimestamp":"2018-10-26T13:49:19.452048Z","requestURI":"/api/v1/namespaces/kube-public/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-26T13:49:19Z","generateName":"nginx-deployment-7998647bdf-","labels":{"app":"nginx","pod-template-hash":"3554203689"},"name":"nginx-deployment-7998647bdf-tlpkp","namespace":"kube-public","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"efbad48a-d925-11e8-a2e6-080027728ac4"}],"resourceVersion":"267705","selfLink":"/api/v1/namespaces/kube-public/pods/nginx-deployment-7998647bdf-tlpkp","uid":"efc695f8-d925-11e8-a2e6-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-9m2xw","readOnly":true}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"name":"default-token-9m2xw","secret":{"defaultMode":420,"secretName":"default-token-9m2xw"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:49:19.465798Z","timestamp":"2018-10-26T13:49:19Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:controller:replicaset-controller\" of ClusterRole \"system:controller:replicaset-controller\" to ServiceAccount \"replicaset-controller/kube-system\""},"auditID":"5fad6ec1-4e15-4753-b180-ae9fd096f511","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:47:20Z"},"objectRef":{"apiVersion":"v1","namespace":"kube-system","resource":"pods"},"requestObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"generateName":"nginx-deployment-7998647bdf-","labels":{"app":"nginx","pod-template-hash":"3554203689"},"ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"a8beebfd-d925-11e8-a2e6-080027728ac4"}]},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}},"requestReceivedTimestamp":"2018-10-26T13:47:20.328666Z","requestURI":"/api/v1/namespaces/kube-system/pods","responseObject":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2018-10-26T13:47:20Z","generateName":"nginx-deployment-7998647bdf-","labels":{"app":"nginx","pod-template-hash":"3554203689"},"name":"nginx-deployment-7998647bdf-7q2sp","namespace":"kube-system","ownerReferences":[{"apiVersion":"apps/v1","blockOwnerDeletion":true,"controller":true,"kind":"ReplicaSet","name":"nginx-deployment-7998647bdf","uid":"a8beebfd-d925-11e8-a2e6-080027728ac4"}],"resourceVersion":"267526","selfLink":"/api/v1/namespaces/kube-system/pods/nginx-deployment-7998647bdf-7q2sp","uid":"a8c4e188-d925-11e8-a2e6-080027728ac4"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"Always","name":"nginx1","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"default-token-swtbm","readOnly":true}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],"volumes":[{"name":"default-token-swtbm","secret":{"defaultMode":420,"secretName":"default-token-swtbm"}}]},"status":{"phase":"Pending","qosClass":"BestEffort"}},"responseStatus":{"code":201,"metadata":{}},"sourceIPs":["::1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:47:20.367055Z","timestamp":"2018-10-26T13:47:20Z","user":{"groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"uid":"8d5e1349-d30f-11e8-96d9-080027728ac4","username":"system:serviceaccount:kube-system:replicaset-controller"},"verb":"create"}
|
1
test/trace_files/k8s_audit/create_service.json
Normal file
1
test/trace_files/k8s_audit/create_service.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"75b22022-d079-44e5-90a7-25be235ab235","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:12:04Z"},"objectRef":{"apiVersion":"v1","name":"nginx","namespace":"default","resource":"services"},"requestObject":{"apiVersion":"v1","kind":"Service","metadata":{"creationTimestamp":null,"labels":{"app":"nginx"},"name":"nginx","namespace":"default"},"spec":{"ports":[{"port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"nginx"},"sessionAffinity":"None","type":"ClusterIP"},"status":{"loadBalancer":{}}},"requestReceivedTimestamp":"2018-10-26T13:12:04.404107Z","requestURI":"/api/v1/namespaces/default/services","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:12:04.420072Z","timestamp":"2018-10-26T13:12:04Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
1
test/trace_files/k8s_audit/create_serviceaccount.json
Normal file
1
test/trace_files/k8s_audit/create_serviceaccount.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"db4a8c97-3bd2-4ee7-a0d9-b6e367a458e3","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:19:22Z"},"objectRef":{"apiVersion":"v1","name":"myacct","namespace":"default","resource":"serviceaccounts"},"requestObject":{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":null,"name":"myacct"}},"requestReceivedTimestamp":"2018-10-26T13:19:22.126375Z","requestURI":"/api/v1/namespaces/default/serviceaccounts","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:19:22.135026Z","timestamp":"2018-10-26T13:19:22Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"3380c3ae-8bb5-40af-b247-fc0838abfb56","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:56:56Z"},"objectRef":{"apiVersion":"v1","name":"myacct","namespace":"kube-public","resource":"serviceaccounts"},"requestObject":{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":null,"name":"myacct"}},"requestReceivedTimestamp":"2018-10-26T13:56:56.598787Z","requestURI":"/api/v1/namespaces/kube-public/serviceaccounts","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:56:56.608329Z","timestamp":"2018-10-26T13:56:56Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"6754ae2b-6b72-4405-a773-28e53a74c720","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:56:43Z"},"objectRef":{"apiVersion":"v1","name":"myacct","namespace":"kube-system","resource":"serviceaccounts"},"requestObject":{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":null,"name":"myacct"}},"requestReceivedTimestamp":"2018-10-26T13:56:43.556449Z","requestURI":"/api/v1/namespaces/kube-system/serviceaccounts","responseStatus":{"code":201,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:56:43.585256Z","timestamp":"2018-10-26T13:56:43Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"5bc4e194-a14b-4ed6-af2c-0583224eb55f","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:02:54Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"system:kube-aggregator","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T14:02:54.905205Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles/system:kube-aggregator","responseObject":{"apiVersion":"v1","details":{"group":"rbac.authorization.k8s.io","kind":"clusterroles","name":"system:kube-aggregator","uid":"8b9bb96e-d30f-11e8-96d9-080027728ac4"},"kind":"Status","metadata":{},"status":"Success"},"responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:02:54.911160Z","timestamp":"2018-10-26T14:02:54Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
1
test/trace_files/k8s_audit/delete_clusterrole.json
Normal file
1
test/trace_files/k8s_audit/delete_clusterrole.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"452ed910-bbe7-419d-a72d-bcd6e085f5a9","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:25:37Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader","resource":"clusterroles"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:25:37.806584Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles/some-reader","responseObject":{"apiVersion":"v1","details":{"group":"rbac.authorization.k8s.io","kind":"clusterroles","name":"some-reader","uid":"94c25719-d922-11e8-a2e6-080027728ac4"},"kind":"Status","metadata":{},"status":"Success"},"responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:25:37.813327Z","timestamp":"2018-10-26T13:25:37Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"0a73dc60-697a-4b92-bb6a-a1083e75bc97","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:25:47Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"some-reader-binding","resource":"clusterrolebindings"},"requestObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:25:47.035892Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/some-reader-binding","responseObject":{"apiVersion":"v1","details":{"group":"rbac.authorization.k8s.io","kind":"clusterrolebindings","name":"some-reader-binding","uid":"94c41db6-d922-11e8-a2e6-080027728ac4"},"kind":"Status","metadata":{},"status":"Success"},"responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:25:47.041484Z","timestamp":"2018-10-26T13:25:47Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
1
test/trace_files/k8s_audit/delete_configmap.json
Normal file
1
test/trace_files/k8s_audit/delete_configmap.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"179a9171-e02e-4227-8c41-8b2575f8da7d","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T13:13:33Z"},"objectRef":{"apiVersion":"v1","name":"my-config","namespace":"default","resource":"configmaps"},"requestObject":{"apiVersion":"v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:13:33.769302Z","requestURI":"/api/v1/namespaces/default/configmaps/my-config","responseObject":{"apiVersion":"v1","details":{"kind":"configmaps","name":"my-config","uid":"ded3abb9-d920-11e8-a2e6-080027728ac4"},"kind":"Status","metadata":{},"status":"Success"},"responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:13:33.773884Z","timestamp":"2018-10-26T13:13:33Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
1
test/trace_files/k8s_audit/delete_deployment.json
Normal file
1
test/trace_files/k8s_audit/delete_deployment.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"8bb19a30-3d68-4fd7-999e-90a9014afac0","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:09:27Z"},"objectRef":{"apiGroup":"extensions","apiVersion":"v1beta1","name":"nginx-deployment","namespace":"default","resource":"deployments"},"requestObject":{"apiVersion":"extensions/v1beta1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:09:27.540198Z","requestURI":"/apis/extensions/v1beta1/namespaces/default/deployments/nginx-deployment","responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:09:27.548380Z","timestamp":"2018-10-26T13:09:27Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
1
test/trace_files/k8s_audit/delete_namespace_foo.json
Normal file
1
test/trace_files/k8s_audit/delete_namespace_foo.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"693f4726-2430-450a-83e1-123c050fde98","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:00:25Z"},"objectRef":{"apiVersion":"v1","name":"foo","namespace":"foo","resource":"namespaces"},"requestObject":{"apiVersion":"v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:00:25.241677Z","requestURI":"/api/v1/namespaces/foo","responseStatus":{"code":200,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:00:25.246927Z","timestamp":"2018-10-26T13:00:25Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
1
test/trace_files/k8s_audit/delete_service.json
Normal file
1
test/trace_files/k8s_audit/delete_service.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"e75d04b8-60f8-43b6-9410-d229f4f855e7","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:11:02Z"},"objectRef":{"apiVersion":"v1","name":"nginx","namespace":"default","resource":"services"},"requestObject":{"apiVersion":"v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:11:02.021214Z","requestURI":"/api/v1/namespaces/default/services/nginx","responseStatus":{"code":200,"metadata":{},"status":"Success"},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:11:02.065150Z","timestamp":"2018-10-26T13:11:02Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
2
test/trace_files/k8s_audit/delete_serviceaccount.json
Normal file
2
test/trace_files/k8s_audit/delete_serviceaccount.json
Normal file
@ -0,0 +1,2 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"9335a5a0-edc7-4ef0-9177-499c50f3d707","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:19:30Z"},"objectRef":{"apiVersion":"v1","name":"myacct","namespace":"default","resource":"serviceaccounts"},"requestObject":{"apiVersion":"v1","kind":"DeleteOptions","propagationPolicy":"Background"},"requestReceivedTimestamp":"2018-10-26T13:19:30.037091Z","requestURI":"/api/v1/namespaces/default/serviceaccounts/myacct","responseStatus":{"code":200,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T13:19:30.044205Z","timestamp":"2018-10-26T13:19:30Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"delete"}
|
||||
|
1
test/trace_files/k8s_audit/exec_pod.json
Normal file
1
test/trace_files/k8s_audit/exec_pod.json
Normal file
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"ca1ed9f4-7bf4-4c85-81a8-1b11253d1ba6","kind":"Event","level":"Request","metadata":{"creationTimestamp":"2018-10-26T13:38:34Z"},"objectRef":{"apiVersion":"v1","name":"nginx-deployment-7998647bdf-phvq7","namespace":"default","resource":"pods","subresource":"exec"},"requestReceivedTimestamp":"2018-10-26T13:38:34.867116Z","requestURI":"/api/v1/namespaces/default/pods/nginx-deployment-7998647bdf-phvq7/exec?command=bash&container=nginx1&container=nginx1&stdin=true&stdout=true&tty=true","responseStatus":{"code":101,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseStarted","stageTimestamp":"2018-10-26T13:38:34.902914Z","timestamp":"2018-10-26T13:38:34Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"create"}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-26T13:52:41Z"},"level":"Request","timestamp":"2018-10-26T13:52:41Z","auditID":"b7e177d2-f462-4f7e-8905-9e0f187e4e92","stage":"ResponseComplete","requestURI":"/api/v1/namespaces","verb":"create","user":{"username":"minikube-user","groups":["system:masters","system:authenticated"]},"sourceIPs":["192.168.99.1"],"objectRef":{"resource":"namespaces","name":"foo","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"foo","creationTimestamp":null},"spec":{},"status":{"phase":"Active"}},"requestReceivedTimestamp":"2018-10-26T13:52:41.553544Z","stageTimestamp":"2018-10-26T13:52:41.558192Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -0,0 +1 @@
|
||||
{"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""},"auditID":"0eaf743e-1668-4de5-940b-0bb5d79aa4c9","kind":"Event","level":"RequestResponse","metadata":{"creationTimestamp":"2018-10-26T14:11:51Z"},"objectRef":{"apiGroup":"rbac.authorization.k8s.io","apiVersion":"v1","name":"system:node-problem-detector","resource":"clusterroles"},"requestObject":{"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"ClusterRole\",\"metadata\":{\"annotations\":{\"rbac.authorization.kubernetes.io/autoupdate\":\"true\"},\"creationTimestamp\":\"2018-10-18T19:53:55Z\",\"labels\":{\"kubernetes.io/bootstrapping\":\"rbac-defaults\"},\"name\":\"system:node-problem-detector\",\"namespace\":\"\",\"resourceVersion\":\"62\",\"selfLink\":\"/apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Anode-problem-detector\",\"uid\":\"8b97f47e-d30f-11e8-96d9-080027728ac4\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"nodes\"],\"verbs\":[\"get\"]},{\"apiGroups\":[\"\"],\"resources\":[\"nodes/status\"],\"verbs\":[\"patch\"]},{\"apiGroups\":[\"\"],\"resources\":[\"events\"],\"verbs\":[\"create\",\"patch\",\"update\",\"list\"]}]}\n"},"namespace":""},"rules":[{"apiGroups":[""],"resources":["nodes"],"verbs":["get"]},{"apiGroups":[""],"resources":["nodes/status"],"verbs":["patch"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch","update","list"]}]},"requestReceivedTimestamp":"2018-10-26T14:11:51.033796Z","requestURI":"/apis/rbac.authorization.k8s.io/v1/clusterroles/system:node-problem-detector","responseObject":{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"ClusterRole\",\"metadata\":{\"annotations\":{\"rbac.authorization.kubernetes.io/autoupdate\":\"true\"},\"creationTimestamp\":\"2018-10-18T19:53:55Z\",\"labels\":{\"kubernetes.io/bootstrapping\":\"rbac-defaults\"},\"name\":\"system:node-problem-detector\",\"namespace\":\"\",\"resourceVersion\":\"62\",\"selfLink\":\"/apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Anode-problem-detector\",\"uid\":\"8b97f47e-d30f-11e8-96d9-080027728ac4\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"nodes\"],\"verbs\":[\"get\"]},{\"apiGroups\":[\"\"],\"resources\":[\"nodes/status\"],\"verbs\":[\"patch\"]},{\"apiGroups\":[\"\"],\"resources\":[\"events\"],\"verbs\":[\"create\",\"patch\",\"update\",\"list\"]}]}\n","rbac.authorization.kubernetes.io/autoupdate":"true"},"creationTimestamp":"2018-10-18T19:53:55Z","labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"system:node-problem-detector","resourceVersion":"269365","selfLink":"/apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Anode-problem-detector","uid":"8b97f47e-d30f-11e8-96d9-080027728ac4"},"rules":[{"apiGroups":[""],"resources":["nodes"],"verbs":["get"]},{"apiGroups":[""],"resources":["nodes/status"],"verbs":["patch"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch","update","list"]}]},"responseStatus":{"code":200,"metadata":{}},"sourceIPs":["192.168.99.1"],"stage":"ResponseComplete","stageTimestamp":"2018-10-26T14:11:51.039120Z","timestamp":"2018-10-26T14:11:51Z","user":{"groups":["system:masters","system:authenticated"],"username":"minikube-user"},"verb":"patch"}
|
@ -0,0 +1 @@
|
||||
{"kind":"Event","apiVersion":"audit.k8s.io/v1beta1","metadata":{"creationTimestamp":"2018-10-25T13:58:49Z"},"level":"Request","timestamp":"2018-10-25T13:58:49Z","auditID":"841d3e6d-90d2-43df-8da4-684738bee3d5","stage":"ResponseComplete","requestURI":"/api/v1/namespaces","verb":"create","user":{"username":"some-user","groups":["system:masters","system:authenticated"]},"sourceIPs":["192.168.99.1"],"objectRef":{"resource":"namespaces","name":"foo","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":201},"requestObject":{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"foo","creationTimestamp":null},"spec":{},"status":{"phase":"Active"}},"requestReceivedTimestamp":"2018-10-25T13:58:49.730588Z","stageTimestamp":"2018-10-25T13:58:49.736141Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
|
@ -20,12 +20,22 @@ include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libscap")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
|
||||
include_directories("${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
include_directories("${LUAJIT_INCLUDE}")
|
||||
include_directories("${NJSON_INCLUDE}")
|
||||
include_directories("${CURL_INCLUDE_DIR}")
|
||||
|
||||
add_library(falco_engine STATIC rules.cpp falco_common.cpp falco_engine.cpp token_bucket.cpp formats.cpp)
|
||||
add_library(falco_engine STATIC
|
||||
rules.cpp
|
||||
falco_common.cpp
|
||||
falco_engine.cpp
|
||||
json_evt.cpp
|
||||
ruleset.cpp
|
||||
token_bucket.cpp
|
||||
formats.cpp)
|
||||
|
||||
target_include_directories(falco_engine PUBLIC
|
||||
"${LUAJIT_INCLUDE}")
|
||||
"${LUAJIT_INCLUDE}"
|
||||
"${NJSON_INCLUDE}"
|
||||
"${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
|
||||
target_link_libraries(falco_engine
|
||||
"${FALCO_SINSP_LIBRARY}"
|
||||
|
@ -20,4 +20,4 @@ limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#define FALCO_ENGINE_LUA_DIR "${FALCO_ABSOLUTE_SHARE_DIR}/lua/"
|
||||
#define FALCO_ENGINE_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/../falco/userspace/engine/lua/"
|
||||
#define FALCO_ENGINE_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/userspace/engine/lua/"
|
||||
|
@ -55,7 +55,7 @@ void falco_common::set_inspector(sinsp *inspector)
|
||||
m_inspector = inspector;
|
||||
}
|
||||
|
||||
void falco_common::init(const char *lua_main_filename, const char *source_dir)
|
||||
void falco_common::init(const char *lua_main_filename, const char *alternate_lua_dir)
|
||||
{
|
||||
ifstream is;
|
||||
string lua_dir = FALCO_ENGINE_LUA_DIR;
|
||||
@ -64,7 +64,7 @@ void falco_common::init(const char *lua_main_filename, const char *source_dir)
|
||||
is.open(lua_main_path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
lua_dir = source_dir;
|
||||
lua_dir = alternate_lua_dir;
|
||||
lua_main_path = lua_dir + lua_main_filename;
|
||||
|
||||
is.open(lua_main_path);
|
||||
@ -72,7 +72,7 @@ void falco_common::init(const char *lua_main_filename, const char *source_dir)
|
||||
{
|
||||
throw falco_exception("Could not find Falco Lua entrypoint (tried " +
|
||||
string(FALCO_ENGINE_LUA_DIR) + lua_main_filename + ", " +
|
||||
string(source_dir) + lua_main_filename + ")");
|
||||
string(alternate_lua_dir) + lua_main_filename + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
falco_common();
|
||||
virtual ~falco_common();
|
||||
|
||||
void init(const char *lua_main_filename, const char *source_dir);
|
||||
void init(const char *lua_main_filename, const char *alternate_lua_dir);
|
||||
|
||||
void set_inspector(sinsp *inspector);
|
||||
|
||||
|
@ -40,7 +40,9 @@ string lua_print_stats = "print_stats";
|
||||
|
||||
using namespace std;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng, const std::string& source_dir)
|
||||
nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_json_pointer;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
|
||||
: m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
@ -49,10 +51,11 @@ falco_engine::falco_engine(bool seed_rng, const std::string& source_dir)
|
||||
luaopen_lpeg(m_ls);
|
||||
luaopen_yaml(m_ls);
|
||||
|
||||
falco_common::init(m_lua_main_filename.c_str(), source_dir.c_str());
|
||||
falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str());
|
||||
falco_rules::init(m_ls);
|
||||
|
||||
m_evttype_filter.reset(new sinsp_evttype_filter());
|
||||
m_sinsp_rules.reset(new falco_sinsp_ruleset());
|
||||
m_k8s_audit_rules.reset(new falco_ruleset());
|
||||
|
||||
if(seed_rng)
|
||||
{
|
||||
@ -60,6 +63,9 @@ falco_engine::falco_engine(bool seed_rng, const std::string& source_dir)
|
||||
}
|
||||
|
||||
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
|
||||
|
||||
// Create this now so we can potentially list filters and exit
|
||||
m_json_factory = make_shared<json_event_filter_factory>();
|
||||
}
|
||||
|
||||
falco_engine::~falco_engine()
|
||||
@ -78,9 +84,16 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
|
||||
throw falco_exception("No inspector provided");
|
||||
}
|
||||
|
||||
if(!m_sinsp_factory)
|
||||
{
|
||||
m_sinsp_factory = make_shared<sinsp_filter_factory>(m_inspector);
|
||||
}
|
||||
|
||||
if(!m_rules)
|
||||
{
|
||||
m_rules = new falco_rules(m_inspector, this, m_ls);
|
||||
m_rules = new falco_rules(m_inspector,
|
||||
this,
|
||||
m_ls);
|
||||
}
|
||||
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
@ -90,7 +103,7 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
|
||||
// json_output to false.
|
||||
bool json_output = false;
|
||||
bool json_include_output_property = false;
|
||||
falco_formats::init(m_inspector, m_ls, json_output, json_include_output_property);
|
||||
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property);
|
||||
|
||||
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority);
|
||||
}
|
||||
@ -117,7 +130,8 @@ void falco_engine::enable_rule(const string &pattern, bool enabled, const string
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
m_evttype_filter->enable(pattern, enabled, ruleset_id);
|
||||
m_sinsp_rules->enable(pattern, enabled, ruleset_id);
|
||||
m_k8s_audit_rules->enable(pattern, enabled, ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(const string &pattern, bool enabled)
|
||||
@ -129,7 +143,8 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
m_evttype_filter->enable_tags(tags, enabled, ruleset_id);
|
||||
m_sinsp_rules->enable_tags(tags, enabled, ruleset_id);
|
||||
m_k8s_audit_rules->enable_tags(tags, enabled, ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled)
|
||||
@ -160,24 +175,24 @@ void falco_engine::evttypes_for_ruleset(std::vector<bool> &evttypes, const std::
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
return m_evttype_filter->evttypes_for_ruleset(evttypes, ruleset_id);
|
||||
return m_sinsp_rules->evttypes_for_ruleset(evttypes, ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::syscalls_for_ruleset(std::vector<bool> &syscalls, const std::string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
return m_evttype_filter->syscalls_for_ruleset(syscalls, ruleset_id);
|
||||
return m_sinsp_rules->syscalls_for_ruleset(syscalls, ruleset_id);
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev, uint16_t ruleset_id)
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev, uint16_t ruleset_id)
|
||||
{
|
||||
if(should_drop_evt())
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
if(!m_evttype_filter->run(ev, ruleset_id))
|
||||
if(!m_sinsp_rules->run(ev, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
@ -188,10 +203,9 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev,
|
||||
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
{
|
||||
lua_pushlightuserdata(m_ls, ev);
|
||||
lua_pushnumber(m_ls, ev->get_check_id());
|
||||
|
||||
if(lua_pcall(m_ls, 2, 3, 0) != 0)
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
@ -200,6 +214,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev,
|
||||
res->evt = ev;
|
||||
const char *p = lua_tostring(m_ls, -3);
|
||||
res->rule = p;
|
||||
res->source = "syscall";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
@ -212,9 +227,101 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev,
|
||||
return res;
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev)
|
||||
{
|
||||
return process_event(ev, m_default_ruleset_id);
|
||||
return process_sinsp_event(ev, m_default_ruleset_id);
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json_event *ev, uint16_t ruleset_id)
|
||||
{
|
||||
if(should_drop_evt())
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
// All k8s audit events have the single tag "1".
|
||||
if(!m_k8s_audit_rules->run((gen_event *) ev, 1, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
|
||||
lua_getglobal(m_ls, lua_on_event.c_str());
|
||||
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
{
|
||||
lua_pushnumber(m_ls, ev->get_check_id());
|
||||
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
res->evt = ev;
|
||||
const char *p = lua_tostring(m_ls, -3);
|
||||
res->rule = p;
|
||||
res->source = "k8s_audit";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("No function " + lua_on_event + " found in lua compiler module");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool falco_engine::parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts)
|
||||
{
|
||||
// If the Kind is EventList, split it into individual events.
|
||||
if(j.value("kind", "<NA>") == "EventList")
|
||||
{
|
||||
for(auto &je : j["items"])
|
||||
{
|
||||
evts.emplace_back();
|
||||
je["kind"] = "Event";
|
||||
|
||||
uint64_t ns = 0;
|
||||
if(!sinsp_utils::parse_iso_8601_utc_string(je.value(k8s_audit_time, "<NA>"), ns))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string tmp;
|
||||
sinsp_utils::ts_to_string(ns, &tmp, false, true);
|
||||
|
||||
evts.back().set_jevt(je, ns);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(j.value("kind", "<NA>") == "Event")
|
||||
{
|
||||
evts.emplace_back();
|
||||
uint64_t ns = 0;
|
||||
if(!sinsp_utils::parse_iso_8601_utc_string(j.value(k8s_audit_time, "<NA>"), ns))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
evts.back().set_jevt(j, ns);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json_event *ev)
|
||||
{
|
||||
return process_k8s_audit_event(ev, m_default_ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::describe_rule(string *rule)
|
||||
@ -243,18 +350,29 @@ void falco_engine::print_stats()
|
||||
|
||||
}
|
||||
|
||||
void falco_engine::add_evttype_filter(string &rule,
|
||||
void falco_engine::add_sinsp_filter(string &rule,
|
||||
set<uint32_t> &evttypes,
|
||||
set<uint32_t> &syscalls,
|
||||
set<string> &tags,
|
||||
sinsp_filter* filter)
|
||||
{
|
||||
m_evttype_filter->add(rule, evttypes, syscalls, tags, filter);
|
||||
m_sinsp_rules->add(rule, evttypes, syscalls, tags, filter);
|
||||
}
|
||||
|
||||
void falco_engine::add_k8s_audit_filter(string &rule,
|
||||
set<string> &tags,
|
||||
json_event_filter* filter)
|
||||
{
|
||||
// All k8s audit events have a single tag "1".
|
||||
std::set<uint32_t> event_tags = {1};
|
||||
|
||||
m_k8s_audit_rules->add(rule, tags, event_tags, filter);
|
||||
}
|
||||
|
||||
void falco_engine::clear_filters()
|
||||
{
|
||||
m_evttype_filter.reset(new sinsp_evttype_filter());
|
||||
m_sinsp_rules.reset(new falco_sinsp_ruleset());
|
||||
m_k8s_audit_rules.reset(new falco_ruleset());
|
||||
}
|
||||
|
||||
void falco_engine::set_sampling_ratio(uint32_t sampling_ratio)
|
||||
@ -288,3 +406,23 @@ inline bool falco_engine::should_drop_evt()
|
||||
double coin = (random() * (1.0/RAND_MAX));
|
||||
return (coin >= (1.0/(m_sampling_multiplier * m_sampling_ratio)));
|
||||
}
|
||||
|
||||
sinsp_filter_factory &falco_engine::sinsp_factory()
|
||||
{
|
||||
if(!m_sinsp_factory)
|
||||
{
|
||||
throw falco_exception("No sinsp factory created yet");
|
||||
}
|
||||
|
||||
return *(m_sinsp_factory.get());
|
||||
}
|
||||
|
||||
json_event_filter_factory &falco_engine::json_factory()
|
||||
{
|
||||
if(!m_json_factory)
|
||||
{
|
||||
throw falco_exception("No json factory created yet");
|
||||
}
|
||||
|
||||
return *(m_json_factory.get());
|
||||
}
|
||||
|
@ -17,16 +17,26 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Gen filtering TODO
|
||||
// - DONE Clean up use/sharing of factories amongst engine-related classes.
|
||||
// - DONE Fix outputs to actually use factories
|
||||
// - Review gen_filter apis to see if they have only the required interfaces
|
||||
// - Fix json filterchecks to split json and evt.time filterchecks.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include "json_evt.h"
|
||||
#include "rules.h"
|
||||
#include "ruleset.h"
|
||||
|
||||
#include "config_falco_engine.h"
|
||||
#include "falco_common.h"
|
||||
@ -40,7 +50,7 @@ limitations under the License.
|
||||
class falco_engine : public falco_common
|
||||
{
|
||||
public:
|
||||
falco_engine(bool seed_rng=true, const std::string& rules_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
virtual ~falco_engine();
|
||||
|
||||
//
|
||||
@ -72,13 +82,6 @@ public:
|
||||
// Only load rules having this priority or more severe.
|
||||
void set_min_priority(falco_common::priority_type priority);
|
||||
|
||||
struct rule_result {
|
||||
sinsp_evt *evt;
|
||||
std::string rule;
|
||||
falco_common::priority_type priority_num;
|
||||
std::string format;
|
||||
};
|
||||
|
||||
//
|
||||
// Return the ruleset id corresponding to this ruleset name,
|
||||
// creating a new one if necessary. If you provide any ruleset
|
||||
@ -87,36 +90,6 @@ public:
|
||||
//
|
||||
uint16_t find_ruleset_id(const std::string &ruleset);
|
||||
|
||||
//
|
||||
// Given a ruleset, fill in a bitset containing the event
|
||||
// types for which this ruleset can run.
|
||||
//
|
||||
void evttypes_for_ruleset(std::vector<bool> &evttypes, const std::string &ruleset);
|
||||
|
||||
//
|
||||
// Given a ruleset, fill in a bitset containing the syscalls
|
||||
// for which this ruleset can run.
|
||||
//
|
||||
void syscalls_for_ruleset(std::vector<bool> &syscalls, const std::string &ruleset);
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
std::unique_ptr<rule_result> process_event(sinsp_evt *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset
|
||||
//
|
||||
std::unique_ptr<rule_result> process_event(sinsp_evt *ev);
|
||||
|
||||
//
|
||||
// Print details on the given rule. If rule is NULL, print
|
||||
// details on all rules.
|
||||
@ -128,16 +101,6 @@ public:
|
||||
//
|
||||
void print_stats();
|
||||
|
||||
//
|
||||
// Add a filter, which is related to the specified set of
|
||||
// event types/syscalls, to the engine.
|
||||
//
|
||||
void add_evttype_filter(std::string &rule,
|
||||
std::set<uint32_t> &evttypes,
|
||||
std::set<uint32_t> &syscalls,
|
||||
std::set<std::string> &tags,
|
||||
sinsp_filter* filter);
|
||||
|
||||
// Clear all existing filters.
|
||||
void clear_filters();
|
||||
|
||||
@ -163,21 +126,116 @@ public:
|
||||
//
|
||||
void set_extra(string &extra, bool replace_container_info);
|
||||
|
||||
// **Methods Related to k8s audit log events, which are
|
||||
// **represented as json objects.
|
||||
struct rule_result {
|
||||
gen_event *evt;
|
||||
std::string rule;
|
||||
std::string source;
|
||||
falco_common::priority_type priority_num;
|
||||
std::string format;
|
||||
};
|
||||
|
||||
//
|
||||
// Given a raw json object, return a list of k8s audit event
|
||||
// objects that represent the object. This method handles
|
||||
// things such as EventList splitting.
|
||||
//
|
||||
// Returns true if the json object was recognized as a k8s
|
||||
// audit event(s), false otherwise.
|
||||
//
|
||||
bool parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts);
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
std::unique_ptr<rule_result> process_k8s_audit_event(json_event *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset
|
||||
//
|
||||
std::unique_ptr<rule_result> process_k8s_audit_event(json_event *ev);
|
||||
|
||||
//
|
||||
// Add a k8s_audit filter to the engine
|
||||
//
|
||||
void add_k8s_audit_filter(std::string &rule,
|
||||
std::set<std::string> &tags,
|
||||
json_event_filter* filter);
|
||||
|
||||
// **Methods Related to Sinsp Events e.g system calls
|
||||
//
|
||||
// Given a ruleset, fill in a bitset containing the event
|
||||
// types for which this ruleset can run.
|
||||
//
|
||||
void evttypes_for_ruleset(std::vector<bool> &evttypes, const std::string &ruleset);
|
||||
|
||||
//
|
||||
// Given a ruleset, fill in a bitset containing the syscalls
|
||||
// for which this ruleset can run.
|
||||
//
|
||||
void syscalls_for_ruleset(std::vector<bool> &syscalls, const std::string &ruleset);
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
std::unique_ptr<rule_result> process_sinsp_event(sinsp_evt *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset
|
||||
//
|
||||
std::unique_ptr<rule_result> process_sinsp_event(sinsp_evt *ev);
|
||||
|
||||
//
|
||||
// Add a filter, which is related to the specified set of
|
||||
// event types/syscalls, to the engine.
|
||||
//
|
||||
void add_sinsp_filter(std::string &rule,
|
||||
std::set<uint32_t> &evttypes,
|
||||
std::set<uint32_t> &syscalls,
|
||||
std::set<std::string> &tags,
|
||||
sinsp_filter* filter);
|
||||
|
||||
sinsp_filter_factory &sinsp_factory();
|
||||
json_event_filter_factory &json_factory();
|
||||
|
||||
private:
|
||||
|
||||
static nlohmann::json::json_pointer k8s_audit_time;
|
||||
|
||||
//
|
||||
// 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();
|
||||
shared_ptr<sinsp_filter_factory> m_sinsp_factory;
|
||||
shared_ptr<json_event_filter_factory> m_json_factory;
|
||||
|
||||
falco_rules *m_rules;
|
||||
uint16_t m_next_ruleset_id;
|
||||
std::map<string, uint16_t> m_known_rulesets;
|
||||
std::unique_ptr<sinsp_evttype_filter> m_evttype_filter;
|
||||
falco_common::priority_type m_min_priority;
|
||||
|
||||
std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules;
|
||||
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
|
||||
|
||||
//
|
||||
// Here's how the sampling ratio and multiplier influence
|
||||
// whether or not an event is dropped in
|
||||
|
@ -25,6 +25,7 @@ limitations under the License.
|
||||
|
||||
|
||||
sinsp* falco_formats::s_inspector = NULL;
|
||||
falco_engine *falco_formats::s_engine = NULL;
|
||||
bool falco_formats::s_json_output = false;
|
||||
bool falco_formats::s_json_include_output_property = true;
|
||||
sinsp_evt_formatter_cache *falco_formats::s_formatters = NULL;
|
||||
@ -38,9 +39,14 @@ const static struct luaL_reg ll_falco [] =
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
void falco_formats::init(sinsp* inspector, lua_State *ls, bool json_output, bool json_include_output_property)
|
||||
void falco_formats::init(sinsp* inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls,
|
||||
bool json_output,
|
||||
bool json_include_output_property)
|
||||
{
|
||||
s_inspector = inspector;
|
||||
s_engine = engine;
|
||||
s_json_output = json_output;
|
||||
s_json_include_output_property = json_include_output_property;
|
||||
if(!s_formatters)
|
||||
@ -53,31 +59,57 @@ void falco_formats::init(sinsp* inspector, lua_State *ls, bool json_output, bool
|
||||
|
||||
int falco_formats::formatter(lua_State *ls)
|
||||
{
|
||||
string format = luaL_checkstring(ls, 1);
|
||||
sinsp_evt_formatter* formatter;
|
||||
string source = luaL_checkstring(ls, -2);
|
||||
string format = luaL_checkstring(ls, -1);
|
||||
|
||||
try
|
||||
{
|
||||
if(source == "syscall")
|
||||
{
|
||||
sinsp_evt_formatter* formatter;
|
||||
formatter = new sinsp_evt_formatter(s_inspector, format);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
else
|
||||
{
|
||||
json_event_formatter *formatter;
|
||||
formatter = new json_event_formatter(s_engine->json_factory(), format);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
}
|
||||
catch(sinsp_exception& e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
catch(falco_exception& e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int falco_formats::free_formatter(lua_State *ls)
|
||||
{
|
||||
if (!lua_islightuserdata(ls, -1))
|
||||
if (!lua_islightuserdata(ls, -1) ||
|
||||
!lua_isstring(ls, -2))
|
||||
|
||||
{
|
||||
luaL_error(ls, "Invalid argument passed to free_formatter");
|
||||
}
|
||||
|
||||
sinsp_evt_formatter *formatter = (sinsp_evt_formatter *) lua_topointer(ls, 1);
|
||||
string source = luaL_checkstring(ls, -2);
|
||||
|
||||
if(source == "syscall")
|
||||
{
|
||||
sinsp_evt_formatter *formatter = (sinsp_evt_formatter *) lua_topointer(ls, -1);
|
||||
delete(formatter);
|
||||
}
|
||||
else
|
||||
{
|
||||
json_event_formatter *formatter = (json_event_formatter *) lua_topointer(ls, -1);
|
||||
delete(formatter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -100,50 +132,36 @@ int falco_formats::format_event (lua_State *ls)
|
||||
if (!lua_isstring(ls, -1) ||
|
||||
!lua_isstring(ls, -2) ||
|
||||
!lua_isstring(ls, -3) ||
|
||||
!lua_islightuserdata(ls, -4)) {
|
||||
!lua_isstring(ls, -4) ||
|
||||
!lua_islightuserdata(ls, -5)) {
|
||||
lua_pushstring(ls, "Invalid arguments passed to format_event()");
|
||||
lua_error(ls);
|
||||
}
|
||||
sinsp_evt* evt = (sinsp_evt*)lua_topointer(ls, 1);
|
||||
gen_event* evt = (gen_event*)lua_topointer(ls, 1);
|
||||
const char *rule = (char *) lua_tostring(ls, 2);
|
||||
const char *level = (char *) lua_tostring(ls, 3);
|
||||
const char *format = (char *) lua_tostring(ls, 4);
|
||||
const char *source = (char *) lua_tostring(ls, 3);
|
||||
const char *level = (char *) lua_tostring(ls, 4);
|
||||
const char *format = (char *) lua_tostring(ls, 5);
|
||||
|
||||
string sformat = format;
|
||||
|
||||
if(strcmp(source, "syscall") == 0)
|
||||
{
|
||||
try {
|
||||
s_formatters->tostring(evt, sformat, &line);
|
||||
s_formatters->tostring((sinsp_evt *) evt, sformat, &line);
|
||||
|
||||
if(s_json_output)
|
||||
{
|
||||
switch(s_inspector->get_buffer_format())
|
||||
{
|
||||
case sinsp_evt::PF_NORMAL:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSON);
|
||||
break;
|
||||
case sinsp_evt::PF_EOLS:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONEOLS);
|
||||
break;
|
||||
case sinsp_evt::PF_HEX:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEX);
|
||||
break;
|
||||
case sinsp_evt::PF_HEXASCII:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEXASCII);
|
||||
break;
|
||||
case sinsp_evt::PF_BASE64:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONBASE64);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
s_formatters->tostring(evt, sformat, &json_line);
|
||||
s_formatters->tostring((sinsp_evt *) evt, sformat, &json_line);
|
||||
|
||||
// The formatted string might have a leading newline. If it does, remove it.
|
||||
if (json_line[0] == '\n')
|
||||
{
|
||||
json_line.erase(0, 1);
|
||||
}
|
||||
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_NORMAL);
|
||||
}
|
||||
}
|
||||
catch (sinsp_exception& e)
|
||||
@ -152,6 +170,27 @@ int falco_formats::format_event (lua_State *ls)
|
||||
lua_pushstring(ls, err.c_str());
|
||||
lua_error(ls);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
|
||||
json_event_formatter formatter(s_engine->json_factory(), sformat);
|
||||
|
||||
line = formatter.tostring((json_event *) evt);
|
||||
|
||||
if(s_json_output)
|
||||
{
|
||||
json_line = formatter.tojson((json_event *) evt);
|
||||
}
|
||||
}
|
||||
catch (exception &e)
|
||||
{
|
||||
string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'";
|
||||
lua_pushstring(ls, err.c_str());
|
||||
lua_error(ls);
|
||||
}
|
||||
}
|
||||
|
||||
// For JSON output, the formatter returned a json-as-text
|
||||
// object containing all the fields in the original format
|
||||
|
@ -27,12 +27,19 @@ extern "C" {
|
||||
#include "lauxlib.h"
|
||||
}
|
||||
|
||||
#include "json_evt.h"
|
||||
#include "falco_engine.h"
|
||||
|
||||
class sinsp_evt_formatter;
|
||||
|
||||
class falco_formats
|
||||
{
|
||||
public:
|
||||
static void init(sinsp* inspector, lua_State *ls, bool json_output, bool json_include_output_property);
|
||||
static void init(sinsp* inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls,
|
||||
bool json_output,
|
||||
bool json_include_output_property);
|
||||
|
||||
// formatter = falco.formatter(format_string)
|
||||
static int formatter(lua_State *ls);
|
||||
@ -47,6 +54,7 @@ class falco_formats
|
||||
static int format_event(lua_State *ls);
|
||||
|
||||
static sinsp* s_inspector;
|
||||
static falco_engine *s_engine;
|
||||
static sinsp_evt_formatter_cache *s_formatters;
|
||||
static bool s_json_output;
|
||||
static bool s_json_include_output_property;
|
||||
|
840
userspace/engine/json_evt.cpp
Normal file
840
userspace/engine/json_evt.cpp
Normal file
@ -0,0 +1,840 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "uri.h"
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "json_evt.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace std;
|
||||
|
||||
json_event::json_event()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
json_event::~json_event()
|
||||
{
|
||||
}
|
||||
|
||||
void json_event::set_jevt(json &evt, uint64_t ts)
|
||||
{
|
||||
m_jevt = evt;
|
||||
m_event_ts = ts;
|
||||
}
|
||||
|
||||
const json &json_event::jevt()
|
||||
{
|
||||
return m_jevt;
|
||||
}
|
||||
|
||||
uint64_t json_event::get_ts()
|
||||
{
|
||||
return m_event_ts;
|
||||
}
|
||||
|
||||
std::string json_event_filter_check::def_format(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
return json_as_string(j);
|
||||
}
|
||||
|
||||
std::string json_event_filter_check::json_as_string(const json &j)
|
||||
{
|
||||
if (j.type() == json::value_t::string)
|
||||
{
|
||||
return j;
|
||||
}
|
||||
else
|
||||
{
|
||||
return j.dump();
|
||||
}
|
||||
}
|
||||
|
||||
json_event_filter_check::alias::alias()
|
||||
: m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr)
|
||||
: m_jptr(ptr), m_format(def_format),
|
||||
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
|
||||
format_t format)
|
||||
: m_jptr(ptr), m_format(format),
|
||||
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
|
||||
format_t format,
|
||||
index_mode mode,
|
||||
index_type itype)
|
||||
: m_jptr(ptr), m_format(format),
|
||||
m_idx_mode(mode), m_idx_type(itype)
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::alias::~alias()
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::json_event_filter_check()
|
||||
: m_format(def_format)
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_check::~json_event_filter_check()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering)
|
||||
{
|
||||
// Look for the longest match amongst the aliases. str is not
|
||||
// necessarily terminated on a filtercheck boundary.
|
||||
size_t match_len = 0;
|
||||
|
||||
size_t idx_len = 0;
|
||||
|
||||
for(auto &pair : m_aliases)
|
||||
{
|
||||
// What follows the match must not be alphanumeric or a dot
|
||||
if(strncmp(pair.first.c_str(), str, pair.first.size()) == 0 &&
|
||||
!isalnum((int) str[pair.first.size()]) &&
|
||||
str[pair.first.size()] != '.' &&
|
||||
pair.first.size() > match_len)
|
||||
{
|
||||
m_jptr = pair.second.m_jptr;
|
||||
m_field = pair.first;
|
||||
m_format = pair.second.m_format;
|
||||
match_len = pair.first.size();
|
||||
|
||||
const char *start = str + m_field.size();
|
||||
|
||||
// Check for an optional index
|
||||
if(*start == '[')
|
||||
{
|
||||
start++;
|
||||
const char *end = strchr(start, ']');
|
||||
|
||||
if(end != NULL)
|
||||
{
|
||||
m_idx = string(start, end-start);
|
||||
}
|
||||
|
||||
idx_len = (end - start + 2);
|
||||
}
|
||||
|
||||
if(m_idx.empty() && pair.second.m_idx_mode == alias::IDX_REQUIRED)
|
||||
{
|
||||
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires an index but none provided"));
|
||||
}
|
||||
|
||||
if(!m_idx.empty() && pair.second.m_idx_mode == alias::IDX_NONE)
|
||||
{
|
||||
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" forbids an index but one provided"));
|
||||
}
|
||||
|
||||
if(!m_idx.empty() &&
|
||||
pair.second.m_idx_type == alias::IDX_NUMERIC &&
|
||||
m_idx.find_first_not_of("0123456789") != string::npos)
|
||||
{
|
||||
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires a numeric index"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match_len + idx_len;
|
||||
}
|
||||
|
||||
void json_event_filter_check::add_filter_value(const char* str, uint32_t len, uint32_t i)
|
||||
{
|
||||
m_values.push_back(string(str));
|
||||
}
|
||||
|
||||
bool json_event_filter_check::compare(gen_event *evt)
|
||||
{
|
||||
json_event *jevt = (json_event *) evt;
|
||||
|
||||
std::string value = extract(jevt);
|
||||
|
||||
switch(m_cmpop)
|
||||
{
|
||||
case CO_EQ:
|
||||
return (value == m_values[0]);
|
||||
break;
|
||||
case CO_NE:
|
||||
return (value != m_values[0]);
|
||||
break;
|
||||
case CO_CONTAINS:
|
||||
return (value.find(m_values[0]) != string::npos);
|
||||
break;
|
||||
case CO_STARTSWITH:
|
||||
return (value.compare(0, m_values[0].size(), m_values[0]) == 0);
|
||||
break;
|
||||
case CO_IN:
|
||||
for(auto &val : m_values)
|
||||
{
|
||||
if (value == val)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case CO_EXISTS:
|
||||
// Any non-empty, non-"<NA>" value is ok
|
||||
return (value != "" && value != "<NA>");
|
||||
break;
|
||||
default:
|
||||
throw falco_exception("filter error: unsupported comparison operator");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &json_event_filter_check::field()
|
||||
{
|
||||
return m_field;
|
||||
}
|
||||
|
||||
const std::string &json_event_filter_check::idx()
|
||||
{
|
||||
return m_idx;
|
||||
}
|
||||
|
||||
size_t json_event_filter_check::parsed_size()
|
||||
{
|
||||
if(m_idx.empty())
|
||||
{
|
||||
return m_field.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_field.size() + m_idx.size() + 2;
|
||||
}
|
||||
}
|
||||
|
||||
json_event_filter_check::check_info &json_event_filter_check::get_fields()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
uint8_t* json_event_filter_check::extract(gen_event *evt, uint32_t* len, bool sanitize_strings)
|
||||
{
|
||||
json_event *jevt = (json_event *) evt;
|
||||
|
||||
try {
|
||||
const json &j = jevt->jevt().at(m_jptr);
|
||||
|
||||
// Only format when the value was actually found in
|
||||
// the object.
|
||||
m_tstr = m_format(j, m_field, m_idx);
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
m_tstr = "<NA>";
|
||||
}
|
||||
|
||||
*len = m_tstr.size();
|
||||
|
||||
return (uint8_t *) m_tstr.c_str();
|
||||
}
|
||||
|
||||
std::string json_event_filter_check::extract(json_event *evt)
|
||||
{
|
||||
uint8_t *res;
|
||||
uint32_t len;
|
||||
std::string ret;
|
||||
|
||||
res = extract(evt, &len, true);
|
||||
|
||||
if(res != NULL)
|
||||
{
|
||||
ret.assign((const char *) res, len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string jevt_filter_check::s_jevt_time_field = "jevt.time";
|
||||
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()
|
||||
{
|
||||
m_info = {"jevt",
|
||||
"generic ways to access json events",
|
||||
{
|
||||
{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
|
||||
{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"},
|
||||
{s_jevt_obj_field, "The entire json object, stringified"}
|
||||
}};
|
||||
}
|
||||
|
||||
jevt_filter_check::~jevt_filter_check()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int32_t jevt_filter_check::parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering)
|
||||
{
|
||||
if(strncmp(s_jevt_time_field.c_str(), str, s_jevt_time_field.size()) == 0)
|
||||
{
|
||||
m_field = s_jevt_time_field;
|
||||
return s_jevt_time_field.size();
|
||||
}
|
||||
|
||||
if(strncmp(s_jevt_rawtime_field.c_str(), str, s_jevt_rawtime_field.size()) == 0)
|
||||
{
|
||||
m_field = s_jevt_rawtime_field;
|
||||
return s_jevt_rawtime_field.size();
|
||||
}
|
||||
|
||||
if(strncmp(s_jevt_obj_field.c_str(), str, s_jevt_obj_field.size()) == 0)
|
||||
{
|
||||
m_field = s_jevt_obj_field;
|
||||
return s_jevt_obj_field.size();
|
||||
}
|
||||
|
||||
if(strncmp(s_jevt_value_field.c_str(), str, s_jevt_value_field.size()) == 0)
|
||||
{
|
||||
const char *end;
|
||||
|
||||
// What follows must be [<json pointer expression>]
|
||||
if (*(str + s_jevt_value_field.size()) != '[' ||
|
||||
((end = strchr(str + 1, ']')) == NULL))
|
||||
|
||||
{
|
||||
throw falco_exception(string("Could not parse filtercheck field \"") + str + "\". Did not have expected format with 'jevt.value[<json pointer>]'");
|
||||
}
|
||||
|
||||
try {
|
||||
m_jptr = json::json_pointer(string(str + (s_jevt_value_field.size()+1), (end-str-(s_jevt_value_field.size()+1))));
|
||||
}
|
||||
catch (json::parse_error& e)
|
||||
{
|
||||
throw falco_exception(string("Could not parse filtercheck field \"") + str + "\". Invalid json selector (" + e.what() + ")");
|
||||
}
|
||||
|
||||
// The +1 accounts for the closing ']'
|
||||
m_field = string(str, end-str + 1);
|
||||
return (end - str + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* jevt_filter_check::extract(gen_event *evt, uint32_t* len, bool sanitize_stings)
|
||||
{
|
||||
if(m_field == s_jevt_rawtime_field)
|
||||
{
|
||||
m_tstr = to_string(evt->get_ts());
|
||||
*len = m_tstr.size();
|
||||
return (uint8_t *) m_tstr.c_str();
|
||||
}
|
||||
else if(m_field == s_jevt_time_field)
|
||||
{
|
||||
sinsp_utils::ts_to_string(evt->get_ts(), &m_tstr, false, true);
|
||||
*len = m_tstr.size();
|
||||
return (uint8_t *) m_tstr.c_str();
|
||||
}
|
||||
else if(m_field == s_jevt_obj_field)
|
||||
{
|
||||
json_event *jevt = (json_event *) evt;
|
||||
m_tstr = jevt->jevt().dump();
|
||||
*len = m_tstr.size();
|
||||
return (uint8_t *) m_tstr.c_str();
|
||||
}
|
||||
|
||||
return json_event_filter_check::extract(evt, len, sanitize_stings);
|
||||
}
|
||||
|
||||
json_event_filter_check *jevt_filter_check::allocate_new()
|
||||
{
|
||||
jevt_filter_check *chk = new jevt_filter_check();
|
||||
|
||||
return (json_event_filter_check *) chk;
|
||||
}
|
||||
|
||||
std::string k8s_audit_filter_check::index_image(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
uint64_t idx_num = (idx.empty() ? 0 : stoi(idx));
|
||||
|
||||
string image;
|
||||
|
||||
try {
|
||||
image = j[idx_num].at("image");
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
return string("<NA>");
|
||||
}
|
||||
|
||||
// If the filtercheck ends with .repository, we want only the
|
||||
// repo name from the image.
|
||||
std::string suffix = ".repository";
|
||||
if(suffix.size() <= field.size() &&
|
||||
std::equal(suffix.rbegin(), suffix.rend(), field.rbegin()))
|
||||
{
|
||||
std::string hostname, port, name, tag, digest;
|
||||
|
||||
sinsp_utils::split_container_image(image,
|
||||
hostname,
|
||||
port,
|
||||
name,
|
||||
tag,
|
||||
digest,
|
||||
false);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
std::string k8s_audit_filter_check::index_has_name(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
for(auto &subject : j)
|
||||
{
|
||||
if(subject.value("name", "N/A") == idx)
|
||||
{
|
||||
return string("true");
|
||||
}
|
||||
}
|
||||
|
||||
return string("false");
|
||||
}
|
||||
|
||||
|
||||
std::string k8s_audit_filter_check::index_query_param(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
string uri = j;
|
||||
std::vector<std::string> uri_parts, query_parts;
|
||||
|
||||
uri_parts = sinsp_split(uri, '?');
|
||||
|
||||
if(uri_parts.size() != 2)
|
||||
{
|
||||
return string("<NA>");
|
||||
}
|
||||
|
||||
query_parts = sinsp_split(uri_parts[1], '&');
|
||||
|
||||
for(auto &part : query_parts)
|
||||
{
|
||||
std::vector<std::string> param_parts = sinsp_split(part, '=');
|
||||
|
||||
if(param_parts.size() == 2 && uri::decode(param_parts[0], true)==idx)
|
||||
{
|
||||
return uri::decode(param_parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return string("<NA>");
|
||||
}
|
||||
|
||||
|
||||
std::string k8s_audit_filter_check::index_generic(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
json item;
|
||||
|
||||
if(idx.empty())
|
||||
{
|
||||
item = j;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t idx_num = (idx.empty() ? 0 : stoi(idx));
|
||||
|
||||
try {
|
||||
item = j[idx_num];
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
return string("<NA>");
|
||||
}
|
||||
}
|
||||
|
||||
return json_event_filter_check::json_as_string(item);
|
||||
}
|
||||
|
||||
std::string k8s_audit_filter_check::index_select(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
json item;
|
||||
|
||||
// Use the suffix of the field to determine which property to
|
||||
// select from each object.
|
||||
std::string prop = field.substr(field.find_last_of(".")+1);
|
||||
|
||||
std::string ret;
|
||||
|
||||
if(idx.empty())
|
||||
{
|
||||
for(auto &obj : j)
|
||||
{
|
||||
if(ret != "")
|
||||
{
|
||||
ret += " ";
|
||||
}
|
||||
|
||||
try {
|
||||
ret += json_event_filter_check::json_as_string(obj.at(prop));
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
ret += "N/A";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
ret = j[stoi(idx)].at(prop);
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
ret = "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string k8s_audit_filter_check::index_privileged(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
nlohmann::json::json_pointer jpriv = "/securityContext/privileged"_json_pointer;
|
||||
|
||||
bool privileged = false;
|
||||
|
||||
if(!idx.empty())
|
||||
{
|
||||
try {
|
||||
privileged = j[stoi(idx)].at(jpriv);
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto &container : j)
|
||||
{
|
||||
try {
|
||||
if(container.at(jpriv))
|
||||
{
|
||||
privileged = true;
|
||||
}
|
||||
}
|
||||
catch(json::out_of_range &e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (privileged ? string("true") : string("false"));
|
||||
}
|
||||
|
||||
std::string k8s_audit_filter_check::check_hostpath_vols(const json &j, std::string &field, std::string &idx)
|
||||
{
|
||||
|
||||
nlohmann::json::json_pointer jpath = "/hostPath/path"_json_pointer;
|
||||
|
||||
for(auto &vol : j)
|
||||
{
|
||||
string path = vol.value(jpath, "N/A");
|
||||
|
||||
if(sinsp_utils::glob_match(idx.c_str(), path.c_str()))
|
||||
{
|
||||
return string("true");
|
||||
}
|
||||
}
|
||||
|
||||
return string("false");
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
{
|
||||
m_info = {"ka",
|
||||
"Access K8s Audit Log Events",
|
||||
{
|
||||
{"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)."},
|
||||
{"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.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists"},
|
||||
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
|
||||
{"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.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image"},
|
||||
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)"},
|
||||
{"ka.req.container.host_network", "When the request object refers to a container, the value of the hostNetwork flag."},
|
||||
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged."},
|
||||
{"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. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated"},
|
||||
{"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. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated"},
|
||||
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated"},
|
||||
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated"},
|
||||
{"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. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports"},
|
||||
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)"},
|
||||
{"ka.resp.name", "The response object name"},
|
||||
{"ka.response.code", "The response code"},
|
||||
{"ka.response.reason", "The response reason (usually present only for failures)"}
|
||||
}};
|
||||
|
||||
{
|
||||
using a = alias;
|
||||
|
||||
m_aliases = {
|
||||
{"ka.auditid", {"/auditID"_json_pointer}},
|
||||
{"ka.stage", {"/stage"_json_pointer}},
|
||||
{"ka.auth.decision", {"/annotations/authorization.k8s.io~1decision"_json_pointer}},
|
||||
{"ka.auth.reason", {"/annotations/authorization.k8s.io~1reason"_json_pointer}},
|
||||
{"ka.user.name", {"/user/username"_json_pointer}},
|
||||
{"ka.user.groups", {"/user/groups"_json_pointer}},
|
||||
{"ka.impuser.name", {"/impersonatedUser/username"_json_pointer}},
|
||||
{"ka.verb", {"/verb"_json_pointer}},
|
||||
{"ka.uri", {"/requestURI"_json_pointer}},
|
||||
{"ka.uri.param", {"/requestURI"_json_pointer, index_query_param, a::IDX_REQUIRED, a::IDX_KEY}},
|
||||
{"ka.target.name", {"/objectRef/name"_json_pointer}},
|
||||
{"ka.target.namespace", {"/objectRef/namespace"_json_pointer}},
|
||||
{"ka.target.resource", {"/objectRef/resource"_json_pointer}},
|
||||
{"ka.target.subresource", {"/objectRef/subresource"_json_pointer}},
|
||||
{"ka.req.binding.subjects", {"/requestObject/subjects"_json_pointer}},
|
||||
{"ka.req.binding.subject.has_name", {"/requestObject/subjects"_json_pointer, index_has_name, a::IDX_REQUIRED, a::IDX_KEY}},
|
||||
{"ka.req.binding.role", {"/requestObject/roleRef/name"_json_pointer}},
|
||||
{"ka.req.configmap.name", {"/objectRef/name"_json_pointer}},
|
||||
{"ka.req.configmap.obj", {"/requestObject/data"_json_pointer}},
|
||||
{"ka.req.container.image", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.container.image.repository", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.container.host_network", {"/requestObject/spec/hostNetwork"_json_pointer}},
|
||||
{"ka.req.container.privileged", {"/requestObject/spec/containers"_json_pointer, index_privileged, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.role.rules", {"/requestObject/rules"_json_pointer}},
|
||||
{"ka.req.role.rules.apiGroups", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.role.rules.nonResourceURLs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.role.rules.resources", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.role.rules.verbs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.service.type", {"/requestObject/spec/type"_json_pointer}},
|
||||
{"ka.req.service.ports", {"/requestObject/spec/ports"_json_pointer, index_generic, a::IDX_ALLOWED, a::IDX_NUMERIC}},
|
||||
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols, a::IDX_REQUIRED, a::IDX_KEY}},
|
||||
{"ka.resp.name", {"/responseObject/metadata/name"_json_pointer}},
|
||||
{"ka.response.code", {"/responseStatus/code"_json_pointer}},
|
||||
{"ka.response.reason", {"/responseStatus/reason"_json_pointer}}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::~k8s_audit_filter_check()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
json_event_filter_check *k8s_audit_filter_check::allocate_new()
|
||||
{
|
||||
k8s_audit_filter_check *chk = new k8s_audit_filter_check();
|
||||
|
||||
return (json_event_filter_check *) chk;
|
||||
}
|
||||
|
||||
json_event_filter::json_event_filter()
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter::~json_event_filter()
|
||||
{
|
||||
}
|
||||
|
||||
json_event_filter_factory::json_event_filter_factory()
|
||||
{
|
||||
m_defined_checks.push_back(shared_ptr<json_event_filter_check>(new jevt_filter_check()));
|
||||
m_defined_checks.push_back(shared_ptr<json_event_filter_check>(new k8s_audit_filter_check()));
|
||||
|
||||
for(auto &chk : m_defined_checks)
|
||||
{
|
||||
m_info.push_back(chk->get_fields());
|
||||
}
|
||||
}
|
||||
|
||||
json_event_filter_factory::~json_event_filter_factory()
|
||||
{
|
||||
}
|
||||
|
||||
gen_event_filter *json_event_filter_factory::new_filter()
|
||||
{
|
||||
return new json_event_filter();
|
||||
}
|
||||
|
||||
gen_event_filter_check *json_event_filter_factory::new_filtercheck(const char *fldname)
|
||||
{
|
||||
for(auto &chk : m_defined_checks)
|
||||
{
|
||||
json_event_filter_check *newchk = chk->allocate_new();
|
||||
|
||||
int32_t parsed = newchk->parse_field_name(fldname, false, true);
|
||||
|
||||
if(parsed > 0)
|
||||
{
|
||||
return newchk;
|
||||
}
|
||||
|
||||
delete newchk;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::list<json_event_filter_check::check_info> &json_event_filter_factory::get_fields()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
json_event_formatter::json_event_formatter(json_event_filter_factory &json_factory, std::string &format)
|
||||
: m_format(format),
|
||||
m_json_factory(json_factory)
|
||||
{
|
||||
parse_format();
|
||||
}
|
||||
|
||||
json_event_formatter::~json_event_formatter()
|
||||
{
|
||||
}
|
||||
|
||||
std::string json_event_formatter::tostring(json_event *ev)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
std::list<std::pair<std::string,std::string>> resolved;
|
||||
|
||||
resolve_tokens(ev, resolved);
|
||||
|
||||
for(auto &res : resolved)
|
||||
{
|
||||
ret += res.second;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string json_event_formatter::tojson(json_event *ev)
|
||||
{
|
||||
nlohmann::json ret;
|
||||
|
||||
std::list<std::pair<std::string,std::string>> resolved;
|
||||
|
||||
resolve_tokens(ev, resolved);
|
||||
|
||||
for(auto &res : resolved)
|
||||
{
|
||||
// Only include the fields and not the raw text blocks.
|
||||
if(!res.first.empty())
|
||||
{
|
||||
ret[res.first] = res.second;
|
||||
}
|
||||
}
|
||||
|
||||
return ret.dump();
|
||||
}
|
||||
|
||||
void json_event_formatter::parse_format()
|
||||
{
|
||||
string tformat = m_format;
|
||||
|
||||
// Remove any leading '*' if present
|
||||
if(tformat.front() == '*')
|
||||
{
|
||||
tformat.erase(0, 1);
|
||||
}
|
||||
|
||||
while(tformat.size() > 0)
|
||||
{
|
||||
size_t size;
|
||||
struct fmt_token tok;
|
||||
|
||||
if(tformat.front() == '%')
|
||||
{
|
||||
// Skip the %
|
||||
tformat.erase(0, 1);
|
||||
json_event_filter_check *chk = (json_event_filter_check *) m_json_factory.new_filtercheck(tformat.c_str());
|
||||
|
||||
if(!chk)
|
||||
{
|
||||
throw falco_exception(string ("Could not parse format string \"") + m_format + "\": unknown filtercheck field " + tformat);
|
||||
}
|
||||
|
||||
size = chk->parsed_size();
|
||||
tok.check.reset(chk);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = tformat.find_first_of("%");
|
||||
if(size == string::npos)
|
||||
{
|
||||
size = tformat.size();
|
||||
}
|
||||
}
|
||||
|
||||
if(size == 0)
|
||||
{
|
||||
// Empty fields are only allowed at the beginning of the string
|
||||
if(m_tokens.size() > 0)
|
||||
{
|
||||
throw falco_exception(string ("Could not parse format string \"" + m_format + "\": empty filtercheck field"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
tok.text = tformat.substr(0, size);
|
||||
m_tokens.push_back(tok);
|
||||
|
||||
tformat.erase(0, size);
|
||||
}
|
||||
}
|
||||
|
||||
void json_event_formatter::resolve_tokens(json_event *ev, std::list<std::pair<std::string,std::string>> &resolved)
|
||||
{
|
||||
for(auto tok : m_tokens)
|
||||
{
|
||||
if(tok.check)
|
||||
{
|
||||
resolved.push_back(std::make_pair(tok.check->field(), tok.check->extract(ev)));
|
||||
}
|
||||
else
|
||||
{
|
||||
resolved.push_back(std::make_pair("", tok.text));
|
||||
}
|
||||
}
|
||||
}
|
310
userspace/engine/json_evt.h
Normal file
310
userspace/engine/json_evt.h
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "gen_filter.h"
|
||||
|
||||
class json_event : public gen_event
|
||||
{
|
||||
public:
|
||||
json_event();
|
||||
virtual ~json_event();
|
||||
|
||||
void set_jevt(nlohmann::json &evt, uint64_t ts);
|
||||
const nlohmann::json &jevt();
|
||||
|
||||
uint64_t get_ts();
|
||||
|
||||
protected:
|
||||
nlohmann::json m_jevt;
|
||||
|
||||
uint64_t m_event_ts;
|
||||
};
|
||||
|
||||
class json_event_filter_check : public gen_event_filter_check
|
||||
{
|
||||
public:
|
||||
|
||||
// A struct describing a single filtercheck field ("ka.user")
|
||||
struct field_info {
|
||||
std::string name;
|
||||
std::string desc;
|
||||
};
|
||||
|
||||
// A struct describing a group of filtercheck fields ("ka")
|
||||
struct check_info {
|
||||
std::string name;
|
||||
std::string desc;
|
||||
|
||||
std::list<field_info> fields;
|
||||
};
|
||||
|
||||
json_event_filter_check();
|
||||
virtual ~json_event_filter_check();
|
||||
|
||||
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 );
|
||||
bool compare(gen_event *evt);
|
||||
virtual uint8_t* extract(gen_event *evt, uint32_t* len, bool sanitize_strings = true);
|
||||
|
||||
// Simpler version that returns a string
|
||||
std::string extract(json_event *evt);
|
||||
|
||||
const std::string &field();
|
||||
const std::string &idx();
|
||||
|
||||
// The combined size of the field, index, and surrounding
|
||||
// brackets (e.g. ka.image[foo])
|
||||
size_t parsed_size();
|
||||
|
||||
check_info &get_fields();
|
||||
|
||||
//
|
||||
// Allocate a new check of the same type. Must be overridden.
|
||||
//
|
||||
virtual json_event_filter_check *allocate_new() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
static std::string def_format(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
static std::string json_as_string(const nlohmann::json &j);
|
||||
|
||||
// Subclasses can define field names that act as aliases for
|
||||
// specific json pointer expressions e.g. ka.user ==
|
||||
// jevt.value[/user/username]. This struct represents one of
|
||||
// those aliases.
|
||||
|
||||
typedef std::function<std::string (const nlohmann::json &, std::string &field, std::string &idx)> format_t;
|
||||
|
||||
struct alias {
|
||||
|
||||
// Whether this alias requires an index, allows an
|
||||
// index, or should not have an index.
|
||||
enum index_mode {
|
||||
IDX_REQUIRED,
|
||||
IDX_ALLOWED,
|
||||
IDX_NONE
|
||||
};
|
||||
|
||||
enum index_type {
|
||||
IDX_KEY,
|
||||
IDX_NUMERIC
|
||||
};
|
||||
|
||||
// The variants allow for brace-initialization either
|
||||
// with just the pointer or with both the pointer and
|
||||
// a format function.
|
||||
alias();
|
||||
alias(nlohmann::json::json_pointer ptr);
|
||||
alias(nlohmann::json::json_pointer ptr, format_t format);
|
||||
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode);
|
||||
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode, index_type itype);
|
||||
virtual ~alias();
|
||||
|
||||
// A json pointer used to extract a referenced value
|
||||
// from a json object.
|
||||
nlohmann::json::json_pointer m_jptr;
|
||||
|
||||
// A function that given the referenced value selected
|
||||
// above, formats and returns the appropriate string. This
|
||||
// function might do further selection (e.g. array
|
||||
// indexing, searches, etc.) or string reformatting to
|
||||
// trim unnecessary parts of the value.
|
||||
format_t m_format;
|
||||
|
||||
index_mode m_idx_mode;
|
||||
|
||||
index_type m_idx_type;
|
||||
};
|
||||
|
||||
// This map defines the aliases defined by this filter check
|
||||
// class.
|
||||
//
|
||||
// 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;
|
||||
|
||||
check_info m_info;
|
||||
|
||||
// The actual field name parsed in parse_field_name.
|
||||
std::string m_field;
|
||||
|
||||
// The field name itself might include an index component
|
||||
// e.g. ka.value[idx]. This holds the index.
|
||||
std::string m_idx;
|
||||
|
||||
// The actual json pointer value to use to extract from events.
|
||||
nlohmann::json::json_pointer m_jptr;
|
||||
|
||||
// Temporary storage to hold extracted value
|
||||
std::string m_tstr;
|
||||
|
||||
// Reformatting function
|
||||
format_t m_format;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::string> m_values;
|
||||
};
|
||||
|
||||
class jevt_filter_check : public json_event_filter_check
|
||||
{
|
||||
public:
|
||||
jevt_filter_check();
|
||||
virtual ~jevt_filter_check();
|
||||
|
||||
int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering);
|
||||
|
||||
virtual uint8_t* extract(gen_event *evt, uint32_t* len, bool sanitize_strings = true);
|
||||
|
||||
json_event_filter_check *allocate_new();
|
||||
|
||||
private:
|
||||
|
||||
static std::string s_jevt_time_field;
|
||||
static std::string s_jevt_rawtime_field;
|
||||
static std::string s_jevt_obj_field;
|
||||
static std::string s_jevt_value_field;
|
||||
};
|
||||
|
||||
class k8s_audit_filter_check : public json_event_filter_check
|
||||
{
|
||||
public:
|
||||
k8s_audit_filter_check();
|
||||
virtual ~k8s_audit_filter_check();
|
||||
|
||||
json_event_filter_check *allocate_new();
|
||||
|
||||
// Index to the appropriate container and/or remove any repo,
|
||||
// port, and tag from the provided image name.
|
||||
static std::string index_image(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Extract the value of the provided query parameter
|
||||
static std::string index_query_param(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Return true if an object in the provided array has a name property with idx as value
|
||||
static std::string index_has_name(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Return whether the ith container (or any container, if an
|
||||
// index is not specified) is run privileged.
|
||||
static std::string index_privileged(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Return whether or not a hostpath mount exists matching the provided index.
|
||||
static std::string check_hostpath_vols(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Index to the ith value from the provided array. If no index is provided, return the entire array as a string.
|
||||
static std::string index_generic(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
|
||||
// Index to the ith value from the provided array, and select
|
||||
// the property which is the last component of the provided
|
||||
// field.
|
||||
static std::string index_select(const nlohmann::json &j, std::string &field, std::string &idx);
|
||||
};
|
||||
|
||||
class json_event_filter : public gen_event_filter
|
||||
{
|
||||
public:
|
||||
json_event_filter();
|
||||
virtual ~json_event_filter();
|
||||
|
||||
std::string m_rule;
|
||||
uint32_t m_rule_idx;
|
||||
std::set<std::string> m_tags;
|
||||
};
|
||||
|
||||
|
||||
class json_event_filter_factory : public gen_event_filter_factory
|
||||
{
|
||||
public:
|
||||
json_event_filter_factory();
|
||||
virtual ~json_event_filter_factory();
|
||||
|
||||
// Create a new filter
|
||||
gen_event_filter *new_filter();
|
||||
|
||||
// Create a new filter_check
|
||||
gen_event_filter_check *new_filtercheck(const char *fldname);
|
||||
|
||||
// All defined field names
|
||||
std::list<json_event_filter_check::check_info> &get_fields();
|
||||
|
||||
private:
|
||||
std::list<std::shared_ptr<json_event_filter_check>> m_defined_checks;
|
||||
std::list<json_event_filter_check::check_info> m_info;
|
||||
|
||||
};
|
||||
|
||||
// Unlike the other classes, this does not inherit from a shared class
|
||||
// that's used both by json events and sinsp events. It might be
|
||||
// worthwhile, but it would require a lot of additional work to pull
|
||||
// up functionality into the generic filtercheck class.
|
||||
|
||||
class json_event_formatter
|
||||
{
|
||||
public:
|
||||
json_event_formatter(json_event_filter_factory &factory, std::string &format);
|
||||
virtual ~json_event_formatter();
|
||||
|
||||
std::string tostring(json_event *ev);
|
||||
std::string tojson(json_event *ev);
|
||||
|
||||
private:
|
||||
void parse_format();
|
||||
|
||||
void resolve_tokens(json_event *ev, std::list<std::pair<std::string,std::string>> &resolved);
|
||||
|
||||
// A format token is either a combination of a filtercheck
|
||||
// name (ka.value) and filtercheck object as key, or an empty
|
||||
// key and a NULL filtercheck object, combined with a value (
|
||||
//
|
||||
// For example, given a format string:
|
||||
// "The value is %ka.value today"
|
||||
// The tokens would be:
|
||||
// [("The value is ", NULL), ("ka.value", <an object>), " today", NULL)]
|
||||
|
||||
struct fmt_token
|
||||
{
|
||||
std::string text;
|
||||
std::shared_ptr<json_event_filter_check> check;
|
||||
};
|
||||
|
||||
// The original format string
|
||||
std::string m_format;
|
||||
|
||||
// The chunks that make up the format string, in order, broken
|
||||
// up between text chunks and filterchecks.
|
||||
std::list<fmt_token> m_tokens;
|
||||
|
||||
// All the filterchecks required to resolve tokens in the format string
|
||||
json_event_filter_factory &m_json_factory;
|
||||
};
|
||||
|
||||
|
||||
|
@ -17,19 +17,8 @@
|
||||
local parser = require("parser")
|
||||
local compiler = {}
|
||||
|
||||
compiler.verbose = false
|
||||
compiler.all_events = false
|
||||
compiler.trim = parser.trim
|
||||
|
||||
function compiler.set_verbose(verbose)
|
||||
compiler.verbose = verbose
|
||||
parser.set_verbose(verbose)
|
||||
end
|
||||
|
||||
function compiler.set_all_events(all_events)
|
||||
compiler.all_events = all_events
|
||||
end
|
||||
|
||||
function map(f, arr)
|
||||
local res = {}
|
||||
for i,v in ipairs(arr) do
|
||||
@ -145,182 +134,6 @@ function get_macros(ast, set)
|
||||
return set
|
||||
end
|
||||
|
||||
function check_for_ignored_syscalls_events(ast, filter_type, source)
|
||||
|
||||
function check_syscall(val)
|
||||
if ignored_syscalls[val] then
|
||||
error("Ignored syscall \""..val.."\" in "..filter_type..": "..source)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function check_event(val)
|
||||
if ignored_events[val] then
|
||||
error("Ignored event \""..val.."\" in "..filter_type..": "..source)
|
||||
end
|
||||
end
|
||||
|
||||
function cb(node)
|
||||
if node.left.type == "FieldName" and
|
||||
(node.left.value == "evt.type" or
|
||||
node.left.value == "syscall.type") then
|
||||
|
||||
if node.operator == "in" or node.operator == "pmatch" then
|
||||
for i, v in ipairs(node.right.elements) do
|
||||
if v.type == "BareString" then
|
||||
if node.left.value == "evt.type" then
|
||||
check_event(v.value)
|
||||
else
|
||||
check_syscall(v.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if node.right.type == "BareString" then
|
||||
if node.left.value == "evt.type" then
|
||||
check_event(node.right.value)
|
||||
else
|
||||
check_syscall(node.right.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.traverse_ast(ast, {BinaryRelOp=1}, cb)
|
||||
end
|
||||
|
||||
-- Examine the ast and find the event types/syscalls for which the
|
||||
-- rule should run. All evt.type references are added as event types
|
||||
-- up until the first "!=" binary operator or unary not operator. If
|
||||
-- no event type checks are found afterward in the rule, the rule is
|
||||
-- considered optimized and is associated with the event type(s).
|
||||
--
|
||||
-- Otherwise, the rule is associated with a 'catchall' category and is
|
||||
-- run for all event types/syscalls. (Also, a warning is printed).
|
||||
--
|
||||
|
||||
function get_evttypes_syscalls(name, ast, source, warn_evttypes)
|
||||
|
||||
local evttypes = {}
|
||||
local syscallnums = {}
|
||||
local evtnames = {}
|
||||
local found_event = false
|
||||
local found_not = false
|
||||
local found_event_after_not = false
|
||||
|
||||
function cb(node)
|
||||
if node.type == "UnaryBoolOp" then
|
||||
if node.operator == "not" then
|
||||
found_not = true
|
||||
end
|
||||
else
|
||||
if node.operator == "!=" then
|
||||
found_not = true
|
||||
end
|
||||
if node.left.type == "FieldName" and node.left.value == "evt.type" then
|
||||
found_event = true
|
||||
if found_not then
|
||||
found_event_after_not = true
|
||||
end
|
||||
if node.operator == "in" or node.operator == "pmatch" then
|
||||
for i, v in ipairs(node.right.elements) do
|
||||
if v.type == "BareString" then
|
||||
|
||||
-- The event must be a known event
|
||||
if events[v.value] == nil and syscalls[v.value] == nil then
|
||||
error("Unknown event/syscall \""..v.value.."\" in filter: "..source)
|
||||
end
|
||||
|
||||
evtnames[v.value] = 1
|
||||
if events[v.value] ~= nil then
|
||||
for id in string.gmatch(events[v.value], "%S+") do
|
||||
evttypes[id] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if syscalls[v.value] ~= nil then
|
||||
for id in string.gmatch(syscalls[v.value], "%S+") do
|
||||
syscallnums[id] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if node.right.type == "BareString" then
|
||||
|
||||
-- The event must be a known event
|
||||
if events[node.right.value] == nil and syscalls[node.right.value] == nil then
|
||||
error("Unknown event/syscall \""..node.right.value.."\" in filter: "..source)
|
||||
end
|
||||
|
||||
evtnames[node.right.value] = 1
|
||||
if events[node.right.value] ~= nil then
|
||||
for id in string.gmatch(events[node.right.value], "%S+") do
|
||||
evttypes[id] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if syscalls[node.right.value] ~= nil then
|
||||
for id in string.gmatch(syscalls[node.right.value], "%S+") do
|
||||
syscallnums[id] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.traverse_ast(ast.filter.value, {BinaryRelOp=1, UnaryBoolOp=1} , cb)
|
||||
|
||||
if not found_event then
|
||||
if warn_evttypes == true then
|
||||
io.stderr:write("Rule "..name..": warning (no-evttype):\n")
|
||||
io.stderr:write(source.."\n")
|
||||
io.stderr:write(" did not contain any evt.type restriction, meaning it will run for all event types.\n")
|
||||
io.stderr:write(" This has a significant performance penalty. Consider adding an evt.type restriction if possible.\n")
|
||||
end
|
||||
evttypes = {}
|
||||
syscallnums = {}
|
||||
evtnames = {}
|
||||
end
|
||||
|
||||
if found_event_after_not then
|
||||
if warn_evttypes == true then
|
||||
io.stderr:write("Rule "..name..": warning (trailing-evttype):\n")
|
||||
io.stderr:write(source.."\n")
|
||||
io.stderr:write(" does not have all evt.type restrictions at the beginning of the condition,\n")
|
||||
io.stderr:write(" or uses a negative match (i.e. \"not\"/\"!=\") for some evt.type restriction.\n")
|
||||
io.stderr:write(" This has a performance penalty, as the rule can not be limited to specific event types.\n")
|
||||
io.stderr:write(" Consider moving all evt.type restrictions to the beginning of the rule and/or\n")
|
||||
io.stderr:write(" replacing negative matches with positive matches if possible.\n")
|
||||
end
|
||||
evttypes = {}
|
||||
syscallnums = {}
|
||||
evtnames = {}
|
||||
end
|
||||
|
||||
evtnames_only = {}
|
||||
local num_evtnames = 0
|
||||
for name, dummy in pairs(evtnames) do
|
||||
table.insert(evtnames_only, name)
|
||||
num_evtnames = num_evtnames + 1
|
||||
end
|
||||
|
||||
if num_evtnames == 0 then
|
||||
table.insert(evtnames_only, "all")
|
||||
end
|
||||
|
||||
table.sort(evtnames_only)
|
||||
|
||||
if compiler.verbose then
|
||||
io.stderr:write("Event types/Syscalls for rule "..name..": "..table.concat(evtnames_only, ",").."\n")
|
||||
end
|
||||
|
||||
return evttypes, syscallnums
|
||||
end
|
||||
|
||||
function get_filters(ast)
|
||||
|
||||
local filters = {}
|
||||
@ -366,12 +179,6 @@ function compiler.compile_macro(line, macro_defs, list_defs)
|
||||
error(msg)
|
||||
end
|
||||
|
||||
-- Traverse the ast looking for events/syscalls in the ignored
|
||||
-- syscalls table. If any are found, return an error.
|
||||
if not compiler.all_events then
|
||||
check_for_ignored_syscalls_events(ast, 'macro', line)
|
||||
end
|
||||
|
||||
-- Simply as a validation step, try to expand all macros in this
|
||||
-- macro's condition. This changes the ast, so we make a copy
|
||||
-- first.
|
||||
@ -393,7 +200,7 @@ end
|
||||
--[[
|
||||
Parses a single filter, then expands macros using passed-in table of definitions. Returns resulting AST.
|
||||
--]]
|
||||
function compiler.compile_filter(name, source, macro_defs, list_defs, warn_evttypes)
|
||||
function compiler.compile_filter(name, source, macro_defs, list_defs)
|
||||
|
||||
source = compiler.expand_lists_in(source, list_defs)
|
||||
|
||||
@ -404,12 +211,6 @@ function compiler.compile_filter(name, source, macro_defs, list_defs, warn_evtty
|
||||
error(msg)
|
||||
end
|
||||
|
||||
-- Traverse the ast looking for events/syscalls in the ignored
|
||||
-- syscalls table. If any are found, return an error.
|
||||
if not compiler.all_events then
|
||||
check_for_ignored_syscalls_events(ast, 'rule', source)
|
||||
end
|
||||
|
||||
if (ast.type == "Rule") then
|
||||
-- Line is a filter, so expand macro references
|
||||
repeat
|
||||
@ -420,11 +221,9 @@ function compiler.compile_filter(name, source, macro_defs, list_defs, warn_evtty
|
||||
error("Unexpected top-level AST type: "..ast.type)
|
||||
end
|
||||
|
||||
evttypes, syscallnums = get_evttypes_syscalls(name, ast, source, warn_evttypes)
|
||||
|
||||
filters = get_filters(ast)
|
||||
|
||||
return ast, evttypes, syscallnums, filters
|
||||
return ast, filters
|
||||
end
|
||||
|
||||
|
||||
|
@ -28,12 +28,6 @@
|
||||
|
||||
local parser = {}
|
||||
|
||||
parser.verbose = false
|
||||
|
||||
function parser.set_verbose(verbose)
|
||||
parser.verbose = verbose
|
||||
end
|
||||
|
||||
local lpeg = require "lpeg"
|
||||
|
||||
lpeg.locale(lpeg)
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
--]]
|
||||
|
||||
local sinsp_rule_utils = require "sinsp_rule_utils"
|
||||
local compiler = require "compiler"
|
||||
local yaml = require"lyaml"
|
||||
|
||||
@ -73,7 +74,7 @@ end
|
||||
--[[
|
||||
Take a filter AST and set it up in the libsinsp runtime, using the filter API.
|
||||
--]]
|
||||
local function install_filter(node, parent_bool_op)
|
||||
local function install_filter(node, filter_api_lib, lua_parser, parent_bool_op)
|
||||
local t = node.type
|
||||
|
||||
if t == "BinaryBoolOp" then
|
||||
@ -82,34 +83,34 @@ local function install_filter(node, parent_bool_op)
|
||||
-- never necessary when we have identical successive operators. so we
|
||||
-- avoid it as a runtime performance optimization.
|
||||
if (not(node.operator == parent_bool_op)) then
|
||||
filter.nest() -- io.write("(")
|
||||
filter_api_lib.nest(lua_parser) -- io.write("(")
|
||||
end
|
||||
|
||||
install_filter(node.left, node.operator)
|
||||
filter.bool_op(node.operator) -- io.write(" "..node.operator.." ")
|
||||
install_filter(node.right, node.operator)
|
||||
install_filter(node.left, filter_api_lib, lua_parser, node.operator)
|
||||
filter_api_lib.bool_op(lua_parser, node.operator) -- io.write(" "..node.operator.." ")
|
||||
install_filter(node.right, filter_api_lib, lua_parser, node.operator)
|
||||
|
||||
if (not (node.operator == parent_bool_op)) then
|
||||
filter.unnest() -- io.write(")")
|
||||
filter_api_lib.unnest(lua_parser) -- io.write(")")
|
||||
end
|
||||
|
||||
elseif t == "UnaryBoolOp" then
|
||||
filter.nest() --io.write("(")
|
||||
filter.bool_op(node.operator) -- io.write(" "..node.operator.." ")
|
||||
install_filter(node.argument)
|
||||
filter.unnest() -- io.write(")")
|
||||
filter_api_lib.nest(lua_parser) --io.write("(")
|
||||
filter_api_lib.bool_op(lua_parser, node.operator) -- io.write(" "..node.operator.." ")
|
||||
install_filter(node.argument, filter_api_lib, lua_parser)
|
||||
filter_api_lib.unnest(lua_parser) -- io.write(")")
|
||||
|
||||
elseif t == "BinaryRelOp" then
|
||||
if (node.operator == "in" or node.operator == "pmatch") then
|
||||
elements = map(function (el) return el.value end, node.right.elements)
|
||||
filter.rel_expr(node.left.value, node.operator, elements, node.index)
|
||||
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, elements, node.index)
|
||||
else
|
||||
filter.rel_expr(node.left.value, node.operator, node.right.value, node.index)
|
||||
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, node.right.value, node.index)
|
||||
end
|
||||
-- io.write(node.left.value.." "..node.operator.." "..node.right.value)
|
||||
|
||||
elseif t == "UnaryRelOp" then
|
||||
filter.rel_expr(node.argument.value, node.operator, node.index)
|
||||
filter_api_lib.rel_expr(lua_parser, node.argument.value, node.operator, node.index)
|
||||
--io.write(node.argument.value.." "..node.operator)
|
||||
|
||||
else
|
||||
@ -183,10 +184,15 @@ function table.tostring( tbl )
|
||||
end
|
||||
|
||||
|
||||
function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replace_container_info, min_priority)
|
||||
|
||||
compiler.set_verbose(verbose)
|
||||
compiler.set_all_events(all_events)
|
||||
function load_rules(sinsp_lua_parser,
|
||||
json_lua_parser,
|
||||
rules_content,
|
||||
rules_mgr,
|
||||
verbose,
|
||||
all_events,
|
||||
extra,
|
||||
replace_container_info,
|
||||
min_priority)
|
||||
|
||||
local rules = yaml.load(rules_content)
|
||||
|
||||
@ -210,6 +216,11 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
end
|
||||
|
||||
if (v['macro']) then
|
||||
|
||||
if v['source'] == nil then
|
||||
v['source'] = "syscall"
|
||||
end
|
||||
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
|
||||
end
|
||||
@ -281,6 +292,10 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
v['skip-if-unknown-filter'] = false
|
||||
end
|
||||
|
||||
if v['source'] == nil then
|
||||
v['source'] = "syscall"
|
||||
end
|
||||
|
||||
-- Possibly append to the condition field of an existing rule
|
||||
append = false
|
||||
|
||||
@ -372,6 +387,13 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
local v = state.macros_by_name[name]
|
||||
|
||||
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
if not all_events then
|
||||
sinsp_rule_utils.check_for_ignored_syscalls_events(ast, 'macro', v['condition'])
|
||||
end
|
||||
end
|
||||
|
||||
state.macros[v['macro']] = {["ast"] = ast.filter.value, ["used"] = false}
|
||||
end
|
||||
|
||||
@ -384,9 +406,19 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
warn_evttypes = v['warn_evttypes']
|
||||
end
|
||||
|
||||
local filter_ast, evttypes, syscallnums, filters = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists,
|
||||
warn_evttypes)
|
||||
local filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists)
|
||||
|
||||
local evtttypes = {}
|
||||
local syscallnums = {}
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
if not all_events then
|
||||
sinsp_rule_utils.check_for_ignored_syscalls_events(filter_ast, 'rule', v['rule'])
|
||||
end
|
||||
|
||||
evttypes, syscallnums = sinsp_rule_utils.get_evttypes_syscalls(name, filter_ast, v['condition'], warn_evttypes, verbose)
|
||||
end
|
||||
|
||||
-- If a filter in the rule doesn't exist, either skip the rule
|
||||
-- or raise an error, depending on the value of
|
||||
@ -425,15 +457,20 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
-- event.
|
||||
mark_relational_nodes(filter_ast.filter.value, state.n_rules)
|
||||
|
||||
install_filter(filter_ast.filter.value)
|
||||
|
||||
if (v['tags'] == nil) then
|
||||
v['tags'] = {}
|
||||
end
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
install_filter(filter_ast.filter.value, filter, sinsp_lua_parser)
|
||||
-- Pass the filter and event types back up
|
||||
falco_rules.add_filter(rules_mgr, v['rule'], evttypes, syscallnums, v['tags'])
|
||||
|
||||
elseif v['source'] == "k8s_audit" then
|
||||
install_filter(filter_ast.filter.value, k8s_audit_filter, json_lua_parser)
|
||||
|
||||
falco_rules.add_k8s_audit_filter(rules_mgr, v['rule'], v['tags'])
|
||||
end
|
||||
|
||||
-- Rule ASTs are merged together into one big AST, with "OR" between each
|
||||
-- rule.
|
||||
if (state.filter_ast == nil) then
|
||||
@ -480,8 +517,8 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
-- Ensure that the output field is properly formatted by
|
||||
-- creating a formatter from it. Any error will be thrown
|
||||
-- up to the top level.
|
||||
formatter = formats.formatter(v['output'])
|
||||
formats.free_formatter(formatter)
|
||||
formatter = formats.formatter(v['source'], v['output'])
|
||||
formats.free_formatter(v['source'], formatter)
|
||||
else
|
||||
error ("Unexpected type in load_rule: "..filter_ast.type)
|
||||
end
|
||||
@ -557,7 +594,7 @@ end
|
||||
|
||||
local rule_output_counts = {total=0, by_priority={}, by_name={}}
|
||||
|
||||
function on_event(evt_, rule_id)
|
||||
function on_event(rule_id)
|
||||
|
||||
if state.rules_by_idx[rule_id] == nil then
|
||||
error ("rule_loader.on_event(): event with invalid rule_id: ", rule_id)
|
||||
|
197
userspace/engine/lua/sinsp_rule_utils.lua
Normal file
197
userspace/engine/lua/sinsp_rule_utils.lua
Normal file
@ -0,0 +1,197 @@
|
||||
-- Copyright (C) 2018 Draios inc.
|
||||
--
|
||||
-- This file is part of falco.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
|
||||
local parser = require("parser")
|
||||
local sinsp_rule_utils = {}
|
||||
|
||||
function sinsp_rule_utils.check_for_ignored_syscalls_events(ast, filter_type, source)
|
||||
|
||||
function check_syscall(val)
|
||||
if ignored_syscalls[val] then
|
||||
error("Ignored syscall \""..val.."\" in "..filter_type..": "..source)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function check_event(val)
|
||||
if ignored_events[val] then
|
||||
error("Ignored event \""..val.."\" in "..filter_type..": "..source)
|
||||
end
|
||||
end
|
||||
|
||||
function cb(node)
|
||||
if node.left.type == "FieldName" and
|
||||
(node.left.value == "evt.type" or
|
||||
node.left.value == "syscall.type") then
|
||||
|
||||
if node.operator == "in" or node.operator == "pmatch" then
|
||||
for i, v in ipairs(node.right.elements) do
|
||||
if v.type == "BareString" then
|
||||
if node.left.value == "evt.type" then
|
||||
check_event(v.value)
|
||||
else
|
||||
check_syscall(v.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if node.right.type == "BareString" then
|
||||
if node.left.value == "evt.type" then
|
||||
check_event(node.right.value)
|
||||
else
|
||||
check_syscall(node.right.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.traverse_ast(ast, {BinaryRelOp=1}, cb)
|
||||
end
|
||||
|
||||
-- Examine the ast and find the event types/syscalls for which the
|
||||
-- rule should run. All evt.type references are added as event types
|
||||
-- up until the first "!=" binary operator or unary not operator. If
|
||||
-- no event type checks are found afterward in the rule, the rule is
|
||||
-- considered optimized and is associated with the event type(s).
|
||||
--
|
||||
-- Otherwise, the rule is associated with a 'catchall' category and is
|
||||
-- run for all event types/syscalls. (Also, a warning is printed).
|
||||
--
|
||||
|
||||
function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes, verbose)
|
||||
|
||||
local evttypes = {}
|
||||
local syscallnums = {}
|
||||
local evtnames = {}
|
||||
local found_event = false
|
||||
local found_not = false
|
||||
local found_event_after_not = false
|
||||
|
||||
function cb(node)
|
||||
if node.type == "UnaryBoolOp" then
|
||||
if node.operator == "not" then
|
||||
found_not = true
|
||||
end
|
||||
else
|
||||
if node.operator == "!=" then
|
||||
found_not = true
|
||||
end
|
||||
if node.left.type == "FieldName" and node.left.value == "evt.type" then
|
||||
found_event = true
|
||||
if found_not then
|
||||
found_event_after_not = true
|
||||
end
|
||||
if node.operator == "in" or node.operator == "pmatch" then
|
||||
for i, v in ipairs(node.right.elements) do
|
||||
if v.type == "BareString" then
|
||||
|
||||
-- The event must be a known event
|
||||
if events[v.value] == nil and syscalls[v.value] == nil then
|
||||
error("Unknown event/syscall \""..v.value.."\" in filter: "..source)
|
||||
end
|
||||
|
||||
evtnames[v.value] = 1
|
||||
if events[v.value] ~= nil then
|
||||
for id in string.gmatch(events[v.value], "%S+") do
|
||||
evttypes[id] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if syscalls[v.value] ~= nil then
|
||||
for id in string.gmatch(syscalls[v.value], "%S+") do
|
||||
syscallnums[id] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if node.right.type == "BareString" then
|
||||
|
||||
-- The event must be a known event
|
||||
if events[node.right.value] == nil and syscalls[node.right.value] == nil then
|
||||
error("Unknown event/syscall \""..node.right.value.."\" in filter: "..source)
|
||||
end
|
||||
|
||||
evtnames[node.right.value] = 1
|
||||
if events[node.right.value] ~= nil then
|
||||
for id in string.gmatch(events[node.right.value], "%S+") do
|
||||
evttypes[id] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if syscalls[node.right.value] ~= nil then
|
||||
for id in string.gmatch(syscalls[node.right.value], "%S+") do
|
||||
syscallnums[id] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.traverse_ast(ast.filter.value, {BinaryRelOp=1, UnaryBoolOp=1} , cb)
|
||||
|
||||
if not found_event then
|
||||
if warn_evttypes == true then
|
||||
io.stderr:write("Rule "..name..": warning (no-evttype):\n")
|
||||
io.stderr:write(source.."\n")
|
||||
io.stderr:write(" did not contain any evt.type restriction, meaning it will run for all event types.\n")
|
||||
io.stderr:write(" This has a significant performance penalty. Consider adding an evt.type restriction if possible.\n")
|
||||
end
|
||||
evttypes = {}
|
||||
syscallnums = {}
|
||||
evtnames = {}
|
||||
end
|
||||
|
||||
if found_event_after_not then
|
||||
if warn_evttypes == true then
|
||||
io.stderr:write("Rule "..name..": warning (trailing-evttype):\n")
|
||||
io.stderr:write(source.."\n")
|
||||
io.stderr:write(" does not have all evt.type restrictions at the beginning of the condition,\n")
|
||||
io.stderr:write(" or uses a negative match (i.e. \"not\"/\"!=\") for some evt.type restriction.\n")
|
||||
io.stderr:write(" This has a performance penalty, as the rule can not be limited to specific event types.\n")
|
||||
io.stderr:write(" Consider moving all evt.type restrictions to the beginning of the rule and/or\n")
|
||||
io.stderr:write(" replacing negative matches with positive matches if possible.\n")
|
||||
end
|
||||
evttypes = {}
|
||||
syscallnums = {}
|
||||
evtnames = {}
|
||||
end
|
||||
|
||||
evtnames_only = {}
|
||||
local num_evtnames = 0
|
||||
for name, dummy in pairs(evtnames) do
|
||||
table.insert(evtnames_only, name)
|
||||
num_evtnames = num_evtnames + 1
|
||||
end
|
||||
|
||||
if num_evtnames == 0 then
|
||||
table.insert(evtnames_only, "all")
|
||||
end
|
||||
|
||||
table.sort(evtnames_only)
|
||||
|
||||
if verbose then
|
||||
io.stderr:write("Event types/Syscalls for rule "..name..": "..table.concat(evtnames_only, ",").."\n")
|
||||
end
|
||||
|
||||
return evttypes, syscallnums
|
||||
end
|
||||
|
||||
return sinsp_rule_utils
|
@ -31,14 +31,20 @@ const static struct luaL_reg ll_falco_rules [] =
|
||||
{
|
||||
{"clear_filters", &falco_rules::clear_filters},
|
||||
{"add_filter", &falco_rules::add_filter},
|
||||
{"add_k8s_audit_filter", &falco_rules::add_k8s_audit_filter},
|
||||
{"enable_rule", &falco_rules::enable_rule},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
falco_rules::falco_rules(sinsp* inspector, falco_engine *engine, lua_State *ls)
|
||||
: m_inspector(inspector), m_engine(engine), m_ls(ls)
|
||||
falco_rules::falco_rules(sinsp* inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls)
|
||||
: m_inspector(inspector),
|
||||
m_engine(engine),
|
||||
m_ls(ls)
|
||||
{
|
||||
m_lua_parser = new lua_parser(inspector, m_ls);
|
||||
m_sinsp_lua_parser = new lua_parser(engine->sinsp_factory(), m_ls, "filter");
|
||||
m_json_lua_parser = new lua_parser(engine->json_factory(), m_ls, "k8s_audit_filter");
|
||||
}
|
||||
|
||||
void falco_rules::init(lua_State *ls)
|
||||
@ -122,14 +128,55 @@ int falco_rules::add_filter(lua_State *ls)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falco_rules::add_k8s_audit_filter(lua_State *ls)
|
||||
{
|
||||
if (! lua_islightuserdata(ls, -3) ||
|
||||
! lua_isstring(ls, -2) ||
|
||||
! lua_istable(ls, -1))
|
||||
{
|
||||
lua_pushstring(ls, "Invalid arguments passed to add_k8s_audit_filter()");
|
||||
lua_error(ls);
|
||||
}
|
||||
|
||||
falco_rules *rules = (falco_rules *) lua_topointer(ls, -3);
|
||||
const char *rulec = lua_tostring(ls, -2);
|
||||
|
||||
set<string> tags;
|
||||
|
||||
lua_pushnil(ls); /* first key */
|
||||
while (lua_next(ls, -2) != 0) {
|
||||
// key is at index -2, value is at index
|
||||
// -1. We want the values.
|
||||
tags.insert(lua_tostring(ls, -1));
|
||||
|
||||
// Remove value, keep key for next iteration
|
||||
lua_pop(ls, 1);
|
||||
}
|
||||
|
||||
std::string rule = rulec;
|
||||
rules->add_k8s_audit_filter(rule, tags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void falco_rules::add_filter(string &rule, set<uint32_t> &evttypes, set<uint32_t> &syscalls, set<string> &tags)
|
||||
{
|
||||
// While the current rule was being parsed, a sinsp_filter
|
||||
// object was being populated by lua_parser. Grab that filter
|
||||
// and pass it to the engine.
|
||||
sinsp_filter *filter = m_lua_parser->get_filter(true);
|
||||
sinsp_filter *filter = (sinsp_filter *) m_sinsp_lua_parser->get_filter(true);
|
||||
|
||||
m_engine->add_evttype_filter(rule, evttypes, syscalls, tags, filter);
|
||||
m_engine->add_sinsp_filter(rule, evttypes, syscalls, tags, filter);
|
||||
}
|
||||
|
||||
void falco_rules::add_k8s_audit_filter(string &rule, set<string> &tags)
|
||||
{
|
||||
// While the current rule was being parsed, a sinsp_filter
|
||||
// object was being populated by lua_parser. Grab that filter
|
||||
// and pass it to the engine.
|
||||
json_event_filter *filter = (json_event_filter *) m_json_lua_parser->get_filter(true);
|
||||
|
||||
m_engine->add_k8s_audit_filter(rule, tags, filter);
|
||||
}
|
||||
|
||||
int falco_rules::enable_rule(lua_State *ls)
|
||||
@ -314,8 +361,20 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &chk_field : m_engine->json_factory().get_fields())
|
||||
{
|
||||
for(auto &field : chk_field.fields)
|
||||
{
|
||||
lua_pushstring(m_ls, field.name.c_str());
|
||||
lua_pushnumber(m_ls, 1);
|
||||
lua_settable(m_ls, -3);
|
||||
}
|
||||
}
|
||||
|
||||
lua_setglobal(m_ls, m_lua_defined_filters.c_str());
|
||||
|
||||
lua_pushlightuserdata(m_ls, m_sinsp_lua_parser);
|
||||
lua_pushlightuserdata(m_ls, m_json_lua_parser);
|
||||
lua_pushstring(m_ls, rules_content.c_str());
|
||||
lua_pushlightuserdata(m_ls, this);
|
||||
lua_pushboolean(m_ls, (verbose ? 1 : 0));
|
||||
@ -323,7 +382,7 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
lua_pushstring(m_ls, extra.c_str());
|
||||
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
|
||||
lua_pushnumber(m_ls, min_priority);
|
||||
if(lua_pcall(m_ls, 7, 0, 0) != 0)
|
||||
if(lua_pcall(m_ls, 9, 0, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error loading rules: " + string(lerr);
|
||||
@ -360,6 +419,7 @@ void falco_rules::describe_rule(std::string *rule)
|
||||
|
||||
falco_rules::~falco_rules()
|
||||
{
|
||||
delete m_lua_parser;
|
||||
delete m_sinsp_lua_parser;
|
||||
delete m_json_lua_parser;
|
||||
}
|
||||
|
||||
|
@ -20,11 +20,14 @@ limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include "lua_parser.h"
|
||||
|
||||
#include "json_evt.h"
|
||||
#include "falco_common.h"
|
||||
|
||||
class falco_engine;
|
||||
@ -32,7 +35,9 @@ class falco_engine;
|
||||
class falco_rules
|
||||
{
|
||||
public:
|
||||
falco_rules(sinsp* inspector, falco_engine *engine, lua_State *ls);
|
||||
falco_rules(sinsp* inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls);
|
||||
~falco_rules();
|
||||
void load_rules(const string &rules_content, bool verbose, bool all_events,
|
||||
std::string &extra, bool replace_container_info,
|
||||
@ -42,14 +47,17 @@ class falco_rules
|
||||
static void init(lua_State *ls);
|
||||
static int clear_filters(lua_State *ls);
|
||||
static int add_filter(lua_State *ls);
|
||||
static int add_k8s_audit_filter(lua_State *ls);
|
||||
static int enable_rule(lua_State *ls);
|
||||
|
||||
private:
|
||||
void clear_filters();
|
||||
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<uint32_t> &syscalls, std::set<string> &tags);
|
||||
void add_k8s_audit_filter(string &rule, std::set<string> &tags);
|
||||
void enable_rule(string &rule, bool enabled);
|
||||
|
||||
lua_parser* m_lua_parser;
|
||||
lua_parser* m_sinsp_lua_parser;
|
||||
lua_parser* m_json_lua_parser;
|
||||
sinsp* m_inspector;
|
||||
falco_engine *m_engine;
|
||||
lua_State* m_ls;
|
||||
|
352
userspace/engine/ruleset.cpp
Normal file
352
userspace/engine/ruleset.cpp
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#include "ruleset.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
falco_ruleset::falco_ruleset()
|
||||
{
|
||||
}
|
||||
|
||||
falco_ruleset::~falco_ruleset()
|
||||
{
|
||||
for(const auto &val : m_filters)
|
||||
{
|
||||
delete val.second->filter;
|
||||
delete val.second;
|
||||
}
|
||||
|
||||
for(auto &ruleset : m_rulesets)
|
||||
{
|
||||
delete ruleset;
|
||||
}
|
||||
m_filters.clear();
|
||||
}
|
||||
|
||||
falco_ruleset::ruleset_filters::ruleset_filters()
|
||||
{
|
||||
}
|
||||
|
||||
falco_ruleset::ruleset_filters::~ruleset_filters()
|
||||
{
|
||||
for(uint32_t i = 0; i < m_filter_by_event_tag.size(); i++)
|
||||
{
|
||||
if(m_filter_by_event_tag[i])
|
||||
{
|
||||
delete m_filter_by_event_tag[i];
|
||||
m_filter_by_event_tag[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_ruleset::ruleset_filters::add_filter(filter_wrapper *wrap)
|
||||
{
|
||||
for(uint32_t etag = 0; etag < wrap->event_tags.size(); etag++)
|
||||
{
|
||||
if(wrap->event_tags[etag])
|
||||
{
|
||||
if(m_filter_by_event_tag.size() <= etag)
|
||||
{
|
||||
m_filter_by_event_tag.resize(etag+1);
|
||||
}
|
||||
|
||||
if(!m_filter_by_event_tag[etag])
|
||||
{
|
||||
m_filter_by_event_tag[etag] = new list<filter_wrapper *>();
|
||||
}
|
||||
|
||||
m_filter_by_event_tag[etag]->push_back(wrap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_ruleset::ruleset_filters::remove_filter(filter_wrapper *wrap)
|
||||
{
|
||||
for(uint32_t etag = 0; etag < wrap->event_tags.size(); etag++)
|
||||
{
|
||||
if(wrap->event_tags[etag])
|
||||
{
|
||||
if(etag < m_filter_by_event_tag.size())
|
||||
{
|
||||
list<filter_wrapper *> *l = m_filter_by_event_tag[etag];
|
||||
if(l)
|
||||
{
|
||||
l->erase(remove(l->begin(),
|
||||
l->end(),
|
||||
wrap),
|
||||
l->end());
|
||||
|
||||
if(l->size() == 0)
|
||||
{
|
||||
delete l;
|
||||
m_filter_by_event_tag[etag] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool falco_ruleset::ruleset_filters::run(gen_event *evt, uint32_t etag)
|
||||
{
|
||||
if(etag >= m_filter_by_event_tag.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
list<filter_wrapper *> *filters = m_filter_by_event_tag[etag];
|
||||
|
||||
if (!filters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &wrap : *filters)
|
||||
{
|
||||
if(wrap->filter->run(evt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void falco_ruleset::ruleset_filters::event_tags_for_ruleset(vector<bool> &event_tags)
|
||||
{
|
||||
event_tags.assign(m_filter_by_event_tag.size(), false);
|
||||
|
||||
for(uint32_t etag = 0; etag < m_filter_by_event_tag.size(); etag++)
|
||||
{
|
||||
list<filter_wrapper *> *filters = m_filter_by_event_tag[etag];
|
||||
if(filters)
|
||||
{
|
||||
event_tags[etag] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_ruleset::add(string &name,
|
||||
set<string> &tags,
|
||||
set<uint32_t> &event_tags,
|
||||
gen_event_filter *filter)
|
||||
{
|
||||
filter_wrapper *wrap = new filter_wrapper();
|
||||
wrap->filter = filter;
|
||||
|
||||
for(auto &etag : event_tags)
|
||||
{
|
||||
wrap->event_tags.resize(etag+1);
|
||||
wrap->event_tags[etag] = true;
|
||||
}
|
||||
|
||||
m_filters.insert(pair<string,filter_wrapper *>(name, wrap));
|
||||
|
||||
for(const auto &tag: tags)
|
||||
{
|
||||
auto it = m_filter_by_event_tag.lower_bound(tag);
|
||||
|
||||
if(it == m_filter_by_event_tag.end() ||
|
||||
it->first != tag)
|
||||
{
|
||||
it = m_filter_by_event_tag.emplace_hint(it,
|
||||
make_pair(tag, list<filter_wrapper*>()));
|
||||
}
|
||||
|
||||
it->second.push_back(wrap);
|
||||
}
|
||||
}
|
||||
|
||||
void falco_ruleset::enable(const string &pattern, bool enabled, uint16_t ruleset)
|
||||
{
|
||||
regex re(pattern);
|
||||
|
||||
while (m_rulesets.size() < (size_t) ruleset + 1)
|
||||
{
|
||||
m_rulesets.push_back(new ruleset_filters());
|
||||
}
|
||||
|
||||
for(const auto &val : m_filters)
|
||||
{
|
||||
if (regex_match(val.first, re))
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
m_rulesets[ruleset]->add_filter(val.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rulesets[ruleset]->remove_filter(val.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_ruleset::enable_tags(const set<string> &tags, bool enabled, uint16_t ruleset)
|
||||
{
|
||||
while (m_rulesets.size() < (size_t) ruleset + 1)
|
||||
{
|
||||
m_rulesets.push_back(new ruleset_filters());
|
||||
}
|
||||
|
||||
for(const auto &tag : tags)
|
||||
{
|
||||
for(const auto &wrap : m_filter_by_event_tag[tag])
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
m_rulesets[ruleset]->add_filter(wrap);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rulesets[ruleset]->remove_filter(wrap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool falco_ruleset::run(gen_event *evt, uint32_t etag, uint16_t ruleset)
|
||||
{
|
||||
if(m_rulesets.size() < (size_t) ruleset + 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_rulesets[ruleset]->run(evt, etag);
|
||||
}
|
||||
|
||||
void falco_ruleset::event_tags_for_ruleset(vector<bool> &evttypes, uint16_t ruleset)
|
||||
{
|
||||
return m_rulesets[ruleset]->event_tags_for_ruleset(evttypes);
|
||||
}
|
||||
|
||||
falco_sinsp_ruleset::falco_sinsp_ruleset()
|
||||
{
|
||||
}
|
||||
|
||||
falco_sinsp_ruleset::~falco_sinsp_ruleset()
|
||||
{
|
||||
}
|
||||
|
||||
void falco_sinsp_ruleset::add(string &name,
|
||||
set<uint32_t> &evttypes,
|
||||
set<uint32_t> &syscalls,
|
||||
set<string> &tags,
|
||||
sinsp_filter* filter)
|
||||
{
|
||||
set<uint32_t> event_tags;
|
||||
|
||||
if(evttypes.size() + syscalls.size() == 0)
|
||||
{
|
||||
// If no evttypes or syscalls are specified, the filter is
|
||||
// enabled for all evttypes/syscalls.
|
||||
for(uint32_t i=0; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
evttypes.insert(i);
|
||||
}
|
||||
|
||||
for(uint32_t i=0; i < PPM_SC_MAX; i++)
|
||||
{
|
||||
syscalls.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto evttype: evttypes)
|
||||
{
|
||||
event_tags.insert(evttype_to_event_tag(evttype));
|
||||
}
|
||||
|
||||
for(auto syscallid: syscalls)
|
||||
{
|
||||
event_tags.insert(syscall_to_event_tag(syscallid));
|
||||
}
|
||||
|
||||
falco_ruleset::add(name, tags, event_tags, (gen_event_filter *) filter);
|
||||
}
|
||||
|
||||
bool falco_sinsp_ruleset::run(sinsp_evt *evt, uint16_t ruleset)
|
||||
{
|
||||
uint32_t etag;
|
||||
|
||||
uint16_t etype = evt->get_type();
|
||||
|
||||
if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X)
|
||||
{
|
||||
sinsp_evt_param *parinfo = evt->get_param(0);
|
||||
uint16_t syscallid = *(uint16_t *)parinfo->m_val;
|
||||
|
||||
etag = syscall_to_event_tag(syscallid);
|
||||
}
|
||||
else
|
||||
{
|
||||
etag = evttype_to_event_tag(etype);
|
||||
}
|
||||
|
||||
return falco_ruleset::run((gen_event*) evt, etag, ruleset);
|
||||
}
|
||||
|
||||
void falco_sinsp_ruleset::evttypes_for_ruleset(vector<bool> &evttypes, uint16_t ruleset)
|
||||
{
|
||||
vector<bool> event_tags;
|
||||
|
||||
event_tags_for_ruleset(event_tags, ruleset);
|
||||
|
||||
evttypes.assign(PPM_EVENT_MAX+1, false);
|
||||
|
||||
for(uint32_t etype = 0; etype < PPM_EVENT_MAX; etype++)
|
||||
{
|
||||
uint32_t etag = evttype_to_event_tag(etype);
|
||||
|
||||
if(event_tags[etag])
|
||||
{
|
||||
evttypes[etype] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_sinsp_ruleset::syscalls_for_ruleset(vector<bool> &syscalls, uint16_t ruleset)
|
||||
{
|
||||
vector<bool> event_tags;
|
||||
|
||||
event_tags_for_ruleset(event_tags, ruleset);
|
||||
|
||||
syscalls.assign(PPM_EVENT_MAX+1, false);
|
||||
|
||||
for(uint32_t syscallid = 0; syscallid < PPM_SC_MAX; syscallid++)
|
||||
{
|
||||
uint32_t etag = evttype_to_event_tag(syscallid);
|
||||
|
||||
if(event_tags[etag])
|
||||
{
|
||||
syscalls[syscallid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t falco_sinsp_ruleset::evttype_to_event_tag(uint32_t evttype)
|
||||
{
|
||||
return evttype;
|
||||
}
|
||||
|
||||
uint32_t falco_sinsp_ruleset::syscall_to_event_tag(uint32_t syscallid)
|
||||
{
|
||||
return PPM_EVENT_MAX+1+syscallid;
|
||||
}
|
||||
|
144
userspace/engine/ruleset.h
Normal file
144
userspace/engine/ruleset.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
#include "event.h"
|
||||
|
||||
#include "gen_filter.h"
|
||||
|
||||
class falco_ruleset
|
||||
{
|
||||
public:
|
||||
falco_ruleset();
|
||||
virtual ~falco_ruleset();
|
||||
|
||||
void add(std::string &name,
|
||||
std::set<std::string> &tags,
|
||||
std::set<uint32_t> &event_tags,
|
||||
gen_event_filter* filter);
|
||||
|
||||
// rulesets are arbitrary numbers and should be managed by the caller.
|
||||
// Note that rulesets are used to index into a std::vector so
|
||||
// specifying unnecessarily large rulesets will result in
|
||||
// unnecessarily large vectors.
|
||||
|
||||
// Find those rules matching the provided pattern and set
|
||||
// their enabled status to enabled.
|
||||
void enable(const std::string &pattern, bool enabled, uint16_t ruleset = 0);
|
||||
|
||||
// Find those rules that have a tag in the set of tags and set
|
||||
// their enabled status to enabled. Note that the enabled
|
||||
// status is on the rules, and not the tags--if a rule R has
|
||||
// tags (a, b), and you call enable_tags([a], true) and then
|
||||
// enable_tags([b], false), R will be disabled despite the
|
||||
// fact it has tag a and was enabled by the first call to
|
||||
// enable_tags.
|
||||
void enable_tags(const std::set<std::string> &tags, bool enabled, uint16_t ruleset = 0);
|
||||
|
||||
// Match all filters against the provided event.
|
||||
bool run(gen_event *evt, uint32_t etag, uint16_t ruleset = 0);
|
||||
|
||||
// Populate the provided vector, indexed by event tag, of the
|
||||
// event tags associated with the given ruleset id. For
|
||||
// example, event_tags[10] = true would mean that this ruleset
|
||||
// relates to event tag 10.
|
||||
void event_tags_for_ruleset(std::vector<bool> &event_tags, uint16_t ruleset);
|
||||
|
||||
private:
|
||||
|
||||
struct filter_wrapper {
|
||||
gen_event_filter *filter;
|
||||
|
||||
// Indexes from event tag to enabled/disabled.
|
||||
std::vector<bool> event_tags;
|
||||
};
|
||||
|
||||
// A group of filters all having the same ruleset
|
||||
class ruleset_filters {
|
||||
public:
|
||||
ruleset_filters();
|
||||
|
||||
virtual ~ruleset_filters();
|
||||
|
||||
void add_filter(filter_wrapper *wrap);
|
||||
void remove_filter(filter_wrapper *wrap);
|
||||
|
||||
bool run(gen_event *evt, uint32_t etag);
|
||||
|
||||
void event_tags_for_ruleset(std::vector<bool> &event_tags);
|
||||
|
||||
private:
|
||||
// Maps from event tag to a list of filters. There can
|
||||
// be multiple filters for a given event tag.
|
||||
std::vector<std::list<filter_wrapper *> *> m_filter_by_event_tag;
|
||||
|
||||
};
|
||||
|
||||
std::vector<ruleset_filters *> m_rulesets;
|
||||
|
||||
// Maps from tag to list of filters having that tag.
|
||||
std::map<std::string, std::list<filter_wrapper *>> m_filter_by_event_tag;
|
||||
|
||||
// This holds all the filters passed to add(), so they can
|
||||
// be cleaned up.
|
||||
std::map<std::string,filter_wrapper *> m_filters;
|
||||
};
|
||||
|
||||
// falco_sinsp_ruleset is a specialization of falco_ruleset that
|
||||
// maps sinsp evttypes/syscalls to event tags.
|
||||
class falco_sinsp_ruleset : public falco_ruleset
|
||||
{
|
||||
public:
|
||||
falco_sinsp_ruleset();
|
||||
virtual ~falco_sinsp_ruleset();
|
||||
|
||||
void add(std::string &name,
|
||||
std::set<uint32_t> &evttypes,
|
||||
std::set<uint32_t> &syscalls,
|
||||
std::set<std::string> &tags,
|
||||
sinsp_filter* filter);
|
||||
|
||||
bool run(sinsp_evt *evt, uint16_t ruleset = 0);
|
||||
|
||||
// Populate the provided vector, indexed by event type, of the
|
||||
// event types associated with the given ruleset id. For
|
||||
// example, evttypes[10] = true would mean that this ruleset
|
||||
// relates to event type 10.
|
||||
void evttypes_for_ruleset(std::vector<bool> &evttypes, uint16_t ruleset);
|
||||
|
||||
// Populate the provided vector, indexed by syscall code, of the
|
||||
// syscall codes associated with the given ruleset id. For
|
||||
// example, syscalls[10] = true would mean that this ruleset
|
||||
// relates to syscall code 10.
|
||||
void syscalls_for_ruleset(std::vector<bool> &syscalls, uint16_t ruleset);
|
||||
|
||||
private:
|
||||
uint32_t evttype_to_event_tag(uint32_t evttype);
|
||||
uint32_t syscall_to_event_tag(uint32_t syscallid);
|
||||
};
|
@ -20,20 +20,31 @@ include_directories("${LUAJIT_INCLUDE}")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libscap")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/userspace/engine")
|
||||
include_directories("${PROJECT_BINARY_DIR}/userspace/falco")
|
||||
include_directories("${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
include_directories("${CURL_INCLUDE_DIR}")
|
||||
include_directories("${NJSON_INCLUDE}")
|
||||
include_directories("${YAMLCPP_INCLUDE_DIR}")
|
||||
include_directories("${CIVETWEB_INCLUDE_DIR}")
|
||||
include_directories("${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include")
|
||||
|
||||
add_executable(falco configuration.cpp logger.cpp falco_outputs.cpp statsfilewriter.cpp falco.cpp)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig/config_sysdig.h.in" config_sysdig.h)
|
||||
|
||||
add_executable(falco
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
statsfilewriter.cpp
|
||||
falco.cpp
|
||||
"${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig/fields_info.cpp"
|
||||
webserver.cpp)
|
||||
|
||||
target_link_libraries(falco falco_engine sinsp)
|
||||
target_link_libraries(falco
|
||||
"${LIBYAML_LIB}"
|
||||
"${YAMLCPP_LIB}")
|
||||
|
||||
"${YAMLCPP_LIB}"
|
||||
"${CIVETWEB_LIB}")
|
||||
|
||||
configure_file(config_falco.h.in config_falco.h)
|
||||
|
||||
|
@ -31,6 +31,9 @@ using namespace std;
|
||||
|
||||
falco_configuration::falco_configuration()
|
||||
: m_buffered_outputs(true),
|
||||
m_webserver_enabled(false),
|
||||
m_webserver_listen_port(8765),
|
||||
m_webserver_k8s_audit_endpoint("/k8s_audit"),
|
||||
m_config(NULL)
|
||||
{
|
||||
}
|
||||
@ -156,6 +159,10 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
|
||||
falco_logger::log_stderr = m_config->get_scalar<bool>("log_stderr", false);
|
||||
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
|
||||
|
||||
m_webserver_enabled = m_config->get_scalar<bool>("webserver", "enabled", false);
|
||||
m_webserver_listen_port = m_config->get_scalar<uint32_t>("webserver", "listen_port", 8765);
|
||||
m_webserver_k8s_audit_endpoint = m_config->get_scalar<string>("websever", "k8s_audit_endpoint", "/k8s_audit");
|
||||
}
|
||||
|
||||
void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames)
|
||||
|
@ -178,6 +178,11 @@ class falco_configuration
|
||||
falco_common::priority_type m_min_priority;
|
||||
|
||||
bool m_buffered_outputs;
|
||||
|
||||
bool m_webserver_enabled;
|
||||
uint32_t m_webserver_listen_port;
|
||||
std::string m_webserver_k8s_audit_endpoint;
|
||||
|
||||
private:
|
||||
void init_cmdline_options(std::list<std::string> &cmdline_options);
|
||||
|
||||
|
@ -36,11 +36,14 @@ limitations under the License.
|
||||
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include "chisel.h"
|
||||
#include "sysdig.h"
|
||||
|
||||
#include "configuration.h"
|
||||
#include "falco_engine.h"
|
||||
#include "config_falco.h"
|
||||
#include "statsfilewriter.h"
|
||||
#include "webserver.h"
|
||||
|
||||
bool g_terminate = false;
|
||||
bool g_reopen_outputs = false;
|
||||
@ -82,7 +85,8 @@ static void usage()
|
||||
" -d, --daemon Run as a daemon\n"
|
||||
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -t.\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n"
|
||||
" k8s audit events) instead of tapping into live.\n"
|
||||
" -k <url>, --k8s-api=<url>\n"
|
||||
" Enable Kubernetes support by connecting to the API server\n"
|
||||
" specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\".\n"
|
||||
@ -101,6 +105,8 @@ static void usage()
|
||||
" ':' or '#' characters in the file name.\n"
|
||||
" -L Show the name and description of all rules and exit.\n"
|
||||
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
|
||||
" --list [<source>] List all defined fields. If <source> is provided, only list those fields for\n"
|
||||
" the source <source>. Current values for <source> are \"syscall\", \"k8s_audit\"\n"
|
||||
" -m <url[,marathon_url]>, --mesos-api=<url[,marathon_url]>\n"
|
||||
" Enable Mesos support by connecting to the API server\n"
|
||||
" specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
|
||||
@ -161,6 +167,35 @@ static void display_fatal_err(const string &msg)
|
||||
// Splitting into key=value or key.subkey=value will be handled by configuration class.
|
||||
std::list<string> cmdline_options;
|
||||
|
||||
// Read a jsonl file containing k8s audit events and pass each to the engine.
|
||||
void read_k8s_audit_trace_file(falco_engine *engine,
|
||||
falco_outputs *outputs,
|
||||
string &trace_filename)
|
||||
{
|
||||
ifstream ifs(trace_filename);
|
||||
|
||||
uint64_t line_num = 0;
|
||||
|
||||
while(ifs)
|
||||
{
|
||||
string line, errstr;
|
||||
|
||||
getline(ifs, line);
|
||||
line_num++;
|
||||
|
||||
if(line == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!k8s_audit_handler::accept_data(engine, outputs, line, errstr))
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not read k8s audit event line #" + to_string(line_num) + ", \"" + line + "\": " + errstr + ", stopping");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Event processing loop
|
||||
//
|
||||
@ -247,10 +282,10 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
// engine, which will match the event against the set
|
||||
// of rules. If a match is found, pass the event to
|
||||
// the outputs.
|
||||
unique_ptr<falco_engine::rule_result> res = engine->process_event(ev);
|
||||
unique_ptr<falco_engine::rule_result> res = engine->process_sinsp_event(ev);
|
||||
if(res)
|
||||
{
|
||||
outputs->handle_event(res->evt, res->rule, res->priority_num, res->format);
|
||||
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format);
|
||||
}
|
||||
|
||||
num_evts++;
|
||||
@ -300,6 +335,75 @@ static void print_all_ignored_events(sinsp *inspector)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Must match the value in the zsh tab completion
|
||||
#define DESCRIPTION_TEXT_START 16
|
||||
|
||||
#define CONSOLE_LINE_LEN 79
|
||||
|
||||
static void list_falco_fields(falco_engine *engine)
|
||||
{
|
||||
for(auto &chk_field : engine->json_factory().get_fields())
|
||||
{
|
||||
printf("\n----------------------\n");
|
||||
printf("Field Class: %s (%s)\n\n", chk_field.name.c_str(), chk_field.desc.c_str());
|
||||
|
||||
for(auto &field : chk_field.fields)
|
||||
{
|
||||
uint32_t l, m;
|
||||
|
||||
printf("%s", field.name.c_str());
|
||||
uint32_t namelen = field.name.size();
|
||||
|
||||
if(namelen >= DESCRIPTION_TEXT_START)
|
||||
{
|
||||
printf("\n");
|
||||
namelen = 0;
|
||||
}
|
||||
|
||||
for(l = 0; l < DESCRIPTION_TEXT_START - namelen; l++)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
size_t desclen = field.desc.size();
|
||||
|
||||
for(l = 0; l < desclen; l++)
|
||||
{
|
||||
if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0)
|
||||
{
|
||||
printf("\n");
|
||||
|
||||
for(m = 0; m < DESCRIPTION_TEXT_START; m++)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf("%c", field.desc.at(l));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void list_source_fields(falco_engine *engine, bool verbose, std::string &source)
|
||||
{
|
||||
if(source.size() > 0 &&
|
||||
!(source == "syscall" || source == "k8s_audit"))
|
||||
{
|
||||
throw std::invalid_argument("Value for --list must be \"syscall\" or \"k8s_audit\"");
|
||||
}
|
||||
if(source == "" || source == "syscall")
|
||||
{
|
||||
list_fields(verbose, false);
|
||||
}
|
||||
if(source == "" || source == "k8s_audit")
|
||||
{
|
||||
list_falco_fields(engine);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ARGUMENT PARSING AND PROGRAM SETUP
|
||||
//
|
||||
@ -312,7 +416,8 @@ int falco_init(int argc, char **argv)
|
||||
falco_outputs *outputs = NULL;
|
||||
int op;
|
||||
int long_index = 0;
|
||||
string scap_filename;
|
||||
string trace_filename;
|
||||
bool trace_is_scap = true;
|
||||
string conf_filename;
|
||||
string outfile;
|
||||
list<string> rules_filenames;
|
||||
@ -332,6 +437,8 @@ int falco_init(int argc, char **argv)
|
||||
bool replace_container_info = false;
|
||||
int duration_to_tot = 0;
|
||||
bool print_ignored_events = false;
|
||||
bool list_flds = false;
|
||||
string list_flds_source = "";
|
||||
|
||||
// Used for writing trace files
|
||||
int duration_seconds = 0;
|
||||
@ -343,16 +450,18 @@ int falco_init(int argc, char **argv)
|
||||
bool buffered_cmdline = false;
|
||||
|
||||
// Used for stats
|
||||
uint64_t num_evts;
|
||||
double duration;
|
||||
scap_stats cstats;
|
||||
|
||||
falco_webserver webserver;
|
||||
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{"daemon", no_argument, 0, 'd' },
|
||||
{"k8s-api", required_argument, 0, 'k'},
|
||||
{"k8s-api-cert", required_argument, 0, 'K' },
|
||||
{"list", optional_argument, 0},
|
||||
{"mesos-api", required_argument, 0, 'm'},
|
||||
{"option", required_argument, 0, 'o'},
|
||||
{"print", required_argument, 0, 'p' },
|
||||
@ -379,7 +488,7 @@ int falco_init(int argc, char **argv)
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"hc:AbdD:e:ik:K:Ll:m:M:o:P:p:r:S:s:T:t:UvV:w:",
|
||||
"hc:AdD:e:ik:K:Ll:m:M:o:P:p:r:s:T:t:UvV:w:",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
@ -404,10 +513,13 @@ int falco_init(int argc, char **argv)
|
||||
disabled_rule_patterns.insert(pattern);
|
||||
break;
|
||||
case 'e':
|
||||
scap_filename = optarg;
|
||||
trace_filename = optarg;
|
||||
k8s_api = new string();
|
||||
mesos_api = new string();
|
||||
break;
|
||||
case 'F':
|
||||
list_flds = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
print_ignored_events = true;
|
||||
break;
|
||||
@ -492,17 +604,28 @@ int falco_init(int argc, char **argv)
|
||||
case '?':
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case 0:
|
||||
if(string(long_options[long_index].name) == "version")
|
||||
{
|
||||
printf("falco version %s\n", FALCO_VERSION);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "list")
|
||||
{
|
||||
list_flds = true;
|
||||
if(optarg != NULL)
|
||||
{
|
||||
list_flds_source = optarg;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inspector = new sinsp();
|
||||
inspector->set_buffer_format(event_buffer_format);
|
||||
@ -526,8 +649,13 @@ int falco_init(int argc, char **argv)
|
||||
engine->set_inspector(inspector);
|
||||
engine->set_extra(output_format, replace_container_info);
|
||||
|
||||
if(list_flds)
|
||||
{
|
||||
list_source_fields(engine, verbose, list_flds_source);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
outputs = new falco_outputs();
|
||||
outputs = new falco_outputs(engine);
|
||||
outputs->set_inspector(inspector);
|
||||
|
||||
// Some combinations of arguments are not allowed.
|
||||
@ -714,9 +842,46 @@ int falco_init(int argc, char **argv)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (scap_filename.size())
|
||||
if (trace_filename.size())
|
||||
{
|
||||
inspector->open(scap_filename);
|
||||
// Try to open the trace file as a sysdig
|
||||
// capture file first.
|
||||
try {
|
||||
inspector->open(trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n");
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
trace_is_scap=false;
|
||||
}
|
||||
|
||||
if(!trace_is_scap)
|
||||
{
|
||||
try {
|
||||
string line;
|
||||
nlohmann::json j;
|
||||
|
||||
// Note we only temporarily open the file here.
|
||||
// The read file read loop will be later.
|
||||
ifstream ifs(trace_filename);
|
||||
getline(ifs, line);
|
||||
j = nlohmann::json::parse(line);
|
||||
|
||||
falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + trace_filename + "\n");
|
||||
}
|
||||
catch (nlohmann::json::parse_error& e)
|
||||
{
|
||||
fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", trace_filename.c_str());
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
catch (exception &e)
|
||||
{
|
||||
fprintf(stderr, "Could not open trace filename %s for reading: %s\n", trace_filename.c_str(), e.what());
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -724,7 +889,7 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
inspector->open(200);
|
||||
}
|
||||
catch(sinsp_exception e)
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
|
||||
{
|
||||
@ -856,6 +1021,23 @@ int falco_init(int argc, char **argv)
|
||||
delete mesos_api;
|
||||
mesos_api = 0;
|
||||
|
||||
if(trace_filename.empty() && config.m_webserver_enabled)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + "\n");
|
||||
webserver.init(&config, engine, outputs);
|
||||
webserver.start();
|
||||
}
|
||||
|
||||
if(!trace_filename.empty() && !trace_is_scap)
|
||||
{
|
||||
read_k8s_audit_trace_file(engine,
|
||||
outputs,
|
||||
trace_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t num_evts;
|
||||
|
||||
num_evts = do_inspect(engine,
|
||||
outputs,
|
||||
inspector,
|
||||
@ -879,15 +1061,19 @@ int falco_init(int argc, char **argv)
|
||||
num_evts / duration);
|
||||
}
|
||||
|
||||
inspector->close();
|
||||
}
|
||||
|
||||
inspector->close();
|
||||
engine->print_stats();
|
||||
webserver.stop();
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
display_fatal_err("Runtime error: " + string(e.what()) + ". Exiting.\n");
|
||||
|
||||
result = EXIT_FAILURE;
|
||||
|
||||
webserver.stop();
|
||||
}
|
||||
|
||||
exit:
|
||||
|
@ -27,8 +27,9 @@ limitations under the License.
|
||||
|
||||
using namespace std;
|
||||
|
||||
falco_outputs::falco_outputs()
|
||||
: m_initialized(false),
|
||||
falco_outputs::falco_outputs(falco_engine *engine)
|
||||
: m_falco_engine(engine),
|
||||
m_initialized(false),
|
||||
m_buffered(true)
|
||||
{
|
||||
|
||||
@ -68,7 +69,7 @@ void falco_outputs::init(bool json_output,
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
// by the falco engine as well as the separate lua state used
|
||||
// by falco outputs.
|
||||
falco_formats::init(m_inspector, m_ls, json_output, json_include_output_property);
|
||||
falco_formats::init(m_inspector, m_falco_engine, m_ls, json_output, json_include_output_property);
|
||||
|
||||
falco_logger::init(m_ls);
|
||||
|
||||
@ -112,7 +113,8 @@ void falco_outputs::add_output(output_config oc)
|
||||
|
||||
}
|
||||
|
||||
void falco_outputs::handle_event(sinsp_evt *ev, string &rule, falco_common::priority_type priority, string &format)
|
||||
void falco_outputs::handle_event(gen_event *ev, string &rule, string &source,
|
||||
falco_common::priority_type priority, string &format)
|
||||
{
|
||||
if(!m_notifications_tb.claim())
|
||||
{
|
||||
@ -126,11 +128,12 @@ void falco_outputs::handle_event(sinsp_evt *ev, string &rule, falco_common::prio
|
||||
{
|
||||
lua_pushlightuserdata(m_ls, ev);
|
||||
lua_pushstring(m_ls, rule.c_str());
|
||||
lua_pushstring(m_ls, source.c_str());
|
||||
lua_pushstring(m_ls, falco_common::priority_names[priority].c_str());
|
||||
lua_pushnumber(m_ls, priority);
|
||||
lua_pushstring(m_ls, format.c_str());
|
||||
|
||||
if(lua_pcall(m_ls, 5, 0, 0) != 0)
|
||||
if(lua_pcall(m_ls, 6, 0, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
|
@ -19,8 +19,13 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "gen_filter.h"
|
||||
#include "json_evt.h"
|
||||
#include "falco_common.h"
|
||||
#include "token_bucket.h"
|
||||
#include "falco_engine.h"
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
@ -31,7 +36,7 @@ limitations under the License.
|
||||
class falco_outputs : public falco_common
|
||||
{
|
||||
public:
|
||||
falco_outputs();
|
||||
falco_outputs(falco_engine *engine);
|
||||
virtual ~falco_outputs();
|
||||
|
||||
// The way to refer to an output (file, syslog, stdout,
|
||||
@ -52,11 +57,15 @@ public:
|
||||
// ev is an event that has matched some rule. Pass the event
|
||||
// to all configured outputs.
|
||||
//
|
||||
void handle_event(sinsp_evt *ev, std::string &rule, falco_common::priority_type priority, std::string &format);
|
||||
void handle_event(gen_event *ev, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format);
|
||||
|
||||
void reopen_outputs();
|
||||
|
||||
private:
|
||||
|
||||
falco_engine *m_falco_engine;
|
||||
|
||||
bool m_initialized;
|
||||
|
||||
// Rate limits notifications
|
||||
|
@ -142,16 +142,20 @@ function mod.program_reopen(options)
|
||||
end
|
||||
end
|
||||
|
||||
function output_event(event, rule, priority, priority_num, format)
|
||||
function output_event(event, rule, source, priority, priority_num, format)
|
||||
-- If format starts with a *, remove it, as we're adding our own
|
||||
-- prefix here.
|
||||
if format:sub(1,1) == "*" then
|
||||
format = format:sub(2)
|
||||
end
|
||||
|
||||
if source == "syscall" then
|
||||
format = "*%evt.time: "..priority.." "..format
|
||||
else
|
||||
format = "*%jevt.time: "..priority.." "..format
|
||||
end
|
||||
|
||||
msg = formats.format_event(event, rule, priority, format)
|
||||
msg = formats.format_event(event, rule, source, priority, format)
|
||||
|
||||
for index,o in ipairs(outputs) do
|
||||
o.output(priority, priority_num, msg, o.options)
|
||||
|
219
userspace/falco/webserver.cpp
Normal file
219
userspace/falco/webserver.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "webserver.h"
|
||||
#include "json_evt.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace std;
|
||||
|
||||
k8s_audit_handler::k8s_audit_handler(falco_engine *engine, falco_outputs *outputs)
|
||||
: m_engine(engine), m_outputs(outputs)
|
||||
{
|
||||
}
|
||||
|
||||
k8s_audit_handler::~k8s_audit_handler()
|
||||
{
|
||||
}
|
||||
|
||||
bool k8s_audit_handler::accept_data(falco_engine *engine,
|
||||
falco_outputs *outputs,
|
||||
std::string &data,
|
||||
std::string &errstr)
|
||||
{
|
||||
std::list<json_event> jevts;
|
||||
json j;
|
||||
|
||||
try {
|
||||
j = json::parse(data);
|
||||
}
|
||||
catch (json::parse_error& e)
|
||||
{
|
||||
errstr = string("Could not parse data: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!engine->parse_k8s_audit_json(j, jevts))
|
||||
{
|
||||
errstr = string("Data not recognized as a k8s audit event");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto &jev : jevts)
|
||||
{
|
||||
std::unique_ptr<falco_engine::rule_result> res;
|
||||
res = engine->process_k8s_audit_event(&jev);
|
||||
|
||||
if(res)
|
||||
{
|
||||
try {
|
||||
outputs->handle_event(res->evt, res->rule,
|
||||
res->source, res->priority_num,
|
||||
res->format);
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
errstr = string("Internal error handling output: ") + e.what();
|
||||
fprintf(stderr, "%s\n", errstr.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool k8s_audit_handler::accept_uploaded_data(std::string &post_data, std::string &errstr)
|
||||
{
|
||||
return k8s_audit_handler::accept_data(m_engine, m_outputs, post_data, errstr);
|
||||
}
|
||||
|
||||
|
||||
bool k8s_audit_handler::handleGet(CivetServer *server, struct mg_connection *conn)
|
||||
{
|
||||
mg_send_http_error(conn, 405, "GET method not allowed");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The version in CivetServer.cpp has valgrind compliants due to
|
||||
// unguarded initialization of c++ string from buffer.
|
||||
static void get_post_data(struct mg_connection *conn, std::string &postdata)
|
||||
{
|
||||
mg_lock_connection(conn);
|
||||
char buf[2048];
|
||||
int r = mg_read(conn, buf, sizeof(buf));
|
||||
while (r > 0) {
|
||||
postdata.append(buf, r);
|
||||
r = mg_read(conn, buf, sizeof(buf));
|
||||
}
|
||||
mg_unlock_connection(conn);
|
||||
}
|
||||
|
||||
bool k8s_audit_handler::handlePost(CivetServer *server, struct mg_connection *conn)
|
||||
{
|
||||
// Ensure that the content-type is application/json
|
||||
const char *ct = server->getHeader(conn, string("Content-Type"));
|
||||
|
||||
if(ct == NULL || string(ct) != "application/json")
|
||||
{
|
||||
mg_send_http_error(conn, 400, "Wrong Content Type");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string post_data;
|
||||
get_post_data(conn, post_data);
|
||||
std::string errstr;
|
||||
|
||||
if(!accept_uploaded_data(post_data, errstr))
|
||||
{
|
||||
errstr = "Bad Request: " + errstr;
|
||||
mg_send_http_error(conn, 400, "%s", errstr.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ok_body = "<html><body>Ok</body></html>";
|
||||
mg_send_http_ok(conn, "text/html", ok_body.size());
|
||||
mg_printf(conn, "%s", ok_body.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
falco_webserver::falco_webserver()
|
||||
: m_config(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
falco_webserver::~falco_webserver()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void falco_webserver::init(falco_configuration *config,
|
||||
falco_engine *engine,
|
||||
falco_outputs *outputs)
|
||||
{
|
||||
m_config = config;
|
||||
m_engine = engine;
|
||||
m_outputs = outputs;
|
||||
}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
std::unique_ptr<T> make_unique( Args&& ...args )
|
||||
{
|
||||
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
|
||||
}
|
||||
|
||||
void falco_webserver::start()
|
||||
{
|
||||
if(m_server)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
if(!m_config)
|
||||
{
|
||||
throw falco_exception("No config provided to webserver");
|
||||
}
|
||||
|
||||
if(!m_engine)
|
||||
{
|
||||
throw falco_exception("No engine provided to webserver");
|
||||
}
|
||||
|
||||
if(!m_outputs)
|
||||
{
|
||||
throw falco_exception("No outputs provided to webserver");
|
||||
}
|
||||
|
||||
std::vector<std::string> cpp_options = {
|
||||
"listening_ports", to_string(m_config->m_webserver_listen_port),
|
||||
"num_threads", to_string(1)
|
||||
};
|
||||
|
||||
try {
|
||||
m_server = make_unique<CivetServer>(cpp_options);
|
||||
}
|
||||
catch (CivetException &e)
|
||||
{
|
||||
throw falco_exception(std::string("Could not create embedded webserver: ") + e.what());
|
||||
}
|
||||
if(!m_server->getContext())
|
||||
{
|
||||
throw falco_exception("Could not create embedded webserver");
|
||||
}
|
||||
|
||||
m_k8s_audit_handler = make_unique<k8s_audit_handler>(m_engine, m_outputs);
|
||||
m_server->addHandler(m_config->m_webserver_k8s_audit_endpoint, *m_k8s_audit_handler);
|
||||
}
|
||||
|
||||
void falco_webserver::stop()
|
||||
{
|
||||
if(m_server)
|
||||
{
|
||||
m_server = NULL;
|
||||
m_k8s_audit_handler = NULL;
|
||||
}
|
||||
}
|
68
userspace/falco/webserver.h
Normal file
68
userspace/falco/webserver.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (C) 2018 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "CivetServer.h"
|
||||
|
||||
#include "configuration.h"
|
||||
#include "falco_engine.h"
|
||||
#include "falco_outputs.h"
|
||||
|
||||
class k8s_audit_handler : public CivetHandler
|
||||
{
|
||||
public:
|
||||
k8s_audit_handler(falco_engine *engine, falco_outputs *outputs);
|
||||
virtual ~k8s_audit_handler();
|
||||
|
||||
bool handleGet(CivetServer *server, struct mg_connection *conn);
|
||||
bool handlePost(CivetServer *server, struct mg_connection *conn);
|
||||
|
||||
static bool accept_data(falco_engine *engine,
|
||||
falco_outputs *outputs,
|
||||
std::string &post_data, std::string &errstr);
|
||||
|
||||
private:
|
||||
falco_engine *m_engine;
|
||||
falco_outputs *m_outputs;
|
||||
bool accept_uploaded_data(std::string &post_data, std::string &errstr);
|
||||
};
|
||||
|
||||
class falco_webserver
|
||||
{
|
||||
public:
|
||||
|
||||
falco_webserver();
|
||||
virtual ~falco_webserver();
|
||||
|
||||
void init(falco_configuration *config,
|
||||
falco_engine *engine,
|
||||
falco_outputs *outputs);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
|
||||
falco_engine *m_engine;
|
||||
falco_configuration *m_config;
|
||||
falco_outputs *m_outputs;
|
||||
unique_ptr<CivetServer> m_server;
|
||||
unique_ptr<k8s_audit_handler> m_k8s_audit_handler;
|
||||
};
|
Loading…
Reference in New Issue
Block a user