github.com/zntrio/harp/v2@v2.0.9/pkg/bundle/encryption.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package bundle
    19  
    20  import (
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  
    25  	"github.com/awnumar/memguard"
    26  	"github.com/golang/protobuf/ptypes/wrappers"
    27  
    28  	bundlev1 "github.com/zntrio/harp/v2/api/gen/go/harp/bundle/v1"
    29  	"github.com/zntrio/harp/v2/pkg/bundle/secret"
    30  	"github.com/zntrio/harp/v2/pkg/sdk/types"
    31  	"github.com/zntrio/harp/v2/pkg/sdk/value"
    32  )
    33  
    34  // PartialLock apply conditional transformer according to applicable annotation
    35  // on the given package.
    36  // The annotation is referring to a key alias provided.
    37  func PartialLock(ctx context.Context, b *bundlev1.Bundle, transformerMap map[string]value.Transformer, skipUnresolved bool) error {
    38  	// Check bundle
    39  	if b == nil {
    40  		return fmt.Errorf("unable to process nil bundle")
    41  	}
    42  	if transformerMap == nil {
    43  		return fmt.Errorf("unable to process nil transformer map")
    44  	}
    45  
    46  	// For each packages
    47  	for _, p := range b.Packages {
    48  		// Check annotation usage
    49  		keyAlias, hasKeyAlias := p.Annotations[packageEncryptionAnnotation]
    50  		if !hasKeyAlias {
    51  			// Skip package processing
    52  			continue
    53  		}
    54  
    55  		// Check key alias declaration
    56  		transformer, hasTransformer := transformerMap[keyAlias]
    57  		if !hasTransformer {
    58  			if skipUnresolved {
    59  				// Skip unresolved transformer alias.
    60  				continue
    61  			}
    62  			return fmt.Errorf("package encryption annotation found, but no key alias for %q provided", keyAlias)
    63  		}
    64  		if types.IsNil(transformer) {
    65  			return fmt.Errorf("key alias %q refers to a nil transformer", keyAlias)
    66  		}
    67  
    68  		// Convert secret as a map
    69  		secrets := map[string]interface{}{}
    70  		for _, s := range p.Secrets.Data {
    71  			var out interface{}
    72  			if err := secret.Unpack(s.Value, &out); err != nil {
    73  				return fmt.Errorf("unable to load secret value, corrupted bundle: %w", err)
    74  			}
    75  
    76  			// Assign to secret map
    77  			secrets[s.Key] = out
    78  		}
    79  
    80  		// Export secrets as JSON
    81  		content, err := json.Marshal(secrets)
    82  		if err != nil {
    83  			return fmt.Errorf("unable to extract secret map as json")
    84  		}
    85  
    86  		// Apply transformer
    87  		out, err := transformer.To(ctx, content)
    88  		if err != nil {
    89  			return fmt.Errorf("unable to apply secret transformer: %w", err)
    90  		}
    91  
    92  		// Cleanup
    93  		memguard.WipeBytes(content)
    94  		p.Secrets.Data = nil
    95  
    96  		// Assign locked secret
    97  		p.Secrets.Locked = &wrappers.BytesValue{
    98  			Value: out,
    99  		}
   100  	}
   101  
   102  	// No error
   103  	return nil
   104  }
   105  
   106  // Lock apply transformer function to all secret values and set as locked.
   107  func Lock(ctx context.Context, b *bundlev1.Bundle, transformer value.Transformer) error {
   108  	// Check bundle
   109  	if b == nil {
   110  		return fmt.Errorf("unable to process nil bundle")
   111  	}
   112  	if types.IsNil(transformer) {
   113  		return fmt.Errorf("unable to process nil transformer")
   114  	}
   115  
   116  	// For each packages
   117  	for _, p := range b.Packages {
   118  		// Convert secret as a map
   119  		secrets := map[string]interface{}{}
   120  		for _, s := range p.Secrets.Data {
   121  			var out interface{}
   122  			if err := secret.Unpack(s.Value, &out); err != nil {
   123  				return fmt.Errorf("unable to load secret value, corrupted bundle: %w", err)
   124  			}
   125  
   126  			// Assign to secret map
   127  			secrets[s.Key] = out
   128  		}
   129  
   130  		// Export secrets as JSON
   131  		content, err := json.Marshal(secrets)
   132  		if err != nil {
   133  			return fmt.Errorf("unable to extract secret map as json")
   134  		}
   135  
   136  		// Apply transformer
   137  		out, err := transformer.To(ctx, content)
   138  		if err != nil {
   139  			return fmt.Errorf("unable to apply secret transformer: %w", err)
   140  		}
   141  
   142  		// Cleanup
   143  		memguard.WipeBytes(content)
   144  		p.Secrets.Data = nil
   145  
   146  		// Assign locked secret
   147  		p.Secrets.Locked = &wrappers.BytesValue{
   148  			Value: out,
   149  		}
   150  	}
   151  
   152  	// No error
   153  	return nil
   154  }
   155  
   156  // UnLock apply transformer function to all secret values and set as unlocked.
   157  func UnLock(ctx context.Context, b *bundlev1.Bundle, transformers []value.Transformer, skipNotDecryptable bool) error {
   158  	// Check bundle
   159  	if b == nil {
   160  		return fmt.Errorf("unable to process nil bundle")
   161  	}
   162  	if len(transformers) == 0 {
   163  		return fmt.Errorf("unable to process empty transformer list")
   164  	}
   165  
   166  	// For each packages
   167  	for _, p := range b.Packages {
   168  		// Skip not locked package
   169  		if p.Secrets.Locked == nil {
   170  			continue
   171  		}
   172  		if len(p.Secrets.Locked.Value) == 0 {
   173  			continue
   174  		}
   175  
   176  		// Try all transformers
   177  		var (
   178  			out          []byte
   179  			errTransform error
   180  		)
   181  	LOOP:
   182  		for _, t := range transformers {
   183  			// Apply transformation
   184  			out, errTransform = t.From(ctx, p.Secrets.Locked.Value)
   185  			switch {
   186  			case errTransform != nil:
   187  				// Try next transformer
   188  				continue
   189  			default:
   190  				break LOOP
   191  			}
   192  		}
   193  		if errTransform != nil {
   194  			if skipNotDecryptable {
   195  				// Skip not decrypted secrets.
   196  				continue
   197  			}
   198  			return fmt.Errorf("unable to transform %q: %w", p.Name, errTransform)
   199  		}
   200  
   201  		// Unpack secrets
   202  		raw := map[string]interface{}{}
   203  		if err := json.Unmarshal(out, &raw); err != nil {
   204  			return fmt.Errorf("unable to unpack locked secret: %w", err)
   205  		}
   206  
   207  		// Prepare secrets collection
   208  		secrets := []*bundlev1.KV{}
   209  		for key, value := range raw {
   210  			// Pack secret value
   211  			s, err := secret.Pack(value)
   212  			if err != nil {
   213  				return fmt.Errorf("unable to pack as secret bundle: %w", err)
   214  			}
   215  
   216  			// Add to secret collection
   217  			secrets = append(secrets, &bundlev1.KV{
   218  				Key:   key,
   219  				Type:  fmt.Sprintf("%T", value),
   220  				Value: s,
   221  			})
   222  		}
   223  
   224  		// Cleanup
   225  		memguard.WipeBytes(p.Secrets.Locked.Value)
   226  		p.Secrets.Locked = nil
   227  
   228  		// Assign unlocked secrets
   229  		p.Secrets.Data = secrets
   230  	}
   231  
   232  	// No error
   233  	return nil
   234  }