forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kubernetes.go
129 lines (106 loc) · 3.07 KB
/
kubernetes.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
package kubernetes
import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/hashicorp/errwrap"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
)
const (
serviceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
)
type kubernetesMethod struct {
logger hclog.Logger
mountPath string
role string
// tokenPath is an optional path to a projected service account token inside
// the pod, for use instead of the default service account token.
tokenPath string
// jwtData is a ReadCloser used to inject a ReadCloser for mocking tests.
jwtData io.ReadCloser
}
// NewKubernetesAuthMethod reads the user configuration and returns a configured
// AuthMethod
func NewKubernetesAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
if conf == nil {
return nil, errors.New("empty config")
}
if conf.Config == nil {
return nil, errors.New("empty config data")
}
k := &kubernetesMethod{
logger: conf.Logger,
mountPath: conf.MountPath,
}
roleRaw, ok := conf.Config["role"]
if !ok {
return nil, errors.New("missing 'role' value")
}
k.role, ok = roleRaw.(string)
if !ok {
return nil, errors.New("could not convert 'role' config value to string")
}
tokenPathRaw, ok := conf.Config["token_path"]
if ok {
k.tokenPath, ok = tokenPathRaw.(string)
if !ok {
return nil, errors.New("could not convert 'token_path' config value to string")
}
}
if k.role == "" {
return nil, errors.New("'role' value is empty")
}
return k, nil
}
func (k *kubernetesMethod) Authenticate(ctx context.Context, client *api.Client) (string, map[string]interface{}, error) {
k.logger.Trace("beginning authentication")
jwtString, err := k.readJWT()
if err != nil {
return "", nil, errwrap.Wrapf("error reading JWT with Kubernetes Auth: {{err}}", err)
}
return fmt.Sprintf("%s/login", k.mountPath), map[string]interface{}{
"role": k.role,
"jwt": jwtString,
}, nil
}
func (k *kubernetesMethod) NewCreds() chan struct{} {
return nil
}
func (k *kubernetesMethod) CredSuccess() {
}
func (k *kubernetesMethod) Shutdown() {
}
// readJWT reads the JWT data for the Agent to submit to Vault. The default is
// to read the JWT from the default service account location, defined by the
// constant serviceAccountFile. In normal use k.jwtData is nil at invocation and
// the method falls back to reading the token path with os.Open, opening a file
// from either the default location or from the token_path path specified in
// configuration.
func (k *kubernetesMethod) readJWT() (string, error) {
// load configured token path if set, default to serviceAccountFile
tokenFilePath := serviceAccountFile
if k.tokenPath != "" {
tokenFilePath = k.tokenPath
}
data := k.jwtData
// k.jwtData should only be non-nil in tests
if data == nil {
f, err := os.Open(tokenFilePath)
if err != nil {
return "", err
}
data = f
}
defer data.Close()
contentBytes, err := ioutil.ReadAll(data)
if err != nil {
return "", err
}
return strings.TrimSpace(string(contentBytes)), nil
}