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:
Mark Stemm 2018-11-09 10:15:39 -08:00 committed by GitHub
parent ff4f7ca13b
commit 1f28f85bdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 4105 additions and 427 deletions

View File

@ -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"

View File

@ -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}")

View 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.

View 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"

View 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"

View 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: []

View File

@ -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"

View File

@ -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
View 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]

View 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

View File

@ -0,0 +1,3 @@
- list: allowed_namespaces
items: [foo]
append: true

View File

@ -0,0 +1,3 @@
- macro: allowed_k8s_containers
condition: (ka.req.container.image.repository=nginx)

View File

@ -0,0 +1,3 @@
- macro: allowed_k8s_containers
condition: (ka.req.container.image.repository=apache)

View File

@ -0,0 +1,3 @@
- list: allowed_k8s_users
items: [some-user]
append: true

View File

@ -0,0 +1,2 @@
- macro: consider_activity_events
condition: (not jevt.rawtime exists)

View File

@ -0,0 +1,3 @@
- list: trusted_k8s_containers
items: [nginx]
append: true

View File

@ -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

View File

@ -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":""}}

View File

@ -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"}

View 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"}

View File

@ -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"}

View File

@ -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"}

View File

@ -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"}

View File

@ -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"}

View 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"}

View File

@ -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"}

View 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"}

View File

@ -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":""}}

View File

@ -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"}

View 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"}

View File

@ -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"}

View File

@ -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\""}}

View File

@ -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\""}}

View File

@ -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"}

View File

@ -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"}

View File

@ -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"}

View File

@ -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\""}}

View File

@ -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":""}}

View File

@ -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"}

View File

@ -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":""}}

View File

@ -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"}

View File

@ -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"}

View 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"}

View 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"}

View File

@ -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"}

View File

@ -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"}

View File

@ -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"}

View 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"}

View File

@ -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"}

View 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"}

View 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"}

View 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"}

View 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"}

View 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"}

View 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"}

View File

@ -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":""}}

View File

@ -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"}

View File

@ -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":""}}

View File

@ -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}"

View File

@ -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/"

View File

@ -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 + ")");
}
}

View File

@ -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);

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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;

View 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
View 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;
};

View File

@ -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

View File

@ -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)

View File

@ -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)

View 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

View File

@ -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;
}

View File

@ -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;

View 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
View 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);
};

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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)

View 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;
}
}

View 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;
};