feat: Add TLS config to the analytics client (#15227)

**What this PR does / why we need it**:
Add a `tls_config` block and a `proxy_url` config to the analytics client. In conjunction they can be useful in scenarios where Loki is running behind a proxy.
pull/15323/head^2
Dylan Guedes 1 year ago committed by GitHub
parent 00d58e6053
commit 2c5eabdb72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 30
      docs/sources/shared/configuration.md
  2. 38
      pkg/analytics/reporter.go
  3. 45
      pkg/analytics/reporter_test.go
  4. 3
      pkg/analytics/stats.go

@ -967,6 +967,14 @@ Configuration for `analytics`.
# URL to which reports are sent
# CLI flag: -reporting.usage-stats-url
[usage_stats_url: <string> | default = "https://stats.grafana.org/loki-usage-report"]
# URL to the proxy server
# CLI flag: -reporting.proxy-url
[proxy_url: <string> | default = ""]
# The TLS configuration.
# The CLI flags prefix for this block configuration is: reporting.tls-config
[tls_config: <tls_config>]
```
### attributes_config
@ -2551,6 +2559,7 @@ The `frontend` block configures the Loki query-frontend.
[tail_proxy_url: <string> | default = ""]
# The TLS configuration.
# The CLI flags prefix for this block configuration is: frontend.tail-tls-config
[tail_tls_config: <tls_config>]
```
@ -6355,30 +6364,35 @@ chunk_tables_provisioning:
### tls_config
The TLS configuration.
The TLS configuration. The supported CLI flags `<prefix>` used to reference this configuration block are:
- `frontend.tail-tls-config`
- `reporting.tls-config`
&nbsp;
```yaml
# Path to the client certificate, which will be used for authenticating with the
# server. Also requires the key path to be configured.
# CLI flag: -frontend.tail-tls-config.tls-cert-path
# CLI flag: -<prefix>.tls-cert-path
[tls_cert_path: <string> | default = ""]
# Path to the key for the client certificate. Also requires the client
# certificate to be configured.
# CLI flag: -frontend.tail-tls-config.tls-key-path
# CLI flag: -<prefix>.tls-key-path
[tls_key_path: <string> | default = ""]
# Path to the CA certificates to validate server certificate against. If not
# set, the host's root CA certificates are used.
# CLI flag: -frontend.tail-tls-config.tls-ca-path
# CLI flag: -<prefix>.tls-ca-path
[tls_ca_path: <string> | default = ""]
# Override the expected name on the server certificate.
# CLI flag: -frontend.tail-tls-config.tls-server-name
# CLI flag: -<prefix>.tls-server-name
[tls_server_name: <string> | default = ""]
# Skip validating server certificate.
# CLI flag: -frontend.tail-tls-config.tls-insecure-skip-verify
# CLI flag: -<prefix>.tls-insecure-skip-verify
[tls_insecure_skip_verify: <boolean> | default = false]
# Override the default cipher suite list (separated by commas). Allowed values:
@ -6411,12 +6425,12 @@ The TLS configuration.
# - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
# - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
# - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
# CLI flag: -frontend.tail-tls-config.tls-cipher-suites
# CLI flag: -<prefix>.tls-cipher-suites
[tls_cipher_suites: <string> | default = ""]
# Override the default minimum TLS version. Allowed values: VersionTLS10,
# VersionTLS11, VersionTLS12, VersionTLS13
# CLI flag: -frontend.tail-tls-config.tls-min-version
# CLI flag: -<prefix>.tls-min-version
[tls_min_version: <string> | default = ""]
```

@ -7,6 +7,8 @@ import (
"flag"
"io"
"math"
"net/http"
"net/url"
"os"
"time"
@ -14,6 +16,7 @@ import (
"github.com/go-kit/log/level"
"github.com/google/uuid"
"github.com/grafana/dskit/backoff"
"github.com/grafana/dskit/crypto/tls"
"github.com/grafana/dskit/kv"
"github.com/grafana/dskit/multierror"
"github.com/grafana/dskit/services"
@ -43,15 +46,19 @@ var (
)
type Config struct {
Enabled bool `yaml:"reporting_enabled"`
Leader bool `yaml:"-"`
UsageStatsURL string `yaml:"usage_stats_url"`
Enabled bool `yaml:"reporting_enabled"`
Leader bool `yaml:"-"`
UsageStatsURL string `yaml:"usage_stats_url"`
ProxyURL string `yaml:"proxy_url"`
TLSConfig tls.ClientConfig `yaml:"tls_config"`
}
// RegisterFlags adds the flags required to config this to the given FlagSet
func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&cfg.Enabled, "reporting.enabled", true, "Enable anonymous usage reporting.")
f.StringVar(&cfg.UsageStatsURL, "reporting.usage-stats-url", usageStatsURL, "URL to which reports are sent")
f.StringVar(&cfg.ProxyURL, "reporting.proxy-url", "", "URL to the proxy server")
cfg.TLSConfig.RegisterFlagsWithPrefix("reporting.tls-config.", f)
}
type Reporter struct {
@ -61,6 +68,8 @@ type Reporter struct {
services.Service
httpClient *http.Client
conf Config
kvConfig kv.Config
cluster *ClusterSeed
@ -71,12 +80,33 @@ func NewReporter(config Config, kvConfig kv.Config, objectClient client.ObjectCl
if !config.Enabled {
return nil, nil
}
originalDefaultTransport := http.DefaultTransport.(*http.Transport)
tr := originalDefaultTransport.Clone()
if config.TLSConfig.CertPath != "" || config.TLSConfig.KeyPath != "" {
var err error
tr.TLSClientConfig, err = config.TLSConfig.GetTLSConfig()
if err != nil {
return nil, err
}
}
if config.ProxyURL != "" {
proxyURL, err := url.ParseRequestURI(config.ProxyURL)
if err != nil {
return nil, err
}
tr.Proxy = http.ProxyURL(proxyURL)
}
r := &Reporter{
logger: logger,
objectClient: objectClient,
conf: config,
kvConfig: kvConfig,
reg: reg,
httpClient: &http.Client{
Timeout: 5 * time.Second,
Transport: tr,
},
}
r.Service = services.NewBasicService(nil, r.running, nil)
return r, nil
@ -308,7 +338,7 @@ func (rep *Reporter) reportUsage(ctx context.Context, interval time.Time) error
})
var errs multierror.MultiError
for backoff.Ongoing() {
if err := sendReport(ctx, rep.cluster, interval, rep.conf.UsageStatsURL); err != nil {
if err := sendReport(ctx, rep.cluster, interval, rep.conf.UsageStatsURL, rep.httpClient); err != nil {
level.Info(rep.logger).Log("msg", "failed to send usage report", "retries", backoff.NumRetries(), "err", err)
errs.Add(err)
backoff.Wait()

@ -170,3 +170,48 @@ func TestStartCPUCollection(t *testing.T) {
return cpuUsage.Value() > 0
}, 5*time.Second, 1*time.Second)
}
func Test_ProxyURL(t *testing.T) {
// Create a channel to track received messages
received := make(chan bool, 1)
// Using this variable to use `http` for this test as `https` is not supported by `httptest`.
target := "http://stats.grafana.org/loki-usage-report"
// Start local test server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, target, r.URL.String())
received <- true
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
proxyStr := server.URL
reporterCfg := Config{
Leader: true,
Enabled: true,
UsageStatsURL: target,
ProxyURL: proxyStr,
}
reporter, err := NewReporter(
reporterCfg,
kv.Config{
Store: "inmemory",
},
nil,
log.NewLogfmtLogger(os.Stdout),
prometheus.NewPedanticRegistry(),
)
require.NoError(t, err)
reporter.cluster = &ClusterSeed{
UID: "test",
}
require.NoError(t, reporter.reportUsage(context.Background(), time.Now()))
// Verify we received the report
select {
case <-received:
case <-time.After(5 * time.Second):
t.Fatal("Timeout waiting for report")
}
}

@ -23,7 +23,6 @@ import (
)
var (
httpClient = http.Client{Timeout: 5 * time.Second}
usageStatsURL = "https://stats.grafana.org/loki-usage-report"
statsPrefix = "github.com/grafana/loki/"
targetKey = "target"
@ -73,7 +72,7 @@ type Report struct {
}
// sendReport sends the report to the stats server
func sendReport(ctx context.Context, seed *ClusterSeed, interval time.Time, URL string) error {
func sendReport(ctx context.Context, seed *ClusterSeed, interval time.Time, URL string, httpClient *http.Client) error {
report := buildReport(seed, interval)
out, err := jsoniter.MarshalIndent(report, "", " ")
if err != nil {

Loading…
Cancel
Save