VAULT-5935 agent: redact renew-self if using auto auth (#15380)
Vault agent redacts the token and accessor for `/auth/token/lookup-self` (and `lookup`) if the token is the auto auth token to prevent it from leaking. Similarly, we need to redact the token and accessor from `renew-self` and `renew`, which also leak the token and accessor. I tested this locally by starting up a Vault agent and querying the agent endpoints, and ensuring that the accessor and token were set to the empty string in the response.
This commit is contained in:
parent
8497010acd
commit
0af0543bbe
3
changelog/15380.txt
Normal file
3
changelog/15380.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
agent: Redact auto auth token from renew endpoints
|
||||||
|
```
|
31
command/agent/cache/cache_test.go
vendored
31
command/agent/cache/cache_test.go
vendored
|
@ -2,6 +2,7 @@ package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -13,7 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
kv "github.com/hashicorp/vault-plugin-secrets-kv"
|
kv "github.com/hashicorp/vault-plugin-secrets-kv"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/builtin/credential/userpass"
|
"github.com/hashicorp/vault/builtin/credential/userpass"
|
||||||
|
@ -214,9 +215,13 @@ func tokenRevocationValidation(t *testing.T, sampleSpace map[string]string, expe
|
||||||
func TestCache_AutoAuthTokenStripping(t *testing.T) {
|
func TestCache_AutoAuthTokenStripping(t *testing.T) {
|
||||||
response1 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup-self"}}`
|
response1 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup-self"}}`
|
||||||
response2 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup"}}`
|
response2 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup"}}`
|
||||||
|
response3 := `{"auth": {"client_token": "testid", "accessor": "testaccessor"}}`
|
||||||
|
response4 := `{"auth": {"client_token": "testid", "accessor": "testaccessor"}}`
|
||||||
responses := []*SendResponse{
|
responses := []*SendResponse{
|
||||||
newTestSendResponse(http.StatusOK, response1),
|
newTestSendResponse(http.StatusOK, response1),
|
||||||
newTestSendResponse(http.StatusOK, response2),
|
newTestSendResponse(http.StatusOK, response2),
|
||||||
|
newTestSendResponse(http.StatusOK, response3),
|
||||||
|
newTestSendResponse(http.StatusOK, response4),
|
||||||
}
|
}
|
||||||
|
|
||||||
leaseCache := testNewLeaseCache(t, responses)
|
leaseCache := testNewLeaseCache(t, responses)
|
||||||
|
@ -279,6 +284,30 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) {
|
||||||
if secret.Data["id"] != nil || secret.Data["accessor"] != nil || secret.Data["request"].(string) != "lookup" {
|
if secret.Data["id"] != nil || secret.Data["accessor"] != nil || secret.Data["request"].(string) != "lookup" {
|
||||||
t.Fatalf("failed to strip off auto-auth token on lookup")
|
t.Fatalf("failed to strip off auto-auth token on lookup")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secret, err = testClient.Auth().Token().RenewSelf(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth == nil {
|
||||||
|
secretJson, _ := json.Marshal(secret)
|
||||||
|
t.Fatalf("Expected secret to have Auth but was %s", secretJson)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken != "" || secret.Auth.Accessor != "" {
|
||||||
|
t.Fatalf("failed to strip off auto-auth token on renew-self")
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err = testClient.Auth().Token().Renew("testid", 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth == nil {
|
||||||
|
secretJson, _ := json.Marshal(secret)
|
||||||
|
t.Fatalf("Expected secret to have Auth but was %s", secretJson)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken != "" || secret.Auth.Accessor != "" {
|
||||||
|
t.Fatalf("failed to strip off auto-auth token on renew")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCache_AutoAuthClientTokenProxyStripping(t *testing.T) {
|
func TestCache_AutoAuthClientTokenProxyStripping(t *testing.T) {
|
||||||
|
|
34
command/agent/cache/handler.go
vendored
34
command/agent/cache/handler.go
vendored
|
@ -13,7 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/command/agent/sink"
|
"github.com/hashicorp/vault/command/agent/sink"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
|
@ -67,7 +67,7 @@ func Handler(ctx context.Context, logger hclog.Logger, proxier Proxier, inmemSin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = processTokenLookupResponse(ctx, logger, inmemSink, req, resp)
|
err = sanitizeAutoAuthTokenResponse(ctx, logger, inmemSink, req, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logical.RespondError(w, http.StatusInternalServerError, fmt.Errorf("failed to process token lookup response: %w", err))
|
logical.RespondError(w, http.StatusInternalServerError, fmt.Errorf("failed to process token lookup response: %w", err))
|
||||||
return
|
return
|
||||||
|
@ -120,11 +120,10 @@ func setHeaders(w http.ResponseWriter, resp *SendResponse) {
|
||||||
w.WriteHeader(resp.Response.StatusCode)
|
w.WriteHeader(resp.Response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// processTokenLookupResponse checks if the request was one of token
|
// sanitizeAutoAuthTokenResponse checks if the request was a lookup or renew
|
||||||
// lookup-self. If the auto-auth token was used to perform lookup-self, the
|
// and if the auto-auth token was used to perform lookup-self, the identifier
|
||||||
// identifier of the token and its accessor same will be stripped off of the
|
// of the token and its accessor same will be stripped off of the response.
|
||||||
// response.
|
func sanitizeAutoAuthTokenResponse(ctx context.Context, logger hclog.Logger, inmemSink sink.Sink, req *SendRequest, resp *SendResponse) error {
|
||||||
func processTokenLookupResponse(ctx context.Context, logger hclog.Logger, inmemSink sink.Sink, req *SendRequest, resp *SendResponse) error {
|
|
||||||
// If auto-auth token is not being used, there is nothing to do.
|
// If auto-auth token is not being used, there is nothing to do.
|
||||||
if inmemSink == nil {
|
if inmemSink == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -138,11 +137,11 @@ func processTokenLookupResponse(ctx context.Context, logger hclog.Logger, inmemS
|
||||||
|
|
||||||
_, path := deriveNamespaceAndRevocationPath(req)
|
_, path := deriveNamespaceAndRevocationPath(req)
|
||||||
switch path {
|
switch path {
|
||||||
case vaultPathTokenLookupSelf:
|
case vaultPathTokenLookupSelf, vaultPathTokenRenewSelf:
|
||||||
if req.Token != autoAuthToken {
|
if req.Token != autoAuthToken {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case vaultPathTokenLookup:
|
case vaultPathTokenLookup, vaultPathTokenRenew:
|
||||||
jsonBody := map[string]interface{}{}
|
jsonBody := map[string]interface{}{}
|
||||||
if err := json.Unmarshal(req.RequestBody, &jsonBody); err != nil {
|
if err := json.Unmarshal(req.RequestBody, &jsonBody); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -170,15 +169,26 @@ func processTokenLookupResponse(ctx context.Context, logger hclog.Logger, inmemS
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse token lookup response: %v", err)
|
return fmt.Errorf("failed to parse token lookup response: %v", err)
|
||||||
}
|
}
|
||||||
if secret == nil || secret.Data == nil {
|
if secret == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
} else if secret.Data != nil {
|
||||||
|
// lookup endpoints
|
||||||
if secret.Data["id"] == nil && secret.Data["accessor"] == nil {
|
if secret.Data["id"] == nil && secret.Data["accessor"] == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(secret.Data, "id")
|
delete(secret.Data, "id")
|
||||||
delete(secret.Data, "accessor")
|
delete(secret.Data, "accessor")
|
||||||
|
} else if secret.Auth != nil {
|
||||||
|
// renew endpoints
|
||||||
|
if secret.Auth.Accessor == "" && secret.Auth.ClientToken == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
secret.Auth.Accessor = ""
|
||||||
|
secret.Auth.ClientToken = ""
|
||||||
|
} else {
|
||||||
|
// nothing to redact
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(secret)
|
bodyBytes, err := json.Marshal(secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
6
command/agent/cache/lease_cache.go
vendored
6
command/agent/cache/lease_cache.go
vendored
|
@ -16,12 +16,12 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/base62"
|
"github.com/hashicorp/go-secure-stdlib/base62"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/command/agent/cache/cacheboltdb"
|
"github.com/hashicorp/vault/command/agent/cache/cacheboltdb"
|
||||||
cachememdb "github.com/hashicorp/vault/command/agent/cache/cachememdb"
|
"github.com/hashicorp/vault/command/agent/cache/cachememdb"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
nshelper "github.com/hashicorp/vault/helper/namespace"
|
nshelper "github.com/hashicorp/vault/helper/namespace"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
|
@ -42,6 +42,8 @@ const (
|
||||||
vaultPathTokenRevokeOrphan = "/v1/auth/token/revoke-orphan"
|
vaultPathTokenRevokeOrphan = "/v1/auth/token/revoke-orphan"
|
||||||
vaultPathTokenLookup = "/v1/auth/token/lookup"
|
vaultPathTokenLookup = "/v1/auth/token/lookup"
|
||||||
vaultPathTokenLookupSelf = "/v1/auth/token/lookup-self"
|
vaultPathTokenLookupSelf = "/v1/auth/token/lookup-self"
|
||||||
|
vaultPathTokenRenew = "/v1/auth/token/renew"
|
||||||
|
vaultPathTokenRenewSelf = "/v1/auth/token/renew-self"
|
||||||
vaultPathLeaseRevoke = "/v1/sys/leases/revoke"
|
vaultPathLeaseRevoke = "/v1/sys/leases/revoke"
|
||||||
vaultPathLeaseRevokeForce = "/v1/sys/leases/revoke-force"
|
vaultPathLeaseRevokeForce = "/v1/sys/leases/revoke-force"
|
||||||
vaultPathLeaseRevokePrefix = "/v1/sys/leases/revoke-prefix"
|
vaultPathLeaseRevokePrefix = "/v1/sys/leases/revoke-prefix"
|
||||||
|
|
Loading…
Reference in a new issue