vault: configure user agent on Nomad vault clients (#15745)

* 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
This commit is contained in:
Seth Hoenig 2023-01-10 10:39:45 -06:00 committed by GitHub
parent 32f6ce1c54
commit 83450c8762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 17 deletions

3
.changelog/15745.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
vault: configure Nomad User-Agent on vault clients
```

View File

@ -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"
)
@ -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

View File

@ -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"
@ -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
@ -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 != "" {
@ -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()
@ -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()
@ -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

View File

@ -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"
)
@ -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)
}

View File

@ -5,16 +5,16 @@ 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
}
@ -22,7 +22,7 @@ func NomadClient(t *testing.T) *napi.Client {
// 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
}
@ -30,6 +30,7 @@ func ConsulClient(t *testing.T) *capi.Client {
// 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
}

View File

@ -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"
)
@ -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",

View File

@ -2,6 +2,7 @@ package useragent
import (
"fmt"
"net/http"
"runtime"
"github.com/hashicorp/nomad/version"
@ -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()},
})
}

View File

@ -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"
@ -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
@ -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

View File

@ -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"
@ -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{
@ -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{