diff --git a/lib/config/config.go b/lib/config/config.go index b4c5865a..7667eff6 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -2,6 +2,7 @@ package config import ( "errors" + "flag" "fmt" "io" "io/fs" @@ -334,6 +335,7 @@ type fileConfig struct { DNSBL bool `json:"dnsbl"` DNSTTL DnsTTL `json:"dns_ttl"` Logging *Logging `json:"logging"` + Metrics *Metrics `json:"metrics,omitempty"` } func (c *fileConfig) Valid() error { @@ -375,6 +377,12 @@ func (c *fileConfig) Valid() error { } } + if c.Metrics != nil { + if err := c.Metrics.Valid(); err != nil { + errs = append(errs, err) + } + } + if len(errs) != 0 { return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...)) } @@ -382,6 +390,16 @@ func (c *fileConfig) Valid() error { return nil } +func flagLookup(name string) string { + fl := flag.CommandLine.Lookup(name) + + if fl == nil { + return "" + } + + return fl.Value.String() +} + func Load(fin io.Reader, fname string) (*Config, error) { c := &fileConfig{ StatusCodes: StatusCodes{ @@ -396,6 +414,10 @@ func Load(fin io.Reader, fname string) (*Config, error) { Backend: "memory", }, Logging: (Logging{}).Default(), + Metrics: &Metrics{ + Bind: flagLookup("metrics-bind"), + Network: flagLookup("metrics-bind-network"), + }, } if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil { @@ -417,6 +439,7 @@ func Load(fin io.Reader, fname string) (*Config, error) { StatusCodes: c.StatusCodes, Store: c.Store, Logging: c.Logging, + Metrics: c.Metrics, } if c.OpenGraph.TimeToLive != "" { @@ -508,6 +531,7 @@ type Config struct { Logging *Logging DNSBL bool DNSTTL DnsTTL + Metrics *Metrics } func (c Config) Valid() error { diff --git a/lib/config/metrics.go b/lib/config/metrics.go new file mode 100644 index 00000000..939f240a --- /dev/null +++ b/lib/config/metrics.go @@ -0,0 +1,39 @@ +package config + +import "errors" + +var ( + ErrInvalidMetricsConfig = errors.New("config: invalid metrics configuration") + ErrNoMetricsBind = errors.New("config.Metrics: must define bind") + ErrNoMetricsNetwork = errors.New("config.Metrics: must define network") + ErrInvalidMetricsNetwork = errors.New("config.Metrics: invalid metrics network") +) + +type Metrics struct { + Bind string `json:"bind" yaml:"bind"` + Network string `json:"network" yaml:"network"` +} + +func (m *Metrics) Valid() error { + var errs []error + + if m.Bind == "" { + errs = append(errs, ErrNoMetricsBind) + } + + if m.Network == "" { + errs = append(errs, ErrNoMetricsNetwork) + } + + switch m.Network { + case "tcp", "tcp4", "tcp6", "unix": // https://pkg.go.dev/net#Listen + default: + errs = append(errs, ErrInvalidMetricsNetwork) + } + + if len(errs) != 0 { + return errors.Join(ErrInvalidMetricsConfig, errors.Join(errs...)) + } + + return nil +} diff --git a/lib/config/metrics_test.go b/lib/config/metrics_test.go new file mode 100644 index 00000000..bd1cf8af --- /dev/null +++ b/lib/config/metrics_test.go @@ -0,0 +1,48 @@ +package config + +import ( + "errors" + "testing" +) + +func TestMetricsValid(t *testing.T) { + for _, tt := range []struct { + name string + input *Metrics + err error + }{ + { + name: "basic TCP", + input: &Metrics{ + Bind: ":9090", + Network: "tcp", + }, + }, + { + name: "no bind", + input: &Metrics{}, + err: ErrNoMetricsBind, + }, + { + name: "no network", + input: &Metrics{}, + err: ErrNoMetricsNetwork, + }, + { + name: "invalid network", + input: &Metrics{ + Bind: ":9090", + Network: "taco", + }, + err: ErrInvalidMetricsNetwork, + }, + } { + t.Run(tt.name, func(t *testing.T) { + if err := tt.input.Valid(); !errors.Is(err, tt.err) { + t.Logf("wanted error: %v", tt.err) + t.Logf("got error: %v", err) + t.Error("validation failed") + } + }) + } +} diff --git a/lib/config/testdata/bad/metrics-invalid-net.yaml b/lib/config/testdata/bad/metrics-invalid-net.yaml new file mode 100644 index 00000000..3d3dfc71 --- /dev/null +++ b/lib/config/testdata/bad/metrics-invalid-net.yaml @@ -0,0 +1,3 @@ +metrics: + bind: ":9090" + network: taco \ No newline at end of file diff --git a/lib/config/testdata/good/allow_everyone.json b/lib/config/testdata/good/allow_everyone.json index de298c6b..b1d6eb0a 100644 --- a/lib/config/testdata/good/allow_everyone.json +++ b/lib/config/testdata/good/allow_everyone.json @@ -5,5 +5,9 @@ "remote_addresses": ["0.0.0.0/0", "::/0"], "action": "ALLOW" } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/allow_everyone.yaml b/lib/config/testdata/good/allow_everyone.yaml index 80dae8b2..085ca775 100644 --- a/lib/config/testdata/good/allow_everyone.yaml +++ b/lib/config/testdata/good/allow_everyone.yaml @@ -4,3 +4,7 @@ bots: - "0.0.0.0/0" - "::/0" action: ALLOW + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/block_cf_workers.json b/lib/config/testdata/good/block_cf_workers.json index 380fdf04..867533ee 100644 --- a/lib/config/testdata/good/block_cf_workers.json +++ b/lib/config/testdata/good/block_cf_workers.json @@ -8,5 +8,9 @@ "action": "DENY" } ], - "dnsbl": false + "dnsbl": false, + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/block_cf_workers.yaml b/lib/config/testdata/good/block_cf_workers.yaml index 353c921a..d539be95 100644 --- a/lib/config/testdata/good/block_cf_workers.yaml +++ b/lib/config/testdata/good/block_cf_workers.yaml @@ -3,3 +3,7 @@ bots: headers_regex: CF-Worker: .* action: DENY + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/challenge_cloudflare.yaml b/lib/config/testdata/good/challenge_cloudflare.yaml index 1c728cba..31de3333 100644 --- a/lib/config/testdata/good/challenge_cloudflare.yaml +++ b/lib/config/testdata/good/challenge_cloudflare.yaml @@ -4,3 +4,7 @@ bots: asns: match: - 13335 # Cloudflare + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/challengemozilla.json b/lib/config/testdata/good/challengemozilla.json index eaee8490..30b60b92 100644 --- a/lib/config/testdata/good/challengemozilla.json +++ b/lib/config/testdata/good/challengemozilla.json @@ -5,5 +5,9 @@ "user_agent_regex": "Mozilla", "action": "CHALLENGE" } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/challengemozilla.yaml b/lib/config/testdata/good/challengemozilla.yaml index f6d8e9ac..eb87a4f0 100644 --- a/lib/config/testdata/good/challengemozilla.yaml +++ b/lib/config/testdata/good/challengemozilla.yaml @@ -2,3 +2,7 @@ bots: - name: generic-browser user_agent_regex: Mozilla action: CHALLENGE + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/dns-ttl-custom.yaml b/lib/config/testdata/good/dns-ttl-custom.yaml index eb89e126..66fa30e2 100644 --- a/lib/config/testdata/good/dns-ttl-custom.yaml +++ b/lib/config/testdata/good/dns-ttl-custom.yaml @@ -6,3 +6,7 @@ bots: - name: "test" user_agent_regex: ".*" action: "DENY" + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/entropy.yaml b/lib/config/testdata/good/entropy.yaml index 80110c13..47be9d48 100644 --- a/lib/config/testdata/good/entropy.yaml +++ b/lib/config/testdata/good/entropy.yaml @@ -6,3 +6,7 @@ bots: - '"Accept" in headers' - headers["Accept"].contains("text/html") - randInt(1) == 0 + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/everything_blocked.json b/lib/config/testdata/good/everything_blocked.json index ab694f25..98b35eaf 100644 --- a/lib/config/testdata/good/everything_blocked.json +++ b/lib/config/testdata/good/everything_blocked.json @@ -6,5 +6,9 @@ "action": "DENY" } ], - "dnsbl": false + "dnsbl": false, + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/everything_blocked.yaml b/lib/config/testdata/good/everything_blocked.yaml index 1c805581..3f5e1dbe 100644 --- a/lib/config/testdata/good/everything_blocked.yaml +++ b/lib/config/testdata/good/everything_blocked.yaml @@ -2,3 +2,7 @@ bots: - name: everything user_agent_regex: .* action: DENY + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/geoip_us.yaml b/lib/config/testdata/good/geoip_us.yaml index b5e42804..6c3c6b7a 100644 --- a/lib/config/testdata/good/geoip_us.yaml +++ b/lib/config/testdata/good/geoip_us.yaml @@ -4,3 +4,7 @@ bots: geoip: countries: - US + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/git_client.json b/lib/config/testdata/good/git_client.json index 52c5e11f..b1be750f 100644 --- a/lib/config/testdata/good/git_client.json +++ b/lib/config/testdata/good/git_client.json @@ -10,5 +10,9 @@ ] } } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/git_client.yaml b/lib/config/testdata/good/git_client.yaml index 1c496789..6620b673 100644 --- a/lib/config/testdata/good/git_client.yaml +++ b/lib/config/testdata/good/git_client.yaml @@ -6,3 +6,7 @@ bots: - userAgent.startsWith("git/") || userAgent.contains("libgit") - > "Git-Protocol" in headers && headers["Git-Protocol"] == "version=2" + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/import_filesystem.json b/lib/config/testdata/good/import_filesystem.json index ba0228a7..a45881d4 100644 --- a/lib/config/testdata/good/import_filesystem.json +++ b/lib/config/testdata/good/import_filesystem.json @@ -3,5 +3,9 @@ { "import": "./testdata/hack-test.json" } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/import_filesystem.yaml b/lib/config/testdata/good/import_filesystem.yaml index 2ea0d54e..cac73096 100644 --- a/lib/config/testdata/good/import_filesystem.yaml +++ b/lib/config/testdata/good/import_filesystem.yaml @@ -1,2 +1,6 @@ bots: - import: ./testdata/hack-test.yaml + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/import_keep_internet_working.json b/lib/config/testdata/good/import_keep_internet_working.json index 06b08fe1..53d6743e 100644 --- a/lib/config/testdata/good/import_keep_internet_working.json +++ b/lib/config/testdata/good/import_keep_internet_working.json @@ -3,5 +3,9 @@ { "import": "(data)/common/keep-internet-working.yaml" } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/import_keep_internet_working.yaml b/lib/config/testdata/good/import_keep_internet_working.yaml index 19ff33eb..dbb191e6 100644 --- a/lib/config/testdata/good/import_keep_internet_working.yaml +++ b/lib/config/testdata/good/import_keep_internet_working.yaml @@ -1,2 +1,6 @@ bots: - import: (data)/common/keep-internet-working.yaml + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/impressum.yaml b/lib/config/testdata/good/impressum.yaml index 9a1a03fc..460e26d1 100644 --- a/lib/config/testdata/good/impressum.yaml +++ b/lib/config/testdata/good/impressum.yaml @@ -8,3 +8,7 @@ impressum: page: title: Test body:

This is a test

+ +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/logging-file.yaml b/lib/config/testdata/good/logging-file.yaml index c1f09b36..a2a63ede 100644 --- a/lib/config/testdata/good/logging-file.yaml +++ b/lib/config/testdata/good/logging-file.yaml @@ -13,3 +13,7 @@ logs: oldFileTimeFormat: 2006-01-02T15-04-05 # RFC 3339-ish compress: true useLocalTime: false # timezone for rotated files is UTC + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/logging-stdio.yaml b/lib/config/testdata/good/logging-stdio.yaml index d0fcf54b..26c94037 100644 --- a/lib/config/testdata/good/logging-stdio.yaml +++ b/lib/config/testdata/good/logging-stdio.yaml @@ -5,3 +5,7 @@ bots: logging: sink: "stdio" + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/no-thresholds.yaml b/lib/config/testdata/good/no-thresholds.yaml index bf981fdd..66ec9905 100644 --- a/lib/config/testdata/good/no-thresholds.yaml +++ b/lib/config/testdata/good/no-thresholds.yaml @@ -6,3 +6,7 @@ bots: adjust: 5 thresholds: [] + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/old_xesite.json b/lib/config/testdata/good/old_xesite.json index 763d6785..39c2fbe1 100644 --- a/lib/config/testdata/good/old_xesite.json +++ b/lib/config/testdata/good/old_xesite.json @@ -75,5 +75,9 @@ "user_agent_regex": "Mozilla", "action": "CHALLENGE" } - ] + ], + "metrics": { + "bind": ":9090", + "network": "tcp" + } } diff --git a/lib/config/testdata/good/opengraph_all_good.yaml b/lib/config/testdata/good/opengraph_all_good.yaml index ae26d99b..f514c39c 100644 --- a/lib/config/testdata/good/opengraph_all_good.yaml +++ b/lib/config/testdata/good/opengraph_all_good.yaml @@ -10,3 +10,7 @@ openGraph: default: "og:title": "Xe's magic land of fun" "og:description": "We're no strangers to love, you know the rules and so do I" + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/simple-weight.yaml b/lib/config/testdata/good/simple-weight.yaml index ec7a92e9..3130f182 100644 --- a/lib/config/testdata/good/simple-weight.yaml +++ b/lib/config/testdata/good/simple-weight.yaml @@ -4,3 +4,7 @@ bots: user_agent_regex: Mozilla weight: adjust: 5 + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/status-codes-paranoid.json b/lib/config/testdata/good/status-codes-paranoid.json index f84dde91..716a1922 100644 --- a/lib/config/testdata/good/status-codes-paranoid.json +++ b/lib/config/testdata/good/status-codes-paranoid.json @@ -9,5 +9,9 @@ "status_codes": { "CHALLENGE": 200, "DENY": 200 + }, + "metrics": { + "bind": ":9090", + "network": "tcp" } } diff --git a/lib/config/testdata/good/status-codes-paranoid.yaml b/lib/config/testdata/good/status-codes-paranoid.yaml index 0310388b..783482b7 100644 --- a/lib/config/testdata/good/status-codes-paranoid.yaml +++ b/lib/config/testdata/good/status-codes-paranoid.yaml @@ -6,3 +6,7 @@ bots: status_codes: CHALLENGE: 200 DENY: 200 + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/status-codes-rfc.json b/lib/config/testdata/good/status-codes-rfc.json index 2fdaac05..da169a1c 100644 --- a/lib/config/testdata/good/status-codes-rfc.json +++ b/lib/config/testdata/good/status-codes-rfc.json @@ -9,5 +9,9 @@ "status_codes": { "CHALLENGE": 403, "DENY": 403 + }, + "metrics": { + "bind": ":9090", + "network": "tcp" } } diff --git a/lib/config/testdata/good/status-codes-rfc.yaml b/lib/config/testdata/good/status-codes-rfc.yaml index c70ea126..26c35791 100644 --- a/lib/config/testdata/good/status-codes-rfc.yaml +++ b/lib/config/testdata/good/status-codes-rfc.yaml @@ -6,3 +6,7 @@ bots: status_codes: CHALLENGE: 403 DENY: 403 + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/thresholds.yaml b/lib/config/testdata/good/thresholds.yaml index 9365c715..06966e80 100644 --- a/lib/config/testdata/good/thresholds.yaml +++ b/lib/config/testdata/good/thresholds.yaml @@ -33,3 +33,7 @@ thresholds: challenge: algorithm: fast difficulty: 4 + +metrics: + bind: ":9090" + network: "tcp" diff --git a/lib/config/testdata/good/weight-no-weight.yaml b/lib/config/testdata/good/weight-no-weight.yaml index f137f8ac..d88bd785 100644 --- a/lib/config/testdata/good/weight-no-weight.yaml +++ b/lib/config/testdata/good/weight-no-weight.yaml @@ -2,3 +2,7 @@ bots: - name: weight action: WEIGH user_agent_regex: Mozilla + +metrics: + bind: ":9090" + network: "tcp"