pkg/bundle/encryption.go (146 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package bundle import ( "context" "encoding/json" "fmt" "github.com/awnumar/memguard" "github.com/golang/protobuf/ptypes/wrappers" bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1" "github.com/elastic/harp/pkg/bundle/secret" "github.com/elastic/harp/pkg/sdk/types" "github.com/elastic/harp/pkg/sdk/value" ) // PartialLock apply conditional transformer according to applicable annotation // on the given package. // The annotation is referring to a key alias provided. func PartialLock(ctx context.Context, b *bundlev1.Bundle, transformerMap map[string]value.Transformer, skipUnresolved bool) error { // Check bundle if b == nil { return fmt.Errorf("unable to process nil bundle") } if transformerMap == nil { return fmt.Errorf("unable to process nil transformer map") } // For each packages for _, p := range b.Packages { // Check annotation usage keyAlias, hasKeyAlias := p.Annotations[packageEncryptionAnnotation] if !hasKeyAlias { // Skip package processing continue } // Check key alias declaration transformer, hasTransformer := transformerMap[keyAlias] if !hasTransformer { if skipUnresolved { // Skip unresolved transformer alias. continue } return fmt.Errorf("package encryption annotation found, but no key alias for '%s' provided", keyAlias) } if types.IsNil(transformer) { return fmt.Errorf("key alias '%s' refers to a nil transformer", keyAlias) } // Convert secret as a map secrets := map[string]interface{}{} for _, s := range p.Secrets.Data { var out interface{} if err := secret.Unpack(s.Value, &out); err != nil { return fmt.Errorf("unable to load secret value, corrupted bundle: %w", err) } // Assign to secret map secrets[s.Key] = out } // Export secrets as JSON content, err := json.Marshal(secrets) if err != nil { return fmt.Errorf("unable to extract secret map as json") } // Apply transformer out, err := transformer.To(ctx, content) if err != nil { return fmt.Errorf("unable to apply secret transformer: %w", err) } // Cleanup memguard.WipeBytes(content) p.Secrets.Data = nil // Assign locked secret p.Secrets.Locked = &wrappers.BytesValue{ Value: out, } } // No error return nil } // Lock apply transformer function to all secret values and set as locked. func Lock(ctx context.Context, b *bundlev1.Bundle, transformer value.Transformer) error { // Check bundle if b == nil { return fmt.Errorf("unable to process nil bundle") } if types.IsNil(transformer) { return fmt.Errorf("unable to process nil transformer") } // For each packages for _, p := range b.Packages { // Convert secret as a map secrets := map[string]interface{}{} for _, s := range p.Secrets.Data { var out interface{} if err := secret.Unpack(s.Value, &out); err != nil { return fmt.Errorf("unable to load secret value, corrupted bundle: %w", err) } // Assign to secret map secrets[s.Key] = out } // Export secrets as JSON content, err := json.Marshal(secrets) if err != nil { return fmt.Errorf("unable to extract secret map as json") } // Apply transformer out, err := transformer.To(ctx, content) if err != nil { return fmt.Errorf("unable to apply secret transformer: %w", err) } // Cleanup memguard.WipeBytes(content) p.Secrets.Data = nil // Assign locked secret p.Secrets.Locked = &wrappers.BytesValue{ Value: out, } } // No error return nil } // UnLock apply transformer function to all secret values and set as unlocked. func UnLock(ctx context.Context, b *bundlev1.Bundle, transformers []value.Transformer, skipNotDecryptable bool) error { // Check bundle if b == nil { return fmt.Errorf("unable to process nil bundle") } if len(transformers) == 0 { return fmt.Errorf("unable to process empty transformer list") } // For each packages for _, p := range b.Packages { // Skip not locked package if p.Secrets.Locked == nil { continue } if len(p.Secrets.Locked.Value) == 0 { continue } // Try all transformers var ( out []byte errTransform error ) LOOP: for _, t := range transformers { // Apply transformation out, errTransform = t.From(ctx, p.Secrets.Locked.Value) switch { case errTransform != nil: // Try next transformer continue default: break LOOP } } if errTransform != nil { if skipNotDecryptable { // Skip not decrypted secrets. continue } return fmt.Errorf("unable to transform '%s': %w", p.Name, errTransform) } // Unpack secrets raw := map[string]interface{}{} if err := json.Unmarshal(out, &raw); err != nil { return fmt.Errorf("unable to unpack locked secret: %w", err) } // Prepare secrets collection secrets := []*bundlev1.KV{} for key, value := range raw { // Pack secret value s, err := secret.Pack(value) if err != nil { return fmt.Errorf("unable to pack as secret bundle: %w", err) } // Add to secret collection secrets = append(secrets, &bundlev1.KV{ Key: key, Type: fmt.Sprintf("%T", value), Value: s, }) } // Cleanup memguard.WipeBytes(p.Secrets.Locked.Value) p.Secrets.Locked = nil // Assign unlocked secrets p.Secrets.Data = secrets } // No error return nil }