Skip to content

Commit 68a24fc

Browse files
committed
redact secret values
This commit adds a feature that shows what changes have been done to a secret without exposing the secret values. Its enabled by default and can disabled by adding the --show-secrets flag
1 parent a25763a commit 68a24fc

File tree

5 files changed

+106
-5
lines changed

5 files changed

+106
-5
lines changed

cmd/release.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type release struct {
1919
releases []string
2020
outputContext int
2121
includeTests bool
22+
showSecrets bool
2223
}
2324

2425
const releaseCmdLongUsage = `
@@ -71,6 +72,7 @@ func releaseCmd() *cobra.Command {
7172
}
7273

7374
releaseCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
75+
releaseCmd.Flags().BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
7476
releaseCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
7577
releaseCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
7678
releaseCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
@@ -112,6 +114,7 @@ func (d *release) differentiateHelm3() error {
112114
manifest.Parse(string(releaseResponse1), namespace, excludes...),
113115
manifest.Parse(string(releaseResponse2), namespace, excludes...),
114116
d.suppressedKinds,
117+
d.showSecrets,
115118
d.outputContext,
116119
os.Stdout)
117120

@@ -144,6 +147,7 @@ func (d *release) differentiate() error {
144147
manifest.ParseRelease(releaseResponse1.Release, d.includeTests),
145148
manifest.ParseRelease(releaseResponse2.Release, d.includeTests),
146149
d.suppressedKinds,
150+
d.showSecrets,
147151
d.outputContext,
148152
os.Stdout)
149153

cmd/revision.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type revision struct {
2121
revisions []string
2222
outputContext int
2323
includeTests bool
24+
showSecrets bool
2425
}
2526

2627
const revisionCmdLongUsage = `
@@ -81,6 +82,7 @@ func revisionCmd() *cobra.Command {
8182
}
8283

8384
revisionCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
85+
revisionCmd.Flags().BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
8486
revisionCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
8587
revisionCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
8688
revisionCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
@@ -117,6 +119,7 @@ func (d *revision) differentiateHelm3() error {
117119
manifest.Parse(string(revisionResponse), namespace, excludes...),
118120
manifest.Parse(string(releaseResponse), namespace, excludes...),
119121
d.suppressedKinds,
122+
d.showSecrets,
120123
d.outputContext,
121124
os.Stdout)
122125

@@ -141,6 +144,7 @@ func (d *revision) differentiateHelm3() error {
141144
manifest.Parse(string(revisionResponse1), namespace, excludes...),
142145
manifest.Parse(string(revisionResponse2), namespace, excludes...),
143146
d.suppressedKinds,
147+
d.showSecrets,
144148
d.outputContext,
145149
os.Stdout)
146150

@@ -178,6 +182,7 @@ func (d *revision) differentiate() error {
178182
manifest.ParseRelease(revisionResponse.Release, d.includeTests),
179183
manifest.ParseRelease(releaseResponse.Release, d.includeTests),
180184
d.suppressedKinds,
185+
d.showSecrets,
181186
d.outputContext,
182187
os.Stdout)
183188

@@ -202,6 +207,7 @@ func (d *revision) differentiate() error {
202207
manifest.ParseRelease(revisionResponse1.Release, d.includeTests),
203208
manifest.ParseRelease(revisionResponse2.Release, d.includeTests),
204209
d.suppressedKinds,
210+
d.showSecrets,
205211
d.outputContext,
206212
os.Stdout)
207213

cmd/rollback.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type rollback struct {
2121
revisions []string
2222
outputContext int
2323
includeTests bool
24+
showSecrets bool
2425
}
2526

2627
const rollbackCmdLongUsage = `
@@ -73,6 +74,7 @@ func rollbackCmd() *cobra.Command {
7374
}
7475

7576
rollbackCmd.Flags().BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
77+
rollbackCmd.Flags().BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
7678
rollbackCmd.Flags().StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
7779
rollbackCmd.Flags().IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
7880
rollbackCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
@@ -110,6 +112,7 @@ func (d *rollback) backcastHelm3() error {
110112
manifest.Parse(string(releaseResponse), namespace, excludes...),
111113
manifest.Parse(string(revisionResponse), namespace, excludes...),
112114
d.suppressedKinds,
115+
d.showSecrets,
113116
d.outputContext,
114117
os.Stdout)
115118

@@ -144,6 +147,7 @@ func (d *rollback) backcast() error {
144147
manifest.ParseRelease(releaseResponse.Release, d.includeTests),
145148
manifest.ParseRelease(revisionResponse.Release, d.includeTests),
146149
d.suppressedKinds,
150+
d.showSecrets,
147151
d.outputContext,
148152
os.Stdout)
149153

cmd/upgrade.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type diffCmd struct {
3232
includeTests bool
3333
suppressedKinds []string
3434
outputContext int
35+
showSecrets bool
3536
}
3637

3738
const globalUsage = `Show a diff explaining what a helm upgrade would change.
@@ -82,6 +83,7 @@ func newChartCommand() *cobra.Command {
8283
f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
8384
f.BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
8485
f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
86+
f.BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
8587
f.VarP(&diff.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
8688
f.StringArrayVar(&diff.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
8789
f.StringArrayVar(&diff.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
@@ -155,7 +157,7 @@ func (d *diffCmd) runHelm3() error {
155157
newSpecs = manifest.Parse(string(installManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
156158
}
157159

158-
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.outputContext, os.Stdout)
160+
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)
159161

160162
if d.detailedExitCode && seenAnyChanges {
161163
return Error{
@@ -241,7 +243,7 @@ func (d *diffCmd) run() error {
241243
}
242244
}
243245

244-
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.outputContext, os.Stdout)
246+
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)
245247

246248
if d.detailedExitCode && seenAnyChanges {
247249
return Error{

diff/diff.go

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package diff
22

33
import (
4+
"bytes"
45
"fmt"
56
"io"
67
"math"
@@ -9,19 +10,27 @@ import (
910

1011
"github.com/aryann/difflib"
1112
"github.com/mgutz/ansi"
13+
v1 "k8s.io/api/core/v1"
14+
"k8s.io/apimachinery/pkg/runtime/serializer/json"
15+
"k8s.io/apimachinery/pkg/util/yaml"
16+
"k8s.io/client-go/kubernetes/scheme"
1217

1318
"github.com/databus23/helm-diff/manifest"
1419
)
1520

1621
// Manifests diff on manifests
17-
func Manifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) bool {
22+
func Manifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, showSecrets bool, context int, to io.Writer) bool {
1823
seenAnyChanges := false
1924
emptyMapping := &manifest.MappingResult{}
2025
for key, oldContent := range oldIndex {
2126
if newContent, ok := newIndex[key]; ok {
2227
if oldContent.Content != newContent.Content {
2328
// modified
2429
fmt.Fprintf(to, ansi.Color("%s has changed:", "yellow")+"\n", key)
30+
if !showSecrets {
31+
redactSecrets(oldContent, newContent)
32+
}
33+
2534
diffs := diffMappingResults(oldContent, newContent)
2635
if len(diffs) > 0 {
2736
seenAnyChanges = true
@@ -31,6 +40,10 @@ func Manifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressed
3140
} else {
3241
// removed
3342
fmt.Fprintf(to, ansi.Color("%s has been removed:", "yellow")+"\n", key)
43+
if !showSecrets {
44+
redactSecrets(oldContent, nil)
45+
46+
}
3447
diffs := diffMappingResults(oldContent, emptyMapping)
3548
if len(diffs) > 0 {
3649
seenAnyChanges = true
@@ -43,6 +56,9 @@ func Manifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressed
4356
if _, ok := oldIndex[key]; !ok {
4457
// added
4558
fmt.Fprintf(to, ansi.Color("%s has been added:", "yellow")+"\n", key)
59+
if !showSecrets {
60+
redactSecrets(nil, newContent)
61+
}
4662
diffs := diffMappingResults(emptyMapping, newContent)
4763
if len(diffs) > 0 {
4864
seenAnyChanges = true
@@ -53,11 +69,79 @@ func Manifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressed
5369
return seenAnyChanges
5470
}
5571

72+
func redactSecrets(old, new *manifest.MappingResult) {
73+
if old != nil && old.Kind != "Secret" && new != nil && new.Kind != "Secret" {
74+
return
75+
}
76+
serializer := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme,
77+
scheme.Scheme)
78+
var oldSecret, newSecret v1.Secret
79+
80+
if old != nil {
81+
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&oldSecret); err != nil {
82+
old.Content = fmt.Sprintf("Error parsing old secret: %s", err)
83+
}
84+
}
85+
if new != nil {
86+
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&newSecret); err != nil {
87+
new.Content = fmt.Sprintf("Error parsing new secret: %s", err)
88+
}
89+
}
90+
if old != nil {
91+
oldSecret.StringData = make(map[string]string, len(oldSecret.Data))
92+
for k, v := range oldSecret.Data {
93+
if new != nil && bytes.Equal(v, newSecret.Data[k]) {
94+
oldSecret.StringData[k] = fmt.Sprintf("REDACTED # (%d bytes)", len(v))
95+
} else {
96+
oldSecret.StringData[k] = fmt.Sprintf("-------- # (%d bytes)", len(v))
97+
}
98+
}
99+
}
100+
if new != nil {
101+
newSecret.StringData = make(map[string]string, len(newSecret.Data))
102+
for k, v := range newSecret.Data {
103+
if old != nil && bytes.Equal(v, oldSecret.Data[k]) {
104+
newSecret.StringData[k] = fmt.Sprintf("REDACTED # (%d bytes)", len(v))
105+
} else {
106+
newSecret.StringData[k] = fmt.Sprintf("++++++++ # (%d bytes)", len(v))
107+
}
108+
}
109+
}
110+
// remove Data field now that we are using StringData for serialization
111+
var buf bytes.Buffer
112+
if old != nil {
113+
oldSecret.Data = nil
114+
if err := serializer.Encode(&oldSecret, &buf); err != nil {
115+
116+
}
117+
old.Content = getComment(old.Content) + strings.Replace(strings.Replace(buf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1)
118+
buf.Reset() //reuse buffer for new secret
119+
}
120+
if new != nil {
121+
newSecret.Data = nil
122+
if err := serializer.Encode(&newSecret, &buf); err != nil {
123+
124+
}
125+
new.Content = getComment(new.Content) + strings.Replace(strings.Replace(buf.String(), "stringData", "data", 1), " creationTimestamp: null\n", "", 1)
126+
}
127+
}
128+
129+
// return the first line of a string if its a comment.
130+
// This gives as the # Source: lines from the rendering
131+
func getComment(s string) string {
132+
i := strings.Index(s, "\n")
133+
if i < 0 || !strings.HasPrefix(s, "#") {
134+
return ""
135+
}
136+
return s[:i+1]
137+
138+
}
139+
56140
// Releases reindex the content based on the template names and pass it to Manifests
57-
func Releases(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) bool {
141+
func Releases(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, showSecrets bool, context int, to io.Writer) bool {
58142
oldIndex = reIndexForRelease(oldIndex)
59143
newIndex = reIndexForRelease(newIndex)
60-
return Manifests(oldIndex, newIndex, suppressedKinds, context, to)
144+
return Manifests(oldIndex, newIndex, suppressedKinds, showSecrets, context, to)
61145
}
62146

63147
func diffMappingResults(oldContent *manifest.MappingResult, newContent *manifest.MappingResult) []difflib.DiffRecord {
@@ -71,6 +155,7 @@ func diffStrings(before, after string) []difflib.DiffRecord {
71155

72156
func printDiffRecords(suppressedKinds []string, kind string, context int, diffs []difflib.DiffRecord, to io.Writer) {
73157
for _, ckind := range suppressedKinds {
158+
74159
if ckind == kind {
75160
str := fmt.Sprintf("+ Changes suppressed on sensitive content of type %s\n", kind)
76161
fmt.Fprintf(to, ansi.Color(str, "yellow"))

0 commit comments

Comments
 (0)