-
Notifications
You must be signed in to change notification settings - Fork 409
tetragon: group enable process ancestors flags #3581
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
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for tetragon ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey I didn't realized we added so many flags for one feature, would it be possible to group them under --enable-ancestors=kprobe,tracepoints,lsm
etc, instead of boolean flags? This PR is fine but I would think that would be a better usability thing. You can use StringSlice in viper flags.
Would you think that would be better maybe? Since this is a recent feature, maybe it's fine to just change the flags. Not sure it's been released already or not, if yes we might need to deprecate them before removing next release.
Hey. I think it should be possible to group them, yes. This feature was released with version 1.4.0 not so long ago. I'll see what i can do about it. Should i submit another PR or extend this one? |
I think it would be reasonable to merge this, and create another PR with two things:
OR we can make the UX correct in helm and then change the underlying implem. Any opinion @lambdanis ? |
I agree, this sounds like a better approach. I would update this PR to introduce both a new flag and a Helm value like this: tetragon:
processAncestors:
enabled: "base,kprobe,tracepoint,uprobe,lsm" You can rename and reuse Speaking more generally, the Helm values don't have to be named exactly like the corresponding flags. If we can group related values or follow Helm conventions, then it makes sense to structure them slightly differently than the corresponding flags. As an alternative to the above, I think Helm values like this would make sense too: tetragon:
processAncestors:
base:
enabled: true
kprobe:
enabled: true
tracepoint:
enabled: true
uprobe:
enabled: true
lsm:
enabled: true but then translating these to a comma-separated flag is not as straightforward, so if we're changing the flag format then it seems better to keep the Helm value format same. |
I don't think we need to translate it into a comma-seperated flag. Using multiple flags ( |
Yes iirc viper parses both
If the default is to be disabled, not sure we need a disable ancestor flags? Otherwise we need to resolve conflict etc, it sounds annoying and unnecessary. |
a27083e
to
a7d448d
Compare
Deprecate tetragon enable-process-ancestors boolean flags added in PR 2938 (e.g., see fd57a14) and introduce a new '--enable-ancestors' flag to replace them for better UX. Aforementioned flags are now marked as deprecated, but still usable. Signed-off-by: t0x01 <[email protected]>
Replace tetragon enable-process-ancestors boolean flags added in PR 2938 (e.g., see e2acd5b) with a new '--enable-ancestors' flag. Signed-off-by: t0x01 <[email protected]>
Deprecate tetragon enable-process-ancestors boolean flags added in PR 2938 (e.g., see dc9ba94) and introduce a new '--enable-ancestors' flag to replace them for better UX. Aforementioned flags are now marked as deprecated, but still usable. Signed-off-by: t0x01 <[email protected]>
Add support for process ancestors introduced in PR 2938 (e.g., see fd57a14). Signed-off-by: t0x01 <[email protected]>
a7d448d
to
b4df3ae
Compare
Hey @mtardy , @lambdanis Thanks for the help. I added both new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey thanks a lot for the PR, it looks really clean!
However I think you could simplify by reusing the old variables internally (it's fine to have these variables, I think the important part is just avoid the UX of having all those flags). Also you can reuse the viper.GetStringSlice and just do validation on the values.
// Set the ancestors only if --enable-ancestors flag includes 'kprobe'. | ||
if option.Config.EnableAncestors["kprobe"] && proc.NeededAncestors() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think internally, it's fine to keep the many config static variables EnableProcessKprobeAncestors
and co, so that it avoids these dynamic string to be used with a map. Because otherwise we should use constant string and that for access I'm not sure it would be optimal
} | ||
|
||
// Override deprecated flags, if new --enable-ancestors flag is used | ||
if enableAncestors := viper.GetString(KeyEnableAncestors); enableAncestors != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you maybe use viper.GetStringSlice(key string) : []string
here so that you don't need to split and everything but just validate the values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viper.GetStringSlice(key string) : []string
only splits strings by spaces, if i'm not mistaken. I think i can use pflag.StringSlice
and viper.UnmarshalKey
instead to correctly parse strings with comma-separated values read from both the cmd flag and the config files. So it would be something like this:
var err error
var enableAncestors []string
...
if err = viper.UnmarshalKey(KeyEnableAncestors, &enableAncestors); err != nil {
return fmt.Errorf("failed to parse enable-ancestors value: %w", err)
}
if slices.Contains(enableAncestors, "base") {
Config.EnableProcessAncestors = true
Config.EnableProcessKprobeAncestors = slices.Contains(enableAncestors, "kprobe")
Config.EnableProcessTracepointAncestors = slices.Contains(enableAncestors, "tracepoint")
Config.EnableProcessUprobeAncestors = slices.Contains(enableAncestors, "uprobe")
Config.EnableProcessLsmAncestors = slices.Contains(enableAncestors, "lsm")
}
...
flags.StringSlice(KeyEnableAncestors, []string{}, "Comma-separated list of process event types to enable ancestors for. Supported event types are: base, kprobe, tracepoint, uprobe, lsm. Unknown event types will be ignored. Type 'base' enables ancestors for process_exec and process_exit events and is required by all other supported event types for correct reference counting. An empty string disables ancestors completely")
Would this be fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viper.GetStringSlice(key string) : []string only splits strings by spaces
I checked, it works fine when doing --options A --options B
but also --options A,B
. It doesn't use spaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works fine with cmd flags, yes. That's because the flag's value goes through pflag
first. But, when the value is read from a config file, it doesn't go through pflag
.
viper.GetStringSlice
calls cast.ToStringSlice
. Here.
cast.ToStringSlice
then calls cast.ToStringSliceE
here.
If an option's value is read from a config file, the value's type will be a string
. And cast.ToStringSliceE
will call strings.Fields
here.
I used this code to test it:
package main
import (
"fmt"
"reflect"
"slices"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func main() {
pflag.StringSlice("options", []string{}, "")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.ReadInConfig()
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
options := viper.GetStringSlice("options")
fmt.Printf("options type: %s\n", reflect.TypeOf(viper.Get("options")))
fmt.Printf("options len = %d\n", len(options))
fmt.Printf("options value = %s\n", options)
fmt.Printf("options contains 'A': %t\n", slices.Contains(options, "A"))
}
Tests:
$ go run test.go --options A,B
options type: []string
options len = 2
options value = [A B]
options contains 'A': true
$ go run test.go --options A --options B
options type: []string
options len = 2
options value = [A B]
options contains 'A': true
$ echo 'options: A B' > config.yaml && go run test.go
options type: string
options len = 2
options value = [A B]
options contains 'A': true
$ echo 'options: A,B' > config.yaml && go run test.go
options type: string
options len = 1
options value = [A,B]
options contains 'A': false
$ echo 'options: [A,B]' > config.yaml && go run test.go
options type: []interface {}
options len = 2
options value = [A B]
options contains 'A': true
So, then the option's substrings must be separated with spaces or specified as yaml list elements in config files, if we use viper.GetStringSlice
to parse it. With cmd flags, comma separated values should work fine. I'm just not sure what to choose here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine as YAML list are never defined with comma separated values so it feels okay to ask for YAML list for this. Would that be an issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, i don't think it would be an issue. I'm just trying to clarify it preemptively to make sure i don't commit any unwanted changes. I'll try to update this PR in the next couple of days, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah sure, I'm sure we can find a way to make this works fine with Helm as well! Cool thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, about Helm. Since ConfigMap.data only allows UTF-8 strings, i can't provide a YAML list as a ConfigMap.data field value. I can concatenate list's elements into a string with a space delimiter, though.
In values.yaml
i can make tetragon.processAncestors.enabled
value be a YAML list, like so:
tetragon:
processAncestors:
# -- List of process event types to enable ancestors for.
# Supported event types are: base, kprobe, tracepoint, uprobe, lsm. Unknown event types will be ignored.
# Type "base" is required by all other supported event types for correct reference counting.
# Set it to [] to disable ancestors completely.
enabled: []
And in the tetragon_configmap.yaml
template, i can do this:
data:
{{- if .Values.tetragon.processAncestors.enabled }}
enable-ancestors: {{ join " " .Values.tetragon.processAncestors.enabled | quote }}
{{- end }}
So, the rendered tetragon configMap will look like this:
$ helm template -s templates/tetragon_configmap.yaml . --set "tetragon.processAncestors.enabled={base,kprobe,lsm}"
---
# Source: tetragon/templates/tetragon_configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
enable-ancestors: "base kprobe lsm"
...
viper.GetStringSlice
should parse that value correctly, but i guess it can be confusing if this is the only place where the value would not be a YAML list, but a string. Not sure if i can pass an actual YAML list through a configMap somehow. Would that be ok?
Config.EnableAncestors["base"] = true | ||
Config.EnableAncestors["kprobe"] = viper.GetBool(KeyEnableProcessKprobeAncestors) | ||
Config.EnableAncestors["tracepoint"] = viper.GetBool(KeyEnableProcessTracepointAncestors) | ||
Config.EnableAncestors["uprobe"] = viper.GetBool(KeyEnableProcessUprobeAncestors) | ||
Config.EnableAncestors["lsm"] = viper.GetBool(KeyEnableProcessLsmAncestors) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so here you could still reuse the existing vars
var AncestorsEventTypeMap = map[tetragon.EventType]string{ | ||
tetragon.EventType_PROCESS_EXEC: "base", | ||
tetragon.EventType_PROCESS_EXIT: "base", | ||
tetragon.EventType_PROCESS_KPROBE: "kprobe", | ||
tetragon.EventType_PROCESS_TRACEPOINT: "tracepoint", | ||
tetragon.EventType_PROCESS_UPROBE: "uprobe", | ||
tetragon.EventType_PROCESS_LSM: "lsm", | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could reuse the tetragon.EventType_PROCESS_EXEC.String()
directly, by removing useless suffix, lowercase, etc...
|
||
// NOTE: enable-process-ancestors flags are marked as deprecated and | ||
// planned to be removed in version 1.6 | ||
flags.Bool(KeyEnableProcessAncestors, false, "Deprecated, please, use --enable-ancestors. Include ancestors in process_exec and process_exit events. Disabled by default. Required by other enable ancestors options for correct reference counting") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add the keyword to add in the alternative?
flags.Bool(KeyEnableProcessAncestors, false, "Deprecated, please, use --enable-ancestors. Include ancestors in process_exec and process_exit events. Disabled by default. Required by other enable ancestors options for correct reference counting") | |
flags.Bool(KeyEnableProcessAncestors, false, "Deprecated, please, use --enable-ancestors=base. Include ancestors in process_exec and process_exit events. Disabled by default. Required by other enable ancestors options for correct reference counting") |
flags.Bool(KeyEnableProcessTracepointAncestors, false, fmt.Sprintf("Deprecated, please, use --enable-ancestors. Include ancestors in process_tracepoint events. Only used if '%s' is set to 'true'", KeyEnableProcessAncestors)) | ||
flags.Bool(KeyEnableProcessUprobeAncestors, false, fmt.Sprintf("Deprecated, please, use --enable-ancestors. Include ancestors in process_uprobe events. Only used if '%s' is set to 'true'", KeyEnableProcessAncestors)) | ||
flags.Bool(KeyEnableProcessLsmAncestors, false, fmt.Sprintf("Deprecated, please, use --enable-ancestors. Include ancestors in process_lsm events. Only used if '%s' is set to 'true'", KeyEnableProcessAncestors)) | ||
flags.MarkDeprecated(KeyEnableProcessAncestors, "please use --enable-ancestors") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flags.MarkDeprecated(KeyEnableProcessAncestors, "please use --enable-ancestors") | |
flags.MarkDeprecated(KeyEnableProcessAncestors, "please use --enable-ancestors=base") |
func ParseEnableAncestors(eventTypesString string) []string { | ||
eventTypes := []string{} | ||
for _, t := range strings.Split(eventTypesString, ",") { | ||
t = strings.TrimSpace(t) | ||
eventTypes = append(eventTypes, t) | ||
} | ||
return eventTypes | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you can avoid writing all this by relying on viper.GetStringSlice
This PR deprecates 5 enable-process-ancestors boolean flags added in PR 2938 and replaces them with a new
--enable-ancestors
flag for better UX. All 5 deprecated flags remain functional; the new--enable-ancestors
flag will simply override them if both old and new flags are used.Flags being deprecated:
--enable-process-ancestors
--enable-process-kprobe-ancestors
--enable-process-tracepoint-ancestors
--enable-process-uprobe-ancestors
--enable-process-lsm-ancestors
Also, support for the new flag is added in the Tetragon Helm chart via a
tetragon.processAncestors.enabled
value.