Skip to content

Commit 8dce48a

Browse files
committed
Decrypt SOPS secrets for diff
Signed-off-by: Boris Kreitchman <[email protected]>
1 parent 3226043 commit 8dce48a

File tree

9 files changed

+107
-9
lines changed

9 files changed

+107
-9
lines changed

cmd/flux/build_kustomization_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func setup(t *testing.T, tmpl map[string]string) {
3131
t.Helper()
3232
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
3333
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-kustomization.yaml", tmpl, t)
34+
testEnv.CreateObjectFile("./testdata/build-kustomization/sops-age.yaml", tmpl, t)
3435
}
3536

3637
func TestBuildKustomization(t *testing.T) {

cmd/flux/diff_kustomization.go

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ type diffKsFlags struct {
6161
progressBar bool
6262
strictSubst bool
6363
recursive bool
64+
decryptSecrets bool
6465
localSources map[string]string
6566
}
6667

@@ -74,6 +75,7 @@ func init() {
7475
diffKsCmd.Flags().BoolVar(&diffKsArgs.strictSubst, "strict-substitute", false,
7576
"When enabled, the post build substitutions will fail if a var without a default value is declared in files but is missing from the input vars.")
7677
diffKsCmd.Flags().BoolVarP(&diffKsArgs.recursive, "recursive", "r", false, "Recursively diff Kustomizations")
78+
diffKsCmd.Flags().BoolVar(&diffKsArgs.decryptSecrets, "decrypt-secrets", false, "Decrypt SOPS-encrypted secrets for comparison")
7779
diffKsCmd.Flags().StringToStringVar(&diffKsArgs.localSources, "local-sources", nil, "Comma-separated list of repositories in format: Kind/namespace/name=path")
7880
diffCmd.AddCommand(diffKsCmd)
7981
}
@@ -111,6 +113,7 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
111113
build.WithIgnore(diffKsArgs.ignorePaths),
112114
build.WithStrictSubstitute(diffKsArgs.strictSubst),
113115
build.WithRecursive(diffKsArgs.recursive),
116+
build.WithDecryptSecrets(diffKsArgs.decryptSecrets),
114117
build.WithLocalSources(diffKsArgs.localSources),
115118
build.WithSingleKustomization(),
116119
)

cmd/flux/diff_kustomization_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ func TestDiffKustomization(t *testing.T) {
7979
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
8080
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden"),
8181
},
82+
{
83+
name: "diff with a drifted value in decrypted sops secret object",
84+
args: "diff kustomization podinfo --path ./testdata/build-kustomization/decrypt-secret --progress-bar=false --decrypt-secrets",
85+
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
86+
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-decryption.golden"),
87+
},
8288
{
8389
name: "diff with a sops dockerconfigjson secret object",
8490
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: podinfo-token-77t89m9b67
5+
stringData:
6+
token: ENC[AES256_GCM,data:ut7THDa7SJMTIn26orb2,iv:jBqKk4f8jzOZLpoH7pMnryHRAwwvjaycKwBryEBO3oQ=,tag:193UGSrkhQJzs4pDg5u2mQ==,type:str]
7+
type: Opaque
8+
sops:
9+
kms: []
10+
gcp_kms: []
11+
azure_kv: []
12+
hc_vault: []
13+
age:
14+
- recipient: age1yqval9atdcnzjwhmutcjwdukfe5pk9tsa3lqya90u08grp03zgyss93knx
15+
enc: |
16+
-----BEGIN AGE ENCRYPTED FILE-----
17+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxeGdXSVAyMGUzSFpwRGNF
18+
bUV5bU9scVJsRUVwbERFYWVjSVpJNFlYREJRCm5xaGxzZGRic0ZwY3hJSmJOcWk3
19+
UmYzUDNIU29zd3orYlFlemNGUDhWZVEKLS0tIFJCcEsrdmlZcHBFWFE3SUlCaUNZ
20+
ZkJuMm83a0VNODdXMkxUeDRmemJ2blkKebY+krevnla3Rxhrm3T4mmao8NUishwl
21+
W4sV4fM2m5gjpiz72MVPjUrakqo9lA9ZLUkSue95YzFe09o7uqglRQ==
22+
-----END AGE ENCRYPTED FILE-----
23+
lastmodified: "2025-02-16T20:34:53Z"
24+
mac: ENC[AES256_GCM,data:C6Sv7iAoMztDGMDTYEW5KFUGSdey6O9zLzNdaEHxQL1oTrQB/hSBOjA4jaEBHovdCL/58w9jDTq8G30IGqUEFEu4HM0YUrSDA1gdTrbtvOfza0hQC8CtmCBWgol3tsWBwcLAeFOlE95perdvKkJx10t/r8yb8biCpLtJcxa/WZE=,iv:2CswtWAATMPZ7BHzWSUNhvT9xfmSSGPdOMvG1jHi3Nk=,tag:FZeCX+EuAlsOGaZbErpfJQ==,type:str]
25+
pgp: []
26+
encrypted_regex: ^(data|stringData)$
27+
version: 3.9.1

cmd/flux/testdata/build-kustomization/podinfo-kustomization.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ spec:
1313
kind: GitRepository
1414
name: podinfo
1515
targetNamespace: default
16+
decryption:
17+
provider: sops
18+
secretRef:
19+
name: sops-age
1620
postBuild:
1721
substitute:
1822
cluster_env: "prod"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: sops-age
5+
namespace: {{ .fluxns }}
6+
type: Opaque
7+
stringData:
8+
identity.agekey: |
9+
# public key: age1yqval9atdcnzjwhmutcjwdukfe5pk9tsa3lqya90u08grp03zgyss93knx
10+
AGE-SECRET-KEY-1JYSQLQ4QM6GZHDF4F5JLA3HZD2DFJFCMAA2ASCN2USTC02KC4V6SSZNLA7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
► Secret/default/podinfo-token-77t89m9b67 drifted
2+
3+
data.token
4+
± value change
5+
- *** (before)
6+
+ *** (after)
7+

internal/build/build.go

+48-9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"sigs.k8s.io/kustomize/kyaml/yaml"
4545

4646
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
47+
"github.com/fluxcd/kustomize-controller/decryptor"
4748
"github.com/fluxcd/pkg/kustomize"
4849
runclient "github.com/fluxcd/pkg/runtime/client"
4950
ssautil "github.com/fluxcd/pkg/ssa/utils"
@@ -77,15 +78,16 @@ type Builder struct {
7778
kustomizationFile string
7879
ignore []string
7980
// mu is used to synchronize access to the kustomization file
80-
mu sync.Mutex
81-
action kustomize.Action
82-
kustomization *kustomizev1.Kustomization
83-
timeout time.Duration
84-
spinner *yacspin.Spinner
85-
dryRun bool
86-
strictSubst bool
87-
recursive bool
88-
localSources map[string]string
81+
mu sync.Mutex
82+
action kustomize.Action
83+
kustomization *kustomizev1.Kustomization
84+
timeout time.Duration
85+
spinner *yacspin.Spinner
86+
dryRun bool
87+
strictSubst bool
88+
recursive bool
89+
decryptSecrets bool
90+
localSources map[string]string
8991
// diff needs to handle kustomizations one by one
9092
singleKustomization bool
9193
}
@@ -190,6 +192,14 @@ func WithRecursive(recursive bool) BuilderOptionFunc {
190192
}
191193
}
192194

195+
// WithDecryptSecrets sets the decrypt secrets field
196+
func WithDecryptSecrets(decryptSecrets bool) BuilderOptionFunc {
197+
return func(b *Builder) error {
198+
b.decryptSecrets = decryptSecrets
199+
return nil
200+
}
201+
}
202+
193203
// WithLocalSources sets the local sources field
194204
func WithLocalSources(localSources map[string]string) BuilderOptionFunc {
195205
return func(b *Builder) error {
@@ -514,7 +524,36 @@ func (b *Builder) do(ctx context.Context, kustomization kustomizev1.Kustomizatio
514524
return nil, fmt.Errorf("kustomize build failed: %w", err)
515525
}
516526

527+
var dec *decryptor.Decryptor
528+
var cleanup func()
529+
if b.decryptSecrets {
530+
dec, cleanup, err = decryptor.NewTempDecryptor(b.resourcesPath, b.client, b.kustomization)
531+
if err != nil {
532+
return nil, err
533+
}
534+
defer cleanup()
535+
536+
// Import decryption keys
537+
if err := dec.ImportKeys(ctx); err != nil {
538+
return nil, err
539+
}
540+
}
541+
517542
for _, res := range m.Resources() {
543+
if res.GetKind() == "Secret" && b.decryptSecrets {
544+
outRes, err := dec.DecryptResource(res)
545+
if err != nil {
546+
return nil, fmt.Errorf("decryption failed for '%s': %w", res.GetName(), err)
547+
}
548+
549+
if outRes != nil {
550+
_, err = m.Replace(res)
551+
if err != nil {
552+
return nil, err
553+
}
554+
}
555+
}
556+
518557
// run variable substitutions
519558
if kustomization.Spec.PostBuild != nil {
520559
data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&kustomization)

internal/build/diff.go

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ func (b *Builder) kustomizationDiff(kustomization *kustomizev1.Kustomization) (s
221221
WithIgnore(b.ignore),
222222
WithStrictSubstitute(b.strictSubst),
223223
WithRecursive(b.recursive),
224+
WithDecryptSecrets(b.decryptSecrets),
224225
WithLocalSources(b.localSources),
225226
WithSingleKustomization(),
226227
)

0 commit comments

Comments
 (0)