mirror of
https://github.com/containers/podman.git
synced 2025-11-13 17:47:13 +08:00
170 lines
4.0 KiB
Go
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
|
|
}
|