forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 2
/
helper_external.go
133 lines (119 loc) · 3.51 KB
/
helper_external.go
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
package token
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
// ExternalTokenHelperPath takes the configured path to a helper and expands it to
// a full absolute path that can be executed. As of 0.5, the default token
// helper is internal, to avoid problems running in dev mode (see GH-850 and
// GH-783), so special assumptions of prepending "vault token-" no longer
// apply.
//
// As an additional result, only absolute paths are now allowed. Looking in the
// path or a current directory for an arbitrary executable could allow someone
// to switch the expected binary for one further up the path (or in the current
// directory), potentially opening up execution of an arbitrary binary.
func ExternalTokenHelperPath(path string) (string, error) {
if !filepath.IsAbs(path) {
var err error
path, err = filepath.Abs(path)
if err != nil {
return "", err
}
}
if _, err := os.Stat(path); err != nil {
return "", fmt.Errorf("unknown error getting the external helper path")
}
return path, nil
}
var _ TokenHelper = (*ExternalTokenHelper)(nil)
// ExternalTokenHelper is the struct that has all the logic for storing and retrieving
// tokens from the token helper. The API for the helpers is simple: the
// BinaryPath is executed within a shell with environment Env. The last argument
// appended will be the operation, which is:
//
// * "get" - Read the value of the token and write it to stdout.
// * "store" - Store the value of the token which is on stdin. Output
// nothing.
// * "erase" - Erase the contents stored. Output nothing.
//
// Any errors can be written on stdout. If the helper exits with a non-zero
// exit code then the stderr will be made part of the error value.
type ExternalTokenHelper struct {
BinaryPath string
Env []string
}
// Erase deletes the contents from the helper.
func (h *ExternalTokenHelper) Erase() error {
cmd, err := h.cmd("erase")
if err != nil {
return fmt.Errorf("Error: %s", err)
}
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf(
"Error: %s\n\n%s", err, string(output))
}
return nil
}
// Get gets the token value from the helper.
func (h *ExternalTokenHelper) Get() (string, error) {
var buf, stderr bytes.Buffer
cmd, err := h.cmd("get")
if err != nil {
return "", fmt.Errorf("Error: %s", err)
}
cmd.Stdout = &buf
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", fmt.Errorf(
"Error: %s\n\n%s", err, stderr.String())
}
return buf.String(), nil
}
// Store stores the token value into the helper.
func (h *ExternalTokenHelper) Store(v string) error {
buf := bytes.NewBufferString(v)
cmd, err := h.cmd("store")
if err != nil {
return fmt.Errorf("Error: %s", err)
}
cmd.Stdin = buf
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf(
"Error: %s\n\n%s", err, string(output))
}
return nil
}
func (h *ExternalTokenHelper) Path() string {
return h.BinaryPath
}
func (h *ExternalTokenHelper) cmd(op string) (*exec.Cmd, error) {
script := strings.Replace(h.BinaryPath, "\\", "\\\\", -1) + " " + op
cmd, err := ExecScript(script)
if err != nil {
return nil, err
}
cmd.Env = h.Env
return cmd, nil
}
// ExecScript returns a command to execute a script
func ExecScript(script string) (*exec.Cmd, error) {
var shell, flag string
if runtime.GOOS == "windows" {
shell = "cmd"
flag = "/C"
} else {
shell = "/bin/sh"
flag = "-c"
}
if other := os.Getenv("SHELL"); other != "" {
shell = other
}
cmd := exec.Command(shell, flag, script)
return cmd, nil
}