diff --git a/pkg/beholder/auth.go b/pkg/beholder/auth.go index ae944ab0f..79be402c4 100644 --- a/pkg/beholder/auth.go +++ b/pkg/beholder/auth.go @@ -1,27 +1,170 @@ package beholder import ( + "context" "crypto/ed25519" + "encoding/binary" "fmt" + "sync" + "time" + + "google.golang.org/grpc/credentials" +) + +const ( + // authHeaderKey is the name of the header that the node authenticator will use to send the auth token + authHeaderKey = "X-Beholder-Node-Auth-Token" + // authHeaderVersion is the version of the auth header format + authHeaderVersion1 = "1" + authHeaderVersion2 = "2" + // defaultAuthHeaderTTL is the default time before the auth header is refreshed + defaultAuthHeaderTTL = 1 * time.Minute ) -// authHeaderKey is the name of the header that the node authenticator will use to send the auth token -var authHeaderKey = "X-Beholder-Node-Auth-Token" +type AuthHeaderProvider interface { + // Credentials returns the PerRPCCredentials implementation + Credentials() credentials.PerRPCCredentials + // SetRequireTransportSecurity sets the value of requireTransportSecurity + SetRequireTransportSecurity(bool) +} + +// AuthHeaderProviderConfig configures AuthHeaderProvider +type AuthHeaderProviderConfig struct { + HeaderTTL time.Duration + Version string + RequireTransportSecurity bool +} + +// authHeaderPerRPCredentials is a PerRPCCredentials implementation that provides the auth headers +type authHeaderPerRPCCredentials struct { + privKey ed25519.PrivateKey + lastUpdated time.Time + headerTTL time.Duration + requireTransportSecurity bool + headers map[string]string + version string + mu sync.Mutex +} + +func (config AuthHeaderProviderConfig) New(privKey ed25519.PrivateKey) AuthHeaderProvider { + if config.HeaderTTL <= 0 { + config.HeaderTTL = defaultAuthHeaderTTL + } + if config.Version == "" { + config.Version = authHeaderVersion2 + } + + creds := &authHeaderPerRPCCredentials{ + privKey: privKey, + headerTTL: config.HeaderTTL, + version: config.Version, + requireTransportSecurity: config.RequireTransportSecurity, + } + // Initialize the headers ~ lastUpdated is 0 so the headers are generated on the first call + creds.refresh() + return creds +} + +func NewAuthHeaderProvider(privKey ed25519.PrivateKey) AuthHeaderProvider { + return AuthHeaderProviderConfig{}.New(privKey) +} + +func (a *authHeaderPerRPCCredentials) Credentials() credentials.PerRPCCredentials { + return a +} + +func (a *authHeaderPerRPCCredentials) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { + return a.getHeaders(), nil +} + +func (a *authHeaderPerRPCCredentials) RequireTransportSecurity() bool { + return a.requireTransportSecurity +} + +// SetRequireTransportSecurity sets the value of requireTransportSecurity +// This is to safeguard against inconsistent values between the PerRPCCredentials and the AuthHeaderProvider +func (a *authHeaderPerRPCCredentials) SetRequireTransportSecurity(newValue bool) { + a.mu.Lock() + defer a.mu.Unlock() + a.requireTransportSecurity = newValue +} + +// getHeaders returns the auth headers, refreshing them if they are expired +func (a *authHeaderPerRPCCredentials) getHeaders() map[string]string { + if time.Since(a.lastUpdated) > a.headerTTL { + a.refresh() + } + return a.headers +} + +// refresh creates a new signed auth header token and sets the lastUpdated time to now +func (a *authHeaderPerRPCCredentials) refresh() { + a.mu.Lock() + defer a.mu.Unlock() -// authHeaderVersion is the version of the auth header format -var authHeaderVersion = "1" + timeNow := time.Now() -// BuildAuthHeaders creates the auth header value to be included on requests. -// The current format for the header is: + switch a.version { + // refresh doesn't actually do anything for version 1 since we are only signing the public key + // this for backwards compatibility and smooth transition to version 2 + case authHeaderVersion1: + a.headers = BuildAuthHeaders(a.privKey) + case authHeaderVersion2: + a.headers = buildAuthHeadersV2(a.privKey, &AuthHeaderConfig{timestamp: timeNow.UnixMilli()}) + default: + a.headers = buildAuthHeadersV2(a.privKey, &AuthHeaderConfig{timestamp: timeNow.UnixMilli()}) + } + // Set the lastUpdated time to now + a.lastUpdated = timeNow +} + +// AuthHeaderConfig configures buildAuthHeadersV2 +type AuthHeaderConfig struct { + timestamp int64 + version string +} + +// BuildAuthHeaders creates the auth headers to be included on requests. +// There are two formats for the header. Version `1` is: // // :: // // where the byte value of is what's being signed +// and is the signature of the public key. func BuildAuthHeaders(privKey ed25519.PrivateKey) map[string]string { pubKey := privKey.Public().(ed25519.PublicKey) messageBytes := pubKey signature := ed25519.Sign(privKey, messageBytes) - headerValue := fmt.Sprintf("%s:%x:%x", authHeaderVersion, messageBytes, signature) - return map[string]string{authHeaderKey: headerValue} + return map[string]string{authHeaderKey: fmt.Sprintf("%s:%x:%x", authHeaderVersion1, messageBytes, signature)} +} + +// buildAuthHeadersV2 creates the auth headers to be included on requests. +// Version `2` is: +// +// ::: +// +// where the concatenated byte value of & is what's being signed +func buildAuthHeadersV2(privKey ed25519.PrivateKey, config *AuthHeaderConfig) map[string]string { + if config == nil { + config = &AuthHeaderConfig{} + } + if config.version == "" { + config.version = authHeaderVersion2 + } + // If timestamp is negative or 0, set it to current timestamp. + // negative values cause overflow on conversion to uint64 + if config.timestamp <= 0 { + config.timestamp = time.Now().UnixMilli() + } + + pubKey := privKey.Public().(ed25519.PublicKey) + + timestampUnixMsBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timestampUnixMsBytes, uint64(config.timestamp)) + + messageBytes := append(pubKey, timestampUnixMsBytes...) + signature := ed25519.Sign(privKey, messageBytes) + + return map[string]string{authHeaderKey: fmt.Sprintf("%s:%x:%d:%x", config.version, pubKey, config.timestamp, signature)} } diff --git a/pkg/beholder/auth_test.go b/pkg/beholder/auth_test.go index fd0e2c86c..56af6f231 100644 --- a/pkg/beholder/auth_test.go +++ b/pkg/beholder/auth_test.go @@ -1,18 +1,23 @@ package beholder import ( + "context" "crypto/ed25519" + "encoding/binary" "encoding/hex" + "strconv" + "strings" "testing" + "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestBuildAuthHeaders(t *testing.T) { - csaPrivKeyHex := "1ac84741fa51c633845fa65c06f37a700303619135630a01f2d22fb98eb1c54ecab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159" - csaPrivKeyBytes, err := hex.DecodeString(csaPrivKeyHex) - assert.NoError(t, err) - csaPrivKey := ed25519.PrivateKey(csaPrivKeyBytes) +func TestBuildAuthHeadersV1(t *testing.T) { + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) expectedHeaders := map[string]string{ "X-Beholder-Node-Auth-Token": "1:cab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159:4403178e299e9acc5b48ae97de617d3975c5d431b794cfab1d23eda01c194119b2360f5f74cfb3e4f706237ab57a0ba88ffd3f8addbc1e5197b3d3e13a1fc409", @@ -20,3 +25,226 @@ func TestBuildAuthHeaders(t *testing.T) { assert.Equal(t, expectedHeaders, BuildAuthHeaders(csaPrivKey)) } + +func TestBuildAuthHeadersV2(t *testing.T) { + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) + timestamp := time.Now().UnixMilli() + + authHeaderMap := buildAuthHeadersV2(csaPrivKey, &AuthHeaderConfig{ + timestamp: timestamp, + }) + + authHeaderValue, ok := authHeaderMap[authHeaderKey] + require.True(t, ok, "auth header should be present") + + parts := strings.Split(authHeaderValue, ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + // Check the parts + version, pubKeyHex, timestampStr, signatureHex := parts[0], parts[1], parts[2], parts[3] + assert.Equal(t, authHeaderVersion2, version, "BuildAuthHeadersV2 should should have version 2") + assert.Equal(t, hex.EncodeToString(csaPrivKey.Public().(ed25519.PublicKey)), pubKeyHex) + assert.Equal(t, strconv.FormatInt(timestamp, 10), timestampStr) + + // Decode the public key and signature + pubKeyBytes, err := hex.DecodeString(pubKeyHex) + require.NoError(t, err) + assert.Equal(t, csaPrivKey.Public().(ed25519.PublicKey), ed25519.PublicKey(pubKeyBytes)) + + // Parse the timestamp + timestampParsed, err := strconv.ParseInt(timestampStr, 10, 64) + require.NoError(t, err) + assert.Equal(t, timestamp, timestampParsed) + timestampBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timestampBytes, uint64(timestampParsed)) + + // Reconstruct the message bytes + messageBytes := append(pubKeyBytes, timestampBytes...) + + // Verify the signature + signatureBytes, err := hex.DecodeString(signatureHex) + require.NoError(t, err) + assert.True(t, ed25519.Verify(pubKeyBytes, messageBytes, signatureBytes)) +} + +func TestBuildAuthHeadersV2WithDefaults(t *testing.T) { + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) + + now := time.Now().UnixMilli() + + authHeaderMap := buildAuthHeadersV2(csaPrivKey, nil) + authHeaderValue, ok := authHeaderMap[authHeaderKey] + require.True(t, ok, "auth header should be present") + + parts := strings.Split(authHeaderValue, ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + // Check the parts + version, pubKeyHex, timestampStr, signatureHex := parts[0], parts[1], parts[2], parts[3] + assert.Equal(t, "2", version, "using WithAuthHeaderV2 should should have version 2") + assert.Equal(t, hex.EncodeToString(csaPrivKey.Public().(ed25519.PublicKey)), pubKeyHex) + + // Decode the public key and signature + pubKeyBytes, err := hex.DecodeString(pubKeyHex) + require.NoError(t, err) + assert.Equal(t, csaPrivKey.Public().(ed25519.PublicKey), ed25519.PublicKey(pubKeyBytes)) + + // Parse the timestamp + timestampParsed, err := strconv.ParseInt(timestampStr, 10, 64) + require.NoError(t, err) + + // Verify the timestamp is within the last 50ms + // This verifies that default configuration is to use the current time + assert.InDelta(t, now, timestampParsed, 50, "timestamp should be within the last 50ms") + + timestampBytes := make([]byte, 8) + binary.BigEndian.PutUint64(timestampBytes, uint64(timestampParsed)) + + // Reconstruct the message bytes + messageBytes := append(pubKeyBytes, timestampBytes...) + + // Verify the signature + signatureBytes, err := hex.DecodeString(signatureHex) + require.NoError(t, err) + assert.True(t, ed25519.Verify(pubKeyBytes, messageBytes, signatureBytes)) +} + +func TestBuildAuthHeadersV2WithNegativeTimestamp(t *testing.T) { + // This tests that if the timestamp is negative, it will be set it to current timestamp + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) + timestamp := int64(-111) + + authHeaderMap := buildAuthHeadersV2(csaPrivKey, &AuthHeaderConfig{ + timestamp: timestamp, + }) + + authHeaderValue, ok := authHeaderMap[authHeaderKey] + require.True(t, ok, "auth header should be present") + + parts := strings.Split(authHeaderValue, ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + // Check the the returned timestamp is 0 + _, _, timestampStr, _ := parts[0], parts[1], parts[2], parts[3] + timestampParsed, err := strconv.ParseInt(timestampStr, 10, 64) + require.NoError(t, err) + // Verify the timestamp is within the last 50ms + assert.InDelta(t, time.Now().UnixMilli(), timestampParsed, 50, "timestamp should be 0") +} + +func TestNewAuthHeaderProvider(t *testing.T) { + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) + + t.Run("default config", func(t *testing.T) { + provider := NewAuthHeaderProvider(csaPrivKey) + creds := provider.Credentials() + + md, err := creds.GetRequestMetadata(context.Background()) + require.NoError(t, err) + + authHeaderValue, ok := md[authHeaderKey] + require.True(t, ok, "auth header should be present") + + parts := strings.Split(authHeaderValue, ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + assert.Equal(t, authHeaderVersion2, parts[0], "default version should be 2") + }) + + t.Run("custom config", func(t *testing.T) { + config := AuthHeaderProviderConfig{ + HeaderTTL: 2 * time.Minute, + Version: authHeaderVersion1, + RequireTransportSecurity: true, + } + provider := config.New(csaPrivKey) + creds := provider.Credentials() + + md, err := creds.GetRequestMetadata(context.Background()) + require.NoError(t, err) + + authHeaderValue, ok := md[authHeaderKey] + require.True(t, ok, "auth header should be present") + + parts := strings.Split(authHeaderValue, ":") + assert.Len(t, parts, 3, "auth header v1 should have 3 parts") + assert.Equal(t, authHeaderVersion1, parts[0], "version should be 1") + assert.True(t, creds.RequireTransportSecurity(), "transport security should be required") + }) +} + +func TestAuthHeaderPerRPCCredentials_Refresh(t *testing.T) { + csaPrivKey, err := generateTestCSAPrivateKey() + require.NoError(t, err) + + t.Run("version 1", func(t *testing.T) { + creds := &authHeaderPerRPCCredentials{ + privKey: csaPrivKey, + version: authHeaderVersion1, + } + creds.refresh() + + headers := creds.getHeaders() + assert.NotNil(t, headers) + assert.Contains(t, headers, authHeaderKey) + + parts := strings.Split(headers[authHeaderKey], ":") + assert.Len(t, parts, 3, "auth header v1 should have 3 parts") + assert.Equal(t, authHeaderVersion1, parts[0], "version should be 1") + }) + + t.Run("version 2", func(t *testing.T) { + creds := &authHeaderPerRPCCredentials{ + privKey: csaPrivKey, + version: authHeaderVersion2, + } + creds.refresh() + + headers := creds.getHeaders() + assert.NotNil(t, headers) + assert.Contains(t, headers, authHeaderKey) + + parts := strings.Split(headers[authHeaderKey], ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + assert.Equal(t, authHeaderVersion2, parts[0], "version should be 2") + }) + + t.Run("default version", func(t *testing.T) { + creds := &authHeaderPerRPCCredentials{ + privKey: csaPrivKey, + } + creds.refresh() + + headers := creds.getHeaders() + assert.NotNil(t, headers) + assert.Contains(t, headers, authHeaderKey) + + parts := strings.Split(headers[authHeaderKey], ":") + assert.Len(t, parts, 4, "auth header v2 should have 4 parts") + assert.Equal(t, authHeaderVersion2, parts[0], "default version should be 2") + }) + + t.Run("refresh after TTL", func(t *testing.T) { + creds := &authHeaderPerRPCCredentials{ + privKey: csaPrivKey, + headerTTL: 1 * time.Millisecond, + version: authHeaderVersion2, + } + creds.refresh() + + headers1 := creds.getHeaders() + time.Sleep(2 * time.Millisecond) + headers2 := creds.getHeaders() + + assert.NotEqual(t, headers1[authHeaderKey], headers2[authHeaderKey], "headers should be refreshed after TTL") + }) +} + +func generateTestCSAPrivateKey() (ed25519.PrivateKey, error) { + csaPrivKeyHex := "1ac84741fa51c633845fa65c06f37a700303619135630a01f2d22fb98eb1c54ecab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159" + csaPrivKeyBytes, err := hex.DecodeString(csaPrivKeyHex) + if err != nil { + return nil, err + } + return ed25519.PrivateKey(csaPrivKeyBytes), nil +} diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index 69e15c284..289eb5938 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -16,6 +16,7 @@ import ( sdkresource "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" oteltrace "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" ) @@ -92,7 +93,11 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro opts := []otlploggrpc.Option{ otlploggrpc.WithTLSCredentials(creds), otlploggrpc.WithEndpoint(cfg.OtelExporterGRPCEndpoint), - otlploggrpc.WithHeaders(cfg.AuthHeaders), + } + if cfg.AuthHeaderProvider != nil { + opts = append(opts, otlploggrpc.WithDialOption(authHeaderDialOption(creds, cfg.AuthHeaderProvider))) + } else { + opts = append(opts, otlploggrpc.WithHeaders(cfg.AuthHeaders)) } if cfg.LogRetryConfig != nil { // NOTE: By default, the retry is enabled in the OTel SDK @@ -323,7 +328,11 @@ func newTracerProvider(config Config, resource *sdkresource.Resource, creds cred exporterOpts := []otlptracegrpc.Option{ otlptracegrpc.WithTLSCredentials(creds), otlptracegrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), - otlptracegrpc.WithHeaders(config.AuthHeaders), + } + if config.AuthHeaderProvider != nil { + exporterOpts = append(exporterOpts, otlptracegrpc.WithDialOption(authHeaderDialOption(creds, config.AuthHeaderProvider))) + } else { + exporterOpts = append(exporterOpts, otlptracegrpc.WithHeaders(config.AuthHeaders)) } if config.TraceRetryConfig != nil { // NOTE: By default, the retry is enabled in the OTel SDK @@ -363,7 +372,11 @@ func newMeterProvider(config Config, resource *sdkresource.Resource, creds crede opts := []otlpmetricgrpc.Option{ otlpmetricgrpc.WithTLSCredentials(creds), otlpmetricgrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), - otlpmetricgrpc.WithHeaders(config.AuthHeaders), + } + if config.AuthHeaderProvider != nil { + opts = append(opts, otlpmetricgrpc.WithDialOption(authHeaderDialOption(creds, config.AuthHeaderProvider))) + } else { + opts = append(opts, otlpmetricgrpc.WithHeaders(config.AuthHeaders)) } if config.MetricRetryConfig != nil { // NOTE: By default, the retry is enabled in the OTel SDK @@ -390,3 +403,10 @@ func newMeterProvider(config Config, resource *sdkresource.Resource, creds crede ) return mp, nil } + +func authHeaderDialOption(creds credentials.TransportCredentials, authHeaderProvider AuthHeaderProvider) grpc.DialOption { + if creds.Info().SecurityProtocol == "tls" { + authHeaderProvider.SetRequireTransportSecurity(true) + } + return grpc.WithPerRPCCredentials(authHeaderProvider.Credentials()) +} diff --git a/pkg/beholder/config.go b/pkg/beholder/config.go index c01256b1c..5ce58e2b3 100644 --- a/pkg/beholder/config.go +++ b/pkg/beholder/config.go @@ -44,8 +44,9 @@ type Config struct { LogRetryConfig *RetryConfig // Auth - AuthPublicKeyHex string - AuthHeaders map[string]string + AuthPublicKeyHex string + AuthHeaders map[string]string + AuthHeaderProvider AuthHeaderProvider } type RetryConfig struct { diff --git a/pkg/beholder/config_test.go b/pkg/beholder/config_test.go index 3b4fa86f1..03a82f9db 100644 --- a/pkg/beholder/config_test.go +++ b/pkg/beholder/config_test.go @@ -56,6 +56,6 @@ func ExampleConfig() { } fmt.Printf("%+v\n", *config.LogRetryConfig) // Output: - // {InsecureConnection:true CACertFile: OtelExporterGRPCEndpoint:localhost:4317 OtelExporterHTTPEndpoint:localhost:4318 ResourceAttributes:[{Key:package_name Value:{vtype:4 numeric:0 stringly:beholder slice:}} {Key:sender Value:{vtype:4 numeric:0 stringly:beholderclient slice:}}] EmitterExportTimeout:1s EmitterExportInterval:1s EmitterExportMaxBatchSize:512 EmitterMaxQueueSize:2048 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: TraceRetryConfig: MetricReaderInterval:1s MetricRetryConfig: MetricViews:[] LogExportTimeout:1s LogExportInterval:1s LogExportMaxBatchSize:512 LogMaxQueueSize:2048 LogBatchProcessor:true LogRetryConfig: AuthPublicKeyHex: AuthHeaders:map[]} + // {InsecureConnection:true CACertFile: OtelExporterGRPCEndpoint:localhost:4317 OtelExporterHTTPEndpoint:localhost:4318 ResourceAttributes:[{Key:package_name Value:{vtype:4 numeric:0 stringly:beholder slice:}} {Key:sender Value:{vtype:4 numeric:0 stringly:beholderclient slice:}}] EmitterExportTimeout:1s EmitterExportInterval:1s EmitterExportMaxBatchSize:512 EmitterMaxQueueSize:2048 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: TraceRetryConfig: MetricReaderInterval:1s MetricRetryConfig: MetricViews:[] LogExportTimeout:1s LogExportInterval:1s LogExportMaxBatchSize:512 LogMaxQueueSize:2048 LogBatchProcessor:true LogRetryConfig: AuthPublicKeyHex: AuthHeaders:map[] AuthHeaderProvider:} // {InitialInterval:5s MaxInterval:30s MaxElapsedTime:1m0s} } diff --git a/pkg/loop/config.go b/pkg/loop/config.go index ea68cfa4a..747b432ef 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/go-plugin" + "github.com/smartcontractkit/chainlink-common/pkg/beholder" ) const ( @@ -57,6 +58,7 @@ type EnvConfig struct { TelemetryTraceSampleRatio float64 TelemetryAuthHeaders map[string]string TelemetryAuthPubKeyHex string + TelemetryAuthHeaderProvider beholder.AuthHeaderProvider TelemetryEmitterBatchProcessor bool TelemetryEmitterExportTimeout time.Duration TelemetryEmitterExportInterval time.Duration @@ -92,11 +94,6 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { for k, v := range e.TelemetryAttributes { add(envTelemetryAttribute+k, v) } - - for k, v := range e.TelemetryAuthHeaders { - add(envTelemetryAuthHeader+k, v) - } - add(envTelemetryAuthPubKeyHex, e.TelemetryAuthPubKeyHex) add(envTelemetryEmitterBatchProcessor, strconv.FormatBool(e.TelemetryEmitterBatchProcessor)) add(envTelemetryEmitterExportTimeout, e.TelemetryEmitterExportTimeout.String()) add(envTelemetryEmitterExportInterval, e.TelemetryEmitterExportInterval.String()) @@ -148,8 +145,6 @@ func (e *EnvConfig) parse() error { e.TelemetryCACertFile = os.Getenv(envTelemetryCACertFile) e.TelemetryAttributes = getMap(envTelemetryAttribute) e.TelemetryTraceSampleRatio = getFloat64OrZero(envTelemetryTraceSampleRatio) - e.TelemetryAuthHeaders = getMap(envTelemetryAuthHeader) - e.TelemetryAuthPubKeyHex = os.Getenv(envTelemetryAuthPubKeyHex) e.TelemetryEmitterBatchProcessor, err = getBool(envTelemetryEmitterBatchProcessor) if err != nil { return fmt.Errorf("failed to parse %s: %w", envTelemetryEmitterBatchProcessor, err) diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index f57eff666..41dc1088a 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -165,12 +165,6 @@ func TestEnvConfig_parse(t *testing.T) { if config.TelemetryTraceSampleRatio != tc.expectedTelemetryTraceSampleRatio { t.Errorf("Expected telemetryTraceSampleRatio %f, got %f", tc.expectedTelemetryTraceSampleRatio, config.TelemetryTraceSampleRatio) } - if !maps.Equal(config.TelemetryAuthHeaders, tc.expectedTelemetryAuthHeaders) { - t.Errorf("Expected telemetryAuthHeaders %v, got %v", tc.expectedTelemetryAuthHeaders, config.TelemetryAuthHeaders) - } - if config.TelemetryAuthPubKeyHex != tc.expectedTelemetryAuthPubKeyHex { - t.Errorf("Expected telemetryAuthPubKeyHex %s, got %s", tc.expectedTelemetryAuthPubKeyHex, config.TelemetryAuthPubKeyHex) - } if config.TelemetryEmitterBatchProcessor != tc.expectedTelemetryEmitterBatchProcessor { t.Errorf("Expected telemetryEmitterBatchProcessor %v, got %v", tc.expectedTelemetryEmitterBatchProcessor, config.TelemetryEmitterBatchProcessor) } @@ -264,8 +258,6 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { assert.Equal(t, "0.42", got[envTelemetryTraceSampleRatio]) assert.Equal(t, "bar", got[envTelemetryAttribute+"foo"]) assert.Equal(t, "42", got[envTelemetryAttribute+"baz"]) - assert.Equal(t, "header-value", got[envTelemetryAuthHeader+"header-key"]) - assert.Equal(t, "pub-key-hex", got[envTelemetryAuthPubKeyHex]) assert.Equal(t, "true", got[envTelemetryEmitterBatchProcessor]) assert.Equal(t, "1s", got[envTelemetryEmitterExportTimeout]) assert.Equal(t, "2s", got[envTelemetryEmitterExportInterval]) diff --git a/pkg/loop/server.go b/pkg/loop/server.go index 6cf4f0c5b..61bf41539 100644 --- a/pkg/loop/server.go +++ b/pkg/loop/server.go @@ -99,6 +99,7 @@ func (s *Server) start() error { TraceSampleRatio: envCfg.TelemetryTraceSampleRatio, AuthHeaders: envCfg.TelemetryAuthHeaders, AuthPublicKeyHex: envCfg.TelemetryAuthPubKeyHex, + AuthHeaderProvider: envCfg.TelemetryAuthHeaderProvider, EmitterBatchProcessor: envCfg.TelemetryEmitterBatchProcessor, EmitterExportTimeout: envCfg.TelemetryEmitterExportTimeout, EmitterExportInterval: envCfg.TelemetryEmitterExportInterval,