get.porter.sh/porter@v1.3.0/pkg/storage/sanitizer.go (about) 1 package storage 2 3 import ( 4 "context" 5 "fmt" 6 7 "get.porter.sh/porter/pkg/cnab" 8 "get.porter.sh/porter/pkg/secrets" 9 "github.com/cnabio/cnab-go/secrets/host" 10 ) 11 12 // Sanitizer identifies sensitive data in a database record, and replaces it with 13 // a reference to a secret created by the service in an external secret store. 14 type Sanitizer struct { 15 parameter ParameterSetProvider 16 secrets secrets.Store 17 } 18 19 // NewSanitizer creates a new service for sanitizing sensitive data and save them 20 // to a secret store. 21 func NewSanitizer(parameterstore ParameterSetProvider, secretstore secrets.Store) *Sanitizer { 22 return &Sanitizer{ 23 parameter: parameterstore, 24 secrets: secretstore, 25 } 26 } 27 28 // CleanRawParameters clears out sensitive data in raw parameter values (resolved parameter values stored on a Run) before 29 // transform the raw value into secret strategies. 30 // The id argument is used to associate the reference key with the corresponding 31 // run or installation record in porter's database. 32 func (s *Sanitizer) CleanRawParameters(ctx context.Context, params map[string]interface{}, bun cnab.ExtendedBundle, id string) ([]secrets.SourceMap, error) { 33 strategies := make([]secrets.SourceMap, 0, len(params)) 34 for name, value := range params { 35 stringVal, err := bun.WriteParameterToString(name, value) 36 if err != nil { 37 return nil, err 38 } 39 strategy := ValueStrategy(name, stringVal) 40 strategies = append(strategies, strategy) 41 } 42 43 strategies, err := s.CleanParameters(ctx, strategies, bun, id) 44 if err != nil { 45 return nil, err 46 } 47 48 return strategies, nil 49 50 } 51 52 // CleanParameters clears out sensitive data in strategized parameter data (overrides provided by the user on an Installation record) and return 53 // Sanitized value after saving sensitive data to secrets store. 54 // The id argument is used to associate the reference key with the corresponding 55 // run or installation record in porter's database. 56 func (s *Sanitizer) CleanParameters(ctx context.Context, dirtyParams []secrets.SourceMap, bun cnab.ExtendedBundle, id string) ([]secrets.SourceMap, error) { 57 cleanedParams := make([]secrets.SourceMap, 0, len(dirtyParams)) 58 for _, param := range dirtyParams { 59 // Store sensitive hard-coded values in a secret store 60 if param.Source.Strategy == host.SourceValue && bun.IsSensitiveParameter(param.Name) { 61 cleaned := sanitizedParam(param, id) 62 err := s.secrets.Create(ctx, cleaned.Source.Strategy, cleaned.Source.Hint, cleaned.ResolvedValue) 63 if err != nil { 64 return nil, fmt.Errorf("failed to save sensitive param to secrete store: %w", err) 65 } 66 67 cleanedParams = append(cleanedParams, cleaned) 68 } else { // All other parameters are safe to use without cleaning 69 cleanedParams = append(cleanedParams, param) 70 } 71 } 72 73 if len(cleanedParams) == 0 { 74 return nil, nil 75 } 76 77 return cleanedParams, nil 78 79 } 80 81 // LinkSensitiveParametersToSecrets creates a reference key for sensitive data 82 // and replace the sensitive value with the reference key. 83 // The id argument is used to associate the reference key with the corresponding 84 // run or installation record in porter's database. 85 func LinkSensitiveParametersToSecrets(pset ParameterSet, bun cnab.ExtendedBundle, id string) ParameterSet { 86 for i, param := range pset.Parameters { 87 if !bun.IsSensitiveParameter(param.Name) { 88 continue 89 } 90 pset.Parameters[i] = sanitizedParam(param, id) 91 } 92 93 return pset 94 } 95 96 func sanitizedParam(param secrets.SourceMap, id string) secrets.SourceMap { 97 param.Source.Strategy = secrets.SourceSecret 98 param.Source.Hint = id + "-" + param.Name 99 return param 100 } 101 102 // RestoreParameterSet resolves the raw parameter data from a secrets store. 103 func (s *Sanitizer) RestoreParameterSet(ctx context.Context, pset ParameterSet, bun cnab.ExtendedBundle) (map[string]interface{}, error) { 104 params, err := s.parameter.ResolveAll(ctx, pset, pset.Keys()) 105 if err != nil { 106 return nil, err 107 } 108 109 resolved := make(map[string]interface{}) 110 for name, value := range params { 111 paramValue, err := bun.ConvertParameterValue(name, value) 112 if err != nil { 113 paramValue = value 114 } 115 116 resolved[name] = paramValue 117 118 } 119 return resolved, nil 120 121 } 122 123 // CleanOutput clears data that's defined as sensitive on the bundle definition 124 // by storing the raw data into a secret store and store it's reference key onto 125 // the output record. 126 func (s *Sanitizer) CleanOutput(ctx context.Context, output Output, bun cnab.ExtendedBundle) (Output, error) { 127 // Skip outputs not defined in the bundle, e.g. io.cnab.outputs.invocationImageLogs 128 _, ok := output.GetSchema(bun) 129 if !ok { 130 return output, nil 131 } 132 133 sensitive, err := bun.IsOutputSensitive(output.Name) 134 if err != nil { 135 output.Value = nil 136 return output, err 137 } 138 139 if !sensitive { 140 return output, nil 141 142 } 143 144 secretOt := sanitizedOutput(output) 145 146 err = s.secrets.Create(ctx, secrets.SourceSecret, secretOt.Key, string(output.Value)) 147 if err != nil { 148 return secretOt, err 149 } 150 151 return secretOt, nil 152 } 153 154 func sanitizedOutput(output Output) Output { 155 output.Key = output.RunID + "-" + output.Name 156 output.Value = nil 157 return output 158 159 } 160 161 // RestoreOutputs retrieves all raw output value and return the restored outputs 162 // record. 163 func (s *Sanitizer) RestoreOutputs(ctx context.Context, o Outputs) (Outputs, error) { 164 resolved := make([]Output, 0, o.Len()) 165 for _, ot := range o.Value() { 166 r, err := s.RestoreOutput(ctx, ot) 167 if err != nil { 168 return o, fmt.Errorf("failed to resolve output %q using key %q: %w", ot.Name, ot.Key, err) 169 } 170 resolved = append(resolved, r) 171 } 172 173 return NewOutputs(resolved), nil 174 } 175 176 // RestoreOutput retrieves the raw output value and return the restored output 177 // record. 178 func (s *Sanitizer) RestoreOutput(ctx context.Context, output Output) (Output, error) { 179 if output.Key == "" { 180 return output, nil 181 } 182 resolved, err := s.secrets.Resolve(ctx, secrets.SourceSecret, string(output.Key)) 183 if err != nil { 184 return output, err 185 } 186 187 output.Value = []byte(resolved) 188 return output, nil 189 }