Added flag to disable X-Vault-Token header proxy if client passes the token (#8101)

* Added flag to disable X-Vault-Token header proxy if client passes the token

* Reveresed the flag value to better match the name intent

* Introduced UseAutoAuthTokenRaw for Cache to support triplicate value of true/false/force

Co-authored-by: Clint <catsby@users.noreply.github.com>
This commit is contained in:
Alex Antonov
2020-01-30 09:08:42 -06:00
committed by GitHub
parent 8d123920b3
commit 963e71c33e
10 changed files with 309 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,6 +39,8 @@ func TestLoadConfigFile_AgentCache(t *testing.T) {
},
Cache: &Cache{
UseAutoAuthToken: true,
UseAutoAuthTokenEnforce: false,
UseAutoAuthTokenRaw: true,
},
Listeners: []*Listener{
&Listener{
@@ -269,6 +271,133 @@ func TestLoadConfigFile_AgentCache_AutoAuth_NoSink(t *testing.T) {
},
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_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{

View File

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

View File

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

View File

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