VAULT-15547 First pass at agent/proxy decoupling (#20548)

* VAULT-15547 First pass at agent/proxy decoupling

* VAULT-15547 Fix some imports

* VAULT-15547 cases instead of string.Title

* VAULT-15547 changelog

* VAULT-15547 Fix some imports

* VAULT-15547 some more dependency updates

* VAULT-15547 More dependency paths

* VAULT-15547 godocs for tests

* VAULT-15547 godocs for tests

* VAULT-15547 test package updates

* VAULT-15547 test packages

* VAULT-15547 add proxy to test packages

* VAULT-15547 gitignore

* VAULT-15547 address comments

* VAULT-15547 Some typos and small fixes
This commit is contained in:
Violet Hynes 2023-05-17 09:38:34 -04:00 committed by GitHub
parent 66a6e18283
commit b2468d3481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3381 additions and 200 deletions

View File

@ -27,7 +27,7 @@ fi
# Total time: 1009
test_packages[3]+=" $base/builtin/credential/approle"
test_packages[3]+=" $base/command/agent/sink/file"
test_packages[3]+=" $base/command/agentproxyshared/sink/file"
test_packages[3]+=" $base/command/agent/template"
test_packages[3]+=" $base/helper/random"
test_packages[3]+=" $base/helper/storagepacker"
@ -87,16 +87,17 @@ test_packages[6]+=" $base/builtin/audit/file"
test_packages[6]+=" $base/builtin/credential/github"
test_packages[6]+=" $base/builtin/credential/okta"
test_packages[6]+=" $base/builtin/logical/database/dbplugin"
test_packages[6]+=" $base/command/agent/auth/cert"
test_packages[6]+=" $base/command/agent/auth/jwt"
test_packages[6]+=" $base/command/agent/auth/kerberos"
test_packages[6]+=" $base/command/agent/auth/kubernetes"
test_packages[6]+=" $base/command/agent/auth/token-file"
test_packages[6]+=" $base/command/agent/cache"
test_packages[6]+=" $base/command/agent/cache/cacheboltdb"
test_packages[6]+=" $base/command/agent/cache/cachememdb"
test_packages[6]+=" $base/command/agent/cache/keymanager"
test_packages[6]+=" $base/command/agentproxyshared/auth/cert"
test_packages[6]+=" $base/command/agentproxyshared/auth/jwt"
test_packages[6]+=" $base/command/agentproxyshared/auth/kerberos"
test_packages[6]+=" $base/command/agentproxyshared/auth/kubernetes"
test_packages[6]+=" $base/command/agentproxyshared/auth/token-file"
test_packages[6]+=" $base/command/agentproxyshared/cache"
test_packages[6]+=" $base/command/agentproxyshared/cache/cacheboltdb"
test_packages[6]+=" $base/command/agentproxyshared/cache/cachememdb"
test_packages[6]+=" $base/command/agentproxyshared/cache/keymanager"
test_packages[6]+=" $base/command/agent/config"
test_packages[6]+=" $base/command/proxy/config"
test_packages[6]+=" $base/command/config"
test_packages[6]+=" $base/command/token"
if [ "${ENTERPRISE:+x}" == "x" ] ; then
@ -199,7 +200,7 @@ test_packages[7]+=" $base/vault/quotas"
# Total time: 779
test_packages[8]+=" $base/builtin/credential/aws/pkcs7"
test_packages[8]+=" $base/builtin/logical/totp"
test_packages[8]+=" $base/command/agent/auth"
test_packages[8]+=" $base/command/agentproxyshared/auth"
test_packages[8]+=" $base/physical/raft"
test_packages[8]+=" $base/sdk/framework"
test_packages[8]+=" $base/sdk/plugin"

3
changelog/20548.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
proxy: Introduced Vault Proxy, a new subcommand of the Vault binary that can be invoked using `vault proxy -config=config.hcl`. It currently has the same feature set as Vault Agent's API proxy, but the two may diverge in the future. We plan to deprecate the API proxy functionality of Vault Agent in a future release.
```

View File

@ -19,40 +19,37 @@ import (
"sync"
"time"
token_file "github.com/hashicorp/vault/command/agent/auth/token-file"
ctconfig "github.com/hashicorp/consul-template/config"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/command/agent/sink/inmem"
systemd "github.com/coreos/go-systemd/daemon"
ctconfig "github.com/hashicorp/consul-template/config"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/gatedwriter"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/reloadutil"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agent/auth/alicloud"
"github.com/hashicorp/vault/command/agent/auth/approle"
"github.com/hashicorp/vault/command/agent/auth/aws"
"github.com/hashicorp/vault/command/agent/auth/azure"
"github.com/hashicorp/vault/command/agent/auth/cert"
"github.com/hashicorp/vault/command/agent/auth/cf"
"github.com/hashicorp/vault/command/agent/auth/gcp"
"github.com/hashicorp/vault/command/agent/auth/jwt"
"github.com/hashicorp/vault/command/agent/auth/kerberos"
"github.com/hashicorp/vault/command/agent/auth/kubernetes"
"github.com/hashicorp/vault/command/agent/auth/oci"
"github.com/hashicorp/vault/command/agent/cache"
"github.com/hashicorp/vault/command/agent/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
"github.com/hashicorp/vault/command/agent/cache/keymanager"
agentConfig "github.com/hashicorp/vault/command/agent/config"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agent/template"
"github.com/hashicorp/vault/command/agent/winsvc"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth/alicloud"
"github.com/hashicorp/vault/command/agentproxyshared/auth/approle"
"github.com/hashicorp/vault/command/agentproxyshared/auth/aws"
"github.com/hashicorp/vault/command/agentproxyshared/auth/azure"
"github.com/hashicorp/vault/command/agentproxyshared/auth/cert"
"github.com/hashicorp/vault/command/agentproxyshared/auth/cf"
"github.com/hashicorp/vault/command/agentproxyshared/auth/gcp"
"github.com/hashicorp/vault/command/agentproxyshared/auth/jwt"
"github.com/hashicorp/vault/command/agentproxyshared/auth/kerberos"
"github.com/hashicorp/vault/command/agentproxyshared/auth/kubernetes"
"github.com/hashicorp/vault/command/agentproxyshared/auth/oci"
token_file "github.com/hashicorp/vault/command/agentproxyshared/auth/token-file"
cache "github.com/hashicorp/vault/command/agentproxyshared/cache"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cachememdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/keymanager"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/sink/inmem"
"github.com/hashicorp/vault/command/agentproxyshared/winsvc"
"github.com/hashicorp/vault/helper/logging"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/helper/useragent"
@ -65,6 +62,8 @@ import (
"github.com/mitchellh/cli"
"github.com/oklog/run"
"github.com/posener/complete"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"google.golang.org/grpc/test/bufconn"
)
@ -258,7 +257,7 @@ func (c *AgentCommand) Run(args []string) int {
// Ignore any setting of Agent's address. This client is used by the Agent
// to reach out to Vault. This should never loop back to agent.
c.flagAgentAddress = ""
c.flagAgentProxyAddress = ""
client, err := c.Client()
if err != nil {
c.UI.Error(fmt.Sprintf(
@ -505,10 +504,12 @@ func (c *AgentCommand) Run(args []string) int {
// The API proxy to be used, if listeners are configured
apiProxy, err := cache.NewAPIProxy(&cache.APIProxyConfig{
Client: proxyClient,
Logger: apiProxyLogger,
EnforceConsistency: enforceConsistency,
WhenInconsistentAction: whenInconsistent,
Client: proxyClient,
Logger: apiProxyLogger,
EnforceConsistency: enforceConsistency,
WhenInconsistentAction: whenInconsistent,
UserAgentStringFunction: useragent.AgentProxyStringWithProxiedUserAgent,
UserAgentString: useragent.AgentProxyString(),
})
if err != nil {
c.UI.Error(fmt.Sprintf("Error creating API proxy: %v", err))
@ -873,6 +874,8 @@ func (c *AgentCommand) Run(args []string) int {
EnableTemplateTokenCh: enableTokenCh,
Token: previousToken,
ExitOnError: config.AutoAuth.Method.ExitOnError,
UserAgent: useragent.AgentAutoAuthString(),
MetricsSignifier: "agent",
})
ss := sink.NewSinkServer(&sink.SinkServerConfig{
@ -949,12 +952,13 @@ func (c *AgentCommand) Run(args []string) int {
// Server configuration output
padding := 24
sort.Strings(infoKeys)
caser := cases.Title(language.English)
c.UI.Output("==> Vault Agent configuration:\n")
for _, k := range infoKeys {
c.UI.Output(fmt.Sprintf(
"%s%s: %s",
strings.Repeat(" ", padding-len(k)),
strings.Title(k),
caser.String(k),
info[k]))
}
c.UI.Output("")
@ -1200,7 +1204,7 @@ func (c *AgentCommand) handleMetrics() http.Handler {
w.Header().Set("Content-Type", resp.Data[logical.HTTPContentType].(string))
switch v := resp.Data[logical.HTTPRawBody].(type) {
case string:
w.WriteHeader((status))
w.WriteHeader(status)
w.Write([]byte(v))
case []byte:
w.WriteHeader(status)

View File

@ -19,10 +19,10 @@ import (
uuid "github.com/hashicorp/go-uuid"
vaultalicloud "github.com/hashicorp/vault-plugin-auth-alicloud"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
agentalicloud "github.com/hashicorp/vault/command/agent/auth/alicloud"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentalicloud "github.com/hashicorp/vault/command/agentproxyshared/auth/alicloud"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/helper/testhelpers"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"

View File

@ -16,10 +16,10 @@ import (
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/command/agent/auth"
agentapprole "github.com/hashicorp/vault/command/agent/auth/approle"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentapprole "github.com/hashicorp/vault/command/agentproxyshared/auth/approle"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"

View File

@ -13,10 +13,10 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/command/agent/auth"
agentAppRole "github.com/hashicorp/vault/command/agent/auth/approle"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentAppRole "github.com/hashicorp/vault/command/agentproxyshared/auth/approle"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"

View File

@ -18,10 +18,10 @@ import (
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/api"
vaultaws "github.com/hashicorp/vault/builtin/credential/aws"
"github.com/hashicorp/vault/command/agent/auth"
agentaws "github.com/hashicorp/vault/command/agent/auth/aws"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentaws "github.com/hashicorp/vault/command/agentproxyshared/auth/aws"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/helper/testhelpers"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"

View File

@ -17,12 +17,13 @@ import (
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/command/agent/auth"
agentapprole "github.com/hashicorp/vault/command/agent/auth/approle"
"github.com/hashicorp/vault/command/agent/cache"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agent/sink/inmem"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentapprole "github.com/hashicorp/vault/command/agentproxyshared/auth/approle"
cache "github.com/hashicorp/vault/command/agentproxyshared/cache"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/sink/inmem"
"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/logging"
@ -166,8 +167,10 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
// Create the API proxier
apiProxy, err := cache.NewAPIProxy(&cache.APIProxyConfig{
Client: client,
Logger: cacheLogger.Named("apiproxy"),
Client: client,
Logger: cacheLogger.Named("apiproxy"),
UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent,
UserAgentString: useragent.ProxyString(),
})
if err != nil {
t.Fatal(err)

View File

@ -12,16 +12,14 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/builtin/logical/pki"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
vaultcert "github.com/hashicorp/vault/builtin/credential/cert"
"github.com/hashicorp/vault/command/agent/auth"
agentcert "github.com/hashicorp/vault/command/agent/auth/cert"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/builtin/logical/pki"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentcert "github.com/hashicorp/vault/command/agentproxyshared/auth/cert"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/helper/dhutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/jsonutil"

View File

@ -15,10 +15,10 @@ import (
"github.com/hashicorp/vault-plugin-auth-cf/testing/certificates"
cfAPI "github.com/hashicorp/vault-plugin-auth-cf/testing/cf"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
agentcf "github.com/hashicorp/vault/command/agent/auth/cf"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentcf "github.com/hashicorp/vault/command/agentproxyshared/auth/cf"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"

View File

@ -14,10 +14,10 @@ import (
hclog "github.com/hashicorp/go-hclog"
vaultjwt "github.com/hashicorp/vault-plugin-auth-jwt"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
agentjwt "github.com/hashicorp/vault/command/agent/auth/jwt"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentjwt "github.com/hashicorp/vault/command/agentproxyshared/auth/jwt"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/helper/dhutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/jsonutil"

View File

@ -13,10 +13,10 @@ import (
hclog "github.com/hashicorp/go-hclog"
vaultoci "github.com/hashicorp/vault-plugin-auth-oci"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
agentoci "github.com/hashicorp/vault/command/agent/auth/oci"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
agentoci "github.com/hashicorp/vault/command/agentproxyshared/auth/oci"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
"github.com/hashicorp/vault/helper/testhelpers"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"

View File

@ -11,10 +11,10 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/auth"
token_file "github.com/hashicorp/vault/command/agent/auth/token-file"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agent/sink/file"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
token_file "github.com/hashicorp/vault/command/agentproxyshared/auth/token-file"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink/file"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/vault"

View File

@ -17,7 +17,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault-plugin-auth-alicloud/tools"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
/*

View File

@ -15,7 +15,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
type approleMethod struct {

View File

@ -14,7 +14,6 @@ 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"
)
@ -23,7 +22,7 @@ const (
defaultMaxBackoff = 5 * time.Minute
)
// AuthMethod is the interface that auto-auth methods implement for the agent
// AuthMethod is the interface that auto-auth methods implement for the agent/proxy
// to use.
type AuthMethod interface {
// Authenticate returns a mount path, header, request body, and error.
@ -54,6 +53,8 @@ type AuthHandler struct {
OutputCh chan string
TemplateTokenCh chan string
token string
userAgent string
metricsSignifier string
logger hclog.Logger
client *api.Client
random *rand.Rand
@ -66,12 +67,18 @@ type AuthHandler struct {
}
type AuthHandlerConfig struct {
Logger hclog.Logger
Client *api.Client
WrapTTL time.Duration
MaxBackoff time.Duration
MinBackoff time.Duration
Token string
Logger hclog.Logger
Client *api.Client
WrapTTL time.Duration
MaxBackoff time.Duration
MinBackoff time.Duration
Token string
// UserAgent is the HTTP UserAgent header auto-auth will use when
// communicating with Vault.
UserAgent string
// MetricsSignifier is the first argument we will give to
// metrics.IncrCounter, signifying what the name of the application is
MetricsSignifier string
EnableReauthOnNewCredentials bool
EnableTemplateTokenCh bool
ExitOnError bool
@ -80,7 +87,7 @@ type AuthHandlerConfig struct {
func NewAuthHandler(conf *AuthHandlerConfig) *AuthHandler {
ah := &AuthHandler{
// This is buffered so that if we try to output after the sink server
// has been shut down, during agent shutdown, we won't block
// has been shut down, during agent/proxy shutdown, we won't block
OutputCh: make(chan string, 1),
TemplateTokenCh: make(chan string, 1),
token: conf.Token,
@ -93,12 +100,14 @@ func NewAuthHandler(conf *AuthHandlerConfig) *AuthHandler {
enableReauthOnNewCredentials: conf.EnableReauthOnNewCredentials,
enableTemplateTokenCh: conf.EnableTemplateTokenCh,
exitOnError: conf.ExitOnError,
userAgent: conf.UserAgent,
metricsSignifier: conf.MetricsSignifier,
}
return ah
}
func backoff(ctx context.Context, backoff *agentBackoff) bool {
func backoff(ctx context.Context, backoff *autoAuthBackoff) bool {
if backoff.exitOnErr {
return false
}
@ -123,7 +132,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
ah.minBackoff = defaultMinBackoff
}
backoffCfg := newAgentBackoff(ah.minBackoff, ah.maxBackoff, ah.exitOnError)
backoffCfg := newAutoAuthBackoff(ah.minBackoff, ah.maxBackoff, ah.exitOnError)
if backoffCfg.min >= backoffCfg.max {
return errors.New("auth handler: min_backoff cannot be greater than max_backoff")
@ -162,7 +171,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
if headers == nil {
headers = make(http.Header)
}
headers.Set("User-Agent", useragent.AgentAutoAuthString())
headers.Set("User-Agent", ah.userAgent)
ah.client.SetHeaders(headers)
}
@ -189,7 +198,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
clientToUse, err = am.(AuthMethodWithClient).AuthClient(ah.client)
if err != nil {
ah.logger.Error("error creating client for authentication call", "error", err, "backoff", backoff)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -216,7 +225,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
secret, err = clientToUse.Auth().Token().LookupSelfWithContext(ctx)
if err != nil {
ah.logger.Error("could not look up token", "err", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -236,7 +245,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
path, header, data, err = am.Authenticate(ctx, ah.client)
if err != nil {
ah.logger.Error("error getting path or data from method", "error", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -249,7 +258,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
wrapClient, err := clientToUse.Clone()
if err != nil {
ah.logger.Error("error creating client for wrapped call", "error", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -287,7 +296,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
// Check errors/sanity
if err != nil {
ah.logger.Error("error authenticating", "error", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -302,7 +311,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
case ah.wrapTTL > 0:
if secret.WrapInfo == nil {
ah.logger.Error("authentication returned nil wrap info", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -311,7 +320,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
}
if secret.WrapInfo.Token == "" {
ah.logger.Error("authentication returned empty wrapped client token", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -321,7 +330,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
wrappedResp, err := jsonutil.EncodeJSON(secret.WrapInfo)
if err != nil {
ah.logger.Error("failed to encode wrapinfo", "error", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -357,7 +366,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
// i.e. if the token is invalid, we will fail in the authentication step
if secret == nil || secret.Data == nil {
ah.logger.Error("token file validation failed, token may be invalid", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -367,7 +376,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
token, ok := secret.Data["id"].(string)
if !ok || token == "" {
ah.logger.Error("token file validation returned empty client token", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -396,7 +405,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
} else {
if secret == nil || secret.Auth == nil {
ah.logger.Error("authentication returned nil auth info", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -405,7 +414,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
}
if secret.Auth.ClientToken == "" {
ah.logger.Error("authentication returned empty client token", "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -434,7 +443,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
})
if err != nil {
ah.logger.Error("error creating lifetime watcher", "error", err, "backoff", backoffCfg)
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
if backoff(ctx, backoffCfg) {
continue
@ -442,7 +451,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
return err
}
metrics.IncrCounter([]string{"agent", "auth", "success"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "success"}, 1)
// We don't want to trigger the renewal process for tokens with
// unlimited TTL, such as the root token.
if leaseDuration == 0 && isTokenFileMethod {
@ -463,13 +472,13 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
case err := <-watcher.DoneCh():
ah.logger.Info("lifetime watcher done channel triggered")
if err != nil {
metrics.IncrCounter([]string{"agent", "auth", "failure"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1)
ah.logger.Error("error renewing token", "error", err)
}
break LifetimeWatcherLoop
case <-watcher.RenewCh():
metrics.IncrCounter([]string{"agent", "auth", "success"}, 1)
metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "success"}, 1)
ah.logger.Info("renewed auth token")
case <-credCh:
@ -480,15 +489,15 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error {
}
}
// agentBackoff tracks exponential backoff state.
type agentBackoff struct {
// autoAuthBackoff tracks exponential backoff state.
type autoAuthBackoff struct {
min time.Duration
max time.Duration
current time.Duration
exitOnErr bool
}
func newAgentBackoff(min, max time.Duration, exitErr bool) *agentBackoff {
func newAutoAuthBackoff(min, max time.Duration, exitErr bool) *autoAuthBackoff {
if max <= 0 {
max = defaultMaxBackoff
}
@ -497,7 +506,7 @@ func newAgentBackoff(min, max time.Duration, exitErr bool) *agentBackoff {
min = defaultMinBackoff
}
return &agentBackoff{
return &autoAuthBackoff{
current: min,
max: max,
min: min,
@ -507,7 +516,7 @@ func newAgentBackoff(min, max time.Duration, exitErr bool) *agentBackoff {
// next determines the next backoff duration that is roughly twice
// the current value, capped to a max value, with a measure of randomness.
func (b *agentBackoff) next() {
func (b *autoAuthBackoff) next() {
maxBackoff := 2 * b.current
if maxBackoff > b.max {
@ -519,10 +528,10 @@ func (b *agentBackoff) next() {
b.current = maxBackoff - time.Duration(trim)
}
func (b *agentBackoff) reset() {
func (b *autoAuthBackoff) reset() {
b.current = b.min
}
func (b agentBackoff) String() string {
func (b autoAuthBackoff) String() string {
return b.current.Truncate(10 * time.Millisecond).String()
}

View File

@ -112,7 +112,7 @@ consumption:
func TestAgentBackoff(t *testing.T) {
max := 1024 * time.Second
backoff := newAgentBackoff(defaultMinBackoff, max, false)
backoff := newAutoAuthBackoff(defaultMinBackoff, max, false)
// Test initial value
if backoff.current != defaultMinBackoff {
@ -162,7 +162,7 @@ func TestAgentMinBackoffCustom(t *testing.T) {
for _, test := range tests {
max := 1024 * time.Second
backoff := newAgentBackoff(test.minBackoff, max, false)
backoff := newAutoAuthBackoff(test.minBackoff, max, false)
// Test initial value
if backoff.current != test.want {

View File

@ -20,7 +20,7 @@ import (
"github.com/hashicorp/go-secure-stdlib/awsutil"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
const (

View File

@ -13,7 +13,7 @@ import (
cleanhttp "github.com/hashicorp/go-cleanhttp"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/helper/useragent"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
)

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/sdk/helper/consts"
)

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
func TestCertAuthMethod_Authenticate(t *testing.T) {

View File

@ -15,7 +15,7 @@ import (
cf "github.com/hashicorp/vault-plugin-auth-cf"
"github.com/hashicorp/vault-plugin-auth-cf/signatures"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
type cfMethod struct {

View File

@ -17,7 +17,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"golang.org/x/oauth2"
"google.golang.org/api/iamcredentials/v1"
)

View File

@ -17,7 +17,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/sdk/helper/parseutil"
)

View File

@ -12,7 +12,7 @@ import (
"testing"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
func TestIngressToken(t *testing.T) {

View File

@ -13,7 +13,7 @@ import (
"github.com/hashicorp/go-secure-stdlib/parseutil"
kerberos "github.com/hashicorp/vault-plugin-auth-kerberos"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/jcmturner/gokrb5/v8/spnego"
)

View File

@ -7,7 +7,7 @@ import (
"testing"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
func TestNewKerberosAuthMethod(t *testing.T) {

View File

@ -15,7 +15,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
const (

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/errwrap"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/sdk/helper/logging"
)

View File

@ -18,7 +18,7 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/oracle/oci-go-sdk/common"
ociAuth "github.com/oracle/oci-go-sdk/common/auth"
)

View File

@ -13,7 +13,7 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
)
type tokenFileMethod struct {

View File

@ -9,7 +9,7 @@ import (
"testing"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/auth"
"github.com/hashicorp/vault/command/agentproxyshared/auth"
"github.com/hashicorp/vault/sdk/helper/logging"
)

View File

@ -12,7 +12,6 @@ import (
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"
)
@ -34,12 +33,14 @@ const (
// APIProxy is an implementation of the proxier interface that is used to
// forward the request to Vault and get the response.
type APIProxy struct {
client *api.Client
logger hclog.Logger
enforceConsistency EnforceConsistency
whenInconsistentAction WhenInconsistentAction
l sync.RWMutex
lastIndexStates []string
client *api.Client
logger hclog.Logger
enforceConsistency EnforceConsistency
whenInconsistentAction WhenInconsistentAction
l sync.RWMutex
lastIndexStates []string
userAgentString string
userAgentStringFunction func(string) string
}
var _ Proxier = &APIProxy{}
@ -49,6 +50,12 @@ type APIProxyConfig struct {
Logger hclog.Logger
EnforceConsistency EnforceConsistency
WhenInconsistentAction WhenInconsistentAction
// UserAgentString is used as the User Agent when the proxied client
// does not have a user agent of its own.
UserAgentString string
// UserAgentStringFunction is the function to transform the proxied client's
// user agent into one that includes Vault-specific information.
UserAgentStringFunction func(string) string
}
func NewAPIProxy(config *APIProxyConfig) (Proxier, error) {
@ -56,10 +63,12 @@ func NewAPIProxy(config *APIProxyConfig) (Proxier, error) {
return nil, fmt.Errorf("nil API client")
}
return &APIProxy{
client: config.Client,
logger: config.Logger,
enforceConsistency: config.EnforceConsistency,
whenInconsistentAction: config.WhenInconsistentAction,
client: config.Client,
logger: config.Logger,
enforceConsistency: config.EnforceConsistency,
whenInconsistentAction: config.WhenInconsistentAction,
userAgentString: config.UserAgentString,
userAgentStringFunction: config.UserAgentStringFunction,
}, nil
}
@ -87,9 +96,9 @@ func (ap *APIProxy) Send(ctx context.Context, req *SendRequest) (*SendResponse,
// 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))
req.Request.Header.Set("User-Agent", ap.userAgentStringFunction(initialUserAgent))
} else {
req.Request.Header.Set("User-Agent", useragent.AgentProxyString())
req.Request.Header.Set("User-Agent", ap.userAgentString)
}
client.SetHeaders(req.Request.Header)

View File

@ -12,6 +12,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/useragent"
"github.com/hashicorp/vault/builtin/credential/userpass"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
@ -35,8 +37,10 @@ func TestAPIProxy(t *testing.T) {
defer cleanup()
proxier, err := NewAPIProxy(&APIProxyConfig{
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent,
UserAgentString: useragent.ProxyString(),
})
if err != nil {
t.Fatal(err)
@ -71,8 +75,10 @@ func TestAPIProxyNoCache(t *testing.T) {
defer cleanup()
proxier, err := NewAPIProxy(&APIProxyConfig{
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent,
UserAgentString: useragent.ProxyString(),
})
if err != nil {
t.Fatal(err)
@ -109,8 +115,10 @@ func TestAPIProxy_queryParams(t *testing.T) {
defer cleanup()
proxier, err := NewAPIProxy(&APIProxyConfig{
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
Client: client,
Logger: logging.NewVaultLogger(hclog.Trace),
UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent,
UserAgentString: useragent.ProxyString(),
})
if err != nil {
t.Fatal(err)
@ -253,8 +261,10 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v
// Create the API proxier
apiProxy, err := NewAPIProxy(&APIProxyConfig{
Client: clienToUse,
Logger: apiProxyLogger,
Client: clienToUse,
Logger: apiProxyLogger,
UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent,
UserAgentString: useragent.ProxyString(),
})
if err != nil {
t.Fatal(err)

View File

@ -19,8 +19,8 @@ import (
"github.com/hashicorp/go-hclog"
kv "github.com/hashicorp/vault-plugin-secrets-kv"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
"github.com/hashicorp/vault/command/agent/sink/mock"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cachememdb"
"github.com/hashicorp/vault/command/agentproxyshared/sink/mock"
"github.com/hashicorp/vault/helper/namespace"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/consts"

View File

@ -16,7 +16,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/cache/keymanager"
"github.com/hashicorp/vault/command/agentproxyshared/cache/keymanager"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
bolt "go.etcd.io/bbolt"

View File

@ -18,7 +18,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
)

View File

@ -23,8 +23,8 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/base62"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cachememdb"
"github.com/hashicorp/vault/helper/namespace"
nshelper "github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/useragent"

View File

@ -17,15 +17,14 @@ 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"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/command/agent/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
"github.com/hashicorp/vault/command/agent/cache/keymanager"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cacheboltdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/cachememdb"
"github.com/hashicorp/vault/command/agentproxyshared/cache/keymanager"
"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/logging"

View File

@ -12,7 +12,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
)
// fileSink is a Sink implementation that writes a token to a file

View File

@ -12,7 +12,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/sdk/helper/logging"
)

View File

@ -15,7 +15,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"github.com/hashicorp/vault/sdk/helper/logging"
)

View File

@ -7,8 +7,8 @@ import (
"errors"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/command/agent/cache"
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/cache"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
"go.uber.org/atomic"
)

View File

@ -4,7 +4,7 @@
package mock
import (
"github.com/hashicorp/vault/command/agent/sink"
"github.com/hashicorp/vault/command/agentproxyshared/sink"
)
type mockSink struct {

View File

@ -42,20 +42,20 @@ type BaseCommand struct {
flags *FlagSets
flagsOnce sync.Once
flagAddress string
flagAgentAddress string
flagCACert string
flagCAPath string
flagClientCert string
flagClientKey string
flagNamespace string
flagNS string
flagPolicyOverride bool
flagTLSServerName string
flagTLSSkipVerify bool
flagDisableRedirects bool
flagWrapTTL time.Duration
flagUnlockKey string
flagAddress string
flagAgentProxyAddress string
flagCACert string
flagCAPath string
flagClientCert string
flagClientKey string
flagNamespace string
flagNS string
flagPolicyOverride bool
flagTLSServerName string
flagTLSSkipVerify bool
flagDisableRedirects bool
flagWrapTTL time.Duration
flagUnlockKey string
flagFormat string
flagField string
@ -90,8 +90,8 @@ func (c *BaseCommand) Client() (*api.Client, error) {
if c.flagAddress != "" {
config.Address = c.flagAddress
}
if c.flagAgentAddress != "" {
config.Address = c.flagAgentAddress
if c.flagAgentProxyAddress != "" {
config.Address = c.flagAgentProxyAddress
}
if c.flagOutputCurlString {
@ -330,7 +330,7 @@ func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
agentAddrStringVar := &StringVar{
Name: "agent-address",
Target: &c.flagAgentAddress,
Target: &c.flagAgentProxyAddress,
EnvVar: api.EnvVaultAgentAddr,
Completion: complete.PredictAnything,
Usage: "Address of the Agent.",

View File

@ -603,6 +603,15 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co
BaseCommand: getBaseCommand(),
}, nil
},
"proxy": func() (cli.Command, error) {
return &ProxyCommand{
BaseCommand: &BaseCommand{
UI: serverCmdUi,
},
ShutdownCh: MakeShutdownCh(),
SighupCh: MakeSighupCh(),
}, nil
},
"policy": func() (cli.Command, error) {
return &PolicyCommand{
BaseCommand: getBaseCommand(),

1294
command/proxy.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,840 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package config
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strings"
"time"
ctconfig "github.com/hashicorp/consul-template/config"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/internalshared/configutil"
)
// Config is the configuration for Vault Proxy.
type Config struct {
*configutil.SharedConfig `hcl:"-"`
AutoAuth *AutoAuth `hcl:"auto_auth"`
ExitAfterAuth bool `hcl:"exit_after_auth"`
Cache *Cache `hcl:"cache"`
APIProxy *APIProxy `hcl:"api_proxy""`
Vault *Vault `hcl:"vault"`
DisableIdleConns []string `hcl:"disable_idle_connections"`
DisableIdleConnsAPIProxy bool `hcl:"-"`
DisableIdleConnsAutoAuth bool `hcl:"-"`
DisableKeepAlives []string `hcl:"disable_keep_alives"`
DisableKeepAlivesAPIProxy bool `hcl:"-"`
DisableKeepAlivesAutoAuth bool `hcl:"-"`
}
const (
DisableIdleConnsEnv = "VAULT_PROXY_DISABLE_IDLE_CONNECTIONS"
DisableKeepAlivesEnv = "VAULT_PROXY_DISABLE_KEEP_ALIVES"
)
func (c *Config) Prune() {
for _, l := range c.Listeners {
l.RawConfig = nil
l.Profiling.UnusedKeys = nil
l.Telemetry.UnusedKeys = nil
l.CustomResponseHeaders = nil
}
c.FoundKeys = nil
c.UnusedKeys = nil
c.SharedConfig.FoundKeys = nil
c.SharedConfig.UnusedKeys = nil
if c.Telemetry != nil {
c.Telemetry.FoundKeys = nil
c.Telemetry.UnusedKeys = nil
}
}
type Retry struct {
NumRetries int `hcl:"num_retries"`
}
// Vault contains configuration for connecting to Vault servers
type Vault struct {
Address string `hcl:"address"`
CACert string `hcl:"ca_cert"`
CAPath string `hcl:"ca_path"`
TLSSkipVerify bool `hcl:"-"`
TLSSkipVerifyRaw interface{} `hcl:"tls_skip_verify"`
ClientCert string `hcl:"client_cert"`
ClientKey string `hcl:"client_key"`
TLSServerName string `hcl:"tls_server_name"`
Retry *Retry `hcl:"retry"`
}
// transportDialer is an interface that allows passing a custom dialer function
// to an HTTP client's transport config
type transportDialer interface {
// Dial is intended to match https://pkg.go.dev/net#Dialer.Dial
Dial(network, address string) (net.Conn, error)
// DialContext is intended to match https://pkg.go.dev/net#Dialer.DialContext
DialContext(ctx context.Context, network, address string) (net.Conn, error)
}
// APIProxy contains any configuration needed for proxy mode
type APIProxy struct {
UseAutoAuthTokenRaw interface{} `hcl:"use_auto_auth_token"`
UseAutoAuthToken bool `hcl:"-"`
ForceAutoAuthToken bool `hcl:"-"`
EnforceConsistency string `hcl:"enforce_consistency"`
WhenInconsistent string `hcl:"when_inconsistent"`
}
// Cache contains any configuration needed for Cache mode
type Cache struct {
Persist *Persist `hcl:"persist"`
InProcDialer transportDialer `hcl:"-"`
}
// Persist contains configuration needed for persistent caching
type Persist struct {
Type string
Path string `hcl:"path"`
KeepAfterImport bool `hcl:"keep_after_import"`
ExitOnErr bool `hcl:"exit_on_err"`
ServiceAccountTokenFile string `hcl:"service_account_token_file"`
}
// AutoAuth is the configured authentication method and sinks
type AutoAuth struct {
Method *Method `hcl:"-"`
Sinks []*Sink `hcl:"sinks"`
// NOTE: This is unsupported outside of testing and may disappear at any
// time.
EnableReauthOnNewCredentials bool `hcl:"enable_reauth_on_new_credentials"`
}
// Method represents the configuration for the authentication backend
type Method struct {
Type string
MountPath string `hcl:"mount_path"`
WrapTTLRaw interface{} `hcl:"wrap_ttl"`
WrapTTL time.Duration `hcl:"-"`
MinBackoffRaw interface{} `hcl:"min_backoff"`
MinBackoff time.Duration `hcl:"-"`
MaxBackoffRaw interface{} `hcl:"max_backoff"`
MaxBackoff time.Duration `hcl:"-"`
Namespace string `hcl:"namespace"`
ExitOnError bool `hcl:"exit_on_err"`
Config map[string]interface{}
}
// Sink defines a location to write the authenticated token
type Sink struct {
Type string
WrapTTLRaw interface{} `hcl:"wrap_ttl"`
WrapTTL time.Duration `hcl:"-"`
DHType string `hcl:"dh_type"`
DeriveKey bool `hcl:"derive_key"`
DHPath string `hcl:"dh_path"`
AAD string `hcl:"aad"`
AADEnvVar string `hcl:"aad_env_var"`
Config map[string]interface{}
}
func NewConfig() *Config {
return &Config{
SharedConfig: new(configutil.SharedConfig),
}
}
// Merge merges two Proxy configurations.
func (c *Config) Merge(c2 *Config) *Config {
if c2 == nil {
return c
}
result := NewConfig()
result.SharedConfig = c.SharedConfig
if c2.SharedConfig != nil {
result.SharedConfig = c.SharedConfig.Merge(c2.SharedConfig)
}
result.AutoAuth = c.AutoAuth
if c2.AutoAuth != nil {
result.AutoAuth = c2.AutoAuth
}
result.Cache = c.Cache
if c2.Cache != nil {
result.Cache = c2.Cache
}
result.APIProxy = c.APIProxy
if c2.APIProxy != nil {
result.APIProxy = c2.APIProxy
}
result.DisableMlock = c.DisableMlock
if c2.DisableMlock {
result.DisableMlock = c2.DisableMlock
}
// For these, ignore the non-specific one and overwrite them all
result.DisableIdleConnsAutoAuth = c.DisableIdleConnsAutoAuth
if c2.DisableIdleConnsAutoAuth {
result.DisableIdleConnsAutoAuth = c2.DisableIdleConnsAutoAuth
}
result.DisableIdleConnsAPIProxy = c.DisableIdleConnsAPIProxy
if c2.DisableIdleConnsAPIProxy {
result.DisableIdleConnsAPIProxy = c2.DisableIdleConnsAPIProxy
}
result.DisableKeepAlivesAutoAuth = c.DisableKeepAlivesAutoAuth
if c2.DisableKeepAlivesAutoAuth {
result.DisableKeepAlivesAutoAuth = c2.DisableKeepAlivesAutoAuth
}
result.DisableKeepAlivesAPIProxy = c.DisableKeepAlivesAPIProxy
if c2.DisableKeepAlivesAPIProxy {
result.DisableKeepAlivesAPIProxy = c2.DisableKeepAlivesAPIProxy
}
result.ExitAfterAuth = c.ExitAfterAuth
if c2.ExitAfterAuth {
result.ExitAfterAuth = c2.ExitAfterAuth
}
result.Vault = c.Vault
if c2.Vault != nil {
result.Vault = c2.Vault
}
result.PidFile = c.PidFile
if c2.PidFile != "" {
result.PidFile = c2.PidFile
}
return result
}
// ValidateConfig validates a Vault configuration after it has been fully merged together, to
// ensure that required combinations of configs are there
func (c *Config) ValidateConfig() error {
if c.Cache != nil {
if len(c.Listeners) < 1 {
return fmt.Errorf("enabling the cache requires at least 1 listener to be defined")
}
}
if c.APIProxy != nil {
if len(c.Listeners) < 1 {
return fmt.Errorf("configuring the api_proxy requires at least 1 listener to be defined")
}
if c.APIProxy.UseAutoAuthToken {
if c.AutoAuth == nil {
return fmt.Errorf("api_proxy.use_auto_auth_token is true but auto_auth not configured")
}
if c.AutoAuth != nil && c.AutoAuth.Method != nil && c.AutoAuth.Method.WrapTTL > 0 {
return fmt.Errorf("api_proxy.use_auto_auth_token is true and auto_auth uses wrapping")
}
}
}
if c.AutoAuth != nil {
if len(c.AutoAuth.Sinks) == 0 &&
(c.APIProxy == nil || !c.APIProxy.UseAutoAuthToken) {
return fmt.Errorf("auto_auth requires at least one sink or api_proxy.use_auto_auth_token=true")
}
}
if c.AutoAuth == nil && c.Cache == nil && len(c.Listeners) == 0 {
return fmt.Errorf("no auto_auth, cache, or listener block found in config")
}
return nil
}
// LoadConfig loads the configuration at the given path, regardless if
// it's a file or directory.
func LoadConfig(path string) (*Config, error) {
fi, err := os.Stat(path)
if err != nil {
return nil, err
}
if fi.IsDir() {
return LoadConfigDir(path)
}
return LoadConfigFile(path)
}
// LoadConfigDir loads the configuration at the given path if it's a directory
func LoadConfigDir(dir string) (*Config, error) {
f, err := os.Open(dir)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
if !fi.IsDir() {
return nil, fmt.Errorf("configuration path must be a directory: %q", dir)
}
var files []string
err = nil
for err != io.EOF {
var fis []os.FileInfo
fis, err = f.Readdir(128)
if err != nil && err != io.EOF {
return nil, err
}
for _, fi := range fis {
// Ignore directories
if fi.IsDir() {
continue
}
// Only care about files that are valid to load.
name := fi.Name()
skip := true
if strings.HasSuffix(name, ".hcl") {
skip = false
} else if strings.HasSuffix(name, ".json") {
skip = false
}
if skip || isTemporaryFile(name) {
continue
}
path := filepath.Join(dir, name)
files = append(files, path)
}
}
result := NewConfig()
for _, f := range files {
config, err := LoadConfigFile(f)
if err != nil {
return nil, fmt.Errorf("error loading %q: %w", f, err)
}
if result == nil {
result = config
} else {
result = result.Merge(config)
}
}
return result, nil
}
// isTemporaryFile returns true or false depending on whether the
// provided file name is a temporary file for the following editors:
// emacs or vim.
func isTemporaryFile(name string) bool {
return strings.HasSuffix(name, "~") || // vim
strings.HasPrefix(name, ".#") || // emacs
(strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs
}
// LoadConfigFile loads the configuration at the given path if it's a file
func LoadConfigFile(path string) (*Config, error) {
fi, err := os.Stat(path)
if err != nil {
return nil, err
}
if fi.IsDir() {
return nil, fmt.Errorf("location is a directory, not a file")
}
// Read the file
d, err := os.ReadFile(path)
if err != nil {
return nil, err
}
// Parse!
obj, err := hcl.Parse(string(d))
if err != nil {
return nil, err
}
// Attribute
ast.Walk(obj, func(n ast.Node) (ast.Node, bool) {
if k, ok := n.(*ast.ObjectKey); ok {
k.Token.Pos.Filename = path
}
return n, true
})
// Start building the result
result := NewConfig()
if err := hcl.DecodeObject(result, obj); err != nil {
return nil, err
}
sharedConfig, err := configutil.ParseConfig(string(d))
if err != nil {
return nil, err
}
// Pruning custom headers for Vault for now
for _, ln := range sharedConfig.Listeners {
ln.CustomResponseHeaders = nil
}
result.SharedConfig = sharedConfig
list, ok := obj.Node.(*ast.ObjectList)
if !ok {
return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
}
if err := parseAutoAuth(result, list); err != nil {
return nil, fmt.Errorf("error parsing 'auto_auth': %w", err)
}
if err := parseCache(result, list); err != nil {
return nil, fmt.Errorf("error parsing 'cache':%w", err)
}
if err := parseAPIProxy(result, list); err != nil {
return nil, fmt.Errorf("error parsing 'api_proxy':%w", err)
}
err = parseVault(result, list)
if err != nil {
return nil, fmt.Errorf("error parsing 'vault':%w", err)
}
if result.Vault != nil {
// Set defaults
if result.Vault.Retry == nil {
result.Vault.Retry = &Retry{}
}
switch result.Vault.Retry.NumRetries {
case 0:
result.Vault.Retry.NumRetries = ctconfig.DefaultRetryAttempts
case -1:
result.Vault.Retry.NumRetries = 0
}
}
if disableIdleConnsEnv := os.Getenv(DisableIdleConnsEnv); disableIdleConnsEnv != "" {
result.DisableIdleConns, err = parseutil.ParseCommaStringSlice(strings.ToLower(disableIdleConnsEnv))
if err != nil {
return nil, fmt.Errorf("error parsing environment variable %s: %v", DisableIdleConnsEnv, err)
}
}
for _, subsystem := range result.DisableIdleConns {
switch subsystem {
case "auto-auth":
result.DisableIdleConnsAutoAuth = true
case "caching", "proxying":
result.DisableIdleConnsAPIProxy = true
case "":
continue
default:
return nil, fmt.Errorf("unknown disable_idle_connections value: %s", subsystem)
}
}
if disableKeepAlivesEnv := os.Getenv(DisableKeepAlivesEnv); disableKeepAlivesEnv != "" {
result.DisableKeepAlives, err = parseutil.ParseCommaStringSlice(strings.ToLower(disableKeepAlivesEnv))
if err != nil {
return nil, fmt.Errorf("error parsing environment variable %s: %v", DisableKeepAlivesEnv, err)
}
}
for _, subsystem := range result.DisableKeepAlives {
switch subsystem {
case "auto-auth":
result.DisableKeepAlivesAutoAuth = true
case "caching", "proxying":
result.DisableKeepAlivesAPIProxy = true
case "":
continue
default:
return nil, fmt.Errorf("unknown disable_keep_alives value: %s", subsystem)
}
}
return result, nil
}
func parseVault(result *Config, list *ast.ObjectList) error {
name := "vault"
vaultList := list.Filter(name)
if len(vaultList.Items) == 0 {
return nil
}
if len(vaultList.Items) > 1 {
return fmt.Errorf("one and only one %q block is required", name)
}
item := vaultList.Items[0]
var v Vault
err := hcl.DecodeObject(&v, item.Val)
if err != nil {
return err
}
if v.TLSSkipVerifyRaw != nil {
v.TLSSkipVerify, err = parseutil.ParseBool(v.TLSSkipVerifyRaw)
if err != nil {
return err
}
}
result.Vault = &v
subs, ok := item.Val.(*ast.ObjectType)
if !ok {
return fmt.Errorf("could not parse %q as an object", name)
}
if err := parseRetry(result, subs.List); err != nil {
return fmt.Errorf("error parsing 'retry': %w", err)
}
return nil
}
func parseRetry(result *Config, list *ast.ObjectList) error {
name := "retry"
retryList := list.Filter(name)
if len(retryList.Items) == 0 {
return nil
}
if len(retryList.Items) > 1 {
return fmt.Errorf("one and only one %q block is required", name)
}
item := retryList.Items[0]
var r Retry
err := hcl.DecodeObject(&r, item.Val)
if err != nil {
return err
}
result.Vault.Retry = &r
return nil
}
func parseAPIProxy(result *Config, list *ast.ObjectList) error {
name := "api_proxy"
apiProxyList := list.Filter(name)
if len(apiProxyList.Items) == 0 {
return nil
}
if len(apiProxyList.Items) > 1 {
return fmt.Errorf("one and only one %q block is required", name)
}
item := apiProxyList.Items[0]
var apiProxy APIProxy
err := hcl.DecodeObject(&apiProxy, item.Val)
if err != nil {
return err
}
if apiProxy.UseAutoAuthTokenRaw != nil {
apiProxy.UseAutoAuthToken, err = parseutil.ParseBool(apiProxy.UseAutoAuthTokenRaw)
if err != nil {
// Could be a value of "force" instead of "true"/"false"
switch apiProxy.UseAutoAuthTokenRaw.(type) {
case string:
v := apiProxy.UseAutoAuthTokenRaw.(string)
if !strings.EqualFold(v, "force") {
return fmt.Errorf("value of 'use_auto_auth_token' can be either true/false/force, %q is an invalid option", apiProxy.UseAutoAuthTokenRaw)
}
apiProxy.UseAutoAuthToken = true
apiProxy.ForceAutoAuthToken = true
default:
return err
}
}
}
result.APIProxy = &apiProxy
return nil
}
func parseCache(result *Config, list *ast.ObjectList) error {
name := "cache"
cacheList := list.Filter(name)
if len(cacheList.Items) == 0 {
return nil
}
if len(cacheList.Items) > 1 {
return fmt.Errorf("one and only one %q block is required", name)
}
item := cacheList.Items[0]
var c Cache
err := hcl.DecodeObject(&c, item.Val)
if err != nil {
return err
}
result.Cache = &c
subs, ok := item.Val.(*ast.ObjectType)
if !ok {
return fmt.Errorf("could not parse %q as an object", name)
}
subList := subs.List
if err := parsePersist(result, subList); err != nil {
return fmt.Errorf("error parsing persist: %w", err)
}
return nil
}
func parsePersist(result *Config, list *ast.ObjectList) error {
name := "persist"
persistList := list.Filter(name)
if len(persistList.Items) == 0 {
return nil
}
if len(persistList.Items) > 1 {
return fmt.Errorf("only one %q block is required", name)
}
item := persistList.Items[0]
var p Persist
err := hcl.DecodeObject(&p, item.Val)
if err != nil {
return err
}
if p.Type == "" {
if len(item.Keys) == 1 {
p.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
}
if p.Type == "" {
return errors.New("persist type must be specified")
}
}
result.Cache.Persist = &p
return nil
}
func parseAutoAuth(result *Config, list *ast.ObjectList) error {
name := "auto_auth"
autoAuthList := list.Filter(name)
if len(autoAuthList.Items) == 0 {
return nil
}
if len(autoAuthList.Items) > 1 {
return fmt.Errorf("at most one %q block is allowed", name)
}
// Get our item
item := autoAuthList.Items[0]
var a AutoAuth
if err := hcl.DecodeObject(&a, item.Val); err != nil {
return err
}
result.AutoAuth = &a
subs, ok := item.Val.(*ast.ObjectType)
if !ok {
return fmt.Errorf("could not parse %q as an object", name)
}
subList := subs.List
if err := parseMethod(result, subList); err != nil {
return fmt.Errorf("error parsing 'method': %w", err)
}
if a.Method == nil {
return fmt.Errorf("no 'method' block found")
}
if err := parseSinks(result, subList); err != nil {
return fmt.Errorf("error parsing 'sink' stanzas: %w", err)
}
if result.AutoAuth.Method.WrapTTL > 0 {
if len(result.AutoAuth.Sinks) != 1 {
return fmt.Errorf("error parsing auto_auth: wrapping enabled on auth method and 0 or many sinks defined")
}
if result.AutoAuth.Sinks[0].WrapTTL > 0 {
return fmt.Errorf("error parsing auto_auth: wrapping enabled both on auth method and sink")
}
}
if result.AutoAuth.Method.MaxBackoffRaw != nil {
var err error
if result.AutoAuth.Method.MaxBackoff, err = parseutil.ParseDurationSecond(result.AutoAuth.Method.MaxBackoffRaw); err != nil {
return err
}
result.AutoAuth.Method.MaxBackoffRaw = nil
}
if result.AutoAuth.Method.MinBackoffRaw != nil {
var err error
if result.AutoAuth.Method.MinBackoff, err = parseutil.ParseDurationSecond(result.AutoAuth.Method.MinBackoffRaw); err != nil {
return err
}
result.AutoAuth.Method.MinBackoffRaw = nil
}
return nil
}
func parseMethod(result *Config, list *ast.ObjectList) error {
name := "method"
methodList := list.Filter(name)
if len(methodList.Items) != 1 {
return fmt.Errorf("one and only one %q block is required", name)
}
// Get our item
item := methodList.Items[0]
var m Method
if err := hcl.DecodeObject(&m, item.Val); err != nil {
return err
}
if m.Type == "" {
if len(item.Keys) == 1 {
m.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
}
if m.Type == "" {
return errors.New("method type must be specified")
}
}
// Default to Vault's default
if m.MountPath == "" {
m.MountPath = fmt.Sprintf("auth/%s", m.Type)
}
// Standardize on no trailing slash
m.MountPath = strings.TrimSuffix(m.MountPath, "/")
if m.WrapTTLRaw != nil {
var err error
if m.WrapTTL, err = parseutil.ParseDurationSecond(m.WrapTTLRaw); err != nil {
return err
}
m.WrapTTLRaw = nil
}
// Canonicalize namespace path if provided
m.Namespace = namespace.Canonicalize(m.Namespace)
result.AutoAuth.Method = &m
return nil
}
func parseSinks(result *Config, list *ast.ObjectList) error {
name := "sink"
sinkList := list.Filter(name)
if len(sinkList.Items) < 1 {
return nil
}
var ts []*Sink
for _, item := range sinkList.Items {
var s Sink
if err := hcl.DecodeObject(&s, item.Val); err != nil {
return err
}
if s.Type == "" {
if len(item.Keys) == 1 {
s.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
}
if s.Type == "" {
return errors.New("sink type must be specified")
}
}
if s.WrapTTLRaw != nil {
var err error
if s.WrapTTL, err = parseutil.ParseDurationSecond(s.WrapTTLRaw); err != nil {
return multierror.Prefix(err, fmt.Sprintf("sink.%s", s.Type))
}
s.WrapTTLRaw = nil
}
switch s.DHType {
case "":
case "curve25519":
default:
return multierror.Prefix(errors.New("invalid value for 'dh_type'"), fmt.Sprintf("sink.%s", s.Type))
}
if s.AADEnvVar != "" {
s.AAD = os.Getenv(s.AADEnvVar)
s.AADEnvVar = ""
}
switch {
case s.DHPath == "" && s.DHType == "":
if s.AAD != "" {
return multierror.Prefix(errors.New("specifying AAD data without 'dh_type' does not make sense"), fmt.Sprintf("sink.%s", s.Type))
}
if s.DeriveKey {
return multierror.Prefix(errors.New("specifying 'derive_key' data without 'dh_type' does not make sense"), fmt.Sprintf("sink.%s", s.Type))
}
case s.DHPath != "" && s.DHType != "":
default:
return multierror.Prefix(errors.New("'dh_type' and 'dh_path' must be specified together"), fmt.Sprintf("sink.%s", s.Type))
}
ts = append(ts, &s)
}
result.AutoAuth.Sinks = ts
return nil
}

View File

@ -0,0 +1,118 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package config
import (
"testing"
"github.com/go-test/deep"
"github.com/hashicorp/vault/internalshared/configutil"
)
// TestLoadConfigFile_ProxyCache tests loading a config file containing a cache
// as well as a valid proxy config.
func TestLoadConfigFile_ProxyCache(t *testing.T) {
config, err := LoadConfigFile("./test-fixtures/config-cache.hcl")
if err != nil {
t.Fatal(err)
}
expected := &Config{
SharedConfig: &configutil.SharedConfig{
PidFile: "./pidfile",
Listeners: []*configutil.Listener{
{
Type: "unix",
Address: "/path/to/socket",
TLSDisable: true,
SocketMode: "configmode",
SocketUser: "configuser",
SocketGroup: "configgroup",
},
{
Type: "tcp",
Address: "127.0.0.1:8300",
TLSDisable: true,
},
{
Type: "tcp",
Address: "127.0.0.1:3000",
Role: "metrics_only",
TLSDisable: true,
},
{
Type: "tcp",
Role: "default",
Address: "127.0.0.1:8400",
TLSKeyFile: "/path/to/cakey.pem",
TLSCertFile: "/path/to/cacert.pem",
},
},
},
AutoAuth: &AutoAuth{
Method: &Method{
Type: "aws",
MountPath: "auth/aws",
Config: map[string]interface{}{
"role": "foobar",
},
},
Sinks: []*Sink{
{
Type: "file",
DHType: "curve25519",
DHPath: "/tmp/file-foo-dhpath",
AAD: "foobar",
Config: map[string]interface{}{
"path": "/tmp/file-foo",
},
},
},
},
APIProxy: &APIProxy{
EnforceConsistency: "always",
WhenInconsistent: "retry",
UseAutoAuthTokenRaw: true,
UseAutoAuthToken: true,
ForceAutoAuthToken: false,
},
Cache: &Cache{
Persist: &Persist{
Type: "kubernetes",
Path: "/vault/agent-cache/",
KeepAfterImport: true,
ExitOnErr: true,
ServiceAccountTokenFile: "/tmp/serviceaccount/token",
},
},
Vault: &Vault{
Address: "http://127.0.0.1:1111",
CACert: "config_ca_cert",
CAPath: "config_ca_path",
TLSSkipVerifyRaw: interface{}("true"),
TLSSkipVerify: true,
ClientCert: "config_client_cert",
ClientKey: "config_client_key",
Retry: &Retry{
NumRetries: 12,
},
},
}
config.Prune()
if diff := deep.Equal(config, expected); diff != nil {
t.Fatal(diff)
}
config, err = LoadConfigFile("./test-fixtures/config-cache-embedded-type.hcl")
if err != nil {
t.Fatal(err)
}
expected.Vault.TLSSkipVerifyRaw = interface{}(true)
config.Prune()
if diff := deep.Equal(config, expected); diff != nil {
t.Fatal(diff)
}
}

View File

@ -0,0 +1,77 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0
pid_file = "./pidfile"
auto_auth {
method {
type = "aws"
config = {
role = "foobar"
}
}
sink {
type = "file"
config = {
path = "/tmp/file-foo"
}
aad = "foobar"
dh_type = "curve25519"
dh_path = "/tmp/file-foo-dhpath"
}
}
api_proxy {
use_auto_auth_token = true
enforce_consistency = "always"
when_inconsistent = "retry"
}
cache {
persist "kubernetes" {
path = "/vault/agent-cache/"
keep_after_import = true
exit_on_err = true
service_account_token_file = "/tmp/serviceaccount/token"
}
}
listener {
type = "unix"
address = "/path/to/socket"
tls_disable = true
socket_mode = "configmode"
socket_user = "configuser"
socket_group = "configgroup"
}
listener {
type = "tcp"
address = "127.0.0.1:8300"
tls_disable = true
}
listener {
type = "tcp"
address = "127.0.0.1:3000"
tls_disable = true
role = "metrics_only"
}
listener {
type = "tcp"
role = "default"
address = "127.0.0.1:8400"
tls_key_file = "/path/to/cakey.pem"
tls_cert_file = "/path/to/cacert.pem"
}
vault {
address = "http://127.0.0.1:1111"
ca_cert = "config_ca_cert"
ca_path = "config_ca_path"
tls_skip_verify = true
client_cert = "config_client_cert"
client_key = "config_client_key"
}

View File

@ -0,0 +1,75 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0
pid_file = "./pidfile"
auto_auth {
method {
type = "aws"
config = {
role = "foobar"
}
}
sink {
type = "file"
config = {
path = "/tmp/file-foo"
}
aad = "foobar"
dh_type = "curve25519"
dh_path = "/tmp/file-foo-dhpath"
}
}
api_proxy {
use_auto_auth_token = true
enforce_consistency = "always"
when_inconsistent = "retry"
}
cache {
persist = {
type = "kubernetes"
path = "/vault/agent-cache/"
keep_after_import = true
exit_on_err = true
service_account_token_file = "/tmp/serviceaccount/token"
}
}
listener "unix" {
address = "/path/to/socket"
tls_disable = true
socket_mode = "configmode"
socket_user = "configuser"
socket_group = "configgroup"
}
listener "tcp" {
address = "127.0.0.1:8300"
tls_disable = true
}
listener {
type = "tcp"
address = "127.0.0.1:3000"
tls_disable = true
role = "metrics_only"
}
listener "tcp" {
role = "default"
address = "127.0.0.1:8400"
tls_key_file = "/path/to/cakey.pem"
tls_cert_file = "/path/to/cacert.pem"
}
vault {
address = "http://127.0.0.1:1111"
ca_cert = "config_ca_cert"
ca_path = "config_ca_path"
tls_skip_verify = "true"
client_cert = "config_client_cert"
client_key = "config_client_key"
}

678
command/proxy_test.go Normal file
View File

@ -0,0 +1,678 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"fmt"
"net/http"
"os"
"sync"
"testing"
"time"
"github.com/hashicorp/go-hclog"
vaultjwt "github.com/hashicorp/vault-plugin-auth-jwt"
"github.com/hashicorp/vault/api"
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/command/agent"
"github.com/hashicorp/vault/helper/useragent"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
func testProxyCommand(tb testing.TB, logger hclog.Logger) (*cli.MockUi, *ProxyCommand) {
tb.Helper()
ui := cli.NewMockUi()
return ui, &ProxyCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
ShutdownCh: MakeShutdownCh(),
SighupCh: MakeSighupCh(),
logger: logger,
startedCh: make(chan struct{}, 5),
reloadedCh: make(chan struct{}, 5),
}
}
// TestProxy_ExitAfterAuth tests the exit_after_auth flag, provided both
// as config and via -exit-after-auth.
func TestProxy_ExitAfterAuth(t *testing.T) {
t.Run("via_config", func(t *testing.T) {
testProxyExitAfterAuth(t, false)
})
t.Run("via_flag", func(t *testing.T) {
testProxyExitAfterAuth(t, true)
})
}
func testProxyExitAfterAuth(t *testing.T, viaFlag bool) {
logger := logging.NewVaultLogger(hclog.Trace)
coreConfig := &vault.CoreConfig{
Logger: logger,
CredentialBackends: map[string]logical.Factory{
"jwt": vaultjwt.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
vault.TestWaitActive(t, cluster.Cores[0].Core)
client := cluster.Cores[0].Client
// Setup Vault
err := client.Sys().EnableAuthWithOptions("jwt", &api.EnableAuthOptions{
Type: "jwt",
})
if err != nil {
t.Fatal(err)
}
_, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{
"bound_issuer": "https://team-vault.auth0.com/",
"jwt_validation_pubkeys": agent.TestECDSAPubKey,
"jwt_supported_algs": "ES256",
})
if err != nil {
t.Fatal(err)
}
_, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{
"role_type": "jwt",
"bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients",
"bound_audiences": "https://vault.plugin.auth.jwt.test",
"user_claim": "https://vault/user",
"groups_claim": "https://vault/groups",
"policies": "test",
"period": "3s",
})
if err != nil {
t.Fatal(err)
}
dir := t.TempDir()
inf, err := os.CreateTemp(dir, "auth.jwt.test.")
if err != nil {
t.Fatal(err)
}
in := inf.Name()
inf.Close()
// We remove these files in this test since we don't need the files, we just need
// a non-conflicting file name for the config.
os.Remove(in)
t.Logf("input: %s", in)
sink1f, err := os.CreateTemp(dir, "sink1.jwt.test.")
if err != nil {
t.Fatal(err)
}
sink1 := sink1f.Name()
sink1f.Close()
os.Remove(sink1)
t.Logf("sink1: %s", sink1)
sink2f, err := os.CreateTemp(dir, "sink2.jwt.test.")
if err != nil {
t.Fatal(err)
}
sink2 := sink2f.Name()
sink2f.Close()
os.Remove(sink2)
t.Logf("sink2: %s", sink2)
conff, err := os.CreateTemp(dir, "conf.jwt.test.")
if err != nil {
t.Fatal(err)
}
conf := conff.Name()
conff.Close()
os.Remove(conf)
t.Logf("config: %s", conf)
jwtToken, _ := agent.GetTestJWT(t)
if err := os.WriteFile(in, []byte(jwtToken), 0o600); err != nil {
t.Fatal(err)
} else {
logger.Trace("wrote test jwt", "path", in)
}
exitAfterAuthTemplText := "exit_after_auth = true"
if viaFlag {
exitAfterAuthTemplText = ""
}
config := `
%s
auto_auth {
method {
type = "jwt"
config = {
role = "test"
path = "%s"
}
}
sink {
type = "file"
config = {
path = "%s"
}
}
sink "file" {
config = {
path = "%s"
}
}
}
`
config = fmt.Sprintf(config, exitAfterAuthTemplText, in, sink1, sink2)
if err := os.WriteFile(conf, []byte(config), 0o600); err != nil {
t.Fatal(err)
} else {
logger.Trace("wrote test config", "path", conf)
}
doneCh := make(chan struct{})
go func() {
ui, cmd := testProxyCommand(t, logger)
cmd.client = client
args := []string{"-config", conf}
if viaFlag {
args = append(args, "-exit-after-auth")
}
code := cmd.Run(args)
if code != 0 {
t.Errorf("expected %d to be %d", code, 0)
t.Logf("output from proxy:\n%s", ui.OutputWriter.String())
t.Logf("error from proxy:\n%s", ui.ErrorWriter.String())
}
close(doneCh)
}()
select {
case <-doneCh:
break
case <-time.After(1 * time.Minute):
t.Fatal("timeout reached while waiting for proxy to exit")
}
sink1Bytes, err := os.ReadFile(sink1)
if err != nil {
t.Fatal(err)
}
if len(sink1Bytes) == 0 {
t.Fatal("got no output from sink 1")
}
sink2Bytes, err := os.ReadFile(sink2)
if err != nil {
t.Fatal(err)
}
if len(sink2Bytes) == 0 {
t.Fatal("got no output from sink 2")
}
if string(sink1Bytes) != string(sink2Bytes) {
t.Fatal("sink 1/2 values don't match")
}
}
// TestProxy_AutoAuth_UserAgent tests that the User-Agent sent
// to Vault by Vault Proxy is correct when performing Auto-Auth.
// Uses the custom handler userAgentHandler (defined above) so
// that Vault validates the User-Agent on requests sent by Proxy.
func TestProxy_AutoAuth_UserAgent(t *testing.T) {
logger := logging.NewVaultLogger(hclog.Trace)
var h userAgentHandler
cluster := vault.NewTestCluster(t, &vault.CoreConfig{
Logger: logger,
CredentialBackends: map[string]logical.Factory{
"approle": credAppRole.Factory,
},
}, &vault.TestClusterOptions{
NumCores: 1,
HandlerFunc: vaulthttp.HandlerFunc(
func(properties *vault.HandlerProperties) http.Handler {
h.props = properties
h.userAgentToCheckFor = useragent.ProxyAutoAuthString()
h.requestMethodToCheck = "PUT"
h.pathToCheck = "auth/approle/login"
h.t = t
return &h
}),
})
cluster.Start()
defer cluster.Cleanup()
serverClient := cluster.Cores[0].Client
// Enable the approle auth method
req := serverClient.NewRequest("POST", "/v1/sys/auth/approle")
req.BodyBytes = []byte(`{
"type": "approle"
}`)
request(t, serverClient, req, 204)
// Create a named role
req = serverClient.NewRequest("PUT", "/v1/auth/approle/role/test-role")
req.BodyBytes = []byte(`{
"secret_id_num_uses": "10",
"secret_id_ttl": "1m",
"token_max_ttl": "1m",
"token_num_uses": "10",
"token_ttl": "1m",
"policies": "default"
}`)
request(t, serverClient, req, 204)
// Fetch the RoleID of the named role
req = serverClient.NewRequest("GET", "/v1/auth/approle/role/test-role/role-id")
body := request(t, serverClient, req, 200)
data := body["data"].(map[string]interface{})
roleID := data["role_id"].(string)
// Get a SecretID issued against the named role
req = serverClient.NewRequest("PUT", "/v1/auth/approle/role/test-role/secret-id")
body = request(t, serverClient, req, 200)
data = body["data"].(map[string]interface{})
secretID := data["secret_id"].(string)
// Write the RoleID and SecretID to temp files
roleIDPath := makeTempFile(t, "role_id.txt", roleID+"\n")
secretIDPath := makeTempFile(t, "secret_id.txt", secretID+"\n")
defer os.Remove(roleIDPath)
defer os.Remove(secretIDPath)
sinkf, err := os.CreateTemp("", "sink.test.")
if err != nil {
t.Fatal(err)
}
sink := sinkf.Name()
sinkf.Close()
os.Remove(sink)
autoAuthConfig := fmt.Sprintf(`
auto_auth {
method "approle" {
mount_path = "auth/approle"
config = {
role_id_file_path = "%s"
secret_id_file_path = "%s"
}
}
sink "file" {
config = {
path = "%s"
}
}
}`, roleIDPath, secretIDPath, sink)
listenAddr := generateListenerAddress(t)
listenConfig := fmt.Sprintf(`
listener "tcp" {
address = "%s"
tls_disable = true
}
`, listenAddr)
config := fmt.Sprintf(`
vault {
address = "%s"
tls_skip_verify = true
}
api_proxy {
use_auto_auth_token = true
}
%s
%s
`, serverClient.Address(), listenConfig, autoAuthConfig)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)
// Unset the environment variable so that proxy picks up the right test
// cluster address
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
os.Unsetenv(api.EnvVaultAddress)
// Start proxy
_, cmd := testProxyCommand(t, logger)
cmd.startedCh = make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
cmd.Run([]string{"-config", configPath})
wg.Done()
}()
select {
case <-cmd.startedCh:
case <-time.After(5 * time.Second):
t.Errorf("timeout")
}
// Validate that the auto-auth token has been correctly attained
// and works for LookupSelf
conf := api.DefaultConfig()
conf.Address = "http://" + listenAddr
proxyClient, err := api.NewClient(conf)
if err != nil {
t.Fatalf("err: %s", err)
}
proxyClient.SetToken("")
err = proxyClient.SetAddress("http://" + listenAddr)
if err != nil {
t.Fatal(err)
}
// Wait for the token to be sent to syncs and be available to be used
time.Sleep(5 * time.Second)
req = proxyClient.NewRequest("GET", "/v1/auth/token/lookup-self")
body = request(t, proxyClient, req, 200)
close(cmd.ShutdownCh)
wg.Wait()
}
// TestProxy_APIProxyWithoutCache_UserAgent tests that the User-Agent sent
// to Vault by Vault Proxy is correct using the API proxy without
// the cache configured. Uses the custom handler
// userAgentHandler struct defined in this test package, so that Vault validates the
// User-Agent on requests sent by Proxy.
func TestProxy_APIProxyWithoutCache_UserAgent(t *testing.T) {
logger := logging.NewVaultLogger(hclog.Trace)
userAgentForProxiedClient := "proxied-client"
var h userAgentHandler
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
NumCores: 1,
HandlerFunc: vaulthttp.HandlerFunc(
func(properties *vault.HandlerProperties) http.Handler {
h.props = properties
h.userAgentToCheckFor = useragent.ProxyStringWithProxiedUserAgent(userAgentForProxiedClient)
h.pathToCheck = "/v1/auth/token/lookup-self"
h.requestMethodToCheck = "GET"
h.t = t
return &h
}),
})
cluster.Start()
defer cluster.Cleanup()
serverClient := cluster.Cores[0].Client
// Unset the environment variable so that proxy picks up the right test
// cluster address
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
os.Unsetenv(api.EnvVaultAddress)
listenAddr := generateListenerAddress(t)
listenConfig := fmt.Sprintf(`
listener "tcp" {
address = "%s"
tls_disable = true
}
`, listenAddr)
config := fmt.Sprintf(`
vault {
address = "%s"
tls_skip_verify = true
}
%s
`, serverClient.Address(), listenConfig)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)
// Start the agent
_, cmd := testProxyCommand(t, logger)
cmd.startedCh = make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
cmd.Run([]string{"-config", configPath})
wg.Done()
}()
select {
case <-cmd.startedCh:
case <-time.After(5 * time.Second):
t.Errorf("timeout")
}
proxyClient, err := api.NewClient(api.DefaultConfig())
if err != nil {
t.Fatal(err)
}
proxyClient.AddHeader("User-Agent", userAgentForProxiedClient)
proxyClient.SetToken(serverClient.Token())
proxyClient.SetMaxRetries(0)
err = proxyClient.SetAddress("http://" + listenAddr)
if err != nil {
t.Fatal(err)
}
_, err = proxyClient.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
close(cmd.ShutdownCh)
wg.Wait()
}
// TestProxy_APIProxyWithCache_UserAgent tests that the User-Agent sent
// to Vault by Vault Proxy is correct using the API proxy with
// the cache configured. Uses the custom handler
// userAgentHandler struct defined in this test package, so that Vault validates the
// User-Agent on requests sent by Proxy.
func TestProxy_APIProxyWithCache_UserAgent(t *testing.T) {
logger := logging.NewVaultLogger(hclog.Trace)
userAgentForProxiedClient := "proxied-client"
var h userAgentHandler
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
NumCores: 1,
HandlerFunc: vaulthttp.HandlerFunc(
func(properties *vault.HandlerProperties) http.Handler {
h.props = properties
h.userAgentToCheckFor = useragent.ProxyStringWithProxiedUserAgent(userAgentForProxiedClient)
h.pathToCheck = "/v1/auth/token/lookup-self"
h.requestMethodToCheck = "GET"
h.t = t
return &h
}),
})
cluster.Start()
defer cluster.Cleanup()
serverClient := cluster.Cores[0].Client
// Unset the environment variable so that proxy picks up the right test
// cluster address
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
os.Unsetenv(api.EnvVaultAddress)
listenAddr := generateListenerAddress(t)
listenConfig := fmt.Sprintf(`
listener "tcp" {
address = "%s"
tls_disable = true
}
`, listenAddr)
cacheConfig := `
cache {
}`
config := fmt.Sprintf(`
vault {
address = "%s"
tls_skip_verify = true
}
%s
%s
`, serverClient.Address(), listenConfig, cacheConfig)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)
// Start the agent
_, cmd := testProxyCommand(t, logger)
cmd.startedCh = make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
cmd.Run([]string{"-config", configPath})
wg.Done()
}()
select {
case <-cmd.startedCh:
case <-time.After(5 * time.Second):
t.Errorf("timeout")
}
proxyClient, err := api.NewClient(api.DefaultConfig())
if err != nil {
t.Fatal(err)
}
proxyClient.AddHeader("User-Agent", userAgentForProxiedClient)
proxyClient.SetToken(serverClient.Token())
proxyClient.SetMaxRetries(0)
err = proxyClient.SetAddress("http://" + listenAddr)
if err != nil {
t.Fatal(err)
}
_, err = proxyClient.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
close(cmd.ShutdownCh)
wg.Wait()
}
// TestProxy_Cache_DynamicSecret Tests that the cache successfully caches a dynamic secret
// going through the Proxy,
func TestProxy_Cache_DynamicSecret(t *testing.T) {
logger := logging.NewVaultLogger(hclog.Trace)
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
serverClient := cluster.Cores[0].Client
// Unset the environment variable so that agent picks up the right test
// cluster address
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
os.Unsetenv(api.EnvVaultAddress)
cacheConfig := `
cache {
}
`
listenAddr := generateListenerAddress(t)
listenConfig := fmt.Sprintf(`
listener "tcp" {
address = "%s"
tls_disable = true
}
`, listenAddr)
config := fmt.Sprintf(`
vault {
address = "%s"
tls_skip_verify = true
}
%s
%s
`, serverClient.Address(), cacheConfig, listenConfig)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)
// Start proxy
_, cmd := testProxyCommand(t, logger)
cmd.startedCh = make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
cmd.Run([]string{"-config", configPath})
wg.Done()
}()
select {
case <-cmd.startedCh:
case <-time.After(5 * time.Second):
t.Errorf("timeout")
}
proxyClient, err := api.NewClient(api.DefaultConfig())
if err != nil {
t.Fatal(err)
}
proxyClient.SetToken(serverClient.Token())
proxyClient.SetMaxRetries(0)
err = proxyClient.SetAddress("http://" + listenAddr)
if err != nil {
t.Fatal(err)
}
renewable := true
tokenCreateRequest := &api.TokenCreateRequest{
Policies: []string{"default"},
TTL: "30m",
Renewable: &renewable,
}
// This was the simplest test I could find to trigger the caching behaviour,
// i.e. the most concise I could make the test that I can tell
// creating an orphan token returns Auth, is renewable, and isn't a token
// that's managed elsewhere (since it's an orphan)
secret, err := proxyClient.Auth().Token().CreateOrphan(tokenCreateRequest)
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.Auth == nil {
t.Fatalf("secret not as expected: %v", secret)
}
token := secret.Auth.ClientToken
secret, err = proxyClient.Auth().Token().CreateOrphan(tokenCreateRequest)
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.Auth == nil {
t.Fatalf("secret not as expected: %v", secret)
}
token2 := secret.Auth.ClientToken
if token != token2 {
t.Fatalf("token create response not cached when it should have been, as tokens differ")
}
close(cmd.ShutdownCh)
wg.Wait()
}

View File

@ -851,6 +851,9 @@ listener "tcp" {
agent_api {
enable_quit = true
}
proxy_api {
enable_quit = true
}
}`))
config := Config{
@ -891,6 +894,9 @@ listener "tcp" {
AgentAPI: &configutil.AgentAPI{
EnableQuit: true,
},
ProxyAPI: &configutil.ProxyAPI{
EnableQuit: true,
},
CustomResponseHeaders: DefaultCustomHeaders,
},
},

View File

@ -74,3 +74,29 @@ func AgentAutoAuthString() string {
return fmt.Sprintf("Vault Agent Auto-Auth/%s (+%s; %s)",
versionFunc(), projectURL, rt)
}
// ProxyString returns the consistent user-agent string for Vault Proxy API Proxying.
//
// e.g. Vault Proxy API Proxy/0.10.4 (+https://www.vaultproject.io/; go1.10.1)
func ProxyString() string {
return fmt.Sprintf("Vault Proxy API Proxy/%s (+%s; %s)",
versionFunc(), projectURL, rt)
}
// ProxyStringWithProxiedUserAgent returns the consistent user-agent
// string for Vault Proxy API Proxying, keeping the User-Agent of the proxied
// client as an extension to this UserAgent
//
// e.g. Vault Proxy API Proxy/0.10.4 (+https://www.vaultproject.io/; go1.10.1); proxiedUserAgent
func ProxyStringWithProxiedUserAgent(proxiedUserAgent string) string {
return fmt.Sprintf("Vault Proxy API Proxy/%s (+%s; %s); %s",
versionFunc(), projectURL, rt, proxiedUserAgent)
}
// ProxyAutoAuthString returns the consistent user-agent string for Vault Agent Auto-Auth.
//
// e.g. Vault Proxy Auto-Auth/0.10.4 (+https://www.vaultproject.io/; go1.10.1)
func ProxyAutoAuthString() string {
return fmt.Sprintf("Vault Proxy Auto-Auth/%s (+%s; %s)",
versionFunc(), projectURL, rt)
}

View File

@ -100,6 +100,8 @@ type Listener struct {
AgentAPI *AgentAPI `hcl:"agent_api"`
ProxyAPI *ProxyAPI `hcl:"proxy_api"`
Telemetry ListenerTelemetry `hcl:"telemetry"`
Profiling ListenerProfiling `hcl:"profiling"`
InFlightRequestLogging ListenerInFlightRequestLogging `hcl:"inflight_requests_logging"`
@ -123,6 +125,11 @@ type AgentAPI struct {
EnableQuit bool `hcl:"enable_quit"`
}
// ProxyAPI allows users to select which parts of the Vault Proxy API they want enabled.
type ProxyAPI struct {
EnableQuit bool `hcl:"enable_quit"`
}
func (l *Listener) GoString() string {
return fmt.Sprintf("*%#v", *l)
}

View File

@ -0,0 +1,15 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package consts
// ProxyPathCacheClear is the path that the proxy will use as its cache-clear
// endpoint.
const ProxyPathCacheClear = "/proxy/v1/cache-clear"
// ProxyPathMetrics is the path the proxy will use to expose its internal
// metrics.
const ProxyPathMetrics = "/proxy/v1/metrics"
// ProxyPathQuit is the path that the proxy will use to trigger stopping it.
const ProxyPathQuit = "/proxy/v1/quit"