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
14 changes: 13 additions & 1 deletion pkg/datagatherer/k8s/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,18 @@ func (g *DataGathererDynamic) Fetch() (interface{}, int, error) {
return list, len(items), nil
}

// redactList removes sensitive and superfluous data from the supplied resource list.
// All resources have superfluous managed-data fields removed.
// All resources have sensitive labels and annotations removed.
// Secret and Route are processed as special cases. For these
// resources there is an allow-list of fields that should be retained.
// For Secret resources, the `data` is redacted, to prevent private keys or sensitive
// data being collected; only the tls.crt and ca.crt data keys are retained.
// For Route resources, only the fields related to CA certificate and policy are retained.
// TODO(wallrj): A short coming of the current allow-list implementation is that
// you have to specify absolute fields paths. It is not currently possible to
// select all metadata with: `{metadata}`. This means that the metadata for
// Secret and Route has fewer fields than the metadata for all other resources.
func redactList(list []*api.GatheredResource, excludeAnnotKeys, excludeLabelKeys []*regexp.Regexp) error {
for i := range list {
if item, ok := list[i].Resource.(*unstructured.Unstructured); ok {
Expand All @@ -361,7 +373,7 @@ func redactList(list []*api.GatheredResource, excludeAnnotKeys, excludeLabelKeys

resource := item

// Redact item if it is a:
// Redact item if it is a Secret or a Route.
for _, gvk := range gvks {
// secret object
if gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == "") {
Expand Down
22 changes: 20 additions & 2 deletions pkg/datagatherer/k8s/fieldfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
)

// SecretSelectedFields is the list of fields sent from Secret objects to the
// backend
// backend.
// The `data` is redacted, to prevent private keys or sensitive data being
// collected. Only the following none-sensitive keys are retained: tls.crt,
// ca.crt. These keys are assumed to always contain public TLS certificates.
var SecretSelectedFields = []FieldPath{
{"kind"},
{"apiVersion"},
Expand All @@ -16,14 +19,26 @@ var SecretSelectedFields = []FieldPath{
{"metadata", "ownerReferences"},
{"metadata", "selfLink"},
{"metadata", "uid"},
{"metadata", "creationTimestamp"},
{"metadata", "deletionTimestamp"},
{"metadata", "resourceVersion"},

{"type"},
{"data", "tls.crt"},
{"data", "ca.crt"},
}

// RouteSelectedFields is the list of fields sent from OpenShift Route objects to the
// backend
// backend.
// The Route resource is redacted because it may contain private keys for TLS.
//
// TODO(wallrj): Find out if the `.tls.key` field is the only one that may
// contain sensitive data and if so, that field could be redacted instead
// selecting everything else, for consistency with Ingress or any of the other
// resources that are collected. Or alternatively add an comment to explain why
// for Route, the set of fields is allow-listed while for Ingress, all fields
// are collected.
// https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/network_apis/route-route-openshift-io-v1#spec-tls-3
var RouteSelectedFields = []FieldPath{
{"kind"},
{"apiVersion"},
Expand All @@ -33,6 +48,9 @@ var RouteSelectedFields = []FieldPath{
{"metadata", "ownerReferences"},
{"metadata", "selfLink"},
{"metadata", "uid"},
{"metadata", "creationTimestamp"},
{"metadata", "deletionTimestamp"},
{"metadata", "resourceVersion"},

{"spec", "host"},
{"spec", "to", "kind"},
Expand Down
20 changes: 20 additions & 0 deletions pkg/datagatherer/k8s/fieldfilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ func TestSelect(t *testing.T) {
"labels": map[string]interface{}{
"foo": "bar",
},
"resourceVersion": "fake-resource-version",
"creationTimestamp": "2025-08-15T00:00:01Z",
"deletionTimestamp": "2025-08-15T00:00:02Z",
// Examples of fields which are dropped
"deletionGracePeriodSeconds": 10,
"finalizers": []string{"example.com/fake-finalizer"},
"generation": 11,
},
"type": "kubernetes.io/tls",
"data": map[string]interface{}{
Expand All @@ -47,6 +54,9 @@ func TestSelect(t *testing.T) {
"labels": map[string]interface{}{
"foo": "bar",
},
"resourceVersion": "fake-resource-version",
"creationTimestamp": "2025-08-15T00:00:01Z",
"deletionTimestamp": "2025-08-15T00:00:02Z",
},
"type": "kubernetes.io/tls",
"data": map[string]interface{}{
Expand All @@ -68,6 +78,13 @@ func TestSelect(t *testing.T) {
"labels": map[string]interface{}{
"foo": "bar",
},
"resourceVersion": "fake-resource-version",
"creationTimestamp": "2025-08-15T00:00:01Z",
"deletionTimestamp": "2025-08-15T00:00:02Z",
// Examples of fields which are dropped
"deletionGracePeriodSeconds": 10,
"finalizers": []string{"example.com/fake-finalizer"},
"generation": 11,
},
"spec": map[string]interface{}{
"host": "www.example.com",
Expand All @@ -94,6 +111,9 @@ func TestSelect(t *testing.T) {
// "Select". "Redact" removes it.
"kubectl.kubernetes.io/last-applied-configuration": "secret",
},
"resourceVersion": "fake-resource-version",
"creationTimestamp": "2025-08-15T00:00:01Z",
"deletionTimestamp": "2025-08-15T00:00:02Z",
},
"spec": map[string]interface{}{
"host": "www.example.com",
Expand Down