From 806a799195135ae44bdba73104591e85900597c8 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Tue, 19 Jan 2021 22:12:40 +0100 Subject: [PATCH 1/2] Update exporter-toolkit dependency to 0.5.1 Signed-off-by: beorn7 --- cmd/prometheus/main.go | 6 +- cmd/promtool/main.go | 4 +- go.mod | 2 +- go.sum | 4 +- .../exporter-toolkit/https/README.md | 81 ----------------- .../prometheus/exporter-toolkit/web/README.md | 10 ++ .../prometheus/exporter-toolkit/web/cache.go | 91 +++++++++++++++++++ .../{https => web}/kingpinflag/flag.go | 0 .../{https => web}/tls_config.go | 27 ++++-- .../exporter-toolkit/{https => web}/users.go | 40 ++++++-- .../{https => web}/web-config.yml | 0 vendor/modules.txt | 6 +- web/web.go | 2 +- 13 files changed, 165 insertions(+), 108 deletions(-) delete mode 100644 vendor/github.com/prometheus/exporter-toolkit/https/README.md create mode 100644 vendor/github.com/prometheus/exporter-toolkit/web/README.md create mode 100644 vendor/github.com/prometheus/exporter-toolkit/web/cache.go rename vendor/github.com/prometheus/exporter-toolkit/{https => web}/kingpinflag/flag.go (100%) rename vendor/github.com/prometheus/exporter-toolkit/{https => web}/tls_config.go (96%) rename vendor/github.com/prometheus/exporter-toolkit/{https => web}/users.go (59%) rename vendor/github.com/prometheus/exporter-toolkit/{https => web}/web-config.yml (100%) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 07a9381db..dfec4ea7c 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -46,8 +46,8 @@ import ( "github.com/prometheus/common/promlog" promlogflag "github.com/prometheus/common/promlog/flag" "github.com/prometheus/common/version" - "github.com/prometheus/exporter-toolkit/https" - httpsflag "github.com/prometheus/exporter-toolkit/https/kingpinflag" + toolkit_web "github.com/prometheus/exporter-toolkit/web" + httpsflag "github.com/prometheus/exporter-toolkit/web/kingpinflag" jcfg "github.com/uber/jaeger-client-go/config" jprom "github.com/uber/jaeger-lib/metrics/prometheus" "go.uber.org/atomic" @@ -568,7 +568,7 @@ func main() { os.Exit(1) } - err = https.Validate(*httpsConfig) + err = toolkit_web.Validate(*httpsConfig) if err != nil { level.Error(logger).Log("msg", "Unable to validate web configuration file", "err", err) os.Exit(1) diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go index 1a13c537e..e3dc418cb 100644 --- a/cmd/promtool/main.go +++ b/cmd/promtool/main.go @@ -37,7 +37,7 @@ import ( config_util "github.com/prometheus/common/config" "github.com/prometheus/common/model" "github.com/prometheus/common/version" - "github.com/prometheus/exporter-toolkit/https" + toolkit_web "github.com/prometheus/exporter-toolkit/web" "gopkg.in/alecthomas/kingpin.v2" "github.com/prometheus/prometheus/config" @@ -249,7 +249,7 @@ func CheckWebConfig(files ...string) int { failed := false for _, f := range files { - if err := https.Validate(f); err != nil { + if err := toolkit_web.Validate(f); err != nil { fmt.Fprintln(os.Stderr, f, "FAILED:", err) failed = true continue diff --git a/go.mod b/go.mod index b6f41c170..778954b3e 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/prometheus/client_golang v1.9.0 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.15.0 - github.com/prometheus/exporter-toolkit v0.4.0 + github.com/prometheus/exporter-toolkit v0.5.1 github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 diff --git a/go.sum b/go.sum index 238d8cdf8..09263a031 100644 --- a/go.sum +++ b/go.sum @@ -711,8 +711,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/exporter-toolkit v0.4.0 h1:O7Bw+ZKEMzW7vD10IuVF70b8EE4JIG7BvHFj9UKz49g= -github.com/prometheus/exporter-toolkit v0.4.0/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= +github.com/prometheus/exporter-toolkit v0.5.1 h1:9eqgis5er9xN613ZSADjypCJaDGj9ZlcWBvsIHa8/3c= +github.com/prometheus/exporter-toolkit v0.5.1/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= diff --git a/vendor/github.com/prometheus/exporter-toolkit/https/README.md b/vendor/github.com/prometheus/exporter-toolkit/https/README.md deleted file mode 100644 index e6c9896f1..000000000 --- a/vendor/github.com/prometheus/exporter-toolkit/https/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# HTTPS Package for Prometheus - -The `https` directory contains a Go package and a sample configuration file for -running `node_exporter` with HTTPS instead of HTTP. We currently support TLS 1.3 -and TLS 1.2. - -To run a server with TLS, use the flag `--web.config`. - -e.g. `./node_exporter --web.config="web-config.yml"` -If the config is kept within the https directory. - -The config file should be written in YAML format, and is reloaded on each connection to check for new certificates and/or authentication policy. - -## Sample Config - -``` -tls_server_config: - # Certificate and key files for server to use to authenticate to client. - cert_file: - key_file: - - # Server policy for client authentication. Maps to ClientAuth Policies. - # For more detail on clientAuth options: [ClientAuthType](https://golang.org/pkg/crypto/tls/#ClientAuthType) - [ client_auth_type: | default = "NoClientCert" ] - - # CA certificate for client certificate authentication to the server. - [ client_ca_file: ] - - # Minimum TLS version that is acceptable. - [ min_version: | default = "TLS12" ] - - # Maximum TLS version that is acceptable. - [ max_version: | default = "TLS13" ] - - # List of supported cipher suites for TLS versions up to TLS 1.2. If empty, - # Go default cipher suites are used. Available cipher suites are documented - # in the go documentation: - # https://golang.org/pkg/crypto/tls/#pkg-constants - [ cipher_suites: - [ - ] ] - - # prefer_server_cipher_suites controls whether the server selects the - # client's most preferred ciphersuite, or the server's most preferred - # ciphersuite. If true then the server's preference, as expressed in - # the order of elements in cipher_suites, is used. - [ prefer_server_cipher_suites: | default = true ] - - # Elliptic curves that will be used in an ECDHE handshake, in preference - # order. Available curves are documented in the go documentation: - # https://golang.org/pkg/crypto/tls/#CurveID - [ curve_preferences: - [ - ] ] - -http_server_config: - # Enable HTTP/2 support. Note that HTTP/2 is only supported with TLS. - # This can not be changed on the fly. - [ http2: | default = true ] - -# Usernames and hashed passwords that have full access to the web -# server via basic authentication. If empty, no basic authentication is -# required. Passwords are hashed with bcrypt. -basic_auth_users: - [ : ... ] -``` - -## About bcrypt - -There are several tools out there to generate bcrypt passwords, e.g. -[htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html): - -`htpasswd -nBC 10 "" | tr -d ':\n'` - -That command will prompt you for a password and output the hashed password, -which will look something like: -`$2y$10$X0h1gDsPszWURQaxFh.zoubFi6DXncSjhoQNJgRrnGs7EsimhC7zG` - -The cost (10 in the example) influences the time it takes for computing the -hash. A higher cost will en up slowing down the authentication process. -Depending on the machine, a cost of 10 will take about ~70ms where a cost of -18 can take up to a few seconds. That hash will be computed on every -password-protected request. diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/README.md b/vendor/github.com/prometheus/exporter-toolkit/web/README.md new file mode 100644 index 000000000..1e16644be --- /dev/null +++ b/vendor/github.com/prometheus/exporter-toolkit/web/README.md @@ -0,0 +1,10 @@ +# web package + +This package can be used by Prometheus exporters to enable TLS and +authentication. + +We actively encourage the community to use this repository, to provide a +consistent experience across the ecosystem. + +Developers documentation can be found on +[pkg.go.dev](https://pkg.go.dev/github.com/prometheus/exporter-toolkit/). diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/cache.go b/vendor/github.com/prometheus/exporter-toolkit/web/cache.go new file mode 100644 index 000000000..9425e7ac9 --- /dev/null +++ b/vendor/github.com/prometheus/exporter-toolkit/web/cache.go @@ -0,0 +1,91 @@ +// Copyright 2021 The Prometheus Authors +// This code is partly borrowed from Caddy: +// Copyright 2015 Matthew Holt and The Caddy Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package web + +import ( + weakrand "math/rand" + "sync" + "time" +) + +var cacheSize = 100 + +func init() { + weakrand.Seed(time.Now().UnixNano()) +} + +type cache struct { + cache map[string]bool + mtx sync.Mutex +} + +// newCache returns a cache that contains a mapping of plaintext passwords +// to their hashes (with random eviction). This can greatly improve the +// performance of traffic-heavy servers that use secure password hashing +// algorithms, with the downside that plaintext passwords will be stored in +// memory for a longer time (this should not be a problem as long as your +// machine is not compromised, at which point all bets are off, since basicauth +// necessitates plaintext passwords being received over the wire anyway). +func newCache() *cache { + return &cache{ + cache: make(map[string]bool), + } +} + +func (c *cache) get(key string) (bool, bool) { + c.mtx.Lock() + defer c.mtx.Unlock() + v, ok := c.cache[key] + return v, ok +} + +func (c *cache) set(key string, value bool) { + c.mtx.Lock() + defer c.mtx.Unlock() + c.makeRoom() + c.cache[key] = value +} + +func (c *cache) makeRoom() { + if len(c.cache) < cacheSize { + return + } + // We delete more than just 1 entry so that we don't have + // to do this on every request; assuming the capacity of + // the cache is on a long tail, we can save a lot of CPU + // time by doing a whole bunch of deletions now and then + // we won't have to do them again for a while. + numToDelete := len(c.cache) / 10 + if numToDelete < 1 { + numToDelete = 1 + } + for deleted := 0; deleted <= numToDelete; deleted++ { + // Go maps are "nondeterministic" not actually random, + // so although we could just chop off the "front" of the + // map with less code, this is a heavily skewed eviction + // strategy; generating random numbers is cheap and + // ensures a much better distribution. + rnd := weakrand.Intn(len(c.cache)) + i := 0 + for key := range c.cache { + if i == rnd { + delete(c.cache, key) + break + } + i++ + } + } +} diff --git a/vendor/github.com/prometheus/exporter-toolkit/https/kingpinflag/flag.go b/vendor/github.com/prometheus/exporter-toolkit/web/kingpinflag/flag.go similarity index 100% rename from vendor/github.com/prometheus/exporter-toolkit/https/kingpinflag/flag.go rename to vendor/github.com/prometheus/exporter-toolkit/web/kingpinflag/flag.go diff --git a/vendor/github.com/prometheus/exporter-toolkit/https/tls_config.go b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go similarity index 96% rename from vendor/github.com/prometheus/exporter-toolkit/https/tls_config.go rename to vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go index 192e5338a..06fb1548f 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/https/tls_config.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package https allows the implementation of TLS. -package https +package web import ( "crypto/tls" @@ -174,9 +173,9 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) { return cfg, nil } -// Listen starts the server on the given address. Based on the file +// ListenAndServe starts the server on the given address. Based on the file // tlsConfigPath, TLS or basic auth could be enabled. -func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error { +func ListenAndServe(server *http.Server, tlsConfigPath string, logger log.Logger) error { listener, err := net.Listen("tcp", server.Addr) if err != nil { return err @@ -202,17 +201,19 @@ func Serve(l net.Listener, server *http.Server, tlsConfigPath string, logger log if server.Handler != nil { handler = server.Handler } - server.Handler = &userAuthRoundtrip{ - tlsConfigPath: tlsConfigPath, - logger: logger, - handler: handler, - } c, err := getConfig(tlsConfigPath) if err != nil { return err } + server.Handler = &userAuthRoundtrip{ + tlsConfigPath: tlsConfigPath, + logger: logger, + handler: handler, + cache: newCache(), + } + config, err := ConfigToTLSConfig(&c.TLSConfig) switch err { case nil: @@ -341,3 +342,11 @@ func (tv *tlsVersion) MarshalYAML() (interface{}, error) { } return fmt.Sprintf("%v", tv), nil } + +// Listen starts the server on the given address. Based on the file +// tlsConfigPath, TLS or basic auth could be enabled. +// +// Deprecated: Use ListenAndServe instead. +func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error { + return ListenAndServe(server, tlsConfigPath, logger) +} diff --git a/vendor/github.com/prometheus/exporter-toolkit/https/users.go b/vendor/github.com/prometheus/exporter-toolkit/web/users.go similarity index 59% rename from vendor/github.com/prometheus/exporter-toolkit/https/users.go rename to vendor/github.com/prometheus/exporter-toolkit/web/users.go index 317b0e4f8..8168dabf4 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/https/users.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/users.go @@ -1,4 +1,6 @@ // Copyright 2020 The Prometheus Authors +// This code is partly borrowed from Caddy: +// Copyright 2015 Matthew Holt and The Caddy Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,10 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package https +package web import ( + "encoding/hex" "net/http" + "sync" "github.com/go-kit/kit/log" "golang.org/x/crypto/bcrypt" @@ -40,6 +44,10 @@ type userAuthRoundtrip struct { tlsConfigPath string handler http.Handler logger log.Logger + cache *cache + // bcryptMtx is there to ensure that bcrypt.CompareHashAndPassword is run + // only once in parallel as this is CPU intensive. + bcryptMtx sync.Mutex } func (u *userAuthRoundtrip) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -57,11 +65,31 @@ func (u *userAuthRoundtrip) ServeHTTP(w http.ResponseWriter, r *http.Request) { user, pass, auth := r.BasicAuth() if auth { - if hashedPassword, ok := c.Users[user]; ok { - if err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass)); err == nil { - u.handler.ServeHTTP(w, r) - return - } + hashedPassword, validUser := c.Users[user] + + if !validUser { + // The user is not found. Use a fixed password hash to + // prevent user enumeration by timing requests. + // This is a bcrypt-hashed version of "fakepassword". + hashedPassword = "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSi" + } + + cacheKey := hex.EncodeToString(append(append([]byte(user), []byte(hashedPassword)...), []byte(pass)...)) + authOk, ok := u.cache.get(cacheKey) + + if !ok { + // This user, hashedPassword, password is not cached. + u.bcryptMtx.Lock() + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass)) + u.bcryptMtx.Unlock() + + authOk = err == nil + u.cache.set(cacheKey, authOk) + } + + if authOk && validUser { + u.handler.ServeHTTP(w, r) + return } } diff --git a/vendor/github.com/prometheus/exporter-toolkit/https/web-config.yml b/vendor/github.com/prometheus/exporter-toolkit/web/web-config.yml similarity index 100% rename from vendor/github.com/prometheus/exporter-toolkit/https/web-config.yml rename to vendor/github.com/prometheus/exporter-toolkit/web/web-config.yml diff --git a/vendor/modules.txt b/vendor/modules.txt index fbcb0543d..11f070a44 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -395,10 +395,10 @@ github.com/prometheus/common/promlog/flag github.com/prometheus/common/route github.com/prometheus/common/server github.com/prometheus/common/version -# github.com/prometheus/exporter-toolkit v0.4.0 +# github.com/prometheus/exporter-toolkit v0.5.1 ## explicit -github.com/prometheus/exporter-toolkit/https -github.com/prometheus/exporter-toolkit/https/kingpinflag +github.com/prometheus/exporter-toolkit/web +github.com/prometheus/exporter-toolkit/web/kingpinflag # github.com/prometheus/procfs v0.2.0 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs diff --git a/web/web.go b/web/web.go index 2160e5b4d..defdce8ea 100644 --- a/web/web.go +++ b/web/web.go @@ -50,7 +50,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/common/route" "github.com/prometheus/common/server" - "github.com/prometheus/exporter-toolkit/https" + https "github.com/prometheus/exporter-toolkit/web" "go.uber.org/atomic" "golang.org/x/net/netutil" From e4487274853c587717006eeda8804e597d120340 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Tue, 19 Jan 2021 22:48:10 +0100 Subject: [PATCH 2/2] Cut v2.24.1 Signed-off-by: beorn7 --- CHANGELOG.md | 5 +++++ VERSION | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ed9aa19e..711bd4918 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.24.1 / 2021-01-20 + +* [ENHANCEMENT] Cache basic authentication results to significantly improve performance of HTTP endpoints (via an update of prometheus/exporter-toolkit). +* [BUGFIX] Prevent user enumeration by timing requests sent to authenticated HTTP endpoints (via an update of prometheus/exporter-toolkit). + ## 2.24.0 / 2021-01-06 * [FEATURE] Add TLS and basic authentication to HTTP endpoints. #8316 diff --git a/VERSION b/VERSION index ad2261920..0f5dfbe87 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.24.0 +2.24.1