github.com/drone/runner-go@v1.12.0/secret/encrypted.go (about)

     1  // Copyright 2019 Drone.IO Inc. All rights reserved.
     2  // Use of this source code is governed by the Polyform License
     3  // that can be found in the LICENSE file.
     4  
     5  package secret
     6  
     7  import (
     8  	"context"
     9  	"crypto/aes"
    10  	"crypto/cipher"
    11  	"encoding/base64"
    12  	"errors"
    13  
    14  	"github.com/drone/runner-go/logger"
    15  	"github.com/drone/runner-go/manifest"
    16  
    17  	"github.com/drone/drone-go/drone"
    18  )
    19  
    20  // Encrypted returns a new encrypted secret provider. The
    21  // encrypted secret provider finds and decrypts secrets stored
    22  // inline in the (yaml) configuration.
    23  func Encrypted() Provider {
    24  	return new(encrypted)
    25  }
    26  
    27  type encrypted struct{}
    28  
    29  func (p *encrypted) Find(ctx context.Context, in *Request) (*drone.Secret, error) {
    30  	logger := logger.FromContext(ctx).
    31  		WithField("name", in.Name).
    32  		WithField("kind", "secret")
    33  
    34  	// lookup the named secret in the manifest. If the
    35  	// secret does not exist, return a nil variable,
    36  	// allowing the next secret controller in the chain
    37  	// to be invoked.
    38  	data, ok := getEncrypted(in.Conf, in.Name)
    39  	if !ok {
    40  		logger.Trace("secret: encrypted: no matching secret")
    41  		return nil, nil
    42  	}
    43  
    44  	// if the build event is a pull request and the source
    45  	// repository is a fork, the secret is not exposed to
    46  	// the pipeline, for security reasons.
    47  	if in.Repo.Private == false &&
    48  		in.Build.Event == drone.EventPullRequest &&
    49  		in.Build.Fork != "" {
    50  		logger.Trace("secret: encrypted: restricted from forks")
    51  		return nil, nil
    52  	}
    53  
    54  	decoded, err := base64.StdEncoding.DecodeString(string(data))
    55  	if err != nil {
    56  		logger.WithError(err).Debug("secret: encrypted: cannot decode")
    57  		return nil, err
    58  	}
    59  
    60  	decrypted, err := decrypt(decoded, []byte(in.Repo.Secret))
    61  	if err != nil {
    62  		logger.WithError(err).Debug("secret: encrypted: cannot decrypt")
    63  		return nil, err
    64  	}
    65  
    66  	logger.Trace("secret: encrypted: found matching secret")
    67  
    68  	return &drone.Secret{
    69  		Name: in.Name,
    70  		Data: string(decrypted),
    71  	}, nil
    72  }
    73  
    74  func getEncrypted(spec *manifest.Manifest, match string) (data string, ok bool) {
    75  	for _, resource := range spec.Resources {
    76  		secret, ok := resource.(*manifest.Secret)
    77  		if !ok {
    78  			continue
    79  		}
    80  		if secret.Name != match {
    81  			continue
    82  		}
    83  		if secret.Data == "" {
    84  			continue
    85  		}
    86  		return secret.Data, true
    87  	}
    88  	return
    89  }
    90  
    91  func decrypt(ciphertext []byte, key []byte) (plaintext []byte, err error) {
    92  	block, err := aes.NewCipher(key[:])
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	gcm, err := cipher.NewGCM(block)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	if len(ciphertext) < gcm.NonceSize() {
   103  		return nil, errors.New("malformed ciphertext")
   104  	}
   105  
   106  	return gcm.Open(nil,
   107  		ciphertext[:gcm.NonceSize()],
   108  		ciphertext[gcm.NonceSize():],
   109  		nil,
   110  	)
   111  }