get.porter.sh/porter@v1.3.0/pkg/secrets/plugins/filesystem/store.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"get.porter.sh/porter/pkg/config"
    11  	"get.porter.sh/porter/pkg/secrets"
    12  	"get.porter.sh/porter/pkg/secrets/plugins"
    13  	"get.porter.sh/porter/pkg/secrets/plugins/host"
    14  	"get.porter.sh/porter/pkg/tracing"
    15  )
    16  
    17  var _ plugins.SecretsProtocol = &Store{}
    18  
    19  const (
    20  	SECRET_FOLDER                          = "secrets"
    21  	FileModeSensitiveDirectory os.FileMode = 0700
    22  	FileModeSensitiveWritable  os.FileMode = 0600
    23  )
    24  
    25  // Store implements an file system secrets store for testing and local
    26  // development.
    27  type Store struct {
    28  	config    *config.Config
    29  	secretDir string
    30  	hostStore plugins.SecretsProtocol
    31  }
    32  
    33  // NewStore returns a new instance of the filesystem secret store.
    34  func NewStore(c *config.Config) *Store {
    35  	s := &Store{
    36  		config:    c,
    37  		hostStore: host.NewStore(),
    38  	}
    39  
    40  	return s
    41  }
    42  
    43  // Connect initializes the plugin for use.
    44  // The plugin itself is responsible for ensuring it was called.
    45  // Close is called automatically when the plugin is used by Porter.
    46  func (s *Store) Connect(ctx context.Context) error {
    47  	if s.secretDir != "" {
    48  		return nil
    49  	}
    50  
    51  	_, log := tracing.StartSpan(ctx)
    52  	defer log.EndSpan()
    53  
    54  	if _, err := s.SetSecretDir(); err != nil {
    55  		return log.Error(err)
    56  	}
    57  
    58  	if err := s.config.FileSystem.MkdirAll(s.secretDir, FileModeSensitiveDirectory); err != nil && !errors.Is(err, os.ErrExist) {
    59  		return log.Error(err)
    60  	}
    61  
    62  	log.Debugf("storing secrets in %s", s.secretDir)
    63  	return nil
    64  }
    65  
    66  // SetSecretDir configures the directory path for storing secrets.
    67  func (s *Store) SetSecretDir() (string, error) {
    68  	porterHomeDir, err := s.config.GetHomeDir()
    69  	if err != nil {
    70  		return "", fmt.Errorf("could not get user home directory: %w", err)
    71  	}
    72  
    73  	s.secretDir = filepath.Join(porterHomeDir, SECRET_FOLDER)
    74  	return s.secretDir, nil
    75  }
    76  
    77  // Close implements the Close method on the secret plugins' interface.
    78  func (s *Store) Close() error {
    79  	return nil
    80  }
    81  
    82  // Resolve implements the Resolve method on the secret plugins' interface.
    83  func (s *Store) Resolve(ctx context.Context, keyName string, keyValue string) (string, error) {
    84  	ctx, log := tracing.StartSpan(ctx)
    85  	defer log.EndSpan()
    86  
    87  	if err := s.Connect(ctx); err != nil {
    88  		return "", err
    89  	}
    90  
    91  	// check if the keyName is secret
    92  	if keyName != secrets.SourceSecret {
    93  		return "", log.Errorf("unsupported keyName %s", keyName)
    94  	}
    95  
    96  	path := filepath.Join(s.secretDir, keyValue)
    97  	data, err := s.config.FileSystem.ReadFile(path)
    98  	if err != nil {
    99  		return "", log.Error(fmt.Errorf("error reading secret from filesystem: %w", err))
   100  	}
   101  
   102  	return string(data), nil
   103  }
   104  
   105  // Create implements the Create method on the secret plugins' interface.
   106  func (s *Store) Create(ctx context.Context, keyName string, keyValue string, value string) error {
   107  	ctx, log := tracing.StartSpan(ctx)
   108  	defer log.EndSpan()
   109  
   110  	if err := s.Connect(ctx); err != nil {
   111  		return err
   112  	}
   113  
   114  	// check if the keyName is secret
   115  	if keyName != secrets.SourceSecret {
   116  		return log.Error(errors.New("invalid key name: " + keyName))
   117  	}
   118  
   119  	path := filepath.Join(s.secretDir, keyValue)
   120  	f, err := s.config.FileSystem.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, FileModeSensitiveWritable)
   121  	if err != nil {
   122  		return log.Error(fmt.Errorf("failed to create key: %s: %w", keyName, err))
   123  	}
   124  	defer f.Close()
   125  
   126  	_, err = f.WriteString(value)
   127  	if err != nil {
   128  		return log.Error(fmt.Errorf("error writing secret to filesystem: %w", err))
   129  	}
   130  	return nil
   131  }