diff --git a/command/agent.go b/command/agent.go index 555111a02d..9fca7f2cca 100644 --- a/command/agent.go +++ b/command/agent.go @@ -464,8 +464,10 @@ func (c *AgentCommand) Run(args []string) int { }) } + var proxyVaultToken = !config.Cache.UseAutoAuthTokenEnforce + // Create the request handler - cacheHandler := cache.Handler(ctx, cacheLogger, leaseCache, inmemSink) + cacheHandler := cache.Handler(ctx, cacheLogger, leaseCache, inmemSink, proxyVaultToken) var listeners []net.Listener for i, lnConfig := range config.Listeners { diff --git a/command/agent/cache/cache_test.go b/command/agent/cache/cache_test.go index b9a4d8f7cf..49b3d07f34 100644 --- a/command/agent/cache/cache_test.go +++ b/command/agent/cache/cache_test.go @@ -152,7 +152,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v mux := http.NewServeMux() mux.Handle("/agent/v1/cache-clear", leaseCache.HandleCacheClear(ctx)) - mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, nil)) + mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, nil, true)) server := &http.Server{ Handler: mux, ReadHeaderTimeout: 10 * time.Second, @@ -243,7 +243,7 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { mux := http.NewServeMux() mux.Handle(consts.AgentPathCacheClear, leaseCache.HandleCacheClear(ctx)) - mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, mock.NewSink("testid"))) + mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, mock.NewSink("testid"), true)) server := &http.Server{ Handler: mux, ReadHeaderTimeout: 10 * time.Second, @@ -281,6 +281,63 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { } } +func TestCache_AutoAuthClientTokenProxyStripping(t *testing.T) { + leaseCache := &mockTokenVerifierProxier{} + dummyToken := "DUMMY" + realToken := "testid" + + cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + + cores := cluster.Cores + vault.TestWaitActive(t, cores[0].Core) + client := cores[0].Client + + cacheLogger := logging.NewVaultLogger(hclog.Trace).Named("cache") + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + + ctx := namespace.RootContext(nil) + + // Create a muxer and add paths relevant for the lease cache layer + mux := http.NewServeMux() + //mux.Handle(consts.AgentPathCacheClear, leaseCache.HandleCacheClear(ctx)) + + mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, mock.NewSink(realToken), false)) + server := &http.Server{ + Handler: mux, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, + IdleTimeout: 5 * time.Minute, + ErrorLog: cacheLogger.StandardLogger(nil), + } + go server.Serve(listener) + + testClient, err := client.Clone() + if err != nil { + t.Fatal(err) + } + + if err := testClient.SetAddress("http://" + listener.Addr().String()); err != nil { + t.Fatal(err) + } + + // Empty the token in the client. Auto-auth token should be put to use. + testClient.SetToken(dummyToken) + _, err = testClient.Auth().Token().LookupSelf() + if err != nil { + t.Fatal(err) + } + if leaseCache.currentToken != realToken { + t.Fatalf("failed to use real token from auto-auth") + } +} + func TestCache_ConcurrentRequests(t *testing.T) { coreConfig := &vault.CoreConfig{ DisableMlock: true, diff --git a/command/agent/cache/handler.go b/command/agent/cache/handler.go index b3be9fb179..b9f935d134 100644 --- a/command/agent/cache/handler.go +++ b/command/agent/cache/handler.go @@ -20,11 +20,16 @@ import ( "github.com/hashicorp/vault/sdk/logical" ) -func Handler(ctx context.Context, logger hclog.Logger, proxier Proxier, inmemSink sink.Sink) http.Handler { +func Handler(ctx context.Context, logger hclog.Logger, proxier Proxier, inmemSink sink.Sink, proxyVaultToken bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger.Info("received request", "method", r.Method, "path", r.URL.Path) + if !proxyVaultToken { + r.Header.Del(consts.AuthHeaderName) + } + token := r.Header.Get(consts.AuthHeaderName) + if token == "" && inmemSink != nil { logger.Debug("using auto auth token", "method", r.Method, "path", r.URL.Path) token = inmemSink.(sink.SinkReader).Token() diff --git a/command/agent/cache/testing.go b/command/agent/cache/testing.go index 444f8ae8fe..c6113590d3 100644 --- a/command/agent/cache/testing.go +++ b/command/agent/cache/testing.go @@ -64,3 +64,19 @@ func newTestSendResponse(status int, body string) *SendResponse { return resp } + +type mockTokenVerifierProxier struct { + currentToken string +} + +func (p *mockTokenVerifierProxier) Send(ctx context.Context, req *SendRequest) (*SendResponse, error) { + p.currentToken = req.Token + resp := newTestSendResponse(http.StatusOK, + `{"data": {"id": "` + p.currentToken + `"}}`) + + return resp, nil +} + +func (p *mockTokenVerifierProxier) GetCurrentRequestToken() (string) { + return p.currentToken +} \ No newline at end of file diff --git a/command/agent/cache_end_to_end_test.go b/command/agent/cache_end_to_end_test.go index 23e2d7a879..58515c5851 100644 --- a/command/agent/cache_end_to_end_test.go +++ b/command/agent/cache_end_to_end_test.go @@ -298,7 +298,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { mux.Handle(consts.AgentPathCacheClear, leaseCache.HandleCacheClear(ctx)) // Passing a non-nil inmemsink tells the agent to use the auto-auth token - mux.Handle("/", cache.Handler(ctx, cacheLogger, leaseCache, inmemSink)) + mux.Handle("/", cache.Handler(ctx, cacheLogger, leaseCache, inmemSink, true)) server := &http.Server{ Handler: mux, ReadHeaderTimeout: 10 * time.Second, diff --git a/command/agent/config/config.go b/command/agent/config/config.go index 8ce02b054e..cc832e3156 100644 --- a/command/agent/config/config.go +++ b/command/agent/config/config.go @@ -43,7 +43,9 @@ type Vault struct { // Cache contains any configuration needed for Cache mode type Cache struct { - UseAutoAuthToken bool `hcl:"use_auto_auth_token"` + UseAutoAuthTokenRaw interface{} `hcl:"use_auto_auth_token"` + UseAutoAuthToken bool `hcl:"-"` + UseAutoAuthTokenEnforce bool `hcl:"-"` } // Listener contains configuration for any Vault Agent listeners @@ -219,6 +221,26 @@ func parseCache(result *Config, list *ast.ObjectList) error { return err } + if c.UseAutoAuthTokenRaw != nil { + c.UseAutoAuthToken, err = parseutil.ParseBool(c.UseAutoAuthTokenRaw) + if err != nil { + // Could be a value of "force" instead of "true"/"false" + switch c.UseAutoAuthTokenRaw.(type) { + case string: + v := c.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", c.UseAutoAuthTokenRaw) + } + c.UseAutoAuthToken = true + c.UseAutoAuthTokenEnforce = true + + default: + return err + } + } + } + result.Cache = &c return nil } diff --git a/command/agent/config/config_test.go b/command/agent/config/config_test.go index ea46bf0f7d..3675c324ce 100644 --- a/command/agent/config/config_test.go +++ b/command/agent/config/config_test.go @@ -38,7 +38,9 @@ func TestLoadConfigFile_AgentCache(t *testing.T) { }, }, Cache: &Cache{ - UseAutoAuthToken: true, + UseAutoAuthToken: true, + UseAutoAuthTokenEnforce: false, + UseAutoAuthTokenRaw: true, }, Listeners: []*Listener{ &Listener{ @@ -268,7 +270,134 @@ func TestLoadConfigFile_AgentCache_AutoAuth_NoSink(t *testing.T) { }, }, Cache: &Cache{ - UseAutoAuthToken: true, + UseAutoAuthToken: true, + UseAutoAuthTokenEnforce: false, + UseAutoAuthTokenRaw: true, + }, + Listeners: []*Listener{ + &Listener{ + Type: "tcp", + Config: map[string]interface{}{ + "address": "127.0.0.1:8300", + "tls_disable": true, + }, + }, + }, + PidFile: "./pidfile", + } + + if diff := deep.Equal(config, expected); diff != nil { + t.Fatal(diff) + } +} + +func TestLoadConfigFile_AgentCache_AutoAuth_Force(t *testing.T) { + config, err := LoadConfig("./test-fixtures/config-cache-auto_auth-force.hcl") + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &Config{ + AutoAuth: &AutoAuth{ + Method: &Method{ + Type: "aws", + MountPath: "auth/aws", + Config: map[string]interface{}{ + "role": "foobar", + }, + }, + }, + Cache: &Cache{ + UseAutoAuthToken: true, + UseAutoAuthTokenEnforce: true, + UseAutoAuthTokenRaw: "force", + }, + Listeners: []*Listener{ + &Listener{ + Type: "tcp", + Config: map[string]interface{}{ + "address": "127.0.0.1:8300", + "tls_disable": true, + }, + }, + }, + PidFile: "./pidfile", + } + + if diff := deep.Equal(config, expected); diff != nil { + t.Fatal(diff) + } +} + +func TestLoadConfigFile_AgentCache_AutoAuth_True(t *testing.T) { + config, err := LoadConfig("./test-fixtures/config-cache-auto_auth-true.hcl") + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &Config{ + AutoAuth: &AutoAuth{ + Method: &Method{ + Type: "aws", + MountPath: "auth/aws", + Config: map[string]interface{}{ + "role": "foobar", + }, + }, + }, + Cache: &Cache{ + UseAutoAuthToken: true, + UseAutoAuthTokenEnforce: false, + UseAutoAuthTokenRaw: "true", + }, + Listeners: []*Listener{ + &Listener{ + Type: "tcp", + Config: map[string]interface{}{ + "address": "127.0.0.1:8300", + "tls_disable": true, + }, + }, + }, + PidFile: "./pidfile", + } + + if diff := deep.Equal(config, expected); diff != nil { + t.Fatal(diff) + } +} + +func TestLoadConfigFile_AgentCache_AutoAuth_False(t *testing.T) { + config, err := LoadConfig("./test-fixtures/config-cache-auto_auth-false.hcl") + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &Config{ + AutoAuth: &AutoAuth{ + Method: &Method{ + Type: "aws", + MountPath: "auth/aws", + Config: map[string]interface{}{ + "role": "foobar", + }, + }, + Sinks: []*Sink{ + &Sink{ + Type: "file", + DHType: "curve25519", + DHPath: "/tmp/file-foo-dhpath", + AAD: "foobar", + Config: map[string]interface{}{ + "path": "/tmp/file-foo", + }, + }, + }, + }, + Cache: &Cache{ + UseAutoAuthToken: false, + UseAutoAuthTokenEnforce: false, + UseAutoAuthTokenRaw: "false", }, Listeners: []*Listener{ &Listener{ diff --git a/command/agent/config/test-fixtures/config-cache-auto_auth-false.hcl b/command/agent/config/test-fixtures/config-cache-auto_auth-false.hcl new file mode 100644 index 0000000000..1a2fd91d6c --- /dev/null +++ b/command/agent/config/test-fixtures/config-cache-auto_auth-false.hcl @@ -0,0 +1,30 @@ +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" + } +} + +cache { + use_auto_auth_token = "false" +} + +listener "tcp" { + address = "127.0.0.1:8300" + tls_disable = true +} + diff --git a/command/agent/config/test-fixtures/config-cache-auto_auth-force.hcl b/command/agent/config/test-fixtures/config-cache-auto_auth-force.hcl new file mode 100644 index 0000000000..9aad89cdd2 --- /dev/null +++ b/command/agent/config/test-fixtures/config-cache-auto_auth-force.hcl @@ -0,0 +1,20 @@ +pid_file = "./pidfile" + +auto_auth { + method { + type = "aws" + config = { + role = "foobar" + } + } +} + +cache { + use_auto_auth_token = "force" +} + +listener "tcp" { + address = "127.0.0.1:8300" + tls_disable = true +} + diff --git a/command/agent/config/test-fixtures/config-cache-auto_auth-true.hcl b/command/agent/config/test-fixtures/config-cache-auto_auth-true.hcl new file mode 100644 index 0000000000..ccadc501d0 --- /dev/null +++ b/command/agent/config/test-fixtures/config-cache-auto_auth-true.hcl @@ -0,0 +1,20 @@ +pid_file = "./pidfile" + +auto_auth { + method { + type = "aws" + config = { + role = "foobar" + } + } +} + +cache { + use_auto_auth_token = "true" +} + +listener "tcp" { + address = "127.0.0.1:8300" + tls_disable = true +} +