Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tetra policyfilter listpolicies command #3122

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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 32768 /* same as polMapSize in policyfilter/state.go */

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_reverse_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_reverse_maps SEC(".maps");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think naming the map "reverse" will make the code/etc. harder to understand.
How about cgroup_policies or policyfilter_cgoup_policies?
What do you think?

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

Expand Down
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("--- Direct Map ---")

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

for polId, cgIDs := range data {
for polId, cgIDs := range data.Direct {
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.Reverse != nil {
fmt.Println("--- Reverse Map ---")

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

for cgIDs, polIds := range data.Reverse {
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
137 changes: 137 additions & 0 deletions cmd/tetra/policyfilter/policyfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
package policyfilter

import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"text/tabwriter"

"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 +37,7 @@ func New() *cobra.Command {
addCommand(),
cgroupGetIDCommand(),
dumpDebugCmd(),
listPoliciesForContainer(),
)

return ret
Expand Down Expand Up @@ -138,3 +147,131 @@ 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.Reverse[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)
}

// tabwriter config imitates kubectl default output, i.e. 3 spaces padding
w := tabwriter.NewWriter(os.Stdout, 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 = ""
}
}

if _, ok := policyIds[policyfilter.PolicyID(pol.FilterId)]; !ok {
continue
}

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

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
}
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.enablePolicyFilterReverseMap }}
enable-policy-filter-reverse-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 reverse map.
enablePolicyFilterReverseMap: 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
EnablePolicyFilterReverseMap bool
EnablePolicyFilterDebug bool

EnablePidSetFilter bool

Expand Down
7 changes: 5 additions & 2 deletions pkg/option/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ const (

KeyReleasePinnedBPF = "release-pinned-bpf"

KeyEnablePolicyFilter = "enable-policy-filter"
KeyEnablePolicyFilterDebug = "enable-policy-filter-debug"
KeyEnablePolicyFilter = "enable-policy-filter"
KeyEnablePolicyFilterReverseMap = "enable-policy-filter-reverse-map"
KeyEnablePolicyFilterDebug = "enable-policy-filter-debug"

KeyEnablePidSetFilter = "enable-pid-set-filter"

Expand Down Expand Up @@ -202,6 +203,7 @@ func ReadAndSetFlags() error {

Config.ReleasePinned = viper.GetBool(KeyReleasePinnedBPF)
Config.EnablePolicyFilter = viper.GetBool(KeyEnablePolicyFilter)
Config.EnablePolicyFilterReverseMap = viper.GetBool(KeyEnablePolicyFilterReverseMap)
Config.EnablePolicyFilterDebug = viper.GetBool(KeyEnablePolicyFilterDebug)
Config.EnableMsgHandlingLatency = viper.GetBool(KeyEnableMsgHandlingLatency)

Expand Down Expand Up @@ -378,6 +380,7 @@ func AddFlags(flags *pflag.FlagSet) {
// Provide option to enable policy filtering. Because the code is new,
// this is set to false by default.
flags.Bool(KeyEnablePolicyFilter, false, "Enable policy filter code")
flags.Bool(KeyEnablePolicyFilterReverseMap, false, "Enable reverse mappings for policy filter maps")
flags.Bool(KeyEnablePolicyFilterDebug, false, "Enable policy filter debug messages")

// Provide option to enable the pidSet export filters.
Expand Down
2 changes: 1 addition & 1 deletion pkg/policyfilter/k8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ func TestK8s(t *testing.T) {

// testState implements cgFinder
ts := newTestState(client)
st, err := newState(log, ts)
st, err := newState(log, ts, true)
if err != nil {
t.Skipf("failed to initialize policy filter state: %s", err)
}
Expand Down
Loading
Loading