Skip to content

Commit 6d7bb31

Browse files
committed
Add tetra policyfilter listpolicies command
It is useful to have a debug command to indentify which Kubernetes Identity Aware policies should be applied on a specific container. An example can be found here: Create a pod with "app: ubuntu" and "usage: dev" labels. $ cat << EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: ubuntu labels: app: ubuntu usage: dev spec: containers: - name: ubuntu image: ubuntu:24.10 command: ["/bin/sleep", "3650d"] imagePullPolicy: IfNotPresent restartPolicy: Always EOF And apply several policies where some of them match while others don't. $ cat << EOF | kubectl apply -f - apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "lseek-podfilter-app" spec: podSelector: matchLabels: app: "ubuntu" kprobes: [...] EOF $ cat << EOF | kubectl apply -f - apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "lseek-podfilter-usage" spec: podSelector: matchLabels: usage: "dev" kprobes: [...] EOF $ cat << EOF | kubectl apply -f - apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "lseek-podfilter-prod" spec: podSelector: matchLabels: prod: "true" kprobes: [...] EOF $ cat << EOF | kubectl apply -f - apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "lseek-podfilter-info" spec: podSelector: matchLabels: info: "broken" kprobes: [...] EOF $ cat << EOF | kubectl apply -f - apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "lseek-podfilter-global" spec: kprobes: [...] EOF Based on the labels we expect that policies lseek-podfilter-app and lseek-podfilter-usage to match on that pod. lseek-podfilter-global is not a Kubernetes Identity Aware policy so this will be applied in all cases and we do not report that. First step is to find the container ID that we care about. $ kubectl describe pod/ubuntu | grep containerd Container ID: containerd://ff433e9e16467787a60ac853d9b313150091968731f620776d6d7c514b1e8d6c And then use it to report all Kubernetes Identity Aware policies that match. $ kubectl exec -it ds/tetragon -n kube-system -c tetragon -- tetra policyfilter -r "unix:///procRoot/1/root/run/containerd/containerd.sock" listpolicies ff433e9e16467787a60ac853d9b313150091968731f620776d6d7c514b1e8d6c ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY 5 lseek-podfilter-usage enabled 5 (global) generic_kprobe 1.72 MB 1 lseek-podfilter-app enabled 1 (global) generic_kprobe 1.72 MB We also provide --debug flag to provide more details i.e.: $ kubectl exec -it ds/tetragon -n kube-system -c tetragon -- tetra policyfilter -r "unix:///procRoot/1/root/run/containerd/containerd.sock" listpolicies ff433e9e16467787a60ac853d9b313150091968731f620776d6d7c514b1e8d6c --debug time="2024-12-13T09:47:38Z" level=info msg=cgroup path=/run/tetragon/cgroup2/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod189a8053_9f36_4250_bcae_9ed167172920.slice/cri-containerd-ff433e9e16467787a60ac853d9b313150091968731f620776d6d7c514b1e8d6c.scope time="2024-12-13T09:47:38Z" level=info msg=cgroup id=5695 time="2024-12-13T09:47:39Z" level=debug msg="resolved server address using info file" InitInfoFile=/var/run/tetragon/tetragon-info.json ServerAddress="localhost:54321" ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY 1 lseek-podfilter-app enabled 1 (global) generic_kprobe 1.72 MB 5 lseek-podfilter-usage enabled 5 (global) generic_kprobe 1.72 MB This uses a reverse policy filter map that introduced in a previous commit and maps cgroupIds to policyIds. Signed-off-by: Anastasios Papagiannis <[email protected]>
1 parent 3d357e4 commit 6d7bb31

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

cmd/tetra/policyfilter/policyfilter.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
package policyfilter
55

66
import (
7+
"context"
78
"fmt"
9+
"os"
10+
"path"
811
"path/filepath"
912
"strconv"
13+
"strings"
14+
"text/tabwriter"
1015

16+
"github.com/cilium/tetragon/api/v1/tetragon"
17+
"github.com/cilium/tetragon/cmd/tetra/common"
1118
"github.com/cilium/tetragon/cmd/tetra/debug"
1219
"github.com/cilium/tetragon/pkg/cgroups"
20+
"github.com/cilium/tetragon/pkg/cri"
1321
"github.com/cilium/tetragon/pkg/defaults"
1422
"github.com/cilium/tetragon/pkg/logger"
1523
"github.com/cilium/tetragon/pkg/policyfilter"
@@ -29,6 +37,7 @@ func New() *cobra.Command {
2937
addCommand(),
3038
cgroupGetIDCommand(),
3139
dumpDebugCmd(),
40+
listPoliciesForContainer(),
3241
)
3342

3443
return ret
@@ -138,3 +147,131 @@ func addCgroup(fname string, polID policyfilter.PolicyID, cgID policyfilter.Cgro
138147
}
139148

140149
}
150+
151+
func listPoliciesForContainer() *cobra.Command {
152+
var endpoint, cgroupMnt string
153+
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, policyfilter.MapName)
154+
ret := &cobra.Command{
155+
Use: "listpolicies [container id]",
156+
Short: "list all Kubernetes Identity Aware policies that apply to a specific container",
157+
Args: cobra.ExactArgs(1),
158+
RunE: func(_ *cobra.Command, args []string) error {
159+
ctx := context.Background()
160+
client, err := cri.NewClient(ctx, endpoint)
161+
if err != nil {
162+
return err
163+
}
164+
165+
cgroupPath, err := cri.CgroupPath(ctx, client, args[0])
166+
if err != nil {
167+
return err
168+
}
169+
170+
if cgroupMnt == "" {
171+
cgroupMnt = defaults.Cgroup2Dir
172+
}
173+
fullCgroupPath := path.Join(cgroupMnt, cgroupPath)
174+
if common.Debug {
175+
logger.GetLogger().WithField("path", fullCgroupPath).Info("cgroup")
176+
}
177+
178+
cgID, err := cgroups.GetCgroupIdFromPath(fullCgroupPath)
179+
if err != nil {
180+
logger.GetLogger().WithError(err).Fatal("Failed to parse cgroup")
181+
}
182+
183+
if common.Debug {
184+
logger.GetLogger().WithField("id", cgID).Info("cgroup")
185+
}
186+
187+
m, err := policyfilter.OpenMap(mapFname)
188+
if err != nil {
189+
logger.GetLogger().WithError(err).Fatal("Failed to open policyfilter map")
190+
return err
191+
}
192+
defer m.Close()
193+
194+
data, err := m.Dump()
195+
if err != nil {
196+
logger.GetLogger().WithError(err).Fatal("Failed to open policyfilter map")
197+
return err
198+
}
199+
200+
policyIds, ok := data.Reverse[policyfilter.CgroupID(cgID)]
201+
if !ok {
202+
return nil
203+
}
204+
205+
c, err := common.NewClientWithDefaultContextAndAddress()
206+
if err != nil {
207+
return fmt.Errorf("failed create gRPC client: %w", err)
208+
}
209+
defer c.Close()
210+
211+
res, err := c.Client.ListTracingPolicies(c.Ctx, &tetragon.ListTracingPoliciesRequest{})
212+
if err != nil || res == nil {
213+
return fmt.Errorf("failed to list tracing policies: %w", err)
214+
}
215+
216+
// tabwriter config imitates kubectl default output, i.e. 3 spaces padding
217+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
218+
fmt.Fprintln(w, "ID\tNAME\tSTATE\tFILTERID\tNAMESPACE\tSENSORS\tKERNELMEMORY")
219+
220+
for _, pol := range res.Policies {
221+
namespace := pol.Namespace
222+
if namespace == "" {
223+
namespace = "(global)"
224+
}
225+
226+
sensors := strings.Join(pol.Sensors, ",")
227+
228+
// From v0.11 and before, enabled, filterID and error were
229+
// bundled in a string. To have a retro-compatible tetra
230+
// command, we scan the string. If the scan fails, it means
231+
// something else might be in Info and we print it.
232+
//
233+
// we can drop the following block (and comment) when we
234+
// feel tetra should support only version after v0.11
235+
if pol.Info != "" {
236+
var parsedEnabled bool
237+
var parsedFilterID uint64
238+
var parsedError string
239+
var parsedName string
240+
str := strings.NewReader(pol.Info)
241+
_, err := fmt.Fscanf(str, "%253s enabled:%t filterID:%d error:%512s", &parsedName, &parsedEnabled, &parsedFilterID, &parsedError)
242+
if err == nil {
243+
if parsedEnabled {
244+
pol.State = tetragon.TracingPolicyState_TP_STATE_ENABLED
245+
}
246+
pol.FilterId = parsedFilterID
247+
pol.Error = parsedError
248+
pol.Info = ""
249+
}
250+
}
251+
252+
if _, ok := policyIds[policyfilter.PolicyID(pol.FilterId)]; !ok {
253+
continue
254+
}
255+
256+
fmt.Fprintf(w, "%d\t%s\t%s\t%d\t%s\t%s\t%s\t\n",
257+
pol.Id,
258+
pol.Name,
259+
strings.TrimPrefix(strings.ToLower(pol.State.String()), "tp_state_"),
260+
pol.FilterId,
261+
namespace,
262+
sensors,
263+
common.HumanizeByteCount(int(pol.KernelMemoryBytes)),
264+
)
265+
}
266+
w.Flush()
267+
268+
return nil
269+
},
270+
}
271+
272+
flags := ret.Flags()
273+
flags.StringVarP(&endpoint, "runtime-endpoint", "r", "", "CRI endpoint")
274+
flags.StringVar(&mapFname, "map-fname", mapFname, "policyfilter map filename")
275+
flags.StringVar(&cgroupMnt, "cgroup-mount", cgroupMnt, "cgroupFS mount point")
276+
return ret
277+
}

0 commit comments

Comments
 (0)