-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
approle.go
97 lines (80 loc) · 2.77 KB
/
approle.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
package approle
import (
"fmt"
"io/ioutil"
"strings"
"github.com/hashicorp/vault/api"
)
type AppRoleAuth struct {
mountPath string
roleID string
pathToSecretID string
unwrap bool
}
type LoginOption func(a *AppRoleAuth)
// NewAppRoleAuth initializes a new AppRole auth method interface to be passed as a parameter to the client.Auth().Login method.
//
// For a secret ID, the recommended secure pattern is to unwrap a one-time-use response-wrapping token that was placed here by a trusted orchestrator (https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices)
// To indicate that the filepath points to this wrapping token and not just a plaintext secret ID, initialize NewAppRoleAuth with the WithWrappingToken LoginOption.
//
// Supported options: WithMountPath, WithWrappingToken
func NewAppRoleAuth(roleID, pathToSecretID string, opts ...LoginOption) (api.AuthMethod, error) {
if roleID == "" {
return nil, fmt.Errorf("no role ID provided for login")
}
if pathToSecretID == "" {
return nil, fmt.Errorf("no path to secret ID provided for login")
}
const (
defaultMountPath = "approle"
)
a := &AppRoleAuth{
mountPath: defaultMountPath,
roleID: roleID,
pathToSecretID: pathToSecretID,
}
// Loop through each option
for _, opt := range opts {
// Call the option giving the instantiated
// *AppRoleAuth as the argument
opt(a)
}
// return the modified auth struct instance
return a, nil
}
func (a *AppRoleAuth) Login(client *api.Client) (*api.Secret, error) {
loginData := map[string]interface{}{
"role_id": a.roleID,
}
secretIDBytes, err := ioutil.ReadFile(a.pathToSecretID)
if err != nil {
return nil, fmt.Errorf("unable to read file containing secret ID: %w", err)
}
secretID := string(secretIDBytes)
// if it was indicated that the value in the file was actually a wrapping token, unwrap it first
if a.unwrap {
unwrappedToken, err := client.Logical().Unwrap(strings.TrimSuffix(secretID, "\n"))
if err != nil {
return nil, fmt.Errorf("unable to unwrap token: %w. If the AppRoleAuth struct was initialized with the WithWrappingToken LoginOption, then the filepath used should be a path to a response-wrapping token", err)
}
loginData["secret_id"] = unwrappedToken.Data["secret_id"]
} else {
loginData["secret_id"] = secretID
}
path := fmt.Sprintf("auth/%s/login", a.mountPath)
resp, err := client.Logical().Write(path, loginData)
if err != nil {
return nil, fmt.Errorf("unable to log in with app role auth: %w", err)
}
return resp, nil
}
func WithMountPath(mountPath string) LoginOption {
return func(a *AppRoleAuth) {
a.mountPath = mountPath
}
}
func WithWrappingToken() LoginOption {
return func(a *AppRoleAuth) {
a.unwrap = true
}
}