diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 7cd67dcfb78685f5d10a4eb758f51260ced21886..65eb41c4f5a7f7e0d804639732c910dec9d88dc9 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -167,6 +167,7 @@ func adminConfigYaml() ([]byte, error) { } yamlUtils.DefaultConfigMask().Yaml(document) + yamlUtils.TraverseNode(document) doc, err := yaml.Marshal(document) if err != nil { diff --git a/engine/pkg/util/yaml/custom.go b/engine/pkg/util/yaml/custom.go new file mode 100644 index 0000000000000000000000000000000000000000..703c003de68739ff73fb124151494ca132c9bb3b --- /dev/null +++ b/engine/pkg/util/yaml/custom.go @@ -0,0 +1,46 @@ +// Package yaml contains utilities to work with YAML nodes +package yaml + +import ( + "strings" + + "gopkg.in/yaml.v3" +) + +var secretKeyList = []string{"secret", "key", "token", "password"} + +// TraverseNode traverses node and mask sensitive keys. +func TraverseNode(node *yaml.Node) { + switch node.Kind { + case yaml.DocumentNode: + if len(node.Content) < 1 { + return + } + + TraverseNode(node.Content[0]) + + case yaml.MappingNode: + for i := 0; i < len(node.Content); i += 2 { + if node.Content[i+1].Kind == yaml.ScalarNode { + if containsSecret(strings.ToLower(node.Content[i].Value)) { + node.Content[i+1].Value = maskValue + node.Content[i+1].Tag = "!!str" + } + + continue + } + + TraverseNode(node.Content[i+1]) + } + } +} + +func containsSecret(key string) bool { + for _, secret := range secretKeyList { + if strings.Contains(key, secret) { + return true + } + } + + return false +} diff --git a/engine/pkg/util/yaml/custom_test.go b/engine/pkg/util/yaml/custom_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d4b6481e5db81f2688d58b7b576c003f458bd89a --- /dev/null +++ b/engine/pkg/util/yaml/custom_test.go @@ -0,0 +1,64 @@ +package yaml + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +const customYamlStr = ` +global: + debug: false +retrieval: + spec: + logicalDump: + options: + source: + type: local # local, remote, rds, etc.. + connection: + dbname: test_22 + host: 172.17.0.78 + port: 5455 + username: tony + password: mypass + databases: test1 + envs: + AWS_SECRET_ACCESS_KEY: john + PGBACKREST_REPO1_S3_KEY_SECRET: mysecretkey + TEST_ENV: one +` + +func TestTraverseNode(t *testing.T) { + r := require.New(t) + node := &yaml.Node{} + + err := yaml.Unmarshal([]byte(customYamlStr), node) + r.NoError(err) + TraverseNode(node) + + sensitive, found := FindNodeAtPathString(node, "retrieval.spec.logicalDump.options.envs.AWS_SECRET_ACCESS_KEY") + r.NotNil(sensitive) + r.True(found) + r.Equal(maskValue, sensitive.Value) + + sensitive2, found := FindNodeAtPathString(node, "retrieval.spec.logicalDump.options.envs.PGBACKREST_REPO1_S3_KEY_SECRET") + r.NotNil(sensitive2) + r.True(found) + r.Equal(maskValue, sensitive2.Value) + + nonSensitive, found := FindNodeAtPathString(node, "retrieval.spec.logicalDump.options.envs.TEST_ENV") + r.NotNil(nonSensitive) + r.True(found) + r.Equal("one", nonSensitive.Value) + + password, found := FindNodeAtPathString(node, "retrieval.spec.logicalDump.options.source.connection.password") + r.NotNil(password) + r.True(found) + r.Equal(maskValue, password.Value) + + host, found := FindNodeAtPathString(node, "retrieval.spec.logicalDump.options.source.connection.host") + r.NotNil(host) + r.True(found) + r.Equal("172.17.0.78", host.Value) +}