Skip to content

Commit

Permalink
VAULT-12940 Vault Agent uses Vault Agent specific User-Agent header w…
Browse files Browse the repository at this point in the history
…hen issuing requests (hashicorp#19776)

* VAULT-12940 test for templating user agent

* VAULT-12940 User agent work so far

* VAULT-12940 Vault Agent uses Vault Agent specific User-Agent header when issuing requests

* VAULT-12940 Clean-up and godocs

* VAULT-12940 changelog

* VAULT-12940 Fix test checking headers

* VAULT-12940 Fix test checking headers

* VAULT-12940 Fix test checking headers

* VAULT-12940 Fix test checking headers

* VAULT-12940 copy/paste typos

* VAULT-12940 improve comments, use make(http.Header)

* VAULT-12940 small typos and clean-up
  • Loading branch information
VioletHynes committed Apr 3, 2023
1 parent e7e6ab9 commit 33731d6
Show file tree
Hide file tree
Showing 13 changed files with 832 additions and 33 deletions.
3 changes: 3 additions & 0 deletions changelog/19776.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
agent: Vault Agent now reports its name and version as part of the User-Agent header in all requests issued.
```
2 changes: 1 addition & 1 deletion command/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func (c *AgentCommand) Run(args []string) int {
Ui: c.UI,
ServiceName: "vault",
DisplayName: "Vault",
UserAgent: useragent.String(),
UserAgent: useragent.AgentString(),
ClusterName: config.ClusterName,
})
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions command/agent/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/useragent"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
)

Expand Down Expand Up @@ -156,6 +157,15 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
credCh = make(chan struct{})
}

if ah.client != nil {
headers := ah.client.Headers()
if headers == nil {
headers = make(http.Header)
}
headers.Set("User-Agent", useragent.AgentAutoAuthString())
ah.client.SetHeaders(headers)
}

var watcher *api.LifetimeWatcher
first := true

Expand Down
16 changes: 16 additions & 0 deletions command/agent/cache/api_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ package cache
import (
"context"
"fmt"
gohttp "net/http"
"sync"

hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-retryablehttp"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/useragent"
"github.com/hashicorp/vault/http"
)

Expand Down Expand Up @@ -76,6 +78,20 @@ func (ap *APIProxy) Send(ctx context.Context, req *SendRequest) (*SendResponse,
// the client doesn't manually set the header. Removing any Accept-Encoding header allows the
// transparent compression to occur.
req.Request.Header.Del("Accept-Encoding")

if req.Request.Header == nil {
req.Request.Header = make(gohttp.Header)
}

// Set our User-Agent to be one indicating we are Vault Agent's API proxy.
// If the sending client had one, preserve it.
if req.Request.Header.Get("User-Agent") != "" {
initialUserAgent := req.Request.Header.Get("User-Agent")
req.Request.Header.Set("User-Agent", useragent.AgentProxyStringWithProxiedUserAgent(initialUserAgent))
} else {
req.Request.Header.Set("User-Agent", useragent.AgentProxyString())
}

client.SetHeaders(req.Request.Header)

fwReq := client.NewRequest(req.Request.Method, req.Request.URL.Path)
Expand Down
14 changes: 13 additions & 1 deletion command/agent/cache/lease_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
"github.com/hashicorp/vault/helper/namespace"
nshelper "github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/useragent"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/cryptoutil"
Expand Down Expand Up @@ -477,7 +478,18 @@ func (c *LeaseCache) startRenewing(ctx context.Context, index *cachememdb.Index,
return
}
client.SetToken(req.Token)
client.SetHeaders(req.Request.Header)

headers := client.Headers()
if headers == nil {
headers = make(http.Header)
}

// We do not preserve the initial User-Agent here (i.e. use
// AgentProxyStringWithProxiedUserAgent) since these requests are from
// the proxy subsystem, but are made by Agent's lifetime watcher,
// not triggered by a specific request.
headers.Set("User-Agent", useragent.AgentProxyString())
client.SetHeaders(headers)

watcher, err := client.NewLifetimeWatcher(&api.LifetimeWatcherInput{
Secret: secret,
Expand Down
21 changes: 16 additions & 5 deletions command/agent/cache/lease_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"testing"
"time"

"github.com/hashicorp/vault/helper/useragent"

"github.com/go-test/deep"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"
Expand Down Expand Up @@ -737,7 +739,7 @@ func compareBeforeAndAfter(t *testing.T, before, after *LeaseCache, beforeLen, a
assert.Equal(t, cachedItem.Lease, restoredItem.Lease)
assert.Equal(t, cachedItem.LeaseToken, restoredItem.LeaseToken)
assert.Equal(t, cachedItem.Namespace, restoredItem.Namespace)
assert.Equal(t, cachedItem.RequestHeader, restoredItem.RequestHeader)
assert.EqualValues(t, cachedItem.RequestHeader, restoredItem.RequestHeader)
assert.Equal(t, cachedItem.RequestMethod, restoredItem.RequestMethod)
assert.Equal(t, cachedItem.RequestPath, restoredItem.RequestPath)
assert.Equal(t, cachedItem.RequestToken, restoredItem.RequestToken)
Expand Down Expand Up @@ -842,16 +844,21 @@ func TestLeaseCache_PersistAndRestore(t *testing.T) {
var deleteIDs []string
for i, ct := range cacheTests {
// Send once to cache
req := httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body))
req.Header.Set("User-Agent", useragent.AgentProxyString())

sendReq := &SendRequest{
Token: ct.token,
Request: httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body)),
Request: req,
}
if ct.deleteFromPersistentStore {
deleteID, err := computeIndexID(sendReq)
require.NoError(t, err)
deleteIDs = append(deleteIDs, deleteID)
// Now reset the body after calculating the index
sendReq.Request = httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body))
req = httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body))
req.Header.Set("User-Agent", useragent.AgentProxyString())
sendReq.Request = req
}
resp, err := lc.Send(context.Background(), sendReq)
require.NoError(t, err)
Expand All @@ -860,9 +867,11 @@ func TestLeaseCache_PersistAndRestore(t *testing.T) {

// Send again to test cache. If this isn't cached, the response returned
// will be the next in the list and the status code will not match.
req = httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body))
req.Header.Set("User-Agent", useragent.AgentProxyString())
sendCacheReq := &SendRequest{
Token: ct.token,
Request: httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body)),
Request: req,
}
respCached, err := lc.Send(context.Background(), sendCacheReq)
require.NoError(t, err, "failed to send request %+v", ct)
Expand Down Expand Up @@ -894,9 +903,11 @@ func TestLeaseCache_PersistAndRestore(t *testing.T) {
// And finally send the cache requests once to make sure they're all being
// served from the restoredCache unless they were intended to be missing after restore.
for i, ct := range cacheTests {
req := httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body))
req.Header.Set("User-Agent", useragent.AgentProxyString())
sendCacheReq := &SendRequest{
Token: ct.token,
Request: httptest.NewRequest(ct.method, ct.urlPath, strings.NewReader(ct.body)),
Request: req,
}
respCached, err := restoredCache.Send(context.Background(), sendCacheReq)
require.NoError(t, err, "failed to send request %+v", ct)
Expand Down
6 changes: 5 additions & 1 deletion command/agent/cache/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"strings"
"time"

"github.com/hashicorp/vault/helper/useragent"

"github.com/hashicorp/vault/api"
)

Expand Down Expand Up @@ -47,11 +49,13 @@ func (p *mockProxier) ResponseIndex() int {
}

func newTestSendResponse(status int, body string) *SendResponse {
headers := make(http.Header)
headers.Add("User-Agent", useragent.AgentProxyString())
resp := &SendResponse{
Response: &api.Response{
Response: &http.Response{
StatusCode: status,
Header: http.Header{},
Header: headers,
},
},
}
Expand Down
5 changes: 4 additions & 1 deletion command/agent/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/consul-template/manager"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/config"
"github.com/hashicorp/vault/helper/useragent"
"github.com/hashicorp/vault/sdk/helper/pointerutil"
)

Expand Down Expand Up @@ -160,7 +161,8 @@ func (ts *Server) Run(ctx context.Context, incoming chan string, templates []*ct
*latestToken = token
ctv := ctconfig.Config{
Vault: &ctconfig.VaultConfig{
Token: latestToken,
Token: latestToken,
ClientUserAgent: pointerutil.StringPtr(useragent.AgentTemplatingString()),
},
}

Expand Down Expand Up @@ -239,6 +241,7 @@ func newRunnerConfig(sc *ServerConfig, templates ctconfig.TemplateConfigs) (*ctc
conf.Vault.RenewToken = pointerutil.BoolPtr(false)
conf.Vault.Token = pointerutil.StringPtr("")
conf.Vault.Address = &sc.AgentConfig.Vault.Address
conf.Vault.ClientUserAgent = pointerutil.StringPtr(useragent.AgentTemplatingString())

if sc.Namespace != "" {
conf.Vault.Namespace = &sc.Namespace
Expand Down
Loading

0 comments on commit 33731d6

Please sign in to comment.