Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions bpf/process/policy_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#define POLICY_FILTER_MAX_POLICIES 128
#define POLICY_FILTER_MAX_NAMESPACES 1024
#define POLICY_FILTER_MAX_CGROUP_IDS 1024

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
Expand All @@ -30,6 +31,23 @@ struct {
});
} policy_filter_maps SEC(".maps");

// This map keeps exactly the same information as policy_filter_maps
// but keeps the reverse mappings. i.e.
// policy_filter_maps maps policy_id to cgroup_ids
// policy_filter_cgroup_maps maps cgroup_id to policy_ids
struct {
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
__uint(max_entries, POLICY_FILTER_MAX_CGROUP_IDS);
__uint(key_size, sizeof(__u64)); /* cgroup id */
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, POLICY_FILTER_MAX_POLICIES);
__type(key, __u32); /* policy id */
__type(value, __u8); /* empty */
});
} policy_filter_cgroup_maps SEC(".maps");

// policy_filter_check checks whether the policy applies on the current process.
// Returns true if it does, false otherwise.

Expand Down
63 changes: 62 additions & 1 deletion cmd/tetra/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@

package common

import "fmt"
import (
"fmt"
"io"
"strings"
"text/tabwriter"

"github.com/cilium/tetragon/api/v1/tetragon"
)

// HumanizeByteCount transforms bytes count into a quickly-readable version, for
// example it transforms 4458824 into "4.46 MB". I copied this code from
Expand All @@ -21,3 +28,57 @@ func HumanizeByteCount(b int) string {
return fmt.Sprintf("%.2f %cB",
float64(b)/float64(div), "kMGTPE"[exp])
}

func PrintTracingPolicies(output io.Writer, policies []*tetragon.TracingPolicyStatus, skipPolicy func(pol *tetragon.TracingPolicyStatus) bool) {
// tabwriter config imitates kubectl default output, i.e. 3 spaces padding
w := tabwriter.NewWriter(output, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "ID\tNAME\tSTATE\tFILTERID\tNAMESPACE\tSENSORS\tKERNELMEMORY")

for _, pol := range policies {
if skipPolicy != nil && skipPolicy(pol) {
continue
}

namespace := pol.Namespace
if namespace == "" {
namespace = "(global)"
}

sensors := strings.Join(pol.Sensors, ",")

// From v0.11 and before, enabled, filterID and error were
// bundled in a string. To have a retro-compatible tetra
// command, we scan the string. If the scan fails, it means
// something else might be in Info and we print it.
//
// we can drop the following block (and comment) when we
// feel tetra should support only version after v0.11
if pol.Info != "" {
var parsedEnabled bool
var parsedFilterID uint64
var parsedError string
var parsedName string
str := strings.NewReader(pol.Info)
_, err := fmt.Fscanf(str, "%253s enabled:%t filterID:%d error:%512s", &parsedName, &parsedEnabled, &parsedFilterID, &parsedError)
if err == nil {
if parsedEnabled {
pol.State = tetragon.TracingPolicyState_TP_STATE_ENABLED
}
pol.FilterId = parsedFilterID
pol.Error = parsedError
pol.Info = ""
}
}

fmt.Fprintf(w, "%d\t%s\t%s\t%d\t%s\t%s\t%s\t\n",
pol.Id,
pol.Name,
strings.TrimPrefix(strings.ToLower(pol.State.String()), "tp_state_"),
pol.FilterId,
namespace,
sensors,
HumanizeByteCount(int(pol.KernelMemoryBytes)),
)
}
w.Flush()
}
23 changes: 20 additions & 3 deletions cmd/tetra/debug/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,35 @@ func PolicyfilterState(fname string) {
return
}

if len(data) == 0 {
fmt.Println("--- PolicyID to CgroupIDs mapping ---")

if len(data.Policy) == 0 {
fmt.Printf("(empty)\n")
return
}

for polId, cgIDs := range data {
for polId, cgIDs := range data.Policy {
ids := make([]string, 0, len(cgIDs))
for id := range cgIDs {
ids = append(ids, strconv.FormatUint(uint64(id), 10))
}
fmt.Printf("%d: %s\n", polId, strings.Join(ids, ","))
}

if data.Cgroup != nil {
fmt.Println("--- CgroupID to PolicyIDs mapping ---")

if len(data.Cgroup) == 0 {
fmt.Printf("(empty)\n")
}

for cgIDs, polIds := range data.Cgroup {
ids := make([]string, 0, len(polIds))
for id := range polIds {
ids = append(ids, strconv.FormatUint(uint64(id), 10))
}
fmt.Printf("%d: %s\n", cgIDs, strings.Join(ids, ","))
}
}
}

func NamespaceState(fname string) error {
Expand Down
88 changes: 88 additions & 0 deletions cmd/tetra/policyfilter/policyfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@
package policyfilter

import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strconv"

"github.com/cilium/tetragon/api/v1/tetragon"
"github.com/cilium/tetragon/cmd/tetra/common"
"github.com/cilium/tetragon/cmd/tetra/debug"
"github.com/cilium/tetragon/pkg/cgroups"
"github.com/cilium/tetragon/pkg/cri"
"github.com/cilium/tetragon/pkg/defaults"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/policyfilter"
Expand All @@ -29,6 +35,7 @@ func New() *cobra.Command {
addCommand(),
cgroupGetIDCommand(),
dumpDebugCmd(),
listPoliciesForContainer(),
)

return ret
Expand Down Expand Up @@ -138,3 +145,84 @@ func addCgroup(fname string, polID policyfilter.PolicyID, cgID policyfilter.Cgro
}

}

func listPoliciesForContainer() *cobra.Command {
var endpoint, cgroupMnt string
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, policyfilter.MapName)
ret := &cobra.Command{
Use: "listpolicies [container id]",
Short: "list all Kubernetes Identity Aware policies that apply to a specific container",
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
ctx := context.Background()
client, err := cri.NewClient(ctx, endpoint)
if err != nil {
return err
}

cgroupPath, err := cri.CgroupPath(ctx, client, args[0])
if err != nil {
return err
}

if cgroupMnt == "" {
cgroupMnt = defaults.Cgroup2Dir
}
fullCgroupPath := path.Join(cgroupMnt, cgroupPath)
if common.Debug {
logger.GetLogger().WithField("path", fullCgroupPath).Info("cgroup")
}

cgID, err := cgroups.GetCgroupIdFromPath(fullCgroupPath)
if err != nil {
logger.GetLogger().WithError(err).Fatal("Failed to parse cgroup")
}

if common.Debug {
logger.GetLogger().WithField("id", cgID).Info("cgroup")
}

m, err := policyfilter.OpenMap(mapFname)
if err != nil {
logger.GetLogger().WithError(err).Fatal("Failed to open policyfilter map")
return err
}
defer m.Close()

data, err := m.Dump()
if err != nil {
logger.GetLogger().WithError(err).Fatal("Failed to open policyfilter map")
return err
}

policyIds, ok := data.Cgroup[policyfilter.CgroupID(cgID)]
if !ok {
return nil
}

c, err := common.NewClientWithDefaultContextAndAddress()
if err != nil {
return fmt.Errorf("failed create gRPC client: %w", err)
}
defer c.Close()

res, err := c.Client.ListTracingPolicies(c.Ctx, &tetragon.ListTracingPoliciesRequest{})
if err != nil || res == nil {
return fmt.Errorf("failed to list tracing policies: %w", err)
}

common.PrintTracingPolicies(os.Stdout, res.Policies, func(pol *tetragon.TracingPolicyStatus) bool {
_, ok := policyIds[policyfilter.PolicyID(pol.FilterId)]
return !ok
})

return nil
},
}

flags := ret.Flags()
flags.StringVarP(&endpoint, "runtime-endpoint", "r", "", "CRI endpoint")
flags.StringVar(&mapFname, "map-fname", mapFname, "policyfilter map filename")
flags.StringVar(&cgroupMnt, "cgroup-mount", cgroupMnt, "cgroupFS mount point")
return ret
}
50 changes: 1 addition & 49 deletions cmd/tetra/tracingpolicy/tracingpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package tracingpolicy
import (
"fmt"
"os"
"strings"
"text/tabwriter"

"github.com/cilium/tetragon/api/v1/tetragon"
"github.com/cilium/tetragon/cmd/tetra/common"
Expand Down Expand Up @@ -161,53 +159,7 @@ func New() *cobra.Command {
}
cmd.Println(string(b))
case "text":
// tabwriter config imitates kubectl default output, i.e. 3 spaces padding
w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "ID\tNAME\tSTATE\tFILTERID\tNAMESPACE\tSENSORS\tKERNELMEMORY")

for _, pol := range res.Policies {
namespace := pol.Namespace
if namespace == "" {
namespace = "(global)"
}

sensors := strings.Join(pol.Sensors, ",")

// From v0.11 and before, enabled, filterID and error were
// bundled in a string. To have a retro-compatible tetra
// command, we scan the string. If the scan fails, it means
// something else might be in Info and we print it.
//
// we can drop the following block (and comment) when we
// feel tetra should support only version after v0.11
if pol.Info != "" {
var parsedEnabled bool
var parsedFilterID uint64
var parsedError string
var parsedName string
str := strings.NewReader(pol.Info)
_, err := fmt.Fscanf(str, "%253s enabled:%t filterID:%d error:%512s", &parsedName, &parsedEnabled, &parsedFilterID, &parsedError)
if err == nil {
if parsedEnabled {
pol.State = tetragon.TracingPolicyState_TP_STATE_ENABLED
}
pol.FilterId = parsedFilterID
pol.Error = parsedError
pol.Info = ""
}
}

fmt.Fprintf(w, "%d\t%s\t%s\t%d\t%s\t%s\t%s\t\n",
pol.Id,
pol.Name,
strings.TrimPrefix(strings.ToLower(pol.State.String()), "tp_state_"),
pol.FilterId,
namespace,
sensors,
common.HumanizeByteCount(int(pol.KernelMemoryBytes)),
)
}
w.Flush()
common.PrintTracingPolicies(cmd.OutOrStdout(), res.Policies, nil)
}

return nil
Expand Down
1 change: 1 addition & 0 deletions docs/content/en/docs/reference/helm-chart.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/data/tetragon_flags.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions install/kubernetes/tetragon/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ data:
{{- if .Values.tetragon.enablePolicyFilter }}
enable-policy-filter: "true"
{{- end }}
{{- if .Values.tetragon.enablePolicyFilterCgroupMap }}
enable-policy-filter-cgroup-map: "true"
{{- end }}
{{- if .Values.tetragon.enablePolicyFilterDebug }}
enable-policy-filter-debug: "true"
{{- end }}
Expand Down
2 changes: 2 additions & 0 deletions install/kubernetes/tetragon/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ tetragon:
port: 6060
# -- Enable policy filter. This is required for K8s namespace and pod-label filtering.
enablePolicyFilter: True
# -- Enable policy filter cgroup map.
enablePolicyFilterCgroupMap: false
# -- Enable policy filter debug messages.
enablePolicyFilterDebug: false
# -- Enable latency monitoring in message handling
Expand Down
5 changes: 3 additions & 2 deletions pkg/option/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ type config struct {

ReleasePinned bool

EnablePolicyFilter bool
EnablePolicyFilterDebug bool
EnablePolicyFilter bool
EnablePolicyFilterCgroupMap bool
EnablePolicyFilterDebug bool

EnablePidSetFilter bool

Expand Down
Loading