diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 958d0e344..d15ff460d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,7 @@ * BUGFIX: vmagent: prevent from high CPU usage bug during failing scrapes with small `scrape_timeout` (less than a few seconds). * BUGFIX: vmagent: reduce memory usage when Kubernetes service discovery is used in big number of distinct scrape config jobs by sharing Kubernetes object cache. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1113 * BUGFIX: vmagent: apply `sample_limit` only after `metric_relabel_configs` are applied as Prometheus does. Previously the `sample_limit` was applied before metrics relabeling. +* BUGFIX: vmagent: properly apply `tls_config`, `basic_auth` and `bearer_token` to proxy connections if `proxy_url` option is set. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1116 * BUGFUX: avoid `duplicate time series` error if `prometheus_buckets()` covers a time range with distinct set of buckets. * BUGFIX: prevent exponent overflow when processing extremely small values close to zero such as `2.964393875E-314`. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1114 diff --git a/lib/promauth/config.go b/lib/promauth/config.go index 184f872bf..f80b15ae2 100644 --- a/lib/promauth/config.go +++ b/lib/promauth/config.go @@ -65,13 +65,16 @@ func (ac *Config) tlsCertificateString() string { // NewTLSConfig returns new TLS config for the given ac. func (ac *Config) NewTLSConfig() *tls.Config { tlsCfg := &tls.Config{ - RootCAs: ac.TLSRootCA, ClientSessionCache: tls.NewLRUClientSessionCache(0), } + if ac == nil { + return tlsCfg + } if ac.TLSCertificate != nil { // Do not set tlsCfg.GetClientCertificate, since tlsCfg.Certificates should work OK. tlsCfg.Certificates = []tls.Certificate{*ac.TLSCertificate} } + tlsCfg.RootCAs = ac.TLSRootCA tlsCfg.ServerName = ac.TLSServerName tlsCfg.InsecureSkipVerify = ac.TLSInsecureSkipVerify return tlsCfg diff --git a/lib/promscrape/client.go b/lib/promscrape/client.go index ce8744886..958c06055 100644 --- a/lib/promscrape/client.go +++ b/lib/promscrape/client.go @@ -57,7 +57,7 @@ func newClient(sw *ScrapeWork) *client { requestURI := string(u.RequestURI()) isTLS := string(u.Scheme()) == "https" var tlsCfg *tls.Config - if sw.AuthConfig != nil { + if isTLS { tlsCfg = sw.AuthConfig.NewTLSConfig() } if !strings.Contains(host, ":") { @@ -67,7 +67,7 @@ func newClient(sw *ScrapeWork) *client { host += ":443" } } - dialFunc, err := newStatDialFunc(sw.ProxyURL, tlsCfg) + dialFunc, err := newStatDialFunc(sw.ProxyURL, sw.AuthConfig) if err != nil { logger.Fatalf("cannot create dial func: %s", err) } diff --git a/lib/promscrape/discoveryutils/client.go b/lib/promscrape/discoveryutils/client.go index 58fe34bc9..ecddf92cc 100644 --- a/lib/promscrape/discoveryutils/client.go +++ b/lib/promscrape/discoveryutils/client.go @@ -66,7 +66,7 @@ func NewClient(apiServer string, ac *promauth.Config, proxyURL proxy.URL) (*Clie hostPort := string(u.Host()) isTLS := string(u.Scheme()) == "https" - if ac != nil { + if isTLS { tlsCfg = ac.NewTLSConfig() } if !strings.Contains(hostPort, ":") { @@ -77,7 +77,7 @@ func NewClient(apiServer string, ac *promauth.Config, proxyURL proxy.URL) (*Clie hostPort = net.JoinHostPort(hostPort, port) } if dialFunc == nil { - dialFunc, err = proxyURL.NewDialFunc(tlsCfg) + dialFunc, err = proxyURL.NewDialFunc(ac) if err != nil { return nil, err } diff --git a/lib/promscrape/statconn.go b/lib/promscrape/statconn.go index 2fefaf420..d8bbdce9f 100644 --- a/lib/promscrape/statconn.go +++ b/lib/promscrape/statconn.go @@ -2,7 +2,6 @@ package promscrape import ( "context" - "crypto/tls" "fmt" "net" "sync" @@ -10,6 +9,7 @@ import ( "time" "github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" "github.com/VictoriaMetrics/VictoriaMetrics/lib/proxy" "github.com/VictoriaMetrics/fasthttp" "github.com/VictoriaMetrics/metrics" @@ -49,8 +49,8 @@ var ( stdDialerOnce sync.Once ) -func newStatDialFunc(proxyURL proxy.URL, tlsConfig *tls.Config) (fasthttp.DialFunc, error) { - dialFunc, err := proxyURL.NewDialFunc(tlsConfig) +func newStatDialFunc(proxyURL proxy.URL, ac *promauth.Config) (fasthttp.DialFunc, error) { + dialFunc, err := proxyURL.NewDialFunc(ac) if err != nil { return nil, err } diff --git a/lib/proxy/proxy.go b/lib/proxy/proxy.go index bb3c339ef..0bc87398a 100644 --- a/lib/proxy/proxy.go +++ b/lib/proxy/proxy.go @@ -7,9 +7,11 @@ import ( "fmt" "net" "net/url" + "strings" "time" "github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" "github.com/VictoriaMetrics/fasthttp" ) @@ -48,8 +50,8 @@ func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } -// NewDialFunc returns dial func for the given pu and tlsConfig. -func (u *URL) NewDialFunc(tlsConfig *tls.Config) (fasthttp.DialFunc, error) { +// NewDialFunc returns dial func for the given u and ac. +func (u *URL) NewDialFunc(ac *promauth.Config) (fasthttp.DialFunc, error) { if u == nil || u.url == nil { return defaultDialFunc, nil } @@ -57,18 +59,32 @@ func (u *URL) NewDialFunc(tlsConfig *tls.Config) (fasthttp.DialFunc, error) { if pu.Scheme != "http" && pu.Scheme != "https" { return nil, fmt.Errorf("unknown scheme=%q for proxy_url=%q, must be http or https", pu.Scheme, pu) } + isTLS := pu.Scheme == "https" + proxyAddr := addMissingPort(pu.Host, isTLS) var authHeader string + if ac != nil { + authHeader = ac.Authorization + } if pu.User != nil && len(pu.User.Username()) > 0 { userPasswordEncoded := base64.StdEncoding.EncodeToString([]byte(pu.User.String())) - authHeader = "Proxy-Authorization: Basic " + userPasswordEncoded + "\r\n" + authHeader = "Basic " + userPasswordEncoded } + if authHeader != "" { + authHeader = "Proxy-Authorization: " + authHeader + "\r\n" + } + tlsCfg := ac.NewTLSConfig() dialFunc := func(addr string) (net.Conn, error) { - proxyConn, err := defaultDialFunc(pu.Host) + proxyConn, err := defaultDialFunc(proxyAddr) if err != nil { return nil, fmt.Errorf("cannot connect to proxy %q: %w", pu, err) } - if pu.Scheme == "https" { - proxyConn = tls.Client(proxyConn, tlsConfig) + if isTLS { + tlsCfgLocal := tlsCfg + if !tlsCfgLocal.InsecureSkipVerify && tlsCfgLocal.ServerName == "" { + tlsCfgLocal = tlsCfgLocal.Clone() + tlsCfgLocal.ServerName = tlsServerName(addr) + } + proxyConn = tls.Client(proxyConn, tlsCfgLocal) } conn, err := sendConnectRequest(proxyConn, addr, authHeader) if err != nil { @@ -80,6 +96,25 @@ func (u *URL) NewDialFunc(tlsConfig *tls.Config) (fasthttp.DialFunc, error) { return dialFunc, nil } +func addMissingPort(addr string, isTLS bool) string { + if strings.IndexByte(addr, ':') >= 0 { + return addr + } + port := "80" + if isTLS { + port = "443" + } + return addr + ":" + port +} + +func tlsServerName(addr string) string { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return addr + } + return host +} + func defaultDialFunc(addr string) (net.Conn, error) { network := "tcp4" if netutil.TCP6Enabled() {