Skip to content

Commit

Permalink
docs/k8s-filtering: update
Browse files Browse the repository at this point in the history
Update the docs to include the pod label filtering functionality.

Signed-off-by: Kornilios Kourtis <kornilios@isovalent.com>
  • Loading branch information
kkourt committed Apr 28, 2023
1 parent ef1f05d commit 966b301
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 41 deletions.
2 changes: 1 addition & 1 deletion contrib/rthooks/minikube-containerd-install-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ BASEHOOKNAME=$(basename $HOOKNAME)
LOCALHOOK="$SCRIPTPATH/$BASEHOOKNAME/$BASEHOOKNAME"

xdir=$(mktemp -d /tmp/minikube-containerd-hook-XXXXXX)
trap 'rm -rf -- "$xdir"' EXIT
trap 'echo removing temporar directory; rm -rf -- "$xdir"' EXIT
echo "temporary directory: $xdir"

set -x
Expand Down
148 changes: 108 additions & 40 deletions docs/content/en/docs/features/k8s-filtering.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,53 @@
---
title: "K8s Namespace and pod-label filtering"
title: "K8s namespace and pod label filtering"
weight: 1
description: "Tetragon in-kernel filtering using Kubernetes namespaces and pod label filters"
description: "Tetragon in-kernel filtering based on Kubernetes namespaces and pod label filters"
---

{{< caution >}}
This is currently a beta feature.
{{< /caution >}}

## Motivation

Tetragon is configured via [TracingPolicies]({{< ref "docs/reference/tracing-policy" >}}). Broadly speaking,
TracingPolicies define _what_ situations Tetragon should react to and _how_. The
_what_ can be, for example, specific system calls with specific argument
values. The _how_ defines what action the Tetragon agent should perform when
the specified situation occurs.
The most common action is generating and event, but there
are others (e.g., killing the corresponding processes).

Here, we are concerned with applying tracing policies only on a subset of pods
running on the system based on their namespace, and, in future work, their
labels.

To this end, a new type of policy is introduced: `TracingPolicyNamespaced` that
is exactly the same as the existing `TracingPolicy`, but it is _only_ applied
to pods of the namespace that the policy is defined.

As is the case with TracingPolicies, TracingPoliciesNamespaced are
implemented in-kernel with eBPF. This is important for both observability
and enforcement use-cases. For observability, copying only the relevant events
from kernel- to user-space reduces overhead. For enforcement,
performing the enforcement action in-kernel avoids the
race-condition of doing it in user-space. For example, let us consider the case
where we want to block an application from performing a system call. Performing
the filtering in-kernel means that the application will never finish executing
the system call, which is not possible if enforcement happens in user-space.

To ensure that namespaced tracing policies are always correctly applied,
Tetragon needs to perform actions before containers start executing. Tetragon
supports this via [OCI runtime
hooks](https://github.com/opencontainers/runtime-spec/blob/main/config.md#posix-platform-hooks).
If such hooks are not added, Tetragon will apply policies in a best-effort
manner using information from the k8s API server.
Tetragon is configured via [TracingPolicies]({{< ref "docs/reference/tracing-policy" >}}). Broadly
speaking, TracingPolicies define _what_ situations Tetragon should react to and _how_. The _what_
can be, for example, specific system calls with specific argument values. The _how_ defines what
action the Tetragon agent should perform when the specified situation occurs. The most common action
is generating an event, but there are others (e.g., returning an error without executing a function,
or killing the corresponding process).

Here we discuss how to apply tracing policies only on a subset of pods running on the system via
two mechanisms: namespaced policies, and pod-label filters. Tetragon implements both mechanisms
in-kernel via eBPF. This is important for both observability and enforcement use-cases. For
observability, copying only the relevant events from kernel- to user-space reduces overhead. For
enforcement, performing the enforcement action in the kernel avoids the race-condition of doing it
in user-space. For example, let us consider the case where we want to block an application from
performing a system call. Performing the filtering in-kernel means that the application will never
finish executing the system call, which is not possible if enforcement happens in user-space
(after the fact).

To ensure that namespaced tracing policies are always correctly applied, Tetragon needs to perform
actions before containers start executing. Tetragon supports this via [OCI runtime
hooks](https://github.com/opencontainers/runtime-spec/blob/main/config.md#posix-platform-hooks). If
such hooks are not added, Tetragon will apply policies in a best-effort manner using information
from the k8s API server.

## Namespace filtering

For namespace filtering we use `TracingPolicyNamespaced` which has the same contents as a
`TracingPolicy`, but it is defined in a specific namespace and it is _only_ applied to pods of that
namespace.

## Pod label filters

For pod label filters, we use the `PodSelector` field of tracing policies to select the pods that
the policy is applied to.

## Demo

### Setup

For this demo, we use containerd and configure appropriate run-time hooks using minikube.

First, let us start minikube, build and load images, and install Tetragon and OCI hooks:
Expand All @@ -68,10 +74,6 @@ tetragon_pod=$(kubectl -n kube-system get pods -l app.kubernetes.io/name=tetrago
Next, we check the tetragon-operator logs and tetragon agent logs to ensure
that everything is in order.





First, we check if the operator installed the TracingPolicyNamespaced CRD.


Expand Down Expand Up @@ -99,6 +101,8 @@ The output should include:
level=info msg="Enabling policy filtering"
```

### Namespaced policies

For illustration purposes, we will use the lseek system call with an invalid
argument. Specifically a file descriptor (the first argument) of -1. Normally,
this operation would return a "Bad file descriptor error".
Expand Down Expand Up @@ -136,7 +140,7 @@ metadata:
name: "lseek-namespaced"
spec:
kprobes:
- call: "__x64_sys_lseek"
- call: "sys_lseek"
syscall: true
args:
- index: 0
Expand All @@ -152,6 +156,10 @@ spec:
EOF
```

The above tracing policy will kill the process that performs an lseek system call with a file
descriptor of `-1`. Note that we use a `SigKill` action only for illustration purposes because it's
easier to observe its effects.

Then, attempting the lseek operation on the previous terminal, will result in the process getting
killed:
```
Expand All @@ -175,7 +183,7 @@ pod default/test terminated (Error)
```

Doing the same on another namespace:
```
```shell
kubectl create namespace test
kubectl -n test run test --image=python -it --rm --restart=Never -- python
```
Expand All @@ -190,3 +198,63 @@ Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor
```

### Pod label filters

Let's install a tracing policy with a pod label filter.

```shell
cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: "lseek-podfilter"
spec:
podSelector:
matchLabels:
app: "lseek-test"
kprobes:
- call: "sys_lseek"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchArgs:
- index: 0
operator: "Equal"
values:
- "-1"
matchActions:
- action: Sigkill
EOF
```

Pods without the label will not be affected:

```shell
kubectl run test --image=python -it --rm --restart=Never -- python
```

```
If you don't see a command prompt, try pressing enter.
>>> import os
>>> os.lseek(-1, 0, 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor
>>>
```

But pods with the label will:
```shell
kubectl run test --labels "app=lseek-test" --image=python -it --rm --restart=Never -- python
```

```
If you don't see a command prompt, try pressing enter.
>>> import os
>>> os.lseek(-1, 0, 0)
pod "test" deleted
pod default/test terminated (Error)
```

0 comments on commit 966b301

Please sign in to comment.