diff --git a/cmd/flux/diff_kustomization_test.go b/cmd/flux/diff_kustomization_test.go index e2f62e75f2..eec3d65076 100644 --- a/cmd/flux/diff_kustomization_test.go +++ b/cmd/flux/diff_kustomization_test.go @@ -79,6 +79,18 @@ func TestDiffKustomization(t *testing.T) { objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml", assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden"), }, + { + name: "diff with a sops dockerconfigjson secret object", + args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo", + objectFile: "./testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml", + assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden"), + }, + { + name: "diff with a sops stringdata secret object", + args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo", + objectFile: "./testdata/diff-kustomization/stringdata-sops-secret.yaml", + assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-stringdata-sops-secret.golden"), + }, } tmpl := map[string]string{ diff --git a/cmd/flux/main.go b/cmd/flux/main.go index 5fb1a163ea..cf803e5d59 100644 --- a/cmd/flux/main.go +++ b/cmd/flux/main.go @@ -153,12 +153,18 @@ func NewRootFlags() rootFlags { func main() { log.SetFlags(0) if err := rootCmd.Execute(); err != nil { - logger.Failuref("%v", err) if err, ok := err.(*RequestError); ok { + if err.StatusCode == 1 { + logger.Warningf("%v", err) + } else { + logger.Failuref("%v", err) + } + os.Exit(err.StatusCode) } + logger.Failuref("%v", err) os.Exit(1) } } diff --git a/cmd/flux/testdata/build-kustomization/podinfo-result.yaml b/cmd/flux/testdata/build-kustomization/podinfo-result.yaml index ce66ff0f58..1fe225fd66 100644 --- a/cmd/flux/testdata/build-kustomization/podinfo-result.yaml +++ b/cmd/flux/testdata/build-kustomization/podinfo-result.yaml @@ -123,6 +123,31 @@ spec: type: ClusterIP --- apiVersion: v1 +data: + .dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ== +kind: Secret +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: podinfo + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: docker-secret + namespace: default +type: kubernetes.io/dockerconfigjson +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: podinfo + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: secret-basic-auth-stringdata + namespace: default +stringData: + password: KipTT1BTKio= + username: KipTT1BTKio= +type: kubernetes.io/basic-auth +--- +apiVersion: v1 data: token: KipTT1BTKio= kind: Secret diff --git a/cmd/flux/testdata/build-kustomization/podinfo/dockerconfigjson-sops-secret.yaml b/cmd/flux/testdata/build-kustomization/podinfo/dockerconfigjson-sops-secret.yaml new file mode 100644 index 0000000000..c05731bc6e --- /dev/null +++ b/cmd/flux/testdata/build-kustomization/podinfo/dockerconfigjson-sops-secret.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +data: + .dockerconfigjson: ENC[AES256_GCM,data:KHCFH3hNnc+PMfWLFEPjebf3W4z4WXbGFAANRZyZC+07z7wlrTALJM6rn8YslW4tMAWCoAYxblC5WRCszTy0h9rw0U/RGOv5H0qCgnNg/FILFUqhwo9pNfrUH+MEP4M9qxxbLKZwObpHUE7DUsKx1JYAxsI=,iv:q48lqUbUQD+0cbYcjNMZMJLRdGHi78ZmDhNAT2th9tg=,tag:QRI2SZZXQrAcdql3R5AH2g==,type:str] +kind: Secret +metadata: + name: docker-secret +type: kubernetes.io/dockerconfigjson +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age10la2ge0wtvx3qr7datqf7rs4yngxszdal927fs9rukamr8u2pshsvtz7ce + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3eU1CTEJhVXZ4eEVYYkVV + OU90TEcrR2pYckttN0pBanJoSUZWSW1RQXlRCkUydFJ3V1NZUTBuVFF0aC9GUEcw + bUdhNjJWTkoyL1FUVi9Dc1dxUDBkM0UKLS0tIE1sQXkwcWdGaEFuY0RHQTVXM0J6 + dWpJcThEbW15V3dXYXpPZklBdW1Hd1kKoIAdmGNPrEctV8h1w8KuvQ5S+BGmgqN9 + MgpNmUhJjWhgcQpb5BRYpQesBOgU5TBGK7j58A6DMDKlSiYZsdQchQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2022-02-03T16:03:17Z" + mac: ENC[AES256_GCM,data:AHdYSawajwgAFwlmDN1IPNmT9vWaYKzyVIra2d6sPcjTbZ8/p+VRSRpVm4XZFFsaNnW5AUJaouwXnKYDTmJDXKlr/rQcu9kXqsssQgdzcXaA6l5uJlgsnml8ba7J3OK+iEKMax23mwQEx2EUskCd9ENOwFDkunP02sxqDNOz20k=,iv:8F5OamHt3fAVorf6p+SoIrWoqkcATSGWVoM0EK87S4M=,tag:E1mxXnc7wWkEX5BxhpLtng==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.7.1 diff --git a/cmd/flux/testdata/build-kustomization/podinfo/kustomization.yaml b/cmd/flux/testdata/build-kustomization/podinfo/kustomization.yaml index 0ba076685d..435c23c661 100644 --- a/cmd/flux/testdata/build-kustomization/podinfo/kustomization.yaml +++ b/cmd/flux/testdata/build-kustomization/podinfo/kustomization.yaml @@ -4,6 +4,8 @@ resources: - ./deployment.yaml - ./hpa.yaml - ./service.yaml +- ./dockerconfigjson-sops-secret.yaml +- ./stringdata-secret.yaml secretGenerator: - files: - token=token.encrypted diff --git a/cmd/flux/testdata/build-kustomization/podinfo/stringdata-secret.yaml b/cmd/flux/testdata/build-kustomization/podinfo/stringdata-secret.yaml new file mode 100644 index 0000000000..c9bb980dc1 --- /dev/null +++ b/cmd/flux/testdata/build-kustomization/podinfo/stringdata-secret.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-basic-auth-stringdata +type: kubernetes.io/basic-auth +stringData: + username: ENC[AES256_GCM,data:uKiQR48=,iv:jh2lgyAVu7igJAgoJsnOGhjxFyvUAa9lvT21u3hhqpU=,tag:zXM2JEpk3ZEH7WfkcWXXkw==,type:str] + password: ENC[AES256_GCM,data:PyhZmNhy929JGQ==,iv:PBqPaJmSw21+kn4gIlg5VdjLNZyf613z5RUTCesBoVw=,tag:Hjc7DsuUrtsz7PYPdNkL3g==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age10la2ge0wtvx3qr7datqf7rs4yngxszdal927fs9rukamr8u2pshsvtz7ce + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJd0xxbDZhYjVoZzY4YWhK + d2NvMVgrSGRVUGhHRGg3R1FpVURnbmh1TDBzCjcwby85M3JaK09QVk0yZFNMb2NL + c2NQZW5hS1FhYlBHU0VoUzBVYzZYUUUKLS0tIEdaNEw2Y0VjVHpZc3pyYUtLVmJk + NmN3K2VLU0NiZ1d0VHBYbGlCM1lrNmMKeWz3yfFbMNE+ly21oLfc1XnDSPRmnlPP + wIs8lk/qrzVZ45C9GdWnnPeGZZiia46Yop9TxseUS8gCjJ6KCxJCAg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2022-02-06T12:51:07Z" + mac: ENC[AES256_GCM,data:jtdzwj19uxdxvnmXg1HkAkDA6XlKMJOYFy7uLI5t/t11LwGop5Yeo7a4nQEEELehRx9J7B6U6NiySxAxBxWx5uW5vI5c8+069VV6dkiCIefnYSzuoIhQafjlFl1/KvH7VEjIWfHYuXF09v9PEKXkxEHUYDpS3QqQ3ymHRRI08pU=, iv:xX3E7F+AM29Pm8G5oqxRfYu9E7tEBGIaHeCJYgrtFmc=,tag:MJPGusNvu05z939jg8PAwQ==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.7.1 diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-deployment.golden b/cmd/flux/testdata/diff-kustomization/diff-with-deployment.golden index 098497fccd..7856dd584c 100644 --- a/cmd/flux/testdata/diff-kustomization/diff-with-deployment.golden +++ b/cmd/flux/testdata/diff-kustomization/diff-with-deployment.golden @@ -1,4 +1,6 @@ ► HorizontalPodAutoscaler/default/podinfo created ► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/podinfo-token-77t89m9b67 created ► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden b/cmd/flux/testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden new file mode 100644 index 0000000000..17972fffd9 --- /dev/null +++ b/cmd/flux/testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden @@ -0,0 +1,6 @@ +► Deployment/default/podinfo created +► HorizontalPodAutoscaler/default/podinfo created +► Service/default/podinfo created +► Secret/default/secret-basic-auth-stringdata created +► Secret/default/podinfo-token-77t89m9b67 created +► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden index 3826954479..5608c24e2d 100644 --- a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden +++ b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden @@ -1,6 +1,8 @@ ► Deployment/default/podinfo created ► HorizontalPodAutoscaler/default/podinfo created ► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/podinfo-token-77t89m9b67 drifted data diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-secret.golden b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-secret.golden index ac76978f18..41a65058fd 100644 --- a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-secret.golden +++ b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-secret.golden @@ -1,6 +1,8 @@ ► Deployment/default/podinfo created ► HorizontalPodAutoscaler/default/podinfo created ► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/podinfo-token-77t89m9b67 created ► Secret/default/db-user-pass-bkbd782d2c drifted diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-service.golden b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-service.golden index 6ddd393346..a9fe09f2e8 100644 --- a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-service.golden +++ b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-service.golden @@ -7,5 +7,7 @@ spec.ports.http.port - 9899 + 9898 +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/podinfo-token-77t89m9b67 created ► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden index 033db67e5d..6c5022262b 100644 --- a/cmd/flux/testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden +++ b/cmd/flux/testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden @@ -1,4 +1,6 @@ ► Deployment/default/podinfo created ► HorizontalPodAutoscaler/default/podinfo created ► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/diff-with-stringdata-sops-secret.golden b/cmd/flux/testdata/diff-kustomization/diff-with-stringdata-sops-secret.golden new file mode 100644 index 0000000000..cf7183c8f3 --- /dev/null +++ b/cmd/flux/testdata/diff-kustomization/diff-with-stringdata-sops-secret.golden @@ -0,0 +1,6 @@ +► Deployment/default/podinfo created +► HorizontalPodAutoscaler/default/podinfo created +► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/podinfo-token-77t89m9b67 created +► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml b/cmd/flux/testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml new file mode 100644 index 0000000000..1a689e9239 --- /dev/null +++ b/cmd/flux/testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +data: + .dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ== +kind: Secret +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: podinfo + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: docker-secret + namespace: default +type: kubernetes.io/dockerconfigjson diff --git a/cmd/flux/testdata/diff-kustomization/nothing-is-deployed.golden b/cmd/flux/testdata/diff-kustomization/nothing-is-deployed.golden index da1c23dae0..5c320a1b1f 100644 --- a/cmd/flux/testdata/diff-kustomization/nothing-is-deployed.golden +++ b/cmd/flux/testdata/diff-kustomization/nothing-is-deployed.golden @@ -1,5 +1,7 @@ ► Deployment/default/podinfo created ► HorizontalPodAutoscaler/default/podinfo created ► Service/default/podinfo created +► Secret/default/docker-secret created +► Secret/default/secret-basic-auth-stringdata created ► Secret/default/podinfo-token-77t89m9b67 created ► Secret/default/db-user-pass-bkbd782d2c created diff --git a/cmd/flux/testdata/diff-kustomization/stringdata-sops-secret.yaml b/cmd/flux/testdata/diff-kustomization/stringdata-sops-secret.yaml new file mode 100644 index 0000000000..c088dd7507 --- /dev/null +++ b/cmd/flux/testdata/diff-kustomization/stringdata-sops-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: podinfo + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: secret-basic-auth-stringdata + namespace: default +stringData: + password: KipTT1BTKio= + username: KipTT1BTKio= +type: kubernetes.io/basic-auth diff --git a/internal/build/build.go b/internal/build/build.go index 923852d5f5..6796bdbb08 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "encoding/base64" + "encoding/json" "fmt" "sync" "time" @@ -40,9 +41,13 @@ import ( ) const ( - controllerName = "kustomize-controller" - controllerGroup = "kustomize.toolkit.fluxcd.io" - mask = "**SOPS**" + controllerName = "kustomize-controller" + controllerGroup = "kustomize.toolkit.fluxcd.io" + mask = "**SOPS**" + dockercfgSecretType = "kubernetes.io/dockerconfigjson" + typeField = "type" + dataField = "data" + stringDataField = "stringData" ) var defaultTimeout = 80 * time.Second @@ -183,7 +188,7 @@ func (b *Builder) build() (m resmap.ResMap, err error) { } // make sure secrets are masked - err = trimSopsData(res) + err = maskSopsData(res) if err != nil { return } @@ -257,40 +262,131 @@ func (b *Builder) setOwnerLabels(res *resource.Resource) error { return nil } -func trimSopsData(res *resource.Resource) error { +func maskSopsData(res *resource.Resource) error { // sopsMess is the base64 encoded mask sopsMess := base64.StdEncoding.EncodeToString([]byte(mask)) if res.GetKind() == "Secret" { + // get both data and stringdata maps as a secret can have both dataMap := res.GetDataMap() + stringDataMap := getStringDataMap(res) asYaml, err := res.AsYAML() if err != nil { - return fmt.Errorf("failed to decode secret %s data: %w", res.GetName(), err) + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) } - //delete any sops data as we don't want to expose it + // delete any sops data as we don't want to expose it + // assume that both data and stringdata are encrypted if bytes.Contains(asYaml, []byte("sops:")) && bytes.Contains(asYaml, []byte("mac: ENC[")) { + // delete the sops object res.PipeE(yaml.FieldClearer{Name: "sops"}) - for k := range dataMap { - dataMap[k] = sopsMess + + secretType, err := res.GetFieldValue(typeField) + if err != nil { + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) } - } else { - for k, v := range dataMap { - data, err := base64.StdEncoding.DecodeString(v) + if v, ok := secretType.(string); ok && v == dockercfgSecretType { + // if the secret is a json docker config secret, we need to mask the data with a json object + err := maskDockerconfigjsonSopsData(dataMap) if err != nil { - if _, ok := err.(base64.CorruptInputError); ok { - return fmt.Errorf("failed to decode secret %s data: %w", res.GetName(), err) - } + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) } - if bytes.Contains(data, []byte("sops")) && bytes.Contains(data, []byte("ENC[")) { + err = maskDockerconfigjsonSopsData(stringDataMap) + if err != nil { + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) + } + + } else { + for k := range dataMap { dataMap[k] = sopsMess } + + for k := range stringDataMap { + stringDataMap[k] = sopsMess + } + } + } else { + err := maskBase64EncryptedSopsData(dataMap, sopsMess) + if err != nil { + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) + } + + err = maskSopsDataInStringDataSecret(stringDataMap, sopsMess) + if err != nil { + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) } } + // set the data and stringdata maps res.SetDataMap(dataMap) + + if len(stringDataMap) > 0 { + err = res.SetMapField(yaml.NewMapRNode(&stringDataMap), stringDataField) + if err != nil { + return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err) + } + } + } + + return nil +} + +func getStringDataMap(rn *resource.Resource) map[string]string { + n, err := rn.Pipe(yaml.Lookup(stringDataField)) + if err != nil { + return nil + } + result := map[string]string{} + _ = n.VisitFields(func(node *yaml.MapNode) error { + result[yaml.GetValue(node.Key)] = yaml.GetValue(node.Value) + return nil + }) + return result +} + +func maskDockerconfigjsonSopsData(dataMap map[string]string) error { + sopsMess := struct { + Mask string `json:"mask"` + }{ + Mask: mask, + } + + maskJson, err := json.Marshal(sopsMess) + if err != nil { + return err + } + + for k := range dataMap { + dataMap[k] = base64.StdEncoding.EncodeToString(maskJson) + } + + return nil +} + +func maskBase64EncryptedSopsData(dataMap map[string]string, mask string) error { + for k, v := range dataMap { + data, err := base64.StdEncoding.DecodeString(v) + if err != nil { + if _, ok := err.(base64.CorruptInputError); ok { + return err + } + } + + if bytes.Contains(data, []byte("sops")) && bytes.Contains(data, []byte("ENC[")) { + dataMap[k] = mask + } + } + + return nil +} + +func maskSopsDataInStringDataSecret(stringDataMap map[string]string, mask string) error { + for k, v := range stringDataMap { + if bytes.Contains([]byte(v), []byte("sops")) && bytes.Contains([]byte(v), []byte("ENC[")) { + stringDataMap[k] = mask + } } return nil diff --git a/internal/build/build_test.go b/internal/build/build_test.go index 17b2cf3962..5e1cfffd1c 100644 --- a/internal/build/build_test.go +++ b/internal/build/build_test.go @@ -97,7 +97,7 @@ type: kubernetes.io/basic-auth name: "secret sops secret", yamlStr: `apiVersion: v1 data: - .dockercfg: ENC[AES256_GCM,data:KHCFH3hNnc+PMfWLFEPjebf3W4z4WXbGFAANRZyZC+07z7wlrTALJM6rn8YslW4tMAWCoAYxblC5WRCszTy0h9rw0U/RGOv5H0qCgnNg/FILFUqhwo9pNfrUH+MEP4M9qxxbLKZwObpHUE7DUsKx1JYAxsI=,iv:q48lqUbUQD+0cbYcjNMZMJLRdGHi78ZmDhNAT2th9tg=,tag:QRI2SZZXQrAcdql3R5AH2g==,type:str] + .dockerconfigjson: ENC[AES256_GCM,data:KHCFH3hNnc+PMfWLFEPjebf3W4z4WXbGFAANRZyZC+07z7wlrTALJM6rn8YslW4tMAWCoAYxblC5WRCszTy0h9rw0U/RGOv5H0qCgnNg/FILFUqhwo9pNfrUH+MEP4M9qxxbLKZwObpHUE7DUsKx1JYAxsI=,iv:q48lqUbUQD+0cbYcjNMZMJLRdGHi78ZmDhNAT2th9tg=,tag:QRI2SZZXQrAcdql3R5AH2g==,type:str] kind: Secret metadata: name: secret @@ -125,7 +125,7 @@ sops: `, expected: `apiVersion: v1 data: - .dockercfg: KipTT1BTKio= + .dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ== kind: Secret metadata: name: secret @@ -142,7 +142,7 @@ type: kubernetes.io/dockerconfigjson } resource := &resource.Resource{RNode: *r} - err = trimSopsData(resource) + err = maskSopsData(resource) if err != nil { t.Fatalf("unable to trim sops data: %v", err) } diff --git a/internal/build/diff.go b/internal/build/diff.go index 4e67e10b08..9d9a7b3dea 100644 --- a/internal/build/diff.go +++ b/internal/build/diff.go @@ -199,17 +199,31 @@ func diff(liveFile, mergedFile string, output io.Writer) error { } func diffSopsSecret(obj, liveObject, mergedObject *unstructured.Unstructured, change *ssa.ChangeSetEntry) { - data := obj.Object["data"] - for _, v := range data.(map[string]interface{}) { + // get both data and stringdata maps + data := obj.Object[dataField] + stringData := obj.Object[stringDataField] + + if m, ok := data.(map[string]interface{}); ok && m != nil { + applySopsDiff(m, liveObject, mergedObject, change) + } + + if m, ok := stringData.(map[string]interface{}); ok && m != nil { + applySopsDiff(m, liveObject, mergedObject, change) + } +} + +func applySopsDiff(data map[string]interface{}, liveObject, mergedObject *unstructured.Unstructured, change *ssa.ChangeSetEntry) { + for _, v := range data { v, err := base64.StdEncoding.DecodeString(v.(string)) if err != nil { fmt.Println(err) } + if bytes.Contains(v, []byte(mask)) { if liveObject != nil && mergedObject != nil { change.Action = string(ssa.UnchangedAction) - dataLive := liveObject.Object["data"].(map[string]interface{}) - dataMerged := mergedObject.Object["data"].(map[string]interface{}) + dataLive := liveObject.Object[dataField].(map[string]interface{}) + dataMerged := mergedObject.Object[dataField].(map[string]interface{}) if cmp.Diff(keys(dataLive), keys(dataMerged)) != "" { change.Action = string(ssa.ConfiguredAction) }