Skip to content

Commit

Permalink
vault: configure user agent on Nomad vault clients (#15745)
Browse files Browse the repository at this point in the history
* vault: configure user agent on Nomad vault clients

This PR attempts to set the User-Agent header on each Vault API client
created by Nomad. Still need to figure a way to set User-Agent on the
Vault client created internally by consul-template.

* vault: fixup find-and-replace gone awry
  • Loading branch information
shoenig authored Jan 10, 2023
1 parent cab35b3 commit f05aa6d
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .changelog/15745.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
vault: configure Nomad User-Agent on vault clients
```
6 changes: 3 additions & 3 deletions client/fingerprint/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/useragent"
vapi "github.com/hashicorp/vault/api"
)

Expand Down Expand Up @@ -35,18 +36,17 @@ func (f *VaultFingerprint) Fingerprint(req *FingerprintRequest, resp *Fingerprin
return nil
}

// Only create the client once to avoid creating too many connections to
// Vault.
// Only create the client once to avoid creating too many connections to Vault
if f.client == nil {
vaultConfig, err := config.VaultConfig.ApiConfig()
if err != nil {
return fmt.Errorf("Failed to initialize the Vault client config: %v", err)
}

f.client, err = vapi.NewClient(vaultConfig)
if err != nil {
return fmt.Errorf("Failed to initialize Vault client: %s", err)
}
useragent.SetHeaders(f.client)
}

// Connect to vault and parse its information
Expand Down
16 changes: 7 additions & 9 deletions client/vaultclient/vaultclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"container/heap"
"fmt"
"math/rand"
"net/http"
"strings"
"sync"
"time"

metrics "github.com/armon/go-metrics"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/nomad/structs/config"
vaultapi "github.com/hashicorp/vault/api"
Expand All @@ -21,7 +21,7 @@ import (
// wrapped tokens will be unwrapped using the vault API client.
type TokenDeriverFunc func(*structs.Allocation, []string, *vaultapi.Client) (map[string]string, error)

// The interface which nomad client uses to interact with vault and
// VaultClient is the interface which nomad client uses to interact with vault and
// periodically renews the tokens and secrets.
type VaultClient interface {
// Start initiates the renewal loop of tokens and secrets
Expand Down Expand Up @@ -151,9 +151,8 @@ func NewVaultClient(config *config.VaultConfig, logger hclog.Logger, tokenDerive
return nil, err
}

client.SetHeaders(http.Header{
"User-Agent": []string{"hashicorp/nomad"},
})
// Set our Nomad user agent
useragent.SetHeaders(client)

// SetHeaders above will replace all headers, make this call second
if config.Namespace != "" {
Expand Down Expand Up @@ -193,7 +192,7 @@ func (c *vaultClient) isRunning() bool {
return c.running
}

// Starts the renewal loop of vault client
// Start starts the renewal loop of vault client
func (c *vaultClient) Start() {
c.lock.Lock()
defer c.lock.Unlock()
Expand All @@ -207,7 +206,7 @@ func (c *vaultClient) Start() {
go c.run()
}

// Stops the renewal loop of vault client
// Stop stops the renewal loop of vault client
func (c *vaultClient) Stop() {
c.lock.Lock()
defer c.lock.Unlock()
Expand Down Expand Up @@ -353,8 +352,7 @@ func (c *vaultClient) renew(req *vaultClientRenewalRequest) error {
var renewalErr error
leaseDuration := req.increment
if req.isToken {
// Set the token in the API client to the one that needs
// renewal
// Set the token in the API client to the one that needs renewal
c.client.SetToken(req.id)

// Renew the token
Expand Down
16 changes: 16 additions & 0 deletions client/vaultclient/vaultclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (

"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/helper/pointer"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/testutil"
vaultapi "github.com/hashicorp/vault/api"
vaultconsts "github.com/hashicorp/vault/sdk/helper/consts"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -354,3 +357,16 @@ func TestVaultClient_RenewalTime_Short(t *testing.T) {
assert.Equal(t, 15*time.Second, renewalTime(dice, 30))
assert.Equal(t, 1*time.Second, renewalTime(dice, 2))
}

func TestVaultClient_SetUserAgent(t *testing.T) {
ci.Parallel(t)

conf := config.DefaultConfig()
conf.VaultConfig.Enabled = pointer.Of(true)
logger := testlog.HCLogger(t)
c, err := NewVaultClient(conf.VaultConfig, logger, nil)
must.NoError(t, err)

ua := c.client.Headers().Get("User-Agent")
must.Eq(t, useragent.String(), ua)
}
11 changes: 6 additions & 5 deletions e2e/e2eutil/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,32 @@ import (

capi "github.com/hashicorp/consul/api"
napi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper/useragent"
vapi "github.com/hashicorp/vault/api"

"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)

// NomadClient creates a default Nomad client based on the env vars
// from the test environment. Fails the test if it can't be created
func NomadClient(t *testing.T) *napi.Client {
client, err := napi.NewClient(napi.DefaultConfig())
require.NoError(t, err, "could not create Nomad client")
must.NoError(t, err)
return client
}

// ConsulClient creates a default Consul client based on the env vars
// from the test environment. Fails the test if it can't be created
func ConsulClient(t *testing.T) *capi.Client {
client, err := capi.NewClient(capi.DefaultConfig())
require.NoError(t, err, "could not create Consul client")
must.NoError(t, err)
return client
}

// VaultClient creates a default Vault client based on the env vars
// from the test environment. Fails the test if it can't be created
func VaultClient(t *testing.T) *vapi.Client {
client, err := vapi.NewClient(vapi.DefaultConfig())
require.NoError(t, err, "could not create Vault client")
useragent.SetHeaders(client)
must.NoError(t, err)
return client
}
2 changes: 2 additions & 0 deletions e2e/framework/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

capi "github.com/hashicorp/consul/api"
napi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/helper/uuid"
vapi "github.com/hashicorp/vault/api"
)
Expand Down Expand Up @@ -115,6 +116,7 @@ func (p *singleClusterProvisioner) SetupTestCase(t *testing.T, opts SetupOptions
if err != nil && opts.ExpectVault {
return nil, err
}
useragent.SetHeaders(vaultClient)
info.VaultClient = vaultClient
} else if opts.ExpectVault {
return nil, fmt.Errorf("vault client expected but environment variable %s not set",
Expand Down
13 changes: 13 additions & 0 deletions helper/useragent/useragent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package useragent

import (
"fmt"
"net/http"
"runtime"

"github.com/hashicorp/nomad/version"
Expand All @@ -27,3 +28,15 @@ func String() string {
return fmt.Sprintf("Nomad/%s (+%s; %s)",
versionFunc(), projectURL, rt)
}

// HeaderSetter is anything that implements SetHeaders(http.Header).
type HeaderSetter interface {
SetHeaders(http.Header)
}

// SetHeaders configures the User-Agent http.Header for the client.
func SetHeaders(client HeaderSetter) {
client.SetHeaders(http.Header{
"User-Agent": []string{String()},
})
}
3 changes: 3 additions & 0 deletions nomad/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/useragent"
tomb "gopkg.in/tomb.v2"

metrics "github.com/armon/go-metrics"
Expand Down Expand Up @@ -452,6 +453,7 @@ func (v *vaultClient) buildClient() error {
v.logger.Error("failed to create Vault client and not retrying", "error", err)
return err
}
useragent.SetHeaders(client)

// Store the client, create/assign the /sys client
v.client = client
Expand All @@ -462,6 +464,7 @@ func (v *vaultClient) buildClient() error {
v.logger.Error("failed to create Vault sys client and not retrying", "error", err)
return err
}
useragent.SetHeaders(v.clientSys)
client.SetNamespace(v.config.Namespace)
} else {
v.clientSys = client
Expand Down
3 changes: 3 additions & 0 deletions testutil/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/structs/config"
vapi "github.com/hashicorp/vault/api"
Expand Down Expand Up @@ -57,6 +58,7 @@ func NewTestVaultFromPath(t testing.T, binary string) *TestVault {
t.Fatalf("failed to build Vault API client: %v", err)
}
client.SetToken(token)
useragent.SetHeaders(client)

enable := true
tv := &TestVault{
Expand Down Expand Up @@ -133,6 +135,7 @@ func NewTestVaultDelayed(t testing.T) *TestVault {
t.Fatalf("failed to build Vault API client: %v", err)
}
client.SetToken(token)
useragent.SetHeaders(client)

enable := true
tv := &TestVault{
Expand Down

0 comments on commit f05aa6d

Please sign in to comment.