[VAULT-2776] Add prefix_filter option to Vault (#12025)

* [VAULT-2776] Add prefix_filter support to vault

* [VAULT-2776] Add filter_default config, update docs

* [VAULT-2776] Add changelog file

* [VAULT-2776] Update telemetry tests and error handling

* [VAULT-2776] Add test fixtures, update test

* [VAULT-2776] Update gitignore hcl filter
This commit is contained in:
Pratyoy Mukhopadhyay 2021-07-09 14:49:53 -05:00 committed by GitHub
parent 326590f42e
commit 9b5e89bd34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 167 additions and 2 deletions

2
.gitignore vendored
View File

@ -48,7 +48,7 @@ Vagrantfile
# Configs # Configs
*.hcl *.hcl
!command/agent/config/test-fixtures/*.hcl !command/agent/config/test-fixtures/*.hcl
!command/server/test-fixtures/*.hcl !command/server/test-fixtures/**/*.hcl
.DS_Store .DS_Store

3
changelog/12025.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
core: Add `prefix_filter` to telemetry config
```

View File

@ -0,0 +1,41 @@
package server
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMetricFilterConfigs(t *testing.T) {
t.Parallel()
cases := []struct {
configFile string
expectedFilterDefault *bool
expectedPrefixFilter []string
}{
{
"./test-fixtures/telemetry/valid_prefix_filter.hcl",
nil,
[]string{"-vault.expire", "-vault.audit", "+vault.expire.num_irrevocable_leases"},
},
{
"./test-fixtures/telemetry/filter_default_override.hcl",
boolPointer(false),
[]string(nil),
},
}
t.Run("validate metric filter configs", func(t *testing.T) {
t.Parallel()
for _, tc := range cases {
config, err := LoadConfigFile(tc.configFile)
if err != nil {
t.Fatalf("Error encountered when loading config %+v", err)
}
assert.Equal(t, tc.expectedFilterDefault, config.SharedConfig.Telemetry.FilterDefault)
assert.Equal(t, tc.expectedPrefixFilter, config.SharedConfig.Telemetry.PrefixFilter)
}
})
}

View File

@ -16,6 +16,10 @@ import (
"github.com/hashicorp/vault/internalshared/configutil" "github.com/hashicorp/vault/internalshared/configutil"
) )
func boolPointer(x bool) *bool {
return &x
}
func testConfigRaftRetryJoin(t *testing.T) { func testConfigRaftRetryJoin(t *testing.T) {
config, err := LoadConfigFile("./test-fixtures/raft_retry_join.hcl") config, err := LoadConfigFile("./test-fixtures/raft_retry_join.hcl")
if err != nil { if err != nil {

View File

@ -0,0 +1,7 @@
disable_mlock = true
ui = true
telemetry {
statsd_address = "foo"
filter_default = false
}

View File

@ -0,0 +1,7 @@
disable_mlock = true
ui = true
telemetry {
statsd_address = "foo"
prefix_filter = ["-vault.expire", "-vault.audit", "+vault.expire.num_irrevocable_leases"]
}

View File

@ -4,9 +4,10 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/hashicorp/vault/sdk/helper/parseutil"
"time" "time"
"github.com/hashicorp/vault/sdk/helper/parseutil"
monitoring "cloud.google.com/go/monitoring/apiv3" monitoring "cloud.google.com/go/monitoring/apiv3"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
"github.com/armon/go-metrics/circonus" "github.com/armon/go-metrics/circonus"
@ -150,6 +151,14 @@ type Telemetry struct {
// Whether or not telemetry should add labels for namespaces // Whether or not telemetry should add labels for namespaces
LeaseMetricsNameSpaceLabels bool `hcl:"add_lease_metrics_namespace_labels"` LeaseMetricsNameSpaceLabels bool `hcl:"add_lease_metrics_namespace_labels"`
// FilterDefault is the default for whether to allow a metric that's not
// covered by the prefix filter.
FilterDefault *bool `hcl:"filter_default"`
// PrefixFilter is a list of filter rules to apply for allowing
// or blocking metrics by prefix.
PrefixFilter []string `hcl:"prefix_filter"`
} }
func (t *Telemetry) Validate(source string) []ConfigError { func (t *Telemetry) Validate(source string) []ConfigError {
@ -259,6 +268,9 @@ func SetupTelemetry(opts *SetupTelemetryOpts) (*metrics.InmemSink, *metricsutil.
metricsConf := metrics.DefaultConfig(opts.ServiceName) metricsConf := metrics.DefaultConfig(opts.ServiceName)
metricsConf.EnableHostname = !opts.Config.DisableHostname metricsConf.EnableHostname = !opts.Config.DisableHostname
metricsConf.EnableHostnameLabel = opts.Config.EnableHostnameLabel metricsConf.EnableHostnameLabel = opts.Config.EnableHostnameLabel
if opts.Config.FilterDefault != nil {
metricsConf.FilterDefault = *opts.Config.FilterDefault
}
// Configure the statsite sink // Configure the statsite sink
var fanout metrics.FanoutSink var fanout metrics.FanoutSink
@ -388,5 +400,32 @@ func SetupTelemetry(opts *SetupTelemetryOpts) (*metrics.InmemSink, *metricsutil.
wrapper.TelemetryConsts.LeaseMetricsNameSpaceLabels = opts.Config.LeaseMetricsNameSpaceLabels wrapper.TelemetryConsts.LeaseMetricsNameSpaceLabels = opts.Config.LeaseMetricsNameSpaceLabels
wrapper.TelemetryConsts.NumLeaseMetricsTimeBuckets = opts.Config.NumLeaseMetricsTimeBuckets wrapper.TelemetryConsts.NumLeaseMetricsTimeBuckets = opts.Config.NumLeaseMetricsTimeBuckets
// Parse the metric filters
telemetryAllowedPrefixes, telemetryBlockedPrefixes, err := parsePrefixFilter(opts.Config.PrefixFilter)
if err != nil {
return nil, nil, false, err
}
metrics.UpdateFilter(telemetryAllowedPrefixes, telemetryBlockedPrefixes)
return inm, wrapper, prometheusEnabled, nil return inm, wrapper, prometheusEnabled, nil
} }
func parsePrefixFilter(prefixFilters []string) ([]string, []string, error) {
var telemetryAllowedPrefixes, telemetryBlockedPrefixes []string
for _, rule := range prefixFilters {
if rule == "" {
return nil, nil, fmt.Errorf("Cannot have empty filter rule in prefix_filter")
}
switch rule[0] {
case '+':
telemetryAllowedPrefixes = append(telemetryAllowedPrefixes, rule[1:])
case '-':
telemetryBlockedPrefixes = append(telemetryBlockedPrefixes, rule[1:])
default:
return nil, nil, fmt.Errorf("Filter rule must begin with either '+' or '-': %q", rule)
}
}
return telemetryAllowedPrefixes, telemetryBlockedPrefixes, nil
}

View File

@ -0,0 +1,53 @@
package configutil
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParsePrefixFilters(t *testing.T) {
t.Parallel()
cases := []struct {
inputFilters []string
expectedErrStr string
expectedAllowedPrefixes []string
expectedBlockedPrefixes []string
}{
{
[]string{""},
"Cannot have empty filter rule in prefix_filter",
[]string(nil),
[]string(nil),
},
{
[]string{"vault.abc"},
"Filter rule must begin with either '+' or '-': \"vault.abc\"",
[]string(nil),
[]string(nil),
},
{
[]string{"+vault.abc", "-vault.bcd"},
"",
[]string{"vault.abc"},
[]string{"vault.bcd"},
},
}
t.Run("validate metric filter configs", func(t *testing.T) {
t.Parallel()
for _, tc := range cases {
allowedPrefixes, blockedPrefixes, err := parsePrefixFilter(tc.inputFilters)
if err != nil {
assert.EqualError(t, err, tc.expectedErrStr)
} else {
assert.Equal(t, "", tc.expectedErrStr)
assert.Equal(t, tc.expectedAllowedPrefixes, allowedPrefixes)
assert.Equal(t, tc.expectedBlockedPrefixes, blockedPrefixes)
}
}
})
}

View File

@ -51,6 +51,17 @@ The following options are available on all telemetry configurations.
- `add_lease_metrics_namespace_labels` `(bool: false)` - If this value is set to true, then `vault.expire.leases.by_expiration` - `add_lease_metrics_namespace_labels` `(bool: false)` - If this value is set to true, then `vault.expire.leases.by_expiration`
will break down expiring leases by both time and namespace. This parameter is disabled by default because enabling it can lead will break down expiring leases by both time and namespace. This parameter is disabled by default because enabling it can lead
to a large-cardinality metric. to a large-cardinality metric.
- `filter_default` `(bool: true)` - This controls whether to allow metrics that have not been specified by the filter.
Defaults to `true`, which will allow all metrics when no filters are provided.
When set to `false` with no filters, no metrics will be sent.
- `prefix_filter` `(string array: [])` - This is a list of filter rules to apply for allowing/blocking metrics by
prefix in the following format:
```json
["+vault.token", "-vault.expire", "+vault.expire.num_leases"]
```
A leading "**+**" will enable any metrics with the given prefix, and a leading "**-**" will block them.
If there is overlap between two rules, the more specific rule will take precedence. Blocking will take priority if the same prefix is listed multiple times.
### `statsite` ### `statsite`