From 2d615e3df5956d6ebd4e0b0137ff6dffc20294c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Burzy=C5=84ski?= Date: Mon, 29 Jan 2024 16:22:41 +0100 Subject: [PATCH] feat: sanitize plugins configuration built from secrets (#5495) --- CHANGELOG.md | 5 +- internal/dataplane/kongstate/kongstate.go | 4 +- .../dataplane/kongstate/kongstate_test.go | 10 +- internal/dataplane/kongstate/plugin.go | 126 +- internal/dataplane/kongstate/plugin_test.go | 1034 +++++++++-------- internal/dataplane/kongstate/types.go | 14 - 6 files changed, 703 insertions(+), 490 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 333d91a4c5..668cbad594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,8 +161,11 @@ Adding a new version? You'll need three changes: to Konnect. [#5453](https://github.com/Kong/kubernetes-ingress-controller/pull/5453) - Added `SanitizeKonnectConfigDumps` feature gate allowing to enable sanitizing - sensitive data in Konnect configuration dumps. + sensitive data in Konnect configuration dumps. [#5489](https://github.com/Kong/kubernetes-ingress-controller/pull/5489) +- Kong Plugin's `config` field now is sanitized when it contains sensitive data + sourced from a Secret (i.e. `configFrom` or `configPatches` is used). + [#5495](https://github.com/Kong/kubernetes-ingress-controller/pull/5495) ### Fixed diff --git a/internal/dataplane/kongstate/kongstate.go b/internal/dataplane/kongstate/kongstate.go index 33825fe32a..a25c46b31e 100644 --- a/internal/dataplane/kongstate/kongstate.go +++ b/internal/dataplane/kongstate/kongstate.go @@ -47,7 +47,9 @@ func (ks *KongState) SanitizedCopy() *KongState { return }(), CACertificates: ks.CACertificates, - Plugins: ks.Plugins, + Plugins: lo.Map(ks.Plugins, func(p Plugin, _ int) Plugin { + return p.SanitizedCopy() + }), Consumers: func() (res []Consumer) { for _, v := range ks.Consumers { res = append(res, *v.SanitizedCopy()) diff --git a/internal/dataplane/kongstate/kongstate_test.go b/internal/dataplane/kongstate/kongstate_test.go index 52455a55a2..7c4b9b8937 100644 --- a/internal/dataplane/kongstate/kongstate_test.go +++ b/internal/dataplane/kongstate/kongstate_test.go @@ -57,7 +57,10 @@ func TestKongState_SanitizedCopy(t *testing.T) { Upstreams: []Upstream{{Upstream: kong.Upstream{ID: kong.String("1")}}}, Certificates: []Certificate{{Certificate: kong.Certificate{ID: kong.String("1"), Key: kong.String("secret")}}}, CACertificates: []kong.CACertificate{{ID: kong.String("1")}}, - Plugins: []Plugin{{Plugin: kong.Plugin{ID: kong.String("1")}}}, + Plugins: []Plugin{{ + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{WholeConfigIsSensitive: true}, + Plugin: kong.Plugin{ID: kong.String("1"), Config: kong.Configuration{"secret": "secretValue"}}, + }}, Consumers: []Consumer{{ KeyAuths: []*KeyAuth{{kong.KeyAuth{ID: kong.String("1"), Key: kong.String("secret")}}}, }}, @@ -78,7 +81,10 @@ func TestKongState_SanitizedCopy(t *testing.T) { Upstreams: []Upstream{{Upstream: kong.Upstream{ID: kong.String("1")}}}, Certificates: []Certificate{{Certificate: kong.Certificate{ID: kong.String("1"), Key: redactedString}}}, CACertificates: []kong.CACertificate{{ID: kong.String("1")}}, - Plugins: []Plugin{{Plugin: kong.Plugin{ID: kong.String("1")}}}, + Plugins: []Plugin{{ + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{WholeConfigIsSensitive: true}, + Plugin: kong.Plugin{ID: kong.String("1"), Config: kong.Configuration{"secret": *redactedString}}, + }}, Consumers: []Consumer{{ KeyAuths: []*KeyAuth{{kong.KeyAuth{ID: kong.String("1"), Key: redactedString}}}, }}, diff --git a/internal/dataplane/kongstate/plugin.go b/internal/dataplane/kongstate/plugin.go index 56b7465882..a14de0ae14 100644 --- a/internal/dataplane/kongstate/plugin.go +++ b/internal/dataplane/kongstate/plugin.go @@ -4,11 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "strings" jsonpatch "github.com/evanphx/json-patch/v5" "github.com/kong/go-kong/kong" + "github.com/samber/lo" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" "github.com/kong/kubernetes-ingress-controller/v3/internal/store" @@ -16,6 +19,107 @@ import ( kongv1 "github.com/kong/kubernetes-ingress-controller/v3/pkg/apis/configuration/v1" ) +// Plugin represents a plugin Object in Kong. +type Plugin struct { + kong.Plugin + K8sParent client.Object + SensitiveFieldsMeta PluginSensitiveFieldsMetadata +} + +func (p Plugin) DeepCopy() Plugin { + return Plugin{ + Plugin: *p.Plugin.DeepCopy(), + K8sParent: p.K8sParent, + SensitiveFieldsMeta: p.SensitiveFieldsMeta, + } +} + +func (p Plugin) SanitizedCopy() Plugin { + // We do not want to return an error if any of below fails - the best we can do + // is to return a plugin with wholly redacted config. + // Let's have a closure returning a plugin with wholly redacted config prepared. + whollySanitized := func() Plugin { + p := p.DeepCopy() + p.Config = sanitizeWholePluginConfig(p.Config) + return p + } + + // If the whole config is sensitive, we need to redact the entire config. + if p.SensitiveFieldsMeta.WholeConfigIsSensitive { + return whollySanitized() + } + + // If there are JSON paths, we need to redact them. + if len(p.SensitiveFieldsMeta.JSONPaths) > 0 { + var patchOperations []string + for _, path := range p.SensitiveFieldsMeta.JSONPaths { + // If the path is empty, we need to sanitize the whole config. + // An empty path means that the patch is on the root of the config. + if path == "" { + return whollySanitized() + } + + patchOperations = append(patchOperations, fmt.Sprintf( + `{"op":"replace","path":"%s","value":"%s"}`, + path, + *redactedString, + )) + } + + // Decode the patch and apply it to the config. + // We need to marshal the config to JSON and then unmarshal it back to Configuration + // because the patch library works with bytes. + patch, err := jsonpatch.DecodePatch([]byte(fmt.Sprintf("[%s]", strings.Join(patchOperations, ",")))) + if err != nil { + return whollySanitized() + } + configB, err := json.Marshal(p.Config) + if err != nil { + return whollySanitized() + } + sanitizedConfigB, err := patch.Apply(configB) + if err != nil { + return whollySanitized() + } + sanitizedConfig := kong.Configuration{} + if err := json.Unmarshal(sanitizedConfigB, &sanitizedConfig); err != nil { + return whollySanitized() + } + + sanitized := p.DeepCopy() + sanitized.Config = sanitizedConfig + return sanitized + } + + // Nothing to sanitize. + return p +} + +// sanitizeWholePluginConfig redacts the entire config of a plugin by replacing all of its +// values with a redacted string. +func sanitizeWholePluginConfig(config kong.Configuration) kong.Configuration { + sanitized := config.DeepCopy() + for k := range config { + sanitized[k] = *redactedString + } + return sanitized +} + +// PluginSensitiveFieldsMetadata holds metadata about sensitive fields in a plugin's configuration. +// It can be used to sanitize them before exposing the configuration to the user (e.g. in debug dumps +// or in Konnect Admin API). +type PluginSensitiveFieldsMetadata struct { + // WholeConfigIsSensitive indicates that the entire configuration of the plugin is sensitive. + // If this is true, the configuration should be redacted entirely (each of its fields' values + // should be replaced with a redacted string). + WholeConfigIsSensitive bool + + // JSONPaths holds a list of JSON paths to sensitive fields in the plugin's configuration. + // If this is not empty, the configuration should be redacted by replacing the values of the + // fields at these paths with a redacted string. + JSONPaths []string +} + // getKongPluginOrKongClusterPlugin fetches a KongPlugin or KongClusterPlugin (as fallback) from the store. // If both are not found, an error is returned. func getKongPluginOrKongClusterPlugin(s store.Storer, namespace, name string) ( @@ -77,6 +181,14 @@ func kongPluginFromK8SClusterPlugin( } } + // Prepare sensitive fields metadata for the plugin. + sensitiveFieldsMeta := PluginSensitiveFieldsMetadata{ + JSONPaths: lo.Map(k8sPlugin.ConfigPatches, func(patch kongv1.NamespacedConfigPatch, _ int) string { + return patch.Path + }), + WholeConfigIsSensitive: k8sPlugin.ConfigFrom != nil, + } + return Plugin{ Plugin: plugin{ Name: k8sPlugin.PluginName, @@ -89,7 +201,8 @@ func kongPluginFromK8SClusterPlugin( Protocols: protocolsToStrings(k8sPlugin.Protocols), Tags: util.GenerateTagsForObject(&k8sPlugin), }.toKongPlugin(), - K8sParent: &k8sPlugin, + K8sParent: &k8sPlugin, + SensitiveFieldsMeta: sensitiveFieldsMeta, }, nil } @@ -131,6 +244,14 @@ func kongPluginFromK8SPlugin( } } + // Prepare sensitive fields metadata for the plugin. + sensitiveFieldsMeta := PluginSensitiveFieldsMetadata{ + JSONPaths: lo.Map(k8sPlugin.ConfigPatches, func(patch kongv1.ConfigPatch, _ int) string { + return patch.Path + }), + WholeConfigIsSensitive: k8sPlugin.ConfigFrom != nil, + } + return Plugin{ Plugin: plugin{ Name: k8sPlugin.PluginName, @@ -143,7 +264,8 @@ func kongPluginFromK8SPlugin( Protocols: protocolsToStrings(k8sPlugin.Protocols), Tags: util.GenerateTagsForObject(&k8sPlugin), }.toKongPlugin(), - K8sParent: &k8sPlugin, + K8sParent: &k8sPlugin, + SensitiveFieldsMeta: sensitiveFieldsMeta, }, nil } diff --git a/internal/dataplane/kongstate/plugin_test.go b/internal/dataplane/kongstate/plugin_test.go index fed7b3f1cb..5faf399c26 100644 --- a/internal/dataplane/kongstate/plugin_test.go +++ b/internal/dataplane/kongstate/plugin_test.go @@ -5,6 +5,7 @@ import ( "github.com/kong/go-kong/kong" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -14,7 +15,6 @@ import ( ) func TestKongPluginFromK8SClusterPlugin(t *testing.T) { - assert := assert.New(t) store, _ := store.NewFakeStore(store.FakeObjects{ Secrets: []*corev1.Secret{ { @@ -32,277 +32,284 @@ func TestKongPluginFromK8SClusterPlugin(t *testing.T) { }, }, }) - type args struct { - plugin kongv1.KongClusterPlugin - } + tests := []struct { name string - args args - want kong.Plugin + plugin kongv1.KongClusterPlugin + want Plugin wantErr bool }{ { name: "basic configuration", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - InstanceName: "example", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + InstanceName: "example", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + InstanceName: kong.String("example"), }, - Protocols: kong.StringSlice("http"), - InstanceName: kong.String("example"), + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{JSONPaths: []string{}}, }, wantErr: false, }, { name: "secret configuration", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - ConfigFrom: &kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Key: "correlation-id-config", - Secret: "conf-secret", - Namespace: "default", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + ConfigFrom: &kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Key: "correlation-id-config", + Secret: "conf-secret", + Namespace: "default", }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + WholeConfigIsSensitive: true, + JSONPaths: []string{}, }, - Protocols: kong.StringSlice("http"), }, wantErr: false, }, { name: "missing secret configuration", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - ConfigFrom: &kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Key: "correlation-id-config", - Secret: "missing", - Namespace: "default", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + ConfigFrom: &kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Key: "correlation-id-config", + Secret: "missing", + Namespace: "default", }, }, }, - want: kong.Plugin{}, + want: Plugin{}, wantErr: true, }, { name: "non-JSON configuration", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{{}`), - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{{}`), }, }, - want: kong.Plugin{}, + want: Plugin{}, wantErr: true, }, { name: "both Config and ConfigFrom set", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigFrom: &kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Key: "correlation-id-config", - Secret: "conf-secret", - Namespace: "default", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigFrom: &kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Key: "correlation-id-config", + Secret: "conf-secret", + Namespace: "default", }, }, }, - want: kong.Plugin{}, + want: Plugin{}, wantErr: true, }, { name: "Config and ConfigPatches set", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Key: "correlation-id-generator", - Secret: "conf-secret", - Namespace: "default", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Key: "correlation-id-generator", + Secret: "conf-secret", + Namespace: "default", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", - "generator": "uuid", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + "generator": "uuid", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/generator"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "configPatch on subpath of non-exist path", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "response-transformer", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"replace":{"headers":["foo:bar"]}}`), - }, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/add/headers", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "response-transformer-add-headers", - Secret: "conf-secret", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "response-transformer", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"replace":{"headers":["foo:bar"]}}`), + }, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/add/headers", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "response-transformer-add-headers", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("response-transformer"), - Config: kong.Configuration{ - "replace": map[string]interface{}{ - "headers": []interface{}{ - "foo:bar", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("response-transformer"), + Config: kong.Configuration{ + "replace": map[string]interface{}{ + "headers": []interface{}{ + "foo:bar", + }, }, - }, - "add": map[string]interface{}{ - "headers": []interface{}{ - "h1:v1", - "h2:v2", + "add": map[string]interface{}{ + "headers": []interface{}{ + "h1:v1", + "h2:v2", + }, }, }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/add/headers"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "empty config and configPatch for particular paths", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{}, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/header_name", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-headername", - Secret: "conf-secret", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{}, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/header_name", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-headername", + Secret: "conf-secret", }, }, - { - Path: "/generator", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-generator", - Secret: "conf-secret", - }, + }, + { + Path: "/generator", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-generator", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", - "generator": "uuid", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + "generator": "uuid", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/header_name", "/generator"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "empty config and configPatch for whole object", - args: args{ - plugin: kongv1.KongClusterPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{}, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-config", - Secret: "conf-secret", - }, + plugin: kongv1.KongClusterPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{}, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-config", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{""}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "missing secret in configPatches", - args: args{ - plugin: kongv1.KongClusterPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-generator", - Secret: "missing-secret", - }, + plugin: kongv1.KongClusterPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-generator", + Secret: "missing-secret", }, }, }, @@ -312,25 +319,23 @@ func TestKongPluginFromK8SClusterPlugin(t *testing.T) { }, { name: "missing key of secret in cofigPatches", - args: args{ - plugin: kongv1.KongClusterPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-missing", - Secret: "conf-secret", - }, + plugin: kongv1.KongClusterPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-missing", + Secret: "conf-secret", }, }, }, @@ -340,25 +345,23 @@ func TestKongPluginFromK8SClusterPlugin(t *testing.T) { }, { name: "invalid value in configPatches", - args: args{ - plugin: kongv1.KongClusterPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.NamespacedConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.NamespacedConfigSource{ - SecretValue: kongv1.NamespacedSecretValueFromSource{ - Namespace: "default", - Key: "correlation-id-invalid", - Secret: "conf-secret", - }, + plugin: kongv1.KongClusterPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.NamespacedConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.NamespacedConfigSource{ + SecretValue: kongv1.NamespacedSecretValueFromSource{ + Namespace: "default", + Key: "correlation-id-invalid", + Secret: "conf-secret", }, }, }, @@ -369,19 +372,18 @@ func TestKongPluginFromK8SClusterPlugin(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := kongPluginFromK8SClusterPlugin(store, tt.args.plugin) - if (err != nil) != tt.wantErr { - t.Errorf("kongPluginFromK8SClusterPlugin error = %v, wantErr %v", err, tt.wantErr) + got, err := kongPluginFromK8SClusterPlugin(store, tt.plugin) + if tt.wantErr { + require.Error(t, err) return } - assert.Equal(tt.want, got.Plugin) - assert.NotEmpty(t, got.K8sParent) + tt.want.K8sParent = tt.plugin.DeepCopy() + assert.Equal(t, tt.want, got) }) } } func TestKongPluginFromK8SPlugin(t *testing.T) { - assert := assert.New(t) store, _ := store.NewFakeStore(store.FakeObjects{ Secrets: []*corev1.Secret{ { @@ -399,293 +401,296 @@ func TestKongPluginFromK8SPlugin(t *testing.T) { }, }, }) - type args struct { - plugin kongv1.KongPlugin - } tests := []struct { name string - args args - want kong.Plugin + plugin kongv1.KongPlugin + want Plugin wantErr bool }{ { name: "basic configuration", - args: args{ - plugin: kongv1.KongPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - InstanceName: "example", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, + plugin: kongv1.KongPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + InstanceName: "example", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + InstanceName: kong.String("example"), }, - Protocols: kong.StringSlice("http"), - InstanceName: kong.String("example"), + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{JSONPaths: []string{}}, }, wantErr: false, }, { name: "secret configuration", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - ConfigFrom: &kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-config", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + ConfigFrom: &kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-config", + Secret: "conf-secret", }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + WholeConfigIsSensitive: true, + JSONPaths: []string{}, }, - Protocols: kong.StringSlice("http"), }, wantErr: false, }, { name: "missing secret configuration", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - ConfigFrom: &kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-config", - Secret: "missing", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + ConfigFrom: &kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-config", + Secret: "missing", }, }, }, - want: kong.Plugin{}, wantErr: true, }, { name: "non-JSON configuration", - args: args{ - plugin: kongv1.KongPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{{}`), - }, + plugin: kongv1.KongPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{{}`), }, }, - want: kong.Plugin{}, wantErr: true, }, { name: "both Config and ConfigFrom set", - args: args{ - plugin: kongv1.KongPlugin{ - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigFrom: &kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-config", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigFrom: &kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-config", + Secret: "conf-secret", }, }, }, - want: kong.Plugin{}, wantErr: true, }, { name: "config and configPatches set", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-generator", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-generator", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", - "generator": "uuid", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + "generator": "uuid", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/generator"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "configPatch on subpath of non-exist path", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "response-transformer", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"replace":{"headers":["foo:bar"]}}`), - }, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/add/headers", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "response-transformer-add-headers", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "response-transformer", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"replace":{"headers":["foo:bar"]}}`), + }, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/add/headers", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "response-transformer-add-headers", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("response-transformer"), - Config: kong.Configuration{ - "replace": map[string]interface{}{ - "headers": []interface{}{ - "foo:bar", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("response-transformer"), + Config: kong.Configuration{ + "replace": map[string]interface{}{ + "headers": []interface{}{ + "foo:bar", + }, }, - }, - "add": map[string]interface{}{ - "headers": []interface{}{ - "h1:v1", - "h2:v2", + "add": map[string]interface{}{ + "headers": []interface{}{ + "h1:v1", + "h2:v2", + }, }, }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/add/headers"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "empty config and configPatch for particular paths", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{}, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/header_name", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-headername", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{}, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/header_name", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-headername", + Secret: "conf-secret", }, }, - { - Path: "/generator", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-generator", - Secret: "conf-secret", - }, + }, + { + Path: "/generator", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-generator", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", - "generator": "uuid", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + "generator": "uuid", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{"/header_name", "/generator"}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "empty config and configPatch for whole object", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{}, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-config", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{}, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-config", + Secret: "conf-secret", }, }, }, }, }, - want: kong.Plugin{ - Name: kong.String("correlation-id"), - Config: kong.Configuration{ - "header_name": "foo", + want: Plugin{ + Plugin: kong.Plugin{ + Name: kong.String("correlation-id"), + Config: kong.Configuration{ + "header_name": "foo", + }, + Protocols: kong.StringSlice("http"), + }, + SensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{""}, }, - Protocols: kong.StringSlice("http"), }, }, { name: "missing secret in configPatches", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-generator", - Secret: "missing-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-generator", + Secret: "missing-secret", }, }, }, @@ -695,25 +700,23 @@ func TestKongPluginFromK8SPlugin(t *testing.T) { }, { name: "missing key of secret in configPatches", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-missing", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-missing", + Secret: "conf-secret", }, }, }, @@ -723,25 +726,23 @@ func TestKongPluginFromK8SPlugin(t *testing.T) { }, { name: "invalid value in configPatches", - args: args{ - plugin: kongv1.KongPlugin{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Protocols: []kongv1.KongProtocol{"http"}, - PluginName: "correlation-id", - Config: apiextensionsv1.JSON{ - Raw: []byte(`{"header_name": "foo"}`), - }, - ConfigPatches: []kongv1.ConfigPatch{ - { - Path: "/generator", - ValueFrom: kongv1.ConfigSource{ - SecretValue: kongv1.SecretValueFromSource{ - Key: "correlation-id-invalid", - Secret: "conf-secret", - }, + plugin: kongv1.KongPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Protocols: []kongv1.KongProtocol{"http"}, + PluginName: "correlation-id", + Config: apiextensionsv1.JSON{ + Raw: []byte(`{"header_name": "foo"}`), + }, + ConfigPatches: []kongv1.ConfigPatch{ + { + Path: "/generator", + ValueFrom: kongv1.ConfigSource{ + SecretValue: kongv1.SecretValueFromSource{ + Key: "correlation-id-invalid", + Secret: "conf-secret", }, }, }, @@ -752,15 +753,108 @@ func TestKongPluginFromK8SPlugin(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := kongPluginFromK8SPlugin(store, tt.args.plugin) - if (err != nil) != tt.wantErr { - t.Errorf("kongPluginFromK8SPlugin error = %v, wantErr %v", err, tt.wantErr) + got, err := kongPluginFromK8SPlugin(store, tt.plugin) + if tt.wantErr { + require.Error(t, err) return } // don't care about tags in this test got.Tags = nil - assert.Equal(tt.want, got.Plugin) - assert.NotEmpty(t, got.K8sParent) + tt.want.K8sParent = tt.plugin.DeepCopy() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestPlugin_SanitizedCopy(t *testing.T) { + testCases := []struct { + name string + config kong.Configuration + sensitiveFieldsMeta PluginSensitiveFieldsMetadata + expectedSanitizedConfig kong.Configuration + }{ + { + name: "sensitive fields are redacted with JSONPaths", + config: kong.Configuration{ + "secret": "secret-value", + "object": map[string]interface{}{ + "secretObjectField": "secret-object-field-value", + }, + }, + sensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{ + "/secret", + "/object/secretObjectField", + }, + }, + expectedSanitizedConfig: kong.Configuration{ + "secret": "{vault://redacted-value}", + "object": map[string]interface{}{ + "secretObjectField": "{vault://redacted-value}", + }, + }, + }, + { + name: "invalid JSONPath doesn't panic and redacts whole config as fallback", + config: kong.Configuration{ + "secret": "secret-value", + "object": map[string]interface{}{ + "secretObjectField": "secret-object-field-value", + }, + }, + sensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{ + "/not-existing-path", + }, + }, + expectedSanitizedConfig: kong.Configuration{ + "secret": "{vault://redacted-value}", + "object": "{vault://redacted-value}", + }, + }, + { + name: "whole config to sanitize", + config: kong.Configuration{ + "secret": "secret-value", + "object": map[string]interface{}{ + "secretObjectField": "secret-object-field-value", + }, + }, + sensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + WholeConfigIsSensitive: true, + }, + expectedSanitizedConfig: kong.Configuration{ + "secret": "{vault://redacted-value}", + "object": "{vault://redacted-value}", + }, + }, + { + name: "single empty JSON path - whole config is redacted", + config: kong.Configuration{ + "secret": "secret-value", + "object": map[string]interface{}{ + "secretObjectField": "secret-object-field-value", + }, + }, + sensitiveFieldsMeta: PluginSensitiveFieldsMetadata{ + JSONPaths: []string{""}, + }, + expectedSanitizedConfig: kong.Configuration{ + "secret": "{vault://redacted-value}", + "object": "{vault://redacted-value}", + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := Plugin{ + Plugin: kong.Plugin{ + Config: tc.config, + }, + SensitiveFieldsMeta: tc.sensitiveFieldsMeta, + } + sanitized := p.SanitizedCopy() + assert.Equal(t, tc.expectedSanitizedConfig, sanitized.Config) }) } } diff --git a/internal/dataplane/kongstate/types.go b/internal/dataplane/kongstate/types.go index fcfa7afe5a..282d81081f 100644 --- a/internal/dataplane/kongstate/types.go +++ b/internal/dataplane/kongstate/types.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/kong/go-kong/kong" - "sigs.k8s.io/controller-runtime/pkg/client" ) type PortMode int @@ -66,16 +65,3 @@ func (c *Certificate) SanitizedCopy() *Certificate { }, } } - -// Plugin represents a plugin Object in Kong. -type Plugin struct { - kong.Plugin - K8sParent client.Object -} - -func (p Plugin) DeepCopy() Plugin { - return Plugin{ - Plugin: *p.Plugin.DeepCopy(), - K8sParent: p.K8sParent, - } -}