Files
podman/vendor/github.com/containers/common/pkg/secrets/shelldriver/shelldriver.go
Daniel J Walsh f67ab1eb20 Vendor in containers/(storage,image, common, buildah)
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-07-18 10:42:04 -04:00

170 lines
4.0 KiB
Go

package shelldriver
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"os/exec"
"sort"
"strings"
)
var (
// errMissingConfig indicates that one or more of the external actions are not configured
errMissingConfig = errors.New("missing config value")
// errNoSecretData indicates that there is not data associated with an id
errNoSecretData = errors.New("no secret data with ID")
// errInvalidKey indicates that something about your key is wrong
errInvalidKey = errors.New("invalid key")
)
type driverConfig struct {
// DeleteCommand contains a shell command that deletes a secret.
// The secret id is provided as environment variable SECRET_ID
DeleteCommand string
// ListCommand contains a shell command that lists all secrets.
// The output is expected to be one id per line
ListCommand string
// LookupCommand contains a shell command that retrieves a secret.
// The secret id is provided as environment variable SECRET_ID
LookupCommand string
// StoreCommand contains a shell command that stores a secret.
// The secret id is provided as environment variable SECRET_ID
// The secret value itself is provided over stdin
StoreCommand string
}
func (cfg *driverConfig) ParseOpts(opts map[string]string) error {
for key, value := range opts {
switch key {
case "delete":
cfg.DeleteCommand = value
case "list":
cfg.ListCommand = value
case "lookup":
cfg.LookupCommand = value
case "store":
cfg.StoreCommand = value
default:
return fmt.Errorf("invalid shell driver option: %q", key)
}
}
if cfg.DeleteCommand == "" ||
cfg.ListCommand == "" ||
cfg.LookupCommand == "" ||
cfg.StoreCommand == "" {
return errMissingConfig
}
return nil
}
// Driver is the passdriver object
type Driver struct {
driverConfig
}
// NewDriver creates a new secret driver.
func NewDriver(opts map[string]string) (*Driver, error) {
cfg := &driverConfig{}
if err := cfg.ParseOpts(opts); err != nil {
return nil, err
}
driver := &Driver{
driverConfig: *cfg,
}
return driver, nil
}
// List returns all secret IDs
func (d *Driver) List() (secrets []string, err error) {
cmd := exec.CommandContext(context.TODO(), "/bin/sh", "-c", d.ListCommand)
cmd.Env = os.Environ()
cmd.Stderr = os.Stderr
buf := &bytes.Buffer{}
cmd.Stdout = buf
err = cmd.Run()
if err != nil {
return nil, err
}
parts := bytes.Split(buf.Bytes(), []byte("\n"))
for _, part := range parts {
id := strings.Trim(string(part), " \r\n")
if len(id) > 0 {
secrets = append(secrets, id)
}
}
sort.Strings(secrets)
return secrets, nil
}
// Lookup returns the bytes associated with a secret ID
func (d *Driver) Lookup(id string) ([]byte, error) {
if strings.Contains(id, "..") {
return nil, errInvalidKey
}
cmd := exec.CommandContext(context.TODO(), "/bin/sh", "-c", d.LookupCommand)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "SECRET_ID="+id)
cmd.Stderr = os.Stderr
buf := &bytes.Buffer{}
cmd.Stdout = buf
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("%s: %w", id, errNoSecretData)
}
return buf.Bytes(), nil
}
// Store saves the bytes associated with an ID. An error is returned if the ID already exists
func (d *Driver) Store(id string, data []byte) error {
if strings.Contains(id, "..") {
return errInvalidKey
}
cmd := exec.CommandContext(context.TODO(), "/bin/sh", "-c", d.StoreCommand)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "SECRET_ID="+id)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = bytes.NewReader(data)
return cmd.Run()
}
// Delete removes the secret associated with the specified ID. An error is returned if no matching secret is found.
func (d *Driver) Delete(id string) error {
if strings.Contains(id, "..") {
return errInvalidKey
}
cmd := exec.CommandContext(context.TODO(), "/bin/sh", "-c", d.DeleteCommand)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "SECRET_ID="+id)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
return fmt.Errorf("%s: %w", id, errNoSecretData)
}
return nil
}