File: shelldriver.go

package info (click to toggle)
golang-github-containers-common 0.64.2%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,528 kB
  • sloc: makefile: 130; sh: 102
file content (161 lines) | stat: -rw-r--r-- 3,905 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package shelldriver

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"sort"
	"strings"

	"github.com/containers/common/pkg/secrets/define"
)

// errMissingConfig indicates that one or more of the external actions are not configured
var errMissingConfig = errors.New("missing config value")

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, define.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, define.ErrNoSuchSecret)
	}
	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 define.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 define.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, define.ErrNoSuchSecret)
	}

	return nil
}