@@ -3,10 +3,13 @@ package analytics
33import (
44 "bytes"
55 "context"
6+ "crypto/tls"
67 "errors"
78 "flag"
9+ "fmt"
810 "io"
911 "math"
12+ "net"
1013 "net/http"
1114 "net/url"
1215 "os"
@@ -16,7 +19,7 @@ import (
1619 "github.com/go-kit/log/level"
1720 "github.com/google/uuid"
1821 "github.com/grafana/dskit/backoff"
19- "github.com/grafana/dskit/crypto/tls"
22+ dskittls "github.com/grafana/dskit/crypto/tls"
2023 "github.com/grafana/dskit/kv"
2124 "github.com/grafana/dskit/multierror"
2225 "github.com/grafana/dskit/services"
@@ -46,11 +49,11 @@ var (
4649)
4750
4851type Config struct {
49- Enabled bool `yaml:"reporting_enabled"`
50- Leader bool `yaml:"-"`
51- UsageStatsURL string `yaml:"usage_stats_url"`
52- ProxyURL string `yaml:"proxy_url"`
53- TLSConfig tls .ClientConfig `yaml:"tls_config"`
52+ Enabled bool `yaml:"reporting_enabled"`
53+ Leader bool `yaml:"-"`
54+ UsageStatsURL string `yaml:"usage_stats_url"`
55+ ProxyURL string `yaml:"proxy_url"`
56+ TLSConfig dskittls .ClientConfig `yaml:"tls_config"`
5457}
5558
5659// RegisterFlags adds the flags required to config this to the given FlagSet
@@ -83,19 +86,22 @@ func NewReporter(config Config, kvConfig kv.Config, objectClient client.ObjectCl
8386
8487 originalDefaultTransport := http .DefaultTransport .(* http.Transport )
8588 tr := originalDefaultTransport .Clone ()
86- if config .TLSConfig .CertPath != "" || config .TLSConfig .KeyPath != "" {
87- var err error
88- tr .TLSClientConfig , err = config .TLSConfig .GetTLSConfig ()
89- if err != nil {
90- return nil , err
91- }
92- }
89+
9390 if config .ProxyURL != "" {
9491 proxyURL , err := url .ParseRequestURI (config .ProxyURL )
9592 if err != nil {
9693 return nil , err
9794 }
98- tr .Proxy = http .ProxyURL (proxyURL )
95+
96+ if proxyURL .Scheme == "https" {
97+ // For HTTPS proxies, create a custom transport that handles different TLS configs
98+ tr , err = createCustomTransportForHTTPSProxy (tr , proxyURL , config .TLSConfig )
99+ if err != nil {
100+ return nil , err
101+ }
102+ } else {
103+ tr .Proxy = http .ProxyURL (proxyURL )
104+ }
99105 }
100106 r := & Reporter {
101107 logger : logger ,
@@ -320,6 +326,7 @@ func (rep *Reporter) running(ctx context.Context) error {
320326 continue
321327 }
322328 rep .lastReport = next
329+ level .Debug (rep .logger ).Log ("msg" , "reported cluster stats" , "date" , time .Now ())
323330 next = next .Add (reportInterval )
324331 case <- ctx .Done ():
325332 if err := ctx .Err (); ! errors .Is (err , context .Canceled ) {
@@ -386,3 +393,49 @@ func nextReport(interval time.Duration, createdAt, now time.Time) time.Time {
386393 // createdAt * (x * interval ) >= now
387394 return createdAt .Add (time .Duration (math .Ceil (float64 (now .Sub (createdAt ))/ float64 (interval ))) * interval )
388395}
396+
397+ // createCustomTransportForHTTPSProxy creates a transport that validates the HTTPS proxy certificate
398+ // while maintaining system CA trust for the final destination.
399+ func createCustomTransportForHTTPSProxy (tr * http.Transport , proxyURL * url.URL , tlsConfig dskittls.ClientConfig ) (* http.Transport , error ) {
400+ // Create the proxy TLS config for connecting to the proxy
401+ var proxyTLSConfig * tls.Config
402+ if tlsConfig .CertPath != "" || tlsConfig .KeyPath != "" || tlsConfig .CAPath != "" ||
403+ tlsConfig .ServerName != "" || tlsConfig .InsecureSkipVerify {
404+ var err error
405+ proxyTLSConfig , err = tlsConfig .GetTLSConfig ()
406+ if err != nil {
407+ return nil , fmt .Errorf ("error getting proxy TLS config: %w" , err )
408+ }
409+ }
410+
411+ // function that handles proxy connections differently.
412+ tr .DialTLSContext = func (_ context.Context , network , addr string ) (net.Conn , error ) {
413+ // Parse the address to determine if this is a proxy connection
414+ host , _ , err := net .SplitHostPort (addr )
415+ if err != nil {
416+ host = addr
417+ }
418+
419+ // If this is a connection to our proxy, use the proxy TLS config
420+ if host == proxyURL .Hostname () {
421+ conn , err := tls .Dial (network , addr , proxyTLSConfig )
422+ if err != nil {
423+ return nil , err
424+ }
425+ return conn , nil
426+ }
427+
428+ // For all other connections (to final destinations), use system CAs
429+ defaultConfig := & tls.Config {}
430+ conn , err := tls .Dial (network , addr , defaultConfig )
431+ if err != nil {
432+ return nil , err
433+ }
434+ return conn , nil
435+ }
436+
437+ // Set up the proxy
438+ tr .Proxy = http .ProxyURL (proxyURL )
439+
440+ return tr , nil
441+ }
0 commit comments