Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAULT-1564 report in-flight requests #13024

Merged
merged 36 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3463e90
VAULT-1564 report in-flight requests
hghaf099 Nov 3, 2021
6420c0c
adding a changelog
hghaf099 Nov 3, 2021
2c3fefe
Changing some variable names and fixing comments
hghaf099 Nov 3, 2021
e2031da
minor style change
hghaf099 Nov 3, 2021
29c5f24
adding unauthenticated support for in-flight-req
hghaf099 Nov 3, 2021
17a4928
adding documentation for the listener.profiling stanza
hghaf099 Nov 3, 2021
18ab7da
adding an atomic counter for the inflight requests
hghaf099 Nov 5, 2021
660ff96
addressing comments
hghaf099 Nov 5, 2021
a06c4a7
Merge branch 'main' into report-in-flight-req
hghaf099 Nov 10, 2021
b2979d5
logging completed requests
hghaf099 Nov 17, 2021
5914acd
fixing a test
hghaf099 Nov 18, 2021
f418b0e
providing log_requests_info as a config option to determine at which …
hghaf099 Nov 18, 2021
c181d15
removing a member and a method from the StatusHeaderResponseWriter st…
hghaf099 Nov 18, 2021
16ed14a
adding api docks
hghaf099 Nov 19, 2021
e9f2312
Merge branch 'main' into report-in-flight-req
hghaf099 Nov 23, 2021
d7682a5
revert changes in NewHTTPResponseWriter
hghaf099 Nov 23, 2021
2ad56c2
Fix logging invalid log_requests_info value
hghaf099 Nov 23, 2021
814128b
Addressing comments
hghaf099 Nov 24, 2021
6ecb241
Fixing a test
hghaf099 Nov 25, 2021
ec22517
use an tomic value for logRequestsInfo, and moving the CreateClientID…
hghaf099 Nov 25, 2021
8a6350e
fixing go.sum
hghaf099 Nov 25, 2021
b9dad8c
minor refactoring
hghaf099 Nov 25, 2021
e3079f0
protecting InFlightRequests from data race
hghaf099 Nov 25, 2021
96e5c71
another try on fixing a data race
hghaf099 Nov 25, 2021
03153e7
another try to fix a data race
hghaf099 Nov 25, 2021
da8a1e0
addressing comments
hghaf099 Dec 6, 2021
18d70ba
Merge branch 'main' into report-in-flight-req
hghaf099 Dec 6, 2021
2a53824
fixing couple of tests
hghaf099 Dec 6, 2021
9893d2e
changing log_requests_info to log_requests_level
hghaf099 Dec 6, 2021
d565b64
minor style change
hghaf099 Dec 6, 2021
97c4cf3
fixing a test
hghaf099 Dec 6, 2021
5649a60
removing the lock in InFlightRequests
hghaf099 Dec 7, 2021
a0bec8d
use single-argument form for interface assertion
hghaf099 Dec 7, 2021
087fb00
adding doc for the new configuration paramter
hghaf099 Dec 7, 2021
5ed569b
adding the new doc to the nav data file
hghaf099 Dec 7, 2021
8c8e5d8
minor fix
hghaf099 Dec 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
adding unauthenticated support for in-flight-req
  • Loading branch information
hghaf099 committed Nov 3, 2021
commit 29c5f24bb2d2c3751931db1493a553d4105ec9f6
13 changes: 13 additions & 0 deletions command/server/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"net"
"testing"

)

type testListenerConnFn func(net.Listener) (net.Conn, error)
Expand Down Expand Up @@ -66,3 +67,15 @@ func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn,
t.Fatalf("bad: %v", buf.String())
}
}


func TestProfilingUnauthenticatedInFlightAccess(t *testing.T) {

config, err := LoadConfigFile("./test-fixtures/profiling_unauth_in_flight_access.hcl")
if err != nil {
t.Fatalf("Error encountered when loading config %+v", err)
}
if !config.Listeners[0].Profiling.UnauthenticatedInFlightAccess {
t.Fatalf("failed to read UnauthenticatedInFlightAccess")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
storage "inmem" {}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = true
profiling {
unauthenticated_in_flight_request_access = true
}
}
disable_mlock = true
6 changes: 5 additions & 1 deletion http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ func Handler(props *vault.HandlerProperties) http.Handler {
mux.Handle("/v1/sys/leader", handleSysLeader(core))
mux.Handle("/v1/sys/health", handleSysHealth(core))
mux.Handle("/v1/sys/monitor", handleLogicalNoForward(core))
mux.Handle("/v1/sys/in-flight-req", handleLogicalNoForward(core))
mux.Handle("/v1/sys/generate-root/attempt", handleRequestForwarding(core,
handleAuditNonLogical(core, handleSysGenerateRootAttempt(core, vault.GenerateStandardRootTokenStrategy))))
mux.Handle("/v1/sys/generate-root/update", handleRequestForwarding(core,
Expand Down Expand Up @@ -198,6 +197,11 @@ func Handler(props *vault.HandlerProperties) http.Handler {
mux.Handle("/v1/sys/pprof/", handleLogicalNoForward(core))
}

if props.ListenerConfig != nil && props.ListenerConfig.Profiling.UnauthenticatedInFlightAccess {
mux.Handle("/v1/sys/in-flight-req", handleUnAuthenticatedInFlightRequest(core))
} else {
mux.Handle("/v1/sys/in-flight-req", handleLogicalNoForward(core))
}
additionalRoutes(mux, core)
}

Expand Down
76 changes: 76 additions & 0 deletions http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/textproto"
"net/url"
"reflect"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -324,6 +326,80 @@ func TestHandler_InFlightRequest(t *testing.T) {
}
}

func TestHandler_InFlightRequestWithLoad(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()
TestServerAuth(t, addr, token)

stop := make(chan string)

go func() {
i := 0
for {
select {
case <-stop:
return
default:
break
}
// WRITE
secResp := testHttpPut(t, token, addr+"/v1/secret/foo"+strconv.Itoa(i), map[string]interface{}{
"data": "bar",
})
testResponseStatus(t, secResp, 204)
i++
}
}()

timeout := time.After(10 * time.Second)

for {
select {
case <-timeout:
stop <- "done"
return
default:
}
req, err := http.NewRequest("GET", addr+"/v1/sys/in-flight-req", nil)
if err != nil {
t.Fatalf("err: %s", err)
}
req.Header.Set(consts.AuthHeaderName, token)

client := cleanhttp.DefaultClient()
resp, err := client.Do(req)
if err != nil {
t.Fatalf("err: %s", err)
}

if resp == nil {
t.Fatalf("nil response")
}

var actual map[string]interface{}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)

if actual == nil {
t.Fatalf("")
}
inFlightReqDataRaw, ok := actual["data"]
if !ok {
t.Fatalf("failed to read in-flight-request data")
}
inFlightReqData, ok := inFlightReqDataRaw.(map[string]interface{})
if !ok {
t.Fatalf("data assertion failed")
}
if inFlightReqData != nil || len(inFlightReqData) > 0 {
fmt.Println("found something", inFlightReqData)
stop <- "done"
return
}
}
}

// TestHandler_MissingToken tests the response / error code if a request comes
// in with a missing client token. See
// https://github.com/hashicorp/vault/issues/8377
Expand Down
46 changes: 46 additions & 0 deletions http/sys_in_flight_reqeusts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package http

import (
"testing"

"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/vault"
)

func TestInFlightRequest(t *testing.T) {
conf := &vault.CoreConfig{}
core, _, token := vault.TestCoreUnsealedWithConfig(t, conf)
ln, addr := TestServer(t, core)
TestServerAuth(t, addr, token)

// Default: Only authenticated access
resp := testHttpGet(t, "", addr+"/v1/sys/in-flight-req")
testResponseStatus(t, resp, 400)
resp = testHttpGet(t, token, addr+"/v1/sys/in-flight-req")
testResponseStatus(t, resp, 200)

// Close listener
ln.Close()

// Setup new custom listener with unauthenticated metrics access
ln, addr = TestListener(t)
props := &vault.HandlerProperties{
Core: core,
ListenerConfig: &configutil.Listener{
Profiling: configutil.ListenerProfiling{
UnauthenticatedInFlightAccess: true,
},
},
}
TestServerWithListenerAndProperties(t, ln, addr, core, props)
defer ln.Close()
TestServerAuth(t, addr, token)

// Test without token
resp = testHttpGet(t, "", addr+"/v1/sys/in-flight-req")
testResponseStatus(t, resp, 200)

// Should also work with token
resp = testHttpGet(t, token, addr+"/v1/sys/in-flight-req")
testResponseStatus(t, resp, 200)
}
45 changes: 45 additions & 0 deletions http/sys_in_flight_requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package http

import (
"fmt"
"net/http"
"time"

"github.com/hashicorp/vault/vault"
)

func handleUnAuthenticatedInFlightRequest(core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
default:
respondError(w, http.StatusMethodNotAllowed, nil)
return
}
now := time.Now()
currentInFlightReqMap := make(map[string]interface{})
syncMapRangeResult := true
core.RangeInFlightReqData(func(key, value interface{}) bool {
v, ok := value.(*vault.InFlightReqData)
if !ok {
syncMapRangeResult = false
return false
}
// don't report the request to the in-flight-req path
if v.ReqPath != "/v1/sys/in-flight-req" {
v.Duration = fmt.Sprintf("%v microseconds", now.Sub(v.StartTime).Microseconds())
currentInFlightReqMap[key.(string)] = v
}

return true
})

// TODO: should an error be returned here? and if so, what status code should be returned? 500 or 400?
if !syncMapRangeResult {
respondError(w, http.StatusInternalServerError, fmt.Errorf("failed to read recorded in-flight requests"))
return
}

respondOk(w, currentInFlightReqMap)
})
}
13 changes: 10 additions & 3 deletions internalshared/configutil/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ type ListenerTelemetry struct {
}

type ListenerProfiling struct {
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
UnauthenticatedPProfAccess bool `hcl:"-"`
UnauthenticatedPProfAccessRaw interface{} `hcl:"unauthenticated_pprof_access,alias:UnauthenticatedPProfAccessRaw"`
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
UnauthenticatedPProfAccess bool `hcl:"-"`
UnauthenticatedPProfAccessRaw interface{} `hcl:"unauthenticated_pprof_access,alias:UnauthenticatedPProfAccessRaw"`
UnauthenticatedInFlightAccess bool `hcl:"-"`
UnauthenticatedInFlightAccessRaw interface{} `hcl:"unauthenticated_in_flight_request_access,alias:unauthenticatedInFlightAccessRaw"`
}

// Listener is the listener configuration for the server.
Expand Down Expand Up @@ -353,6 +355,11 @@ func ParseListeners(result *SharedConfig, list *ast.ObjectList) error {

l.Profiling.UnauthenticatedPProfAccessRaw = nil
}
if l.Profiling.UnauthenticatedInFlightAccessRaw != nil {
if l.Profiling.UnauthenticatedInFlightAccess, err = parseutil.ParseBool(l.Profiling.UnauthenticatedInFlightAccessRaw); err != nil {
return multierror.Prefix(fmt.Errorf("invalid value for profiling.unauthenticated_in_flight_request_access: %w", err), fmt.Sprintf("listeners.%d", i))
}
}
}

// CORS
Expand Down
8 changes: 8 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -2967,3 +2967,11 @@ func (c *Core) DeleteInFlightReqData(reqID string) error{
c.inFlightReqMap.Delete(reqID)
return nil
}

func (c *Core) RangeInFlightReqData(rangeFunc func(key, value interface{}) bool) error {
if c.inFlightReqMap == nil {
return fmt.Errorf("failed to range over in-flight request data. Map not initialized")
}
c.inFlightReqMap.Range(rangeFunc)
return nil
}