github.com/splunk/dan1-qbec@v0.7.3/internal/types/secrets.go (about) 1 /* 2 Copyright 2019 Splunk Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "crypto/rand" 21 "crypto/sha256" 22 "encoding/base64" 23 "fmt" 24 25 "github.com/splunk/qbec/internal/model" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 ) 28 29 var randomPrefix string 30 31 func initRandomPrefix() { 32 b := make([]byte, 16) 33 if _, err := rand.Read(b); err != nil { 34 panic(fmt.Errorf("unable to initialize random prefix: %v", err)) 35 } 36 randomPrefix = base64.RawURLEncoding.EncodeToString(b) 37 } 38 39 func init() { 40 initRandomPrefix() 41 } 42 43 func obfuscate(value string) string { 44 realValue := randomPrefix + ":" + value 45 h := sha256.New() 46 _, _ = h.Write([]byte(realValue)) // guaranteed to never fail per docs 47 shasum := h.Sum(nil) 48 return fmt.Sprintf("redacted.%s", base64.RawURLEncoding.EncodeToString(shasum)) 49 } 50 51 // HasSensitiveInfo returns true if the supplied object has sensitive data that might need 52 // to be hidden. 53 func HasSensitiveInfo(obj *unstructured.Unstructured) bool { 54 gk := obj.GroupVersionKind().GroupKind() 55 return gk.Group == "" && gk.Kind == "Secret" 56 } 57 58 // HideSensitiveInfo creates a new object for secrets where secret values have been replaced with 59 // stable strings that can still be diff-ed. It returns a boolean to indicate that the return value 60 // was modified from the original object. When no modifications are needed, the original object 61 // is returned as-is. 62 func HideSensitiveInfo(obj *unstructured.Unstructured) (*unstructured.Unstructured, bool) { 63 if obj == nil { 64 return obj, false 65 } 66 if !HasSensitiveInfo(obj) { 67 return obj, false 68 } 69 clone := obj.DeepCopy() 70 secretData, _, _ := unstructured.NestedMap(obj.Object, "data") 71 if secretData == nil { 72 secretData = map[string]interface{}{} 73 } 74 changedData := map[string]interface{}{} 75 for k, v := range secretData { 76 value := obfuscate(fmt.Sprintf("%s:%s", k, v)) 77 changedData[k] = base64.StdEncoding.EncodeToString([]byte(value)) 78 } 79 clone.Object["data"] = changedData 80 return clone, true 81 } 82 83 // HideSensitiveLocalInfo is like HideSensitiveInfo but for local objects. 84 func HideSensitiveLocalInfo(in model.K8sLocalObject) (model.K8sLocalObject, bool) { 85 obj, changed := HideSensitiveInfo(in.ToUnstructured()) 86 if !changed { 87 return in, false 88 } 89 return model.NewK8sLocalObject(obj.Object, in.Application(), in.Tag(), in.Component(), in.Environment()), true 90 }