Skip to content

Commit

Permalink
implement redaction filters
Browse files Browse the repository at this point in the history
Implement a new field filter type, the refaction filter. Redaction filters use regular
expressions to suppress sensitive information in string fields in Tetragon events. When
a regular expression in a redcation filter matches a string, everything inside of its
capture groups is replaced with `*****`, effectively censoring the output. For example,
the regular expression `(?:--password|-p)(?:\s+|=)(\S*)` will convert the string
"--password=foo" into "--password=*****".

In some cases, it is not desirable to apply a redaction filter to all events. For this use
case, redaction filters also include an event filter which can be used to select events to
redact. This event filter is configured with the same syntax as an export filter. As
a more concrete example:

    {"match": {"binary_regex": ["^foo$"]}, "redact": ["\W(qux)\W"]}

The above filter would redact any occurrences of the word "qux" in events with the binary
name "foo".

Due to the sensitive nature of redaction, these filters are applied as configured in the
agent, regardless of whether an event is exported via gRPC or the JSON exporter. In other
words, redaction filter configuration always happens at the agent config level, not in the
gRPC client CLI.

Signed-off-by: William Findlay <[email protected]>
  • Loading branch information
willfindlay committed Mar 25, 2024
1 parent 505ce7f commit c30f58d
Show file tree
Hide file tree
Showing 31 changed files with 1,993 additions and 383 deletions.
17 changes: 17 additions & 0 deletions api/v1/README.md

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

434 changes: 255 additions & 179 deletions api/v1/tetragon/events.pb.go

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions api/v1/tetragon/events.pb.json.go

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

7 changes: 7 additions & 0 deletions api/v1/tetragon/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ message CapFilterSet {
repeated CapabilitiesType none = 4;
}

message RedactionFilter {
// Match events that the redaction filter will apply to.
repeated Filter match = 1;
// Regular expressions to use for redaction. Strings inside capture groups are redacted.
repeated string redact = 2;
}

// Determines the behavior of a field filter
enum FieldFilterAction {
INCLUDE = 0;
Expand Down
14 changes: 13 additions & 1 deletion cmd/tetragon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ func getFieldFilters() ([]*tetragon.FieldFilter, error) {
return filters, nil
}

func getRedactionFilters() (fieldfilters.RedactionFilterList, error) {
redactionFilters := viper.GetString(option.KeyRedactionFilters)
return fieldfilters.ParseRedactionFilterList(redactionFilters)
}

// Save daemon information so it is used by client cli but
// also by bugtool
func saveInitInfo() error {
Expand Down Expand Up @@ -401,11 +406,17 @@ func tetragonExecute() error {

hookRunner := rthooks.GlobalRunner().WithWatcher(k8sWatcher)

redactionFilters, err := getRedactionFilters()
if err != nil {
return err
}

pm, err := tetragonGrpc.NewProcessManager(
ctx,
&cleanupWg,
observer.GetSensorManager(),
hookRunner)
hookRunner,
redactionFilters)
if err != nil {
return err
}
Expand Down Expand Up @@ -706,6 +717,7 @@ func startExporter(ctx context.Context, server *server.Server) error {
}
req := tetragon.GetEventsRequest{AllowList: allowList, DenyList: denyList, AggregationOptions: aggregationOptions, FieldFilters: fieldFilters}
log.WithFields(logrus.Fields{"fieldFilters": fieldFilters}).Debug("Configured field filters")
log.WithFields(logrus.Fields{"redactionFilters": fieldFilters}).Debug("Configured redaction filters")
log.WithFields(logrus.Fields{"logger": writer, "request": &req}).Info("Starting JSON exporter")
exporter := exporter.NewExporter(ctx, &req, server, encoder, writer, rateLimiter)
return exporter.Start()
Expand Down
45 changes: 44 additions & 1 deletion docs/content/en/docs/concepts/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,49 @@ only `exec_id` and `parent_exec_id` in all event types except for
{"fields":"process.exec_id,process.parent_exec_id", "event_set": ["PROCESS_EXEC"], "invert_event_set": true, "action": "INCLUDE"}
```

#### Redacting Sensitive Information

Since Tetragon traces the entire system, event exports might sometimes contain
sensitive information (for example, a secret passed via a command line argument
to a process). To prevent this information from being exfiltrated via Tetragon
JSON export, Tetragon provides a mechanism called Redaction Filters which can be
used to select events and string patterns to redact. These filters are written
in JSON and passed to the Tetragon agent via the `--redaction-filters` command
line flag or the `redactionFilters` Helm value.

To perform redactions, redaction filters define regular expressions in the
`redact` field. Any capture groups in these regular expressions are redacted and
replaced with `"*****"`.

{{< warning >}}
When writing regular expressions in JSON, it is important to escape backslash
characters. For instance `\Wpasswd\W?` would be written as `{"redact": "\\Wpasswd\\W?"}`.
{{< /warning >}}

Redaction filters select events using the `match` field, which contains one or
more filters (these filters are defined the same way as export filters). If no
match filter is defined, all events are selected.

As a concrete example, the following will redact all passwords passed to
processes with the `"--password"` argument:

```json
{"redact": ["--password(?:\\s+|=)(\\S*)"]}
```

Now, an event that contains the string `"--password=foo"` would have that string
replaced with `"--password=*****"`.

Suppose we also see some passwords passed via the -p shorthand for a specific binary, foo.
We can also redact these as follows:

```json
{"match": [{"binary_regex": "(?:^|/)foo$"}], "redact": ["-p(?:\\s+|=)(\\S*)"]}
```

With both of the above redaction filters in place, we are now redacting all
password arguments.

### `tetra` CLI

A second way is to use the [`tetra`](https://github.com/cilium/tetragon/tree/main/cmd/tetra) CLI. This
Expand Down Expand Up @@ -245,4 +288,4 @@ An example gRPC endpoint is the Tetra CLI when its not piped JSON output directl

```shell
kubectl exec -ti -n kube-system ds/tetragon -c tetragon -- tetra getevents -o compact
```
```
9 changes: 9 additions & 0 deletions docs/content/en/docs/reference/grpc-api.md

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

3 changes: 2 additions & 1 deletion 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.

2 changes: 2 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.

3 changes: 2 additions & 1 deletion install/kubernetes/tetragon/README.md

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

2 changes: 2 additions & 0 deletions install/kubernetes/tetragon/templates/tetragon_configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ data:
{{- .Values.tetragon.exportDenyList | trim | nindent 4 }}
field-filters: |-
{{- .Values.tetragon.fieldFilters | trim | nindent 4 }}
redaction-filters: |-
{{- .Values.tetragon.redactionFilters | trim | nindent 4 }}
export-rate-limit: {{ .Values.tetragon.exportRateLimit | quote }}
{{- end }}
{{- if .Values.tetragon.enableK8sAPI }}
Expand Down
28 changes: 27 additions & 1 deletion install/kubernetes/tetragon/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,33 @@ tetragon:
# {"event_set": ["PROCESS_KPROBE"], "fields": "process", "action": "INCLUDE"}
#
fieldFilters: |-
{}
# Filters to redact secrets from string fields in Tetragon events. To perform
# redactions, redaction filters define regular expressions in the `redact`
# field. Any capture groups in these regular expressions are redacted and
# replaced with "*****".
#
# Redaction filters select events using the `match` field, which contains one
# or more filters (these filters are defined the same way as export filters).
# If no match filter is defined, all events are selected.
#
# As a concrete example, the following will redact all passwords passed to
# processes with the "--password" argument:
#
# {"redact": ["--password(?:\s+|=)(\S*)"]}
#
# Now, an event which contains the string "--password=foo" would have that
# string replaced with "--password=*****".
#
# Suppose we also see some passwords passed via the -p shorthand for a specific binary, foo.
# We can also redact these as follows:
#
# {"match": [{"binary_regex": "(?:^|/)foo$"}], "redact": ["-p(?:\s+|=)(\S*)"]}
#
# With both of the above redaction filters in place, we are now redacting all
# password arguments.
redactionFilters: |-
# Access Kubernetes API to associate Tetragon events with Kubernetes pods.
enableK8sAPI: true
# enableProcessCred enables Capabilities visibility in exec and kprobe events.
Expand Down
4 changes: 3 additions & 1 deletion pkg/bench/bench.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/cilium/tetragon/pkg/cilium"
"github.com/cilium/tetragon/pkg/defaults"
"github.com/cilium/tetragon/pkg/exporter"
"github.com/cilium/tetragon/pkg/fieldfilters"
"github.com/cilium/tetragon/pkg/grpc"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/observer"
Expand Down Expand Up @@ -227,7 +228,8 @@ func startBenchmarkExporter(ctx context.Context, obs *observer.Observer, summary
ctx,
&wg,
observer.GetSensorManager(),
hookRunner)
hookRunner,
fieldfilters.RedactionFilterList{})
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/exporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/cilium/tetragon/api/v1/tetragon"
"github.com/cilium/tetragon/pkg/encoder"
"github.com/cilium/tetragon/pkg/fieldfilters"
"github.com/cilium/tetragon/pkg/ratelimit"
"github.com/cilium/tetragon/pkg/rthooks"
"github.com/cilium/tetragon/pkg/server"
Expand Down Expand Up @@ -85,7 +86,7 @@ func TestExporter_Send(t *testing.T) {
eventNotifier := newFakeNotifier()
ctx, cancel := context.WithCancel(context.Background())
dr := rthooks.DummyHookRunner{}
grpcServer := server.NewServer(ctx, &wg, eventNotifier, &server.FakeObserver{}, dr)
grpcServer := server.NewServer(ctx, &wg, eventNotifier, &server.FakeObserver{}, dr, fieldfilters.RedactionFilterList{})
numRecords := 2
results := newArrayWriter(numRecords)
encoder := encoder.NewProtojsonEncoder(results)
Expand Down Expand Up @@ -190,7 +191,7 @@ func Test_rateLimitExport(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
eventNotifier := newFakeNotifier()
dr := rthooks.DummyHookRunner{}
grpcServer := server.NewServer(ctx, &wg, eventNotifier, &server.FakeObserver{}, dr)
grpcServer := server.NewServer(ctx, &wg, eventNotifier, &server.FakeObserver{}, dr, fieldfilters.RedactionFilterList{})
results := newArrayWriter(tt.totalEvents)
encoder := encoder.NewProtojsonEncoder(results)
request := &tetragon.GetEventsRequest{}
Expand Down
Loading

0 comments on commit c30f58d

Please sign in to comment.