Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/component/remote/vault/auth.go
4096 views
1
package vault
2
3
import (
4
"context"
5
"fmt"
6
7
"github.com/grafana/agent/pkg/river/rivertypes"
8
vault "github.com/hashicorp/vault/api"
9
"github.com/hashicorp/vault/api/auth/approle"
10
"github.com/hashicorp/vault/api/auth/aws"
11
"github.com/hashicorp/vault/api/auth/azure"
12
"github.com/hashicorp/vault/api/auth/gcp"
13
"github.com/hashicorp/vault/api/auth/kubernetes"
14
"github.com/hashicorp/vault/api/auth/ldap"
15
"github.com/hashicorp/vault/api/auth/userpass"
16
)
17
18
// An authMethod can configure a Vault client to be authenticated using a
19
// specific authentication method.
20
//
21
// The vaultAuthenticate method will be called each time a new token is needed
22
// (e.g., if renewal failed). vaultAuthenticate method may return a nil secret
23
// if the authentication method does not generate a secret.
24
type authMethod interface {
25
vaultAuthenticate(context.Context, *vault.Client) (*vault.Secret, error)
26
}
27
28
// AuthArguments defines a single authenticationstring type in a remote.vault
29
// component instance. These are embedded as an enum field so only one may be
30
// set per AuthArguments.
31
type AuthArguments struct {
32
AuthToken *AuthToken `river:"token,block,optional"`
33
AuthAppRole *AuthAppRole `river:"approle,block,optional"`
34
AuthAWS *AuthAWS `river:"aws,block,optional"`
35
AuthAzure *AuthAzure `river:"azure,block,optional"`
36
AuthGCP *AuthGCP `river:"gcp,block,optional"`
37
AuthKubernetes *AuthKubernetes `river:"kubernetes,block,optional"`
38
AuthLDAP *AuthLDAP `river:"ldap,block,optional"`
39
AuthUserPass *AuthUserPass `river:"userpass,block,optional"`
40
AuthCustom *AuthCustom `river:"custom,block,optional"`
41
}
42
43
func (a *AuthArguments) authMethod() authMethod {
44
switch {
45
case a.AuthToken != nil:
46
return a.AuthToken
47
case a.AuthAppRole != nil:
48
return a.AuthAppRole
49
case a.AuthAWS != nil:
50
return a.AuthAWS
51
case a.AuthAzure != nil:
52
return a.AuthAzure
53
case a.AuthGCP != nil:
54
return a.AuthGCP
55
case a.AuthKubernetes != nil:
56
return a.AuthKubernetes
57
case a.AuthLDAP != nil:
58
return a.AuthLDAP
59
case a.AuthUserPass != nil:
60
return a.AuthUserPass
61
case a.AuthCustom != nil:
62
return a.AuthCustom
63
}
64
65
// Return a default authMethod which always fails. This code should not be
66
// reachable unless this function was mistakenly not updated after
67
// implemeneting a new auth block.
68
return invalidAuth{}
69
}
70
71
// AuthToken authenticates against Vault with a token.
72
type AuthToken struct {
73
Token rivertypes.Secret `river:"token,attr"`
74
}
75
76
func (a *AuthToken) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
77
cli.SetToken(string(a.Token))
78
return nil, nil
79
}
80
81
// AuthAppRole authenticates against Vault with AppRole.
82
type AuthAppRole struct {
83
RoleID string `river:"role_id,attr"`
84
Secret rivertypes.Secret `river:"secret,attr"`
85
WrappingToken bool `river:"wrapping_token,attr,optional"`
86
MountPath string `river:"mount_path,attr,optional"`
87
}
88
89
// DefaultAuthAppRole provides default settings for AuthAppRole.
90
var DefaultAuthAppRole = AuthAppRole{
91
MountPath: "approle",
92
}
93
94
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
95
func (a *AuthAppRole) UnmarshalRiver(f func(interface{}) error) error {
96
*a = DefaultAuthAppRole
97
98
type authAppRole AuthAppRole
99
return f((*authAppRole)(a))
100
}
101
102
func (a *AuthAppRole) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
103
secret := &approle.SecretID{FromString: string(a.Secret)}
104
105
var opts []approle.LoginOption
106
if a.WrappingToken {
107
opts = append(opts, approle.WithWrappingToken())
108
}
109
if a.MountPath != "" {
110
opts = append(opts, approle.WithMountPath(a.MountPath))
111
}
112
113
auth, err := approle.NewAppRoleAuth(a.RoleID, secret, opts...)
114
if err != nil {
115
return nil, fmt.Errorf("auth.approle: %w", err)
116
}
117
s, err := cli.Auth().Login(ctx, auth)
118
if err != nil {
119
return nil, fmt.Errorf("auth.approle: %w", err)
120
}
121
return s, nil
122
}
123
124
// AuthAWS authenticates against Vault with AWS.
125
type AuthAWS struct {
126
// Type specifies the mechanism used to authenticate with AWS. Should be
127
// either ec2 or iam.
128
Type string `river:"type,attr"`
129
Region string `river:"region,attr,optional"`
130
Role string `river:"role,attr,optional"`
131
IAMServerIDHeader string `river:"iam_server_id_header,attr,optional"`
132
// EC2SignatureType specifies the signature to use against EC2. Only used
133
// when Type is ec2. Valid options are identity and pkcs7 (default).
134
EC2SignatureType string `river:"ec2_signature_type,attr,optional"`
135
MountPath string `river:"mount_path,attr,optional"`
136
}
137
138
const (
139
authAWSTypeEC2 = "ec2"
140
authAWSTypeIAM = "iam"
141
)
142
143
// DefaultAuthAWS provides default settings for AuthAWS.
144
var DefaultAuthAWS = AuthAWS{
145
MountPath: "aws",
146
Type: authAWSTypeIAM,
147
Region: "us-east-1",
148
EC2SignatureType: "pkcs7",
149
}
150
151
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
152
func (a *AuthAWS) UnmarshalRiver(f func(interface{}) error) error {
153
*a = DefaultAuthAWS
154
155
type authAWS AuthAWS
156
if err := f((*authAWS)(a)); err != nil {
157
return err
158
}
159
160
return a.Validate()
161
}
162
163
// Validate validates settings for AuthAWS.
164
func (a *AuthAWS) Validate() error {
165
switch a.Type {
166
case "":
167
return fmt.Errorf("type must not be empty")
168
case authAWSTypeEC2, authAWSTypeIAM:
169
// no-op
170
default:
171
return fmt.Errorf("unrecognized type %q, expected one of ec2,iam", a.Type)
172
}
173
174
switch a.EC2SignatureType {
175
case "":
176
return fmt.Errorf("ec2_signature_type must not be empty")
177
case "pkcs7", "identity":
178
// no-op
179
default:
180
return fmt.Errorf(": unrecognized ec2_signature_type %q, expected one of pkcs7,identity", a.Type)
181
}
182
183
return nil
184
}
185
186
func (a *AuthAWS) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
187
// Re-validate for safety.
188
if err := a.Validate(); err != nil {
189
return nil, err
190
}
191
192
var opts []aws.LoginOption
193
194
switch a.Type {
195
case authAWSTypeEC2:
196
opts = append(opts, aws.WithEC2Auth())
197
case authAWSTypeIAM:
198
opts = append(opts, aws.WithIAMAuth())
199
}
200
if a.Region != "" {
201
opts = append(opts, aws.WithRegion(a.Region))
202
}
203
if a.Role != "" {
204
opts = append(opts, aws.WithRole(a.Role))
205
}
206
if a.IAMServerIDHeader != "" {
207
opts = append(opts, aws.WithIAMServerIDHeader(a.IAMServerIDHeader))
208
}
209
switch a.EC2SignatureType {
210
case "", "pkcs7":
211
opts = append(opts, aws.WithPKCS7Signature())
212
case "identity":
213
opts = append(opts, aws.WithIdentitySignature())
214
}
215
if a.MountPath != "" {
216
opts = append(opts, aws.WithMountPath(a.MountPath))
217
}
218
219
auth, err := aws.NewAWSAuth(opts...)
220
if err != nil {
221
return nil, fmt.Errorf("auth.aws: %w", err)
222
}
223
s, err := cli.Auth().Login(ctx, auth)
224
if err != nil {
225
return nil, fmt.Errorf("auth.aws: %w", err)
226
}
227
return s, nil
228
}
229
230
// AuthAzure authenticates against Vault with Azure.
231
type AuthAzure struct {
232
Role string `river:"role,attr"`
233
ResourceURL string `river:"resource_url,attr,optional"`
234
MountPath string `river:"mount_path,attr,optional"`
235
}
236
237
// DefaultAuthAzure provides default settings for AuthAzure.
238
var DefaultAuthAzure = AuthAzure{
239
MountPath: "azure",
240
ResourceURL: "https://management.azure.com/",
241
}
242
243
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
244
func (a *AuthAzure) UnmarshalRiver(f func(interface{}) error) error {
245
*a = DefaultAuthAzure
246
247
type authAzure AuthAzure
248
return f((*authAzure)(a))
249
}
250
251
func (a *AuthAzure) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
252
var opts []azure.LoginOption
253
254
if a.ResourceURL != "" {
255
opts = append(opts, azure.WithResource(a.ResourceURL))
256
}
257
if a.MountPath != "" {
258
opts = append(opts, azure.WithMountPath(a.MountPath))
259
}
260
261
auth, err := azure.NewAzureAuth(a.Role, opts...)
262
if err != nil {
263
return nil, fmt.Errorf("auth.azure: %w", err)
264
}
265
s, err := cli.Auth().Login(ctx, auth)
266
if err != nil {
267
return nil, fmt.Errorf("auth.azure: %w", err)
268
}
269
return s, nil
270
}
271
272
// AuthGCP authenticates against Vault with GCP.
273
type AuthGCP struct {
274
Role string `river:"role,attr"`
275
// Type specifies the mechanism used to authenticate with GCS. Should be
276
// either gce or iam.
277
Type string `river:"type,attr"`
278
IAMServiceAccount string `river:"iam_service_account,attr,optional"`
279
MountPath string `river:"mount_path,attr,optional"`
280
}
281
282
const (
283
authGCPTypeGCE = "gce"
284
authGCPTypeIAM = "iam"
285
)
286
287
// DefaultAuthGCP provides default settings for AuthGCP.
288
var DefaultAuthGCP = AuthGCP{
289
MountPath: "gcp",
290
Type: authGCPTypeGCE,
291
}
292
293
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
294
func (a *AuthGCP) UnmarshalRiver(f func(interface{}) error) error {
295
*a = DefaultAuthGCP
296
297
type authGCP AuthGCP
298
if err := f((*authGCP)(a)); err != nil {
299
return err
300
}
301
302
return a.Validate()
303
}
304
305
// Validate returns a non-nil error if AuthGCP is invalid.
306
func (a *AuthGCP) Validate() error {
307
switch a.Type {
308
case authGCPTypeGCE:
309
// no-op
310
case authGCPTypeIAM:
311
if a.IAMServiceAccount == "" {
312
return fmt.Errorf("iam_service_account must be provided when type is iam")
313
}
314
default:
315
return fmt.Errorf("unrecognized type %q, expected one of gce,iam", a.Type)
316
}
317
318
return nil
319
}
320
321
func (a *AuthGCP) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
322
// Re-validate for safety.
323
if err := a.Validate(); err != nil {
324
return nil, err
325
}
326
327
var opts []gcp.LoginOption
328
329
switch a.Type {
330
case authGCPTypeGCE:
331
opts = append(opts, gcp.WithGCEAuth())
332
case authGCPTypeIAM:
333
opts = append(opts, gcp.WithIAMAuth(a.IAMServiceAccount))
334
}
335
336
if a.MountPath != "" {
337
opts = append(opts, gcp.WithMountPath(a.MountPath))
338
}
339
340
auth, err := gcp.NewGCPAuth(a.Role, opts...)
341
if err != nil {
342
return nil, fmt.Errorf("auth.gcp: %w", err)
343
}
344
s, err := cli.Auth().Login(ctx, auth)
345
if err != nil {
346
return nil, fmt.Errorf("auth.gcp: %w", err)
347
}
348
return s, nil
349
}
350
351
// AuthKubernetes authenticates against Vault with Kubernetes.
352
type AuthKubernetes struct {
353
Role string `river:"role,attr"`
354
ServiceAccountTokenFile string `river:"service_account_file,attr,optional"`
355
MountPath string `river:"mount_path,attr,optional"`
356
}
357
358
// DefaultAuthKubernetes provides default settings for AuthKubernetes.
359
var DefaultAuthKubernetes = AuthKubernetes{
360
MountPath: "kubernetes",
361
ServiceAccountTokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token",
362
}
363
364
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
365
func (a *AuthKubernetes) UnmarshalRiver(f func(interface{}) error) error {
366
*a = DefaultAuthKubernetes
367
368
type authKubernetes AuthKubernetes
369
return f((*authKubernetes)(a))
370
}
371
372
func (a *AuthKubernetes) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
373
var opts []kubernetes.LoginOption
374
375
if a.ServiceAccountTokenFile != "" {
376
opts = append(opts, kubernetes.WithServiceAccountTokenPath(a.ServiceAccountTokenFile))
377
}
378
if a.MountPath != "" {
379
opts = append(opts, kubernetes.WithMountPath(a.MountPath))
380
}
381
382
auth, err := kubernetes.NewKubernetesAuth(a.Role, opts...)
383
if err != nil {
384
return nil, fmt.Errorf("auth.kubernetes: %w", err)
385
}
386
s, err := cli.Auth().Login(ctx, auth)
387
if err != nil {
388
return nil, fmt.Errorf("auth.kubernetes: %w", err)
389
}
390
return s, nil
391
}
392
393
// AuthLDAP authenticates against Vault with LDAP.
394
type AuthLDAP struct {
395
Username string `river:"username,attr"`
396
Password rivertypes.Secret `river:"password,attr"`
397
MountPath string `river:"mount_path,attr,optional"`
398
}
399
400
// DefaultAuthLDAP provides default settings for AuthLDAP.
401
var DefaultAuthLDAP = AuthLDAP{
402
MountPath: "ldap",
403
}
404
405
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
406
func (a *AuthLDAP) UnmarshalRiver(f func(interface{}) error) error {
407
*a = DefaultAuthLDAP
408
409
type authLDAP AuthLDAP
410
return f((*authLDAP)(a))
411
}
412
413
func (a *AuthLDAP) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
414
secret := &ldap.Password{FromString: string(a.Password)}
415
416
var opts []ldap.LoginOption
417
418
if a.MountPath != "" {
419
opts = append(opts, ldap.WithMountPath(a.MountPath))
420
}
421
422
auth, err := ldap.NewLDAPAuth(a.Username, secret, opts...)
423
if err != nil {
424
return nil, fmt.Errorf("auth.ldap: %w", err)
425
}
426
s, err := cli.Auth().Login(ctx, auth)
427
if err != nil {
428
return nil, fmt.Errorf("auth.ldap: %w", err)
429
}
430
return s, nil
431
}
432
433
// AuthUserPass authenticates against Vault with a username and password.
434
type AuthUserPass struct {
435
Username string `river:"username,attr"`
436
Password rivertypes.Secret `river:"password,attr"`
437
MountPath string `river:"mount_path,attr,optional"`
438
}
439
440
// DefaultAuthUserPass provides default settings for AuthUserPass.
441
var DefaultAuthUserPass = AuthUserPass{
442
MountPath: "userpass",
443
}
444
445
// UnmarshalRiver implements river.Unmarshaler and applies default settings.
446
func (a *AuthUserPass) UnmarshalRiver(f func(interface{}) error) error {
447
*a = DefaultAuthUserPass
448
449
type authUserPass AuthUserPass
450
return f((*authUserPass)(a))
451
}
452
453
func (a *AuthUserPass) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
454
secret := &userpass.Password{FromString: string(a.Password)}
455
456
var opts []userpass.LoginOption
457
458
if a.MountPath != "" {
459
opts = append(opts, userpass.WithMountPath(a.MountPath))
460
}
461
462
auth, err := userpass.NewUserpassAuth(a.Username, secret, opts...)
463
if err != nil {
464
return nil, fmt.Errorf("auth.userpass: %w", err)
465
}
466
s, err := cli.Auth().Login(ctx, auth)
467
if err != nil {
468
return nil, fmt.Errorf("auth.userpass: %w", err)
469
}
470
return s, nil
471
}
472
473
// AuthCustom provides a custom authentication method.
474
type AuthCustom struct {
475
// Path to use for logging in (e.g., auth/kubernetes/login, etc.)
476
Path string `river:"path,attr"`
477
Data map[string]rivertypes.Secret `river:"data,attr"`
478
}
479
480
// Login implements vault.AuthMethod.
481
func (a *AuthCustom) Login(ctx context.Context, client *vault.Client) (*vault.Secret, error) {
482
data := make(map[string]interface{}, len(a.Data))
483
for k, v := range a.Data {
484
data[k] = string(v)
485
}
486
return client.Logical().WriteWithContext(ctx, a.Path, data)
487
}
488
489
func (a *AuthCustom) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
490
s, err := cli.Auth().Login(ctx, a)
491
if err != nil {
492
return nil, fmt.Errorf("auth.custom: %w", err)
493
}
494
return s, nil
495
}
496
497
type invalidAuth struct {
498
Name string
499
}
500
501
func (a invalidAuth) vaultAuthenticate(ctx context.Context, cli *vault.Client) (*vault.Secret, error) {
502
return nil, fmt.Errorf("unsupported auth method configured")
503
}
504
505