Skip to content

Commit 5683231

Browse files
committed
Add workload identity
1 parent 0495a97 commit 5683231

File tree

9 files changed

+542
-420
lines changed

9 files changed

+542
-420
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ dev:
2323

2424
# test runs the tests
2525
test:
26-
@go test -timeout=240s ./... $(TESTARGS)
26+
@go test -timeout=300s ./... $(TESTARGS)
2727
.PHONY: test

backend.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ import (
99
"sync"
1010
"time"
1111

12-
"github.com/hashicorp/errwrap"
12+
"github.com/hashicorp/go-gcp-common/gcputil"
13+
"github.com/hashicorp/go-hclog"
1314
"github.com/hashicorp/vault/sdk/framework"
15+
"github.com/hashicorp/vault/sdk/helper/pluginutil"
1416
"github.com/hashicorp/vault/sdk/helper/useragent"
1517
"github.com/hashicorp/vault/sdk/logical"
1618
"github.com/patrickmn/go-cache"
1719
"golang.org/x/oauth2/google"
20+
"golang.org/x/oauth2/google/externalaccount"
1821
"google.golang.org/api/option"
1922

2023
kmsapi "cloud.google.com/go/kms/apiv1"
@@ -182,20 +185,32 @@ func (b *backend) KMSClient(s logical.Storage) (*kmsapi.KeyManagementClient, fun
182185
return nil, nil, err
183186
}
184187

185-
// If credentials were provided, use those. Otherwise fall back to the
186-
// default application credentials.
188+
// If credentials were provided or workload identiy, use those.
189+
// Otherwise fall back to the default application credentials.
187190
var creds *google.Credentials
188191
if config.Credentials != "" {
189192
creds, err = google.CredentialsFromJSON(b.ctx, []byte(config.Credentials), config.Scopes...)
190193
if err != nil {
191194
b.kmsClientLock.Unlock()
192-
return nil, nil, errwrap.Wrapf("failed to parse credentials: {{err}}", err)
195+
return nil, nil, fmt.Errorf("failed to parse credentials: %w", err)
196+
}
197+
} else if config.IdentityTokenAudience != "" {
198+
ts := &PluginIdentityTokenSupplier{
199+
sys: b.System(),
200+
logger: b.Logger(),
201+
audience: config.IdentityTokenAudience,
202+
ttl: config.IdentityTokenTTL,
203+
}
204+
205+
creds, err = b.GetExternalAccountConfig(config, ts).GetExternalAccountCredentials(b.ctx)
206+
if err != nil {
207+
return nil, nil, fmt.Errorf("failed to fetch external account credentials: %w", err)
193208
}
194209
} else {
195210
creds, err = google.FindDefaultCredentials(b.ctx, config.Scopes...)
196211
if err != nil {
197212
b.kmsClientLock.Unlock()
198-
return nil, nil, errwrap.Wrapf("failed to get default token source: {{err}}", err)
213+
return nil, nil, fmt.Errorf("failed to get default token source: %w", err)
199214
}
200215
}
201216

@@ -207,7 +222,7 @@ func (b *backend) KMSClient(s logical.Storage) (*kmsapi.KeyManagementClient, fun
207222
)
208223
if err != nil {
209224
b.kmsClientLock.Unlock()
210-
return nil, nil, errwrap.Wrapf("failed to create KMS client: {{err}}", err)
225+
return nil, nil, fmt.Errorf("failed to create KMS client: %w", err)
211226
}
212227

213228
// Cache the client
@@ -228,14 +243,53 @@ func (b *backend) Config(ctx context.Context, s logical.Storage) (*Config, error
228243

229244
entry, err := s.Get(ctx, "config")
230245
if err != nil {
231-
return nil, errwrap.Wrapf("failed to get configuration from storage: {{err}}", err)
246+
return nil, fmt.Errorf("failed to get configuration from storage: %w", err)
232247
}
233248
if entry == nil || len(entry.Value) == 0 {
234249
return c, nil
235250
}
236251

237252
if err := entry.DecodeJSON(&c); err != nil {
238-
return nil, errwrap.Wrapf("failed to decode configuration: {{err}}", err)
253+
return nil, fmt.Errorf("failed to decode configuration: %w", err)
239254
}
240255
return c, nil
241256
}
257+
258+
func (b *backend) GetExternalAccountConfig(c *Config, ts *PluginIdentityTokenSupplier) *gcputil.ExternalAccountConfig {
259+
b.Logger().Debug("adding web identity token fetcher")
260+
cfg := &gcputil.ExternalAccountConfig{
261+
ServiceAccountEmail: c.ServiceAccountEmail,
262+
Audience: c.IdentityTokenAudience,
263+
TTL: c.IdentityTokenTTL,
264+
TokenSupplier: ts,
265+
}
266+
267+
return cfg
268+
}
269+
270+
type PluginIdentityTokenSupplier struct {
271+
sys logical.SystemView
272+
logger hclog.Logger
273+
audience string
274+
ttl time.Duration
275+
}
276+
277+
var _ externalaccount.SubjectTokenSupplier = (*PluginIdentityTokenSupplier)(nil)
278+
279+
func (p *PluginIdentityTokenSupplier) SubjectToken(ctx context.Context, opts externalaccount.SupplierOptions) (string, error) {
280+
p.logger.Info("fetching new plugin identity token")
281+
resp, err := p.sys.GenerateIdentityToken(ctx, &pluginutil.IdentityTokenRequest{
282+
Audience: p.audience,
283+
TTL: p.ttl,
284+
})
285+
if err != nil {
286+
return "", fmt.Errorf("failed to generate plugin identity token: %w", err)
287+
}
288+
289+
if resp.TTL < p.ttl {
290+
p.logger.Debug("generated plugin identity token has shorter TTL than requested",
291+
"requested", p.ttl.Seconds(), "actual", resp.TTL)
292+
}
293+
294+
return resp.Token.Token(), nil
295+
}

config.go

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
package gcpkms
55

66
import (
7-
"strings"
8-
9-
"github.com/hashicorp/go-secure-stdlib/strutil"
10-
"github.com/hashicorp/vault/sdk/framework"
7+
"github.com/hashicorp/vault/sdk/helper/automatedrotationutil"
8+
"github.com/hashicorp/vault/sdk/helper/pluginidentityutil"
119
)
1210

1311
const (
@@ -16,8 +14,11 @@ const (
1614

1715
// Config is the stored configuration.
1816
type Config struct {
19-
Credentials string `json:"credentials"`
20-
Scopes []string `json:"scopes"`
17+
Credentials string `json:"credentials"`
18+
Scopes []string `json:"scopes"`
19+
ServiceAccountEmail string `json:"service_account_email"`
20+
pluginidentityutil.PluginIdentityTokenParams
21+
automatedrotationutil.AutomatedRotationParams
2122
}
2223

2324
// DefaultConfig returns a config with the default values.
@@ -26,30 +27,3 @@ func DefaultConfig() *Config {
2627
Scopes: []string{defaultScope},
2728
}
2829
}
29-
30-
// Update updates the configuration from the given field data.
31-
func (c *Config) Update(d *framework.FieldData) (bool, error) {
32-
if d == nil {
33-
return false, nil
34-
}
35-
36-
changed := false
37-
38-
if v, ok := d.GetOk("credentials"); ok {
39-
nv := strings.TrimSpace(v.(string))
40-
if nv != c.Credentials {
41-
c.Credentials = nv
42-
changed = true
43-
}
44-
}
45-
46-
if v, ok := d.GetOk("scopes"); ok {
47-
nv := strutil.RemoveDuplicates(v.([]string), true)
48-
if !strutil.EquivalentSlices(nv, c.Scopes) {
49-
c.Scopes = nv
50-
changed = true
51-
}
52-
}
53-
54-
return changed, nil
55-
}

config_test.go

Lines changed: 0 additions & 155 deletions
This file was deleted.

0 commit comments

Comments
 (0)