Add config parameter to allow unauthenticated metrics access (#7550)
* Implement config parameter to allow unathenticated metricss access * Add unit test for unauthenticated metrics access parameter * go mod tidy
This commit is contained in:
parent
8239612352
commit
a7a6dd55a5
|
@ -20,25 +20,22 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
|
||||
monitoring "cloud.google.com/go/monitoring/apiv3"
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/armon/go-metrics/circonus"
|
||||
"github.com/armon/go-metrics/datadog"
|
||||
"github.com/armon/go-metrics/prometheus"
|
||||
stackdriver "github.com/google/go-metrics-stackdriver"
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
"github.com/hashicorp/vault/command/server"
|
||||
serverseal "github.com/hashicorp/vault/command/server/seal"
|
||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||
gatedwriter "github.com/hashicorp/vault/helper/gated-writer"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/reload"
|
||||
vaulthttp "github.com/hashicorp/vault/http"
|
||||
|
@ -54,8 +51,9 @@ import (
|
|||
vaultseal "github.com/hashicorp/vault/vault/seal"
|
||||
shamirseal "github.com/hashicorp/vault/vault/seal/shamir"
|
||||
"github.com/mitchellh/cli"
|
||||
testing "github.com/mitchellh/go-testing-interface"
|
||||
"github.com/mitchellh/go-testing-interface"
|
||||
"github.com/posener/complete"
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
@ -125,9 +123,10 @@ type ServerCommand struct {
|
|||
|
||||
type ServerListener struct {
|
||||
net.Listener
|
||||
config map[string]interface{}
|
||||
maxRequestSize int64
|
||||
maxRequestDuration time.Duration
|
||||
config map[string]interface{}
|
||||
maxRequestSize int64
|
||||
maxRequestDuration time.Duration
|
||||
unauthenticatedMetricsAccess bool
|
||||
}
|
||||
|
||||
func (c *ServerCommand) Synopsis() string {
|
||||
|
@ -988,11 +987,31 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
|||
}
|
||||
props["max_request_duration"] = fmt.Sprintf("%s", maxRequestDuration.String())
|
||||
|
||||
var unauthenticatedMetricsAccess bool
|
||||
if telemetryRaw, ok := lnConfig.Config["telemetry"]; ok {
|
||||
telemetry, ok := telemetryRaw.([]map[string]interface{})
|
||||
if !ok {
|
||||
c.UI.Error(fmt.Sprintf("Could not parse telemetry sink value %v", telemetryRaw))
|
||||
return 1
|
||||
}
|
||||
|
||||
for _, item := range telemetry {
|
||||
if valRaw, ok := item["unauthenticated_metrics_access"]; ok {
|
||||
unauthenticatedMetricsAccess, err = parseutil.ParseBool(valRaw)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Could not parse unauthenticated_metrics_access value %v", valRaw))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lns = append(lns, ServerListener{
|
||||
Listener: ln,
|
||||
config: lnConfig.Config,
|
||||
maxRequestSize: maxRequestSize,
|
||||
maxRequestDuration: maxRequestDuration,
|
||||
Listener: ln,
|
||||
config: lnConfig.Config,
|
||||
maxRequestSize: maxRequestSize,
|
||||
maxRequestDuration: maxRequestDuration,
|
||||
unauthenticatedMetricsAccess: unauthenticatedMetricsAccess,
|
||||
})
|
||||
|
||||
// Store the listener props for output later
|
||||
|
@ -1229,10 +1248,11 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
|||
// Initialize the HTTP servers
|
||||
for _, ln := range lns {
|
||||
handler := vaulthttp.Handler(&vault.HandlerProperties{
|
||||
Core: core,
|
||||
MaxRequestSize: ln.maxRequestSize,
|
||||
MaxRequestDuration: ln.maxRequestDuration,
|
||||
DisablePrintableCheck: config.DisablePrintableCheck,
|
||||
Core: core,
|
||||
MaxRequestSize: ln.maxRequestSize,
|
||||
MaxRequestDuration: ln.maxRequestDuration,
|
||||
DisablePrintableCheck: config.DisablePrintableCheck,
|
||||
UnauthenticatedMetricsAccess: ln.unauthenticatedMetricsAccess,
|
||||
})
|
||||
|
||||
// We perform validation on the config earlier, we can just cast here
|
||||
|
|
1
go.mod
1
go.mod
|
@ -91,7 +91,6 @@ require (
|
|||
github.com/joyent/triton-go v0.0.0-20190112182421-51ffac552869
|
||||
github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f
|
||||
github.com/kr/pretty v0.1.0
|
||||
github.com/kr/pty v1.1.3 // indirect
|
||||
github.com/kr/text v0.1.0
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/mattn/go-colorable v0.1.2
|
||||
|
|
|
@ -147,6 +147,11 @@ func Handler(props *vault.HandlerProperties) http.Handler {
|
|||
mux.Handle("/", handleUIRedirect())
|
||||
}
|
||||
|
||||
// Register metrics path without authentication if enabled
|
||||
if props.UnauthenticatedMetricsAccess {
|
||||
mux.Handle("/v1/sys/metrics", handleMetricsUnauthenticated(core))
|
||||
}
|
||||
|
||||
additionalRoutes(mux, core)
|
||||
|
||||
// Wrap the handler in another handler to trigger all help paths.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func handleMetricsUnauthenticated(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
req := &logical.Request{Headers: r.Header}
|
||||
format := r.Form.Get("format")
|
||||
if format == "" {
|
||||
format = metricsutil.FormatFromRequest(req)
|
||||
}
|
||||
|
||||
// Define response
|
||||
resp, err := core.MetricsHelper().ResponseForFormat(format)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Manually extract the logical response and send back the information
|
||||
w.WriteHeader(resp.Data[logical.HTTPStatusCode].(int))
|
||||
w.Header().Set("Content-Type", resp.Data[logical.HTTPContentType].(string))
|
||||
w.Write(resp.Data[logical.HTTPRawBody].([]byte))
|
||||
})
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func TestSysMetricsUnauthenticated(t *testing.T) {
|
||||
inm := metrics.NewInmemSink(10*time.Second, time.Minute)
|
||||
metrics.DefaultInmemSignal(inm)
|
||||
conf := &vault.CoreConfig{
|
||||
BuiltinRegistry: vault.NewMockBuiltinRegistry(),
|
||||
MetricsHelper: metricsutil.NewMetricsHelper(inm, false),
|
||||
}
|
||||
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/metrics")
|
||||
testResponseStatus(t, resp, 400)
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/metrics")
|
||||
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,
|
||||
MaxRequestSize: DefaultMaxRequestSize,
|
||||
UnauthenticatedMetricsAccess: true,
|
||||
}
|
||||
TestServerWithListenerAndProperties(t, ln, addr, core, props)
|
||||
defer ln.Close()
|
||||
TestServerAuth(t, addr, token)
|
||||
|
||||
// Test without token
|
||||
resp = testHttpGet(t, "", addr+"/v1/sys/metrics")
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
// Should also work with token
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/metrics")
|
||||
testResponseStatus(t, resp, 200)
|
||||
}
|
|
@ -1979,6 +1979,12 @@ func (c *Core) SetLogLevel(level log.Level) {
|
|||
}
|
||||
}
|
||||
|
||||
// MetricsHelper returns the global metrics helper which allows external
|
||||
// packages to access Vault's internal metrics.
|
||||
func (c *Core) MetricsHelper() *metricsutil.MetricsHelper {
|
||||
return c.metricsHelper
|
||||
}
|
||||
|
||||
// BuiltinRegistry is an interface that allows the "vault" package to use
|
||||
// the registry of builtin plugins without getting an import cycle. It
|
||||
// also allows for mocking the registry easily.
|
||||
|
|
|
@ -39,10 +39,11 @@ var (
|
|||
// HandlerProperties is used to seed configuration into a vaulthttp.Handler.
|
||||
// It's in this package to avoid a circular dependency
|
||||
type HandlerProperties struct {
|
||||
Core *Core
|
||||
MaxRequestSize int64
|
||||
MaxRequestDuration time.Duration
|
||||
DisablePrintableCheck bool
|
||||
Core *Core
|
||||
MaxRequestSize int64
|
||||
MaxRequestDuration time.Duration
|
||||
DisablePrintableCheck bool
|
||||
UnauthenticatedMetricsAccess bool
|
||||
}
|
||||
|
||||
// fetchEntityAndDerivedPolicies returns the entity object for the given entity
|
||||
|
|
|
@ -156,6 +156,7 @@ func TestCoreWithSealAndUI(t testing.T, opts *CoreConfig) *Core {
|
|||
conf.Seal = opts.Seal
|
||||
conf.LicensingConfig = opts.LicensingConfig
|
||||
conf.DisableKeyEncodingChecks = opts.DisableKeyEncodingChecks
|
||||
conf.MetricsHelper = opts.MetricsHelper
|
||||
|
||||
if opts.Logger != nil {
|
||||
conf.Logger = opts.Logger
|
||||
|
|
Loading…
Reference in New Issue