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:
parent
66a6e18283
commit
b2468d3481
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
```
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
/*
|
|
@ -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 {
|
|
@ -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()
|
||||
}
|
|
@ -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 {
|
|
@ -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 (
|
|
@ -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"
|
||||
)
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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) {
|
|
@ -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 {
|
|
@ -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"
|
||||
)
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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) {
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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) {
|
|
@ -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 (
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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"
|
||||
)
|
|
@ -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 {
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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)
|
|
@ -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)
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
||||
)
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/vault/command/agent/sink"
|
||||
"github.com/hashicorp/vault/command/agentproxyshared/sink"
|
||||
)
|
||||
|
||||
type mockSink struct {
|
|
@ -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.",
|
||||
|
|
|
@ -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(),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
Loading…
Reference in New Issue