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 }