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

STORY-25143 - Add prometheus metrics to smokescreen #1

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ Here are the options you can give Smokescreen:
--deny-address value Add IP[:PORT] to list of blocked IPs. Repeatable.
--allow-address value Add IP[:PORT] to list of allowed IPs. Repeatable.
--egress-acl-file FILE Validate egress traffic against FILE
--prometheus-endpoint ENDPOINT Expose Prometheus metrics on the given endpoint.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the default endpoint /metrics?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had gone for requiring this to be set to enable prometheus, so not having any default value.
I'd been on the fence with having an --expose-prometheus flag which doesn't take a value, and then keep --prometheus-endpoint with a default value of /metrics which may be cleaner?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does ENDPOINT default to /metrics? I'm a bit confused by some of the wording here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-kalinowski: #1 (comment) <- I'm happy to add a --enable-prometheus flag, and have the --prometheus-endpoint default to /metrics - it's probably the cleanest way to structure the config.

If --prometheus-port isn't set, by default listens on 9810
--prometheus-port PORT Expose Prometheus metrics on the given port.
Requires --prometheus-endpoint to be set.
--resolver-address ADDRESS Make DNS requests to ADDRESS (IP:port). Repeatable.
--statsd-address ADDRESS Send metrics to statsd at ADDRESS (IP:port). (default: "127.0.0.1:8200")
--tls-server-bundle-file FILE Authenticate to clients using key and certs from FILE
Expand Down
15 changes: 15 additions & 0 deletions cmd/smokescreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ func NewConfiguration(args []string, logger *log.Logger) (*smokescreen.Config, e
Name: "egress-acl-file",
Usage: "Validate egress traffic against `FILE`",
},
cli.StringFlag{
Name: "prometheus-endpoint",
Usage: "Expose metrics via prometheus on `ENDPOINT`.",
},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is just how my brain works with cli args but can we default this to /metrics and add an additional flag to the effect of metric-type where you would choose statsd and/or prometheus and then ride the defaults if needed?

As of now the default is always shipping statsd metrics. Might want to provide a toggle to flip those off.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-kalinowski, I've pushed a change to add the --expose-prometheus-metrics flag to toggle exposing prometheus metrics.

Currently the default isn't shipping statsd metrics? A default flag is set for statsd-address, but unless the flag is explicitly passed in, IsSet("statsd-address") will return false, and the SetupStatsd() method won't be called.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed IsSet() returned true for defaults as well.

cli.StringFlag{
Name: "prometheus-port",
Value: "9810",
Usage: "Expose metrics via prometheus on `PORT`.",
},
cli.StringSliceFlag{
Name: "resolver-address",
Usage: "Make DNS requests to `ADDRESS` (IP:port). Repeatable.",
Expand Down Expand Up @@ -229,6 +238,12 @@ func NewConfiguration(args []string, logger *log.Logger) (*smokescreen.Config, e
}
}

if c.IsSet("prometheus-endpoint") {
if err := conf.SetupPrometheus(c.String("prometheus-endpoint"), c.String("prometheus-port")); err != nil {
return err
}
}

if c.IsSet("egress-acl-file") {
if err := conf.SetupEgressAcl(c.String("egress-acl-file")); err != nil {
return err
Expand Down
13 changes: 11 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
module github.com/stripe/smokescreen

go 1.17
go 1.18

require (
github.com/DataDog/datadog-go v4.5.1+incompatible
github.com/armon/go-proxyproto v0.0.0-20170620220930-48572f11356f
github.com/carlmjohnson/versioninfo v0.22.4
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.13.0
github.com/rs/xid v1.2.1
github.com/sirupsen/logrus v1.7.0
github.com/stretchr/testify v1.8.0
github.com/stripe/goproxy v0.0.0-20220308202309-3f1dfba6d1a4
golang.org/x/net v0.0.0-20220812174116-3211cb980234
gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
464 changes: 461 additions & 3 deletions go.sum

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion pkg/smokescreen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func (config *Config) SetupStatsdWithNamespace(addr, namespace string) error {
return nil
}

mc, err := metrics.NewMetricsClient(addr, namespace)
mc, err := metrics.NewStatsdMetricsClient(addr, namespace)
if err != nil {
return err
}
Expand All @@ -318,6 +318,15 @@ func (config *Config) SetupStatsd(addr string) error {
return config.SetupStatsdWithNamespace(addr, DefaultStatsdNamespace)
}

func (config *Config) SetupPrometheus(endpoint string, port string) error {
metricsClient, err := metrics.NewPrometheusMetricsClient(endpoint, port)
if err != nil {
return err
}
config.MetricsClient = metricsClient
return nil
}

func (config *Config) SetupEgressAcl(aclFile string) error {
if aclFile == "" {
config.EgressACL = nil
Expand Down
48 changes: 24 additions & 24 deletions pkg/smokescreen/config_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,71 +48,71 @@ type yamlConfig struct {
Tls *yamlConfigTls
// Currently not configurable via YAML: RoleFromRequest, Log, DisabledAclPolicyActions

UnsafeAllowPrivateRanges bool `yaml:"unsafe_allow_private_ranges"`
UnsafeAllowPrivateRanges bool `yaml:"unsafe_allow_private_ranges"`
}

func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
func (config *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
var yc yamlConfig
*c = *NewConfig()
*config = *NewConfig()

err := unmarshal(&yc)
if err != nil {
return err
}

c.Ip = yc.Ip
config.Ip = yc.Ip

if yc.Port != nil {
c.Port = *yc.Port
config.Port = *yc.Port
}

err = c.SetDenyRanges(yc.DenyRanges)
err = config.SetDenyRanges(yc.DenyRanges)
if err != nil {
return err
}

err = c.SetAllowRanges(yc.AllowRanges)
err = config.SetAllowRanges(yc.AllowRanges)
if err != nil {
return err
}

err = c.SetResolverAddresses(yc.Resolvers)
err = config.SetResolverAddresses(yc.Resolvers)
if err != nil {
return err
}

c.IdleTimeout = yc.IdleTimeout
c.ConnectTimeout = yc.ConnectTimeout
config.IdleTimeout = yc.IdleTimeout
config.ConnectTimeout = yc.ConnectTimeout
if yc.ExitTimeout != nil {
c.ExitTimeout = *yc.ExitTimeout
config.ExitTimeout = *yc.ExitTimeout
}

err = c.SetupStatsd(yc.StatsdAddress)
err = config.SetupStatsd(yc.StatsdAddress)
if err != nil {
return err
}

if yc.EgressAclFile != "" {
err = c.SetupEgressAcl(yc.EgressAclFile)
err = config.SetupEgressAcl(yc.EgressAclFile)
if err != nil {
return err
}
}

c.SupportProxyProtocol = yc.SupportProxyProtocol
config.SupportProxyProtocol = yc.SupportProxyProtocol

if yc.StatsSocketDir != "" {
c.StatsSocketDir = yc.StatsSocketDir
config.StatsSocketDir = yc.StatsSocketDir
}

if yc.StatsSocketFileMode != "" {
filemode, err := strconv.ParseInt(yc.StatsSocketFileMode, 8, 9)

if err != nil {
c.Log.Fatal(err)
config.Log.Fatal(err)
}

c.StatsSocketFileMode = os.FileMode(filemode)
config.StatsSocketFileMode = os.FileMode(filemode)
}

if yc.Tls != nil {
Expand All @@ -126,12 +126,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
key_file = yc.Tls.CertFile
}

err = c.SetupTls(yc.Tls.CertFile, key_file, yc.Tls.ClientCAFiles)
err = config.SetupTls(yc.Tls.CertFile, key_file, yc.Tls.ClientCAFiles)
if err != nil {
return err
}

err = c.SetupCrls(yc.Tls.CRLFiles)
err = config.SetupCrls(yc.Tls.CRLFiles)
if err != nil {
return err
}
Expand All @@ -143,13 +143,13 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
default:
return fmt.Errorf("invalid network type: %v", yc.Network)
}
c.Network = yc.Network
config.Network = yc.Network
}

c.AllowMissingRole = yc.AllowMissingRole
c.AdditionalErrorMessageOnDeny = yc.DenyMessageExtra
c.TimeConnect = yc.TimeConnect
c.UnsafeAllowPrivateRanges = yc.UnsafeAllowPrivateRanges
config.AllowMissingRole = yc.AllowMissingRole
config.AdditionalErrorMessageOnDeny = yc.DenyMessageExtra
config.TimeConnect = yc.TimeConnect
config.UnsafeAllowPrivateRanges = yc.UnsafeAllowPrivateRanges

return nil
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/smokescreen/conntrack/conn_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ func TestConnSuccessRateTracker(t *testing.T) {
assert.InDelta(tc.expectedRate, stats.ConnSuccessRate, 0.01)
assert.Equal(tc.totalConns, stats.TotalConns)

v, err := mockMetricsClient.GetValues("cn.atpt.distinct_domains_success_rate")
v, err := mockMetricsClient.GetValues("cn.atpt.distinct_domains_success_rate", map[string]string{})
assert.NoError(err)
assert.Equal(tc.expectedRate, v[len(v)-1])

v, err = mockMetricsClient.GetValues("cn.atpt.distinct_domains")
v, err = mockMetricsClient.GetValues("cn.atpt.distinct_domains", map[string]string{})
assert.NoError(err)
assert.Equal(tc.totalConns, int(v[len(v)-1]))

Expand Down
5 changes: 2 additions & 3 deletions pkg/smokescreen/conntrack/instrumented_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package conntrack

import (
"encoding/json"
"fmt"
"net"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -93,8 +92,8 @@ func (ic *InstrumentedConn) Close() error {
end := time.Now()
duration := end.Sub(ic.Start).Seconds()

tags := []string{
fmt.Sprintf("role:%s", ic.Role),
tags := map[string]string{
"role": ic.Role,
}

ic.tracker.statsc.IncrWithTags("cn.close", tags, 1)
Expand Down
Loading