Skip to content

Diff secrets #5197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
34 changes: 14 additions & 20 deletions cmd/flux/build_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,30 +111,24 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
}
}

var options []build.BuilderOptionFunc
var builder *build.Builder

options = append(options,
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
build.WithIgnore(buildKsArgs.ignorePaths),
build.WithStrictSubstitute(buildKsArgs.strictSubst),
build.WithRecursive(buildKsArgs.recursive),
build.WithLocalSources(buildKsArgs.localSources),
)

if buildKsArgs.dryRun {
builder, err = build.NewBuilder(name, buildKsArgs.path,
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
build.WithDryRun(buildKsArgs.dryRun),
build.WithNamespace(*kubeconfigArgs.Namespace),
build.WithIgnore(buildKsArgs.ignorePaths),
build.WithStrictSubstitute(buildKsArgs.strictSubst),
build.WithRecursive(buildKsArgs.recursive),
build.WithLocalSources(buildKsArgs.localSources),
)
} else {
builder, err = build.NewBuilder(name, buildKsArgs.path,
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
build.WithIgnore(buildKsArgs.ignorePaths),
build.WithStrictSubstitute(buildKsArgs.strictSubst),
build.WithRecursive(buildKsArgs.recursive),
build.WithLocalSources(buildKsArgs.localSources),
)
options = append(options, build.WithDryRun(buildKsArgs.dryRun))
}

builder, err = build.NewBuilder(name, buildKsArgs.path, options...)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/flux/build_kustomization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func setup(t *testing.T, tmpl map[string]string) {
t.Helper()
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-kustomization.yaml", tmpl, t)
testEnv.CreateObjectFile("./testdata/build-kustomization/sops-age.yaml", tmpl, t)
}

func TestBuildKustomization(t *testing.T) {
Expand Down
40 changes: 18 additions & 22 deletions cmd/flux/diff_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type diffKsFlags struct {
progressBar bool
strictSubst bool
recursive bool
decryptSecrets bool
localSources map[string]string
}

Expand All @@ -74,6 +75,7 @@ func init() {
diffKsCmd.Flags().BoolVar(&diffKsArgs.strictSubst, "strict-substitute", false,
"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.")
diffKsCmd.Flags().BoolVarP(&diffKsArgs.recursive, "recursive", "r", false, "Recursively diff Kustomizations")
diffKsCmd.Flags().BoolVar(&diffKsArgs.decryptSecrets, "decrypt-secrets", false, "Decrypt SOPS-encrypted secrets for comparison")
diffKsCmd.Flags().StringToStringVar(&diffKsArgs.localSources, "local-sources", nil, "Comma-separated list of repositories in format: Kind/namespace/name=path")
diffCmd.AddCommand(diffKsCmd)
}
Expand All @@ -99,34 +101,28 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
}

var (
options []build.BuilderOptionFunc
builder *build.Builder
err error
)

options = append(options,
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
build.WithIgnore(diffKsArgs.ignorePaths),
build.WithStrictSubstitute(diffKsArgs.strictSubst),
build.WithRecursive(diffKsArgs.recursive),
build.WithDecryptSecrets(diffKsArgs.decryptSecrets),
build.WithLocalSources(diffKsArgs.localSources),
build.WithSingleKustomization(),
)

if diffKsArgs.progressBar {
builder, err = build.NewBuilder(name, diffKsArgs.path,
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
build.WithProgressBar(),
build.WithIgnore(diffKsArgs.ignorePaths),
build.WithStrictSubstitute(diffKsArgs.strictSubst),
build.WithRecursive(diffKsArgs.recursive),
build.WithLocalSources(diffKsArgs.localSources),
build.WithSingleKustomization(),
)
} else {
builder, err = build.NewBuilder(name, diffKsArgs.path,
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
build.WithTimeout(rootArgs.timeout),
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
build.WithIgnore(diffKsArgs.ignorePaths),
build.WithStrictSubstitute(diffKsArgs.strictSubst),
build.WithRecursive(diffKsArgs.recursive),
build.WithLocalSources(diffKsArgs.localSources),
build.WithSingleKustomization(),
)
options = append(options, build.WithProgressBar())
}

builder, err = build.NewBuilder(name, diffKsArgs.path, options...)
if err != nil {
return &RequestError{StatusCode: 2, Err: err}
}
Expand Down
6 changes: 6 additions & 0 deletions cmd/flux/diff_kustomization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ 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 drifted value in decrypted sops secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/decrypt-secret --progress-bar=false --decrypt-secrets",
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-decryption.golden"),
},
{
name: "diff with a sops dockerconfigjson secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
Expand Down
4 changes: 3 additions & 1 deletion cmd/flux/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,9 @@ func resetCmdArgs() {
checkArgs = checkFlags{}
createArgs = createFlags{}
deleteArgs = deleteFlags{}
diffKsArgs = diffKsFlags{}
diffKsArgs = diffKsFlags{
localSources: map[string]string{},
}
exportArgs = exportFlags{}
getArgs = GetFlags{}
gitArgs = gitFlags{}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: v1
kind: Secret
metadata:
name: podinfo-token-77t89m9b67
stringData:
token: ENC[AES256_GCM,data:ut7THDa7SJMTIn26orb2,iv:jBqKk4f8jzOZLpoH7pMnryHRAwwvjaycKwBryEBO3oQ=,tag:193UGSrkhQJzs4pDg5u2mQ==,type:str]
type: Opaque
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1yqval9atdcnzjwhmutcjwdukfe5pk9tsa3lqya90u08grp03zgyss93knx
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxeGdXSVAyMGUzSFpwRGNF
bUV5bU9scVJsRUVwbERFYWVjSVpJNFlYREJRCm5xaGxzZGRic0ZwY3hJSmJOcWk3
UmYzUDNIU29zd3orYlFlemNGUDhWZVEKLS0tIFJCcEsrdmlZcHBFWFE3SUlCaUNZ
ZkJuMm83a0VNODdXMkxUeDRmemJ2blkKebY+krevnla3Rxhrm3T4mmao8NUishwl
W4sV4fM2m5gjpiz72MVPjUrakqo9lA9ZLUkSue95YzFe09o7uqglRQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-02-16T20:34:53Z"
mac: ENC[AES256_GCM,data:C6Sv7iAoMztDGMDTYEW5KFUGSdey6O9zLzNdaEHxQL1oTrQB/hSBOjA4jaEBHovdCL/58w9jDTq8G30IGqUEFEu4HM0YUrSDA1gdTrbtvOfza0hQC8CtmCBWgol3tsWBwcLAeFOlE95perdvKkJx10t/r8yb8biCpLtJcxa/WZE=,iv:2CswtWAATMPZ7BHzWSUNhvT9xfmSSGPdOMvG1jHi3Nk=,tag:FZeCX+EuAlsOGaZbErpfJQ==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.9.1
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ spec:
kind: GitRepository
name: podinfo
targetNamespace: default
decryption:
provider: sops
secretRef:
name: sops-age
postBuild:
substitute:
cluster_env: "prod"
Expand Down
10 changes: 10 additions & 0 deletions cmd/flux/testdata/build-kustomization/sops-age.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
name: sops-age
namespace: {{ .fluxns }}
type: Opaque
stringData:
identity.agekey: |
# public key: age1yqval9atdcnzjwhmutcjwdukfe5pk9tsa3lqya90u08grp03zgyss93knx
AGE-SECRET-KEY-1JYSQLQ4QM6GZHDF4F5JLA3HZD2DFJFCMAA2ASCN2USTC02KC4V6SSZNLA7
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
► Secret/default/podinfo-token-77t89m9b67 drifted

data.token
± value change
- *** (before)
+ *** (after)

57 changes: 48 additions & 9 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/yaml"

kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/fluxcd/kustomize-controller/decryptor"
"github.com/fluxcd/pkg/kustomize"
runclient "github.com/fluxcd/pkg/runtime/client"
ssautil "github.com/fluxcd/pkg/ssa/utils"
Expand Down Expand Up @@ -77,15 +78,16 @@ type Builder struct {
kustomizationFile string
ignore []string
// mu is used to synchronize access to the kustomization file
mu sync.Mutex
action kustomize.Action
kustomization *kustomizev1.Kustomization
timeout time.Duration
spinner *yacspin.Spinner
dryRun bool
strictSubst bool
recursive bool
localSources map[string]string
mu sync.Mutex
action kustomize.Action
kustomization *kustomizev1.Kustomization
timeout time.Duration
spinner *yacspin.Spinner
dryRun bool
strictSubst bool
recursive bool
decryptSecrets bool
localSources map[string]string
// diff needs to handle kustomizations one by one
singleKustomization bool
}
Expand Down Expand Up @@ -190,6 +192,14 @@ func WithRecursive(recursive bool) BuilderOptionFunc {
}
}

// WithDecryptSecrets sets the decrypt secrets field
func WithDecryptSecrets(decryptSecrets bool) BuilderOptionFunc {
return func(b *Builder) error {
b.decryptSecrets = decryptSecrets
return nil
}
}

// WithLocalSources sets the local sources field
func WithLocalSources(localSources map[string]string) BuilderOptionFunc {
return func(b *Builder) error {
Expand Down Expand Up @@ -514,7 +524,36 @@ func (b *Builder) do(ctx context.Context, kustomization kustomizev1.Kustomizatio
return nil, fmt.Errorf("kustomize build failed: %w", err)
}

var dec *decryptor.Decryptor
var cleanup func()
if b.decryptSecrets {
dec, cleanup, err = decryptor.NewTempDecryptor(b.resourcesPath, b.client, b.kustomization)
if err != nil {
return nil, err
}
defer cleanup()

// Import decryption keys
if err := dec.ImportKeys(ctx); err != nil {
return nil, err
}
}

for _, res := range m.Resources() {
if res.GetKind() == "Secret" && b.decryptSecrets {
outRes, err := dec.DecryptResource(res)
if err != nil {
return nil, fmt.Errorf("decryption failed for '%s': %w", res.GetName(), err)
}

if outRes != nil {
_, err = m.Replace(res)
if err != nil {
return nil, err
}
}
}

// run variable substitutions
if kustomization.Spec.PostBuild != nil {
data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&kustomization)
Expand Down
1 change: 1 addition & 0 deletions internal/build/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func (b *Builder) kustomizationDiff(kustomization *kustomizev1.Kustomization) (s
WithIgnore(b.ignore),
WithStrictSubstitute(b.strictSubst),
WithRecursive(b.recursive),
WithDecryptSecrets(b.decryptSecrets),
WithLocalSources(b.localSources),
WithSingleKustomization(),
)
Expand Down
Loading