From 6ee6307da340cebebee6708dfe6bef4dabb94166 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:45:11 -0400 Subject: [PATCH 01/16] Rename getAttributes to getMap --- pkg/loop/config.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 5165ff1d7..4f6e5e95b 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -105,7 +105,7 @@ func (e *EnvConfig) parse() error { if err != nil { return err } - e.TracingAttributes = getAttributes(envTracingAttribute) + e.TracingAttributes = getMap(envTracingAttribute) e.TracingSamplingRatio = getFloat64OrZero(envTracingSamplingRatio) e.TracingTLSCertPath = os.Getenv(envTracingTLSCertPath) } @@ -122,7 +122,7 @@ func (e *EnvConfig) parse() error { return fmt.Errorf("failed to parse %s: %w", envTelemetryEndpoint, err) } e.TelemetryCACertFile = os.Getenv(envTelemetryCACertFile) - e.TelemetryAttributes = getAttributes(envTelemetryAttribute) + e.TelemetryAttributes = getMap(envTelemetryAttribute) e.TelemetryTraceSampleRatio = getFloat64OrZero(envTelemetryTraceSampleRatio) } return nil @@ -158,14 +158,14 @@ func getValidCollectorTarget() (string, error) { return tracingCollectorTarget, nil } -func getAttributes(envKeyPrefix string) map[string]string { - tracingAttributes := make(map[string]string) +func getMap(envKeyPrefix string) map[string]string { + m := make(map[string]string) for _, env := range os.Environ() { if strings.HasPrefix(env, envKeyPrefix) { - tracingAttributes[strings.TrimPrefix(env, envKeyPrefix)] = os.Getenv(env) + m[strings.TrimPrefix(env, envKeyPrefix)] = os.Getenv(env) } } - return tracingAttributes + return m } // Any errors in parsing result in a sampling ratio of 0.0. From 9cc69c73d2b202431af15d2c7f720d817ab927dd Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:13:36 -0400 Subject: [PATCH 02/16] Fix getMap --- pkg/loop/config.go | 6 +++++- pkg/loop/config_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 4f6e5e95b..16fa906ec 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -162,7 +162,11 @@ func getMap(envKeyPrefix string) map[string]string { m := make(map[string]string) for _, env := range os.Environ() { if strings.HasPrefix(env, envKeyPrefix) { - m[strings.TrimPrefix(env, envKeyPrefix)] = os.Getenv(env) + key, value, found := strings.Cut(env, "=") + if found { + key = strings.TrimPrefix(key, envKeyPrefix) + m[key] = value + } } } return m diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index e0bcc1d5e..f719ae566 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -2,6 +2,7 @@ package loop import ( "net/url" + "os" "strconv" "strings" "testing" @@ -153,6 +154,35 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { assert.Equal(t, "42", got[envTelemetryAttribute+"baz"]) } +func TestGetMap(t *testing.T) { + os.Setenv("TEST_PREFIX_KEY1", "value1") + os.Setenv("TEST_PREFIX_KEY2", "value2") + os.Setenv("OTHER_KEY", "othervalue") + + defer func() { + os.Unsetenv("TEST_PREFIX_KEY1") + os.Unsetenv("TEST_PREFIX_KEY2") + os.Unsetenv("OTHER_KEY") + }() + + result := getMap("TEST_PREFIX_") + + expected := map[string]string{ + "KEY1": "value1", + "KEY2": "value2", + } + + if len(result) != len(expected) { + t.Errorf("Expected map length %d, got %d", len(expected), len(result)) + } + + for k, v := range expected { + if result[k] != v { + t.Errorf("Expected key %s to have value %s, but got %s", k, v, result[k]) + } + } +} + func TestManagedGRPCClientConfig(t *testing.T) { t.Parallel() From daca6c5ccfbcaa29b900d068687263dabb499159 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:06:34 -0400 Subject: [PATCH 03/16] Add Authenticator to Beholder --- pkg/beholder/authenticator.go | 63 +++++++++++++++++++++++++++ pkg/beholder/authenticator_test.go | 70 ++++++++++++++++++++++++++++++ pkg/beholder/config.go | 4 ++ 3 files changed, 137 insertions(+) create mode 100644 pkg/beholder/authenticator.go create mode 100644 pkg/beholder/authenticator_test.go diff --git a/pkg/beholder/authenticator.go b/pkg/beholder/authenticator.go new file mode 100644 index 000000000..1dccff2bf --- /dev/null +++ b/pkg/beholder/authenticator.go @@ -0,0 +1,63 @@ +package beholder + +import ( + "errors" + "fmt" + "maps" +) + +type Authenticator struct { + headers map[string]string +} + +func NewAuthenticator(config Config) (*Authenticator, error) { + if err := validateAuthConfig(config); err != nil { + return nil, err + } + + headers := make(map[string]string) + if config.AuthenticatorSigner != nil { + headers = deriveAuthHeaders(config) + } + if len(config.AuthenticatorHeaders) != 0 { + headers = config.AuthenticatorHeaders + } + + return &Authenticator{headers}, nil +} + +func (a *Authenticator) GetHeaders() map[string]string { + return maps.Clone(a.headers) +} + +func validateAuthConfig(c Config) error { + if c.AuthenticatorSigner != nil && len(c.AuthenticatorHeaders) > 0 { + return errors.New("cannot configure both authenticator signer and authenticator header value") + } + + if c.AuthenticatorSigner != nil && len(c.AuthenticatorPublicKey) == 0 { + return errors.New("authenticator public key must be configured when authenticator signer is configured") + } + + return nil +} + +// 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" + +// authHeaderVersion is the version of the auth header format +var authHeaderVersion = "1" + +// deriveAuthHeaders creates the auth header value to be included on requests. +// The current format for the header is: +// +// :: +// +// where the byte value of is what's being signed +func deriveAuthHeaders(config Config) map[string]string { + messageBytes := config.AuthenticatorPublicKey + signature := config.AuthenticatorSigner(messageBytes) + headerValue := fmt.Sprintf("%s:%x:%x", authHeaderVersion, messageBytes, signature) + + return map[string]string{authHeaderKey: headerValue} +} diff --git a/pkg/beholder/authenticator_test.go b/pkg/beholder/authenticator_test.go new file mode 100644 index 000000000..3dd3778d4 --- /dev/null +++ b/pkg/beholder/authenticator_test.go @@ -0,0 +1,70 @@ +package beholder_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-common/pkg/beholder" +) + +func defaultTestingConfig() beholder.Config { + return beholder.Config{ + AuthenticatorPublicKey: []byte("test-public-key"), + AuthenticatorSigner: func([]byte) []byte { return []byte("test-signature") }, + AuthenticatorHeaders: nil, + } +} +func TestAuthenticator_SignerAuth(t *testing.T) { + // Authenticator should derive headers if the signer is set + c := defaultTestingConfig() + a, err := beholder.NewAuthenticator(c) + assert.NoError(t, err) + + expectedHeaders := map[string]string{ + "X-Beholder-Node-Auth-Token": "1:746573742d7075626c69632d6b6579:746573742d7369676e6174757265", + } + assert.Equal(t, expectedHeaders, a.GetHeaders()) +} + +func TestAuthenticator_HeadersAuth(t *testing.T) { + // Authenticator should use the headers if they are set + expectedHeaders := map[string]string{"test-header-key": "test-header-value"} + c := beholder.Config{ + AuthenticatorHeaders: expectedHeaders, + } + + a, err := beholder.NewAuthenticator(c) + assert.NoError(t, err) + assert.Equal(t, expectedHeaders, a.GetHeaders()) +} + +func TestAuthenticator_NoAuth(t *testing.T) { + // Authenticator should not set any headers if neither signer nor headers are set + c := beholder.Config{} + + a, err := beholder.NewAuthenticator(c) + assert.NoError(t, err) + expectedHeaders := map[string]string{} + assert.Equal(t, expectedHeaders, a.GetHeaders()) +} + +func TestAuthenticator_Config(t *testing.T) { + // Configuring both auth signer and auth header should error + c := defaultTestingConfig() + c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} + _, err := beholder.NewAuthenticator(c) + assert.Error(t, err) + + // Configuring signer with no public key should error + c = defaultTestingConfig() + c.AuthenticatorPublicKey = nil + _, err = beholder.NewAuthenticator(c) + assert.Error(t, err) + + // Configuring neither auth signer nor auth header should not error + c = defaultTestingConfig() + c.AuthenticatorSigner = nil + _, err = beholder.NewAuthenticator(c) + assert.NoError(t, err) +} diff --git a/pkg/beholder/config.go b/pkg/beholder/config.go index 713f8a9ed..264d3f203 100644 --- a/pkg/beholder/config.go +++ b/pkg/beholder/config.go @@ -31,6 +31,10 @@ type Config struct { // Batch processing is enabled by default // Disable it only for testing LogBatchProcessor bool + + AuthenticatorPublicKey []byte + AuthenticatorSigner func([]byte) []byte + AuthenticatorHeaders map[string]string } const ( From 15e14794c64ccdcce5ca1d7630a4f7e95cb9f6af Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:18:57 -0400 Subject: [PATCH 04/16] Use Authenticator in Beholder --- pkg/beholder/client.go | 20 +++++++++++++++----- pkg/beholder/noop.go | 5 ++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index c1ee75599..bf5d7ee6a 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -47,6 +47,9 @@ type Client struct { MeterProvider otelmetric.MeterProvider MessageLoggerProvider otellog.LoggerProvider + // Authenticator + Authenticator *Authenticator + // OnClose OnClose func() error } @@ -90,9 +93,14 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro return nil, err } } + authenticator, err := NewAuthenticator(cfg) + if err != nil { + return noop, err + } sharedLogExporter, err := otlploggrpcNew( otlploggrpc.WithTLSCredentials(creds), otlploggrpc.WithEndpoint(cfg.OtelExporterGRPCEndpoint), + otlploggrpc.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err @@ -125,14 +133,14 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro logger := loggerProvider.Logger(defaultPackageName) // Tracer - tracerProvider, err := newTracerProvider(cfg, baseResource, creds) + tracerProvider, err := newTracerProvider(cfg, baseResource, creds, authenticator) if err != nil { return nil, err } tracer := tracerProvider.Tracer(defaultPackageName) // Meter - meterProvider, err := newMeterProvider(cfg, baseResource, creds) + meterProvider, err := newMeterProvider(cfg, baseResource, creds, authenticator) if err != nil { return nil, err } @@ -176,7 +184,7 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro } return } - return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, onClose}, nil + return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, authenticator, onClose}, nil } // Closes all providers, flushes all data and stops all background processes @@ -258,13 +266,14 @@ type shutdowner interface { Shutdown(ctx context.Context) error } -func newTracerProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials) (*sdktrace.TracerProvider, error) { +func newTracerProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials, authenticator *Authenticator) (*sdktrace.TracerProvider, error) { ctx := context.Background() // note: context is used internally exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithTLSCredentials(creds), otlptracegrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), + otlptracegrpc.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err @@ -285,7 +294,7 @@ func newTracerProvider(config Config, resource *sdkresource.Resource, creds cred return sdktrace.NewTracerProvider(opts...), nil } -func newMeterProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials) (*sdkmetric.MeterProvider, error) { +func newMeterProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials, authenticator *Authenticator) (*sdkmetric.MeterProvider, error) { ctx := context.Background() // note: context is unused internally @@ -293,6 +302,7 @@ func newMeterProvider(config Config, resource *sdkresource.Resource, creds crede ctx, otlpmetricgrpc.WithTLSCredentials(creds), otlpmetricgrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), + otlpmetricgrpc.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err diff --git a/pkg/beholder/noop.go b/pkg/beholder/noop.go index 3012c0304..a7d4495cc 100644 --- a/pkg/beholder/noop.go +++ b/pkg/beholder/noop.go @@ -35,7 +35,10 @@ func NewNoopClient() *Client { // MessageEmitter messageEmitter := noopMessageEmitter{} - return &Client{cfg, logger, tracer, meter, messageEmitter, loggerProvider, tracerProvider, meterProvider, loggerProvider, noopOnClose} + // Authenticator + authenticator := &Authenticator{} + + return &Client{cfg, logger, tracer, meter, messageEmitter, loggerProvider, tracerProvider, meterProvider, loggerProvider, authenticator, noopOnClose} } // NewStdoutClient creates a new Client with exporters which send telemetry data to standard output From a2bd6f127dbf3e4f36ee49329f1b6396e93dddf6 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:34:52 -0400 Subject: [PATCH 05/16] Add Authenticator to Beholder global --- pkg/beholder/global.go | 4 ++++ pkg/beholder/global_test.go | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/beholder/global.go b/pkg/beholder/global.go index 9ec318c87..aa605b818 100644 --- a/pkg/beholder/global.go +++ b/pkg/beholder/global.go @@ -43,6 +43,10 @@ func GetEmitter() Emitter { return GetClient().Emitter } +func GetAuthenticator() *Authenticator { + return GetClient().Authenticator +} + // Sets global OTel logger, tracer, meter providers from Client. // Makes them accessible from anywhere in the code via global otel getters. // Any package that relies on go.opentelemetry.io will be able to pick up configured global providers diff --git a/pkg/beholder/global_test.go b/pkg/beholder/global_test.go index 1dbc9e373..64f9a2231 100644 --- a/pkg/beholder/global_test.go +++ b/pkg/beholder/global_test.go @@ -25,13 +25,14 @@ import ( func TestGlobal(t *testing.T) { // Get global logger, tracer, meter, messageEmitter // If not initialized with beholder.SetClient will return noop client - logger, tracer, meter, messageEmitter := beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter() + logger, tracer, meter, messageEmitter, authenticator := beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter(), beholder.GetAuthenticator() noopClient := beholder.NewNoopClient() assert.IsType(t, otellognoop.Logger{}, logger) assert.IsType(t, oteltracenoop.Tracer{}, tracer) assert.IsType(t, otelmetricnoop.Meter{}, meter) expectedMessageEmitter := beholder.NewNoopClient().Emitter assert.IsType(t, expectedMessageEmitter, messageEmitter) + assert.IsType(t, &beholder.Authenticator{}, authenticator) var noopClientPtr *beholder.Client = noopClient assert.IsType(t, noopClientPtr, beholder.GetClient()) @@ -41,8 +42,8 @@ func TestGlobal(t *testing.T) { beholder.SetClient(noopClientPtr) assert.Same(t, noopClientPtr, beholder.GetClient()) - // After that use beholder functions to get logger, tracer, meter, messageEmitter - logger, tracer, meter, messageEmitter = beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter() + // After that use beholder functions to get logger, tracer, meter, messageEmitter, authenticator + logger, tracer, meter, messageEmitter, authenticator = beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter(), beholder.GetAuthenticator() // Emit otel log record logger.Emit(tests.Context(t), otellog.Record{}) @@ -60,6 +61,10 @@ func TestGlobal(t *testing.T) { if err != nil { t.Fatalf("Error emitting message: %v", err) } + + // Get the auth headers + headers := authenticator.GetHeaders() + assert.Nil(t, headers) } func TestClient_SetGlobalOtelProviders(t *testing.T) { From 145b2e2b53d3226dceddf84af018597df79ee594 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:54:02 -0400 Subject: [PATCH 06/16] Use Authenticator Headers in LOOP --- pkg/loop/config.go | 7 +++++++ pkg/loop/config_test.go | 2 ++ pkg/loop/server.go | 1 + 3 files changed, 10 insertions(+) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 16fa906ec..5708c6bc3 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -26,6 +26,7 @@ const ( envTelemetryCACertFile = "CL_TELEMETRY_CA_CERT_FILE" envTelemetryAttribute = "CL_TELEMETRY_ATTRIBUTE_" envTelemetryTraceSampleRatio = "CL_TELEMETRY_TRACE_SAMPLE_RATIO" + envTelemetryAuthHeader = "CL_TELEMETRY_AUTH_HEADER" ) // EnvConfig is the configuration between the application and the LOOP executable. The values @@ -47,6 +48,7 @@ type EnvConfig struct { TelemetryCACertFile string TelemetryAttributes OtelAttributes TelemetryTraceSampleRatio float64 + TelemetryAuthHeaders map[string]string } // AsCmdEnv returns a slice of environment variable key/value pairs for an exec.Cmd. @@ -78,6 +80,10 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { add(envTelemetryAttribute+k, v) } + for k, v := range e.TelemetryAuthHeaders { + add(envTelemetryAuthHeader+k, v) + } + return } @@ -124,6 +130,7 @@ func (e *EnvConfig) parse() error { e.TelemetryCACertFile = os.Getenv(envTelemetryCACertFile) e.TelemetryAttributes = getMap(envTelemetryAttribute) e.TelemetryTraceSampleRatio = getFloat64OrZero(envTelemetryTraceSampleRatio) + e.TelemetryAuthHeaders = getAttributes(envTelemetryAuthHeader) } return nil } diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index f719ae566..5fb3e00b1 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -128,6 +128,7 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { TelemetryCACertFile: "foo/bar", TelemetryAttributes: OtelAttributes{"foo": "bar", "baz": "42"}, TelemetryTraceSampleRatio: 0.42, + TelemetryAuthHeaders: map[string]string{"header-key": "header-value"}, } got := map[string]string{} for _, kv := range envCfg.AsCmdEnv() { @@ -152,6 +153,7 @@ 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"]) } func TestGetMap(t *testing.T) { diff --git a/pkg/loop/server.go b/pkg/loop/server.go index fd2b02b7f..5f87dfaf9 100644 --- a/pkg/loop/server.go +++ b/pkg/loop/server.go @@ -96,6 +96,7 @@ func (s *Server) start() error { OtelExporterGRPCEndpoint: envCfg.TelemetryEndpoint, ResourceAttributes: append(attributes, envCfg.TelemetryAttributes.AsStringAttributes()...), TraceSampleRatio: envCfg.TelemetryTraceSampleRatio, + AuthenticatorHeaders: envCfg.TelemetryAuthHeaders, } if tracingConfig.Enabled { From 4eff34746ad0315b135690c1237d807474485df7 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:56:32 -0400 Subject: [PATCH 07/16] Add authenticator to HTTP client --- pkg/beholder/client.go | 2 +- pkg/beholder/httpclient.go | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index bf5d7ee6a..0cd612233 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -95,7 +95,7 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro } authenticator, err := NewAuthenticator(cfg) if err != nil { - return noop, err + return nil, err } sharedLogExporter, err := otlploggrpcNew( otlploggrpc.WithTLSCredentials(creds), diff --git a/pkg/beholder/httpclient.go b/pkg/beholder/httpclient.go index 97f1b422f..a209142cd 100644 --- a/pkg/beholder/httpclient.go +++ b/pkg/beholder/httpclient.go @@ -56,9 +56,14 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro if tlsConfig != nil { tlsConfigOption = otlploghttp.WithTLSClientConfig(tlsConfig) } + authenticator, err := NewAuthenticator(cfg) + if err != nil { + return nil, err + } sharedLogExporter, err := otlploghttpNew( tlsConfigOption, otlploghttp.WithEndpoint(cfg.OtelExporterHTTPEndpoint), + otlploghttp.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err @@ -91,14 +96,14 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro logger := loggerProvider.Logger(defaultPackageName) // Tracer - tracerProvider, err := newHTTPTracerProvider(cfg, baseResource, tlsConfig) + tracerProvider, err := newHTTPTracerProvider(cfg, baseResource, tlsConfig, authenticator) if err != nil { return nil, err } tracer := tracerProvider.Tracer(defaultPackageName) // Meter - meterProvider, err := newHTTPMeterProvider(cfg, baseResource, tlsConfig) + meterProvider, err := newHTTPMeterProvider(cfg, baseResource, tlsConfig, authenticator) if err != nil { return nil, err } @@ -142,10 +147,10 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro } return } - return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, onClose}, nil + return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, authenticator, onClose}, nil } -func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config) (*sdktrace.TracerProvider, error) { +func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config, authenticator *Authenticator) (*sdktrace.TracerProvider, error) { ctx := context.Background() tlsConfigOption := otlptracehttp.WithInsecure() @@ -156,6 +161,7 @@ func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsCon exporter, err := otlptracehttp.New(ctx, tlsConfigOption, otlptracehttp.WithEndpoint(config.OtelExporterHTTPEndpoint), + otlptracehttp.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err @@ -176,7 +182,7 @@ func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsCon return sdktrace.NewTracerProvider(opts...), nil } -func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config) (*sdkmetric.MeterProvider, error) { +func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config, authenticator *Authenticator) (*sdkmetric.MeterProvider, error) { ctx := context.Background() tlsConfigOption := otlpmetrichttp.WithInsecure() @@ -187,6 +193,7 @@ func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConf exporter, err := otlpmetrichttp.New(ctx, tlsConfigOption, otlpmetrichttp.WithEndpoint(config.OtelExporterHTTPEndpoint), + otlpmetrichttp.WithHeaders(authenticator.GetHeaders()), ) if err != nil { return nil, err From 5bc0d9958edaa6c8fdaa05b46a4141eaf2ae3568 Mon Sep 17 00:00:00 2001 From: nanchano Date: Thu, 24 Oct 2024 11:05:00 -0300 Subject: [PATCH 08/16] Fix config test --- pkg/beholder/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/beholder/config_test.go b/pkg/beholder/config_test.go index 7d727f383..8f35b5d7f 100644 --- a/pkg/beholder/config_test.go +++ b/pkg/beholder/config_test.go @@ -38,5 +38,5 @@ func ExampleConfig() { } fmt.Printf("%+v", config) // 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 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: MetricReaderInterval:1s LogExportTimeout:1s LogBatchProcessor:true} + // {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 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: MetricReaderInterval:1s LogExportTimeout:1s LogBatchProcessor:true AuthenticatorPublicKey:[] AuthenticatorSigner: AuthenticatorHeaders:map[]} } From 878c915d6cdff7f40d6082400fd0c46c82e5e415 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:17:14 -0400 Subject: [PATCH 09/16] Add pub key getter to authenticator --- pkg/beholder/authenticator.go | 18 +++++++++++++----- pkg/beholder/authenticator_test.go | 18 ++++++++++++++++-- pkg/loop/config.go | 6 +++++- pkg/loop/config_test.go | 2 ++ pkg/loop/server.go | 7 +++++++ 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/pkg/beholder/authenticator.go b/pkg/beholder/authenticator.go index 1dccff2bf..f18255564 100644 --- a/pkg/beholder/authenticator.go +++ b/pkg/beholder/authenticator.go @@ -7,7 +7,8 @@ import ( ) type Authenticator struct { - headers map[string]string + headers map[string]string + publicKey []byte } func NewAuthenticator(config Config) (*Authenticator, error) { @@ -23,20 +24,27 @@ func NewAuthenticator(config Config) (*Authenticator, error) { headers = config.AuthenticatorHeaders } - return &Authenticator{headers}, nil + pubKey := config.AuthenticatorPublicKey + return &Authenticator{headers, pubKey}, nil } func (a *Authenticator) GetHeaders() map[string]string { return maps.Clone(a.headers) } +func (a *Authenticator) GetPubKey() []byte { + return a.publicKey +} + func validateAuthConfig(c Config) error { if c.AuthenticatorSigner != nil && len(c.AuthenticatorHeaders) > 0 { return errors.New("cannot configure both authenticator signer and authenticator header value") } - if c.AuthenticatorSigner != nil && len(c.AuthenticatorPublicKey) == 0 { - return errors.New("authenticator public key must be configured when authenticator signer is configured") + if c.AuthenticatorSigner != nil || len(c.AuthenticatorHeaders) > 0 { + if len(c.AuthenticatorPublicKey) == 0 { + return errors.New("authenticator public key must be set when signer or headers are configured") + } } return nil @@ -51,7 +59,7 @@ var authHeaderVersion = "1" // deriveAuthHeaders creates the auth header value to be included on requests. // The current format for the header is: // -// :: +// :: // // where the byte value of is what's being signed func deriveAuthHeaders(config Config) map[string]string { diff --git a/pkg/beholder/authenticator_test.go b/pkg/beholder/authenticator_test.go index 3dd3778d4..94d964ed6 100644 --- a/pkg/beholder/authenticator_test.go +++ b/pkg/beholder/authenticator_test.go @@ -25,18 +25,21 @@ func TestAuthenticator_SignerAuth(t *testing.T) { "X-Beholder-Node-Auth-Token": "1:746573742d7075626c69632d6b6579:746573742d7369676e6174757265", } assert.Equal(t, expectedHeaders, a.GetHeaders()) + assert.Equal(t, c.AuthenticatorPublicKey, a.GetPubKey()) } func TestAuthenticator_HeadersAuth(t *testing.T) { // Authenticator should use the headers if they are set expectedHeaders := map[string]string{"test-header-key": "test-header-value"} c := beholder.Config{ - AuthenticatorHeaders: expectedHeaders, + AuthenticatorHeaders: expectedHeaders, + AuthenticatorPublicKey: []byte("test-public-key"), } a, err := beholder.NewAuthenticator(c) assert.NoError(t, err) assert.Equal(t, expectedHeaders, a.GetHeaders()) + assert.Equal(t, c.AuthenticatorPublicKey, a.GetPubKey()) } func TestAuthenticator_NoAuth(t *testing.T) { @@ -47,6 +50,7 @@ func TestAuthenticator_NoAuth(t *testing.T) { assert.NoError(t, err) expectedHeaders := map[string]string{} assert.Equal(t, expectedHeaders, a.GetHeaders()) + assert.Equal(t, []byte(nil), a.GetPubKey()) } func TestAuthenticator_Config(t *testing.T) { @@ -62,9 +66,19 @@ func TestAuthenticator_Config(t *testing.T) { _, err = beholder.NewAuthenticator(c) assert.Error(t, err) - // Configuring neither auth signer nor auth header should not error + // Configuring headers with no public key should error c = defaultTestingConfig() c.AuthenticatorSigner = nil + c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} + c.AuthenticatorPublicKey = nil + _, err = beholder.NewAuthenticator(c) + assert.Error(t, err) + + // Configuring neither auth signer, nor auth heade, nor pub key should not error + c = defaultTestingConfig() + c.AuthenticatorSigner = nil + c.AuthenticatorPublicKey = nil + c.AuthenticatorHeaders = nil _, err = beholder.NewAuthenticator(c) assert.NoError(t, err) } diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 5708c6bc3..eebed3268 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -27,6 +27,7 @@ const ( envTelemetryAttribute = "CL_TELEMETRY_ATTRIBUTE_" envTelemetryTraceSampleRatio = "CL_TELEMETRY_TRACE_SAMPLE_RATIO" envTelemetryAuthHeader = "CL_TELEMETRY_AUTH_HEADER" + envTelemetryAuthPubKeyHex = "CL_TELEMETRY_AUTH_PUB_KEY_HEX" ) // EnvConfig is the configuration between the application and the LOOP executable. The values @@ -49,6 +50,7 @@ type EnvConfig struct { TelemetryAttributes OtelAttributes TelemetryTraceSampleRatio float64 TelemetryAuthHeaders map[string]string + TelemetryAuthPubKeyHex string } // AsCmdEnv returns a slice of environment variable key/value pairs for an exec.Cmd. @@ -83,6 +85,7 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { for k, v := range e.TelemetryAuthHeaders { add(envTelemetryAuthHeader+k, v) } + add(envTelemetryAuthPubKeyHex, e.TelemetryAuthPubKeyHex) return } @@ -130,7 +133,8 @@ func (e *EnvConfig) parse() error { e.TelemetryCACertFile = os.Getenv(envTelemetryCACertFile) e.TelemetryAttributes = getMap(envTelemetryAttribute) e.TelemetryTraceSampleRatio = getFloat64OrZero(envTelemetryTraceSampleRatio) - e.TelemetryAuthHeaders = getAttributes(envTelemetryAuthHeader) + e.TelemetryAuthHeaders = getMap(envTelemetryAuthHeader) + e.TelemetryAuthPubKeyHex = os.Getenv(envTelemetryAuthPubKeyHex) } return nil } diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index 5fb3e00b1..a61305f53 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -129,6 +129,7 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { TelemetryAttributes: OtelAttributes{"foo": "bar", "baz": "42"}, TelemetryTraceSampleRatio: 0.42, TelemetryAuthHeaders: map[string]string{"header-key": "header-value"}, + TelemetryAuthPubKeyHex: "pub-key-hex", } got := map[string]string{} for _, kv := range envCfg.AsCmdEnv() { @@ -154,6 +155,7 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { 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]) } func TestGetMap(t *testing.T) { diff --git a/pkg/loop/server.go b/pkg/loop/server.go index 5f87dfaf9..4e471a161 100644 --- a/pkg/loop/server.go +++ b/pkg/loop/server.go @@ -1,6 +1,7 @@ package loop import ( + "encoding/hex" "fmt" "os" @@ -90,6 +91,11 @@ func (s *Server) start() error { if tracingConfig.Enabled { attributes = tracingConfig.Attributes() } + + authPubKey, err := hex.DecodeString(envCfg.TelemetryAuthPubKeyHex) + if err != nil { + return fmt.Errorf("failed to decode telemetry auth public key hex: %w", err) + } beholderCfg := beholder.Config{ InsecureConnection: envCfg.TelemetryInsecureConnection, CACertFile: envCfg.TelemetryCACertFile, @@ -97,6 +103,7 @@ func (s *Server) start() error { ResourceAttributes: append(attributes, envCfg.TelemetryAttributes.AsStringAttributes()...), TraceSampleRatio: envCfg.TelemetryTraceSampleRatio, AuthenticatorHeaders: envCfg.TelemetryAuthHeaders, + AuthenticatorPublicKey: authPubKey, } if tracingConfig.Enabled { From 5f34751c93ab380d58fcf2665ca160b004c72e39 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:17:52 -0400 Subject: [PATCH 10/16] Set CSA pub key on Otel resource --- pkg/beholder/client.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index 0cd612233..755780f30 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -3,6 +3,7 @@ package beholder import ( "context" "errors" + "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" @@ -232,6 +233,21 @@ func newOtelResource(cfg Config) (resource *sdkresource.Resource, err error) { if err != nil { return nil, err } + + // Add csa public key resource attribute + csaPublicKeyHex := "not-configured" + if len(cfg.AuthenticatorPublicKey) > 0 { + csaPublicKeyHex = fmt.Sprintf("%x", cfg.AuthenticatorPublicKey) + } + csaPublicKeyAttr := attribute.String("csa_public_key", csaPublicKeyHex) + resource, err = sdkresource.Merge( + sdkresource.NewSchemaless(csaPublicKeyAttr), + resource, + ) + if err != nil { + return nil, err + } + // Add custom resource attributes resource, err = sdkresource.Merge( sdkresource.NewSchemaless(cfg.ResourceAttributes...), From 3dc6e244834a0114fb644c67b2a499139948f95a Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:29:01 -0400 Subject: [PATCH 11/16] Add noop value to authenticator --- pkg/beholder/global_test.go | 2 +- pkg/beholder/noop.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/beholder/global_test.go b/pkg/beholder/global_test.go index 64f9a2231..b5e122c3d 100644 --- a/pkg/beholder/global_test.go +++ b/pkg/beholder/global_test.go @@ -64,7 +64,7 @@ func TestGlobal(t *testing.T) { // Get the auth headers headers := authenticator.GetHeaders() - assert.Nil(t, headers) + assert.Equal(t, map[string]string{"X-Beholder-Node-Auth-Token": "noop-token"}, headers) } func TestClient_SetGlobalOtelProviders(t *testing.T) { diff --git a/pkg/beholder/noop.go b/pkg/beholder/noop.go index a7d4495cc..84a30d2dc 100644 --- a/pkg/beholder/noop.go +++ b/pkg/beholder/noop.go @@ -36,7 +36,9 @@ func NewNoopClient() *Client { messageEmitter := noopMessageEmitter{} // Authenticator - authenticator := &Authenticator{} + authenticator := &Authenticator{ + headers: map[string]string{"X-Beholder-Node-Auth-Token": "noop-token"}, + } return &Client{cfg, logger, tracer, meter, messageEmitter, loggerProvider, tracerProvider, meterProvider, loggerProvider, authenticator, noopOnClose} } From 210eb67fecd000768569ca6f2c43ea18859269ce Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:09:50 -0400 Subject: [PATCH 12/16] Move auth tests to beholder package, unexport new auth --- pkg/beholder/authenticator.go | 2 +- pkg/beholder/authenticator_test.go | 26 ++++++++++++-------------- pkg/beholder/client.go | 2 +- pkg/beholder/httpclient.go | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/pkg/beholder/authenticator.go b/pkg/beholder/authenticator.go index f18255564..16f8fb2fb 100644 --- a/pkg/beholder/authenticator.go +++ b/pkg/beholder/authenticator.go @@ -11,7 +11,7 @@ type Authenticator struct { publicKey []byte } -func NewAuthenticator(config Config) (*Authenticator, error) { +func newAuthenticator(config Config) (*Authenticator, error) { if err := validateAuthConfig(config); err != nil { return nil, err } diff --git a/pkg/beholder/authenticator_test.go b/pkg/beholder/authenticator_test.go index 94d964ed6..f35e1c0be 100644 --- a/pkg/beholder/authenticator_test.go +++ b/pkg/beholder/authenticator_test.go @@ -1,15 +1,13 @@ -package beholder_test +package beholder import ( "testing" "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink-common/pkg/beholder" ) -func defaultTestingConfig() beholder.Config { - return beholder.Config{ +func defaultTestingConfig() Config { + return Config{ AuthenticatorPublicKey: []byte("test-public-key"), AuthenticatorSigner: func([]byte) []byte { return []byte("test-signature") }, AuthenticatorHeaders: nil, @@ -18,7 +16,7 @@ func defaultTestingConfig() beholder.Config { func TestAuthenticator_SignerAuth(t *testing.T) { // Authenticator should derive headers if the signer is set c := defaultTestingConfig() - a, err := beholder.NewAuthenticator(c) + a, err := newAuthenticator(c) assert.NoError(t, err) expectedHeaders := map[string]string{ @@ -31,12 +29,12 @@ func TestAuthenticator_SignerAuth(t *testing.T) { func TestAuthenticator_HeadersAuth(t *testing.T) { // Authenticator should use the headers if they are set expectedHeaders := map[string]string{"test-header-key": "test-header-value"} - c := beholder.Config{ + c := Config{ AuthenticatorHeaders: expectedHeaders, AuthenticatorPublicKey: []byte("test-public-key"), } - a, err := beholder.NewAuthenticator(c) + a, err := newAuthenticator(c) assert.NoError(t, err) assert.Equal(t, expectedHeaders, a.GetHeaders()) assert.Equal(t, c.AuthenticatorPublicKey, a.GetPubKey()) @@ -44,9 +42,9 @@ func TestAuthenticator_HeadersAuth(t *testing.T) { func TestAuthenticator_NoAuth(t *testing.T) { // Authenticator should not set any headers if neither signer nor headers are set - c := beholder.Config{} + c := Config{} - a, err := beholder.NewAuthenticator(c) + a, err := newAuthenticator(c) assert.NoError(t, err) expectedHeaders := map[string]string{} assert.Equal(t, expectedHeaders, a.GetHeaders()) @@ -57,13 +55,13 @@ func TestAuthenticator_Config(t *testing.T) { // Configuring both auth signer and auth header should error c := defaultTestingConfig() c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} - _, err := beholder.NewAuthenticator(c) + _, err := newAuthenticator(c) assert.Error(t, err) // Configuring signer with no public key should error c = defaultTestingConfig() c.AuthenticatorPublicKey = nil - _, err = beholder.NewAuthenticator(c) + _, err = newAuthenticator(c) assert.Error(t, err) // Configuring headers with no public key should error @@ -71,7 +69,7 @@ func TestAuthenticator_Config(t *testing.T) { c.AuthenticatorSigner = nil c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} c.AuthenticatorPublicKey = nil - _, err = beholder.NewAuthenticator(c) + _, err = newAuthenticator(c) assert.Error(t, err) // Configuring neither auth signer, nor auth heade, nor pub key should not error @@ -79,6 +77,6 @@ func TestAuthenticator_Config(t *testing.T) { c.AuthenticatorSigner = nil c.AuthenticatorPublicKey = nil c.AuthenticatorHeaders = nil - _, err = beholder.NewAuthenticator(c) + _, err = newAuthenticator(c) assert.NoError(t, err) } diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index 755780f30..cc31e55b7 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -94,7 +94,7 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro return nil, err } } - authenticator, err := NewAuthenticator(cfg) + authenticator, err := newAuthenticator(cfg) if err != nil { return nil, err } diff --git a/pkg/beholder/httpclient.go b/pkg/beholder/httpclient.go index a209142cd..b43d78985 100644 --- a/pkg/beholder/httpclient.go +++ b/pkg/beholder/httpclient.go @@ -56,7 +56,7 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro if tlsConfig != nil { tlsConfigOption = otlploghttp.WithTLSClientConfig(tlsConfig) } - authenticator, err := NewAuthenticator(cfg) + authenticator, err := newAuthenticator(cfg) if err != nil { return nil, err } From 5a80788ed40f9fd9c0433e994d9b96743a05f0ab Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:19:14 -0500 Subject: [PATCH 13/16] Simplify auth header approach --- pkg/beholder/auth.go | 23 +++++++++ pkg/beholder/auth_test.go | 20 ++++++++ pkg/beholder/authenticator.go | 71 -------------------------- pkg/beholder/authenticator_test.go | 82 ------------------------------ pkg/beholder/client.go | 28 ++++------ pkg/beholder/config.go | 5 +- pkg/beholder/config_test.go | 2 +- pkg/beholder/global.go | 4 -- pkg/beholder/global_test.go | 11 ++-- pkg/beholder/httpclient.go | 20 +++----- pkg/beholder/noop.go | 7 +-- pkg/loop/server.go | 9 +--- 12 files changed, 70 insertions(+), 212 deletions(-) create mode 100644 pkg/beholder/auth.go create mode 100644 pkg/beholder/auth_test.go delete mode 100644 pkg/beholder/authenticator.go delete mode 100644 pkg/beholder/authenticator_test.go diff --git a/pkg/beholder/auth.go b/pkg/beholder/auth.go new file mode 100644 index 000000000..e698299a4 --- /dev/null +++ b/pkg/beholder/auth.go @@ -0,0 +1,23 @@ +package beholder + +import "fmt" + +// 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" + +// authHeaderVersion is the version of the auth header format +var authHeaderVersion = "1" + +// BuildAuthHeaders creates the auth header value to be included on requests. +// The current format for the header is: +// +// :: +// +// where the byte value of is what's being signed +func BuildAuthHeaders(signer func([]byte) []byte, pubKey []byte) map[string]string { + messageBytes := pubKey + signature := signer(messageBytes) + headerValue := fmt.Sprintf("%s:%x:%x", authHeaderVersion, messageBytes, signature) + + return map[string]string{authHeaderKey: headerValue} +} diff --git a/pkg/beholder/auth_test.go b/pkg/beholder/auth_test.go new file mode 100644 index 000000000..51fc433e2 --- /dev/null +++ b/pkg/beholder/auth_test.go @@ -0,0 +1,20 @@ +package beholder + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildAuthHeaders(t *testing.T) { + mockPubKey := []byte("test-public-key") + mockSigner := func(data []byte) []byte { + return append(data, []byte("__test-signature")...) + } + + expectedHeaders := map[string]string{ + "X-Beholder-Node-Auth-Token": "1:746573742d7075626c69632d6b6579:746573742d7075626c69632d6b65795f5f746573742d7369676e6174757265", + } + + assert.Equal(t, expectedHeaders, BuildAuthHeaders(mockSigner, mockPubKey)) +} diff --git a/pkg/beholder/authenticator.go b/pkg/beholder/authenticator.go deleted file mode 100644 index 16f8fb2fb..000000000 --- a/pkg/beholder/authenticator.go +++ /dev/null @@ -1,71 +0,0 @@ -package beholder - -import ( - "errors" - "fmt" - "maps" -) - -type Authenticator struct { - headers map[string]string - publicKey []byte -} - -func newAuthenticator(config Config) (*Authenticator, error) { - if err := validateAuthConfig(config); err != nil { - return nil, err - } - - headers := make(map[string]string) - if config.AuthenticatorSigner != nil { - headers = deriveAuthHeaders(config) - } - if len(config.AuthenticatorHeaders) != 0 { - headers = config.AuthenticatorHeaders - } - - pubKey := config.AuthenticatorPublicKey - return &Authenticator{headers, pubKey}, nil -} - -func (a *Authenticator) GetHeaders() map[string]string { - return maps.Clone(a.headers) -} - -func (a *Authenticator) GetPubKey() []byte { - return a.publicKey -} - -func validateAuthConfig(c Config) error { - if c.AuthenticatorSigner != nil && len(c.AuthenticatorHeaders) > 0 { - return errors.New("cannot configure both authenticator signer and authenticator header value") - } - - if c.AuthenticatorSigner != nil || len(c.AuthenticatorHeaders) > 0 { - if len(c.AuthenticatorPublicKey) == 0 { - return errors.New("authenticator public key must be set when signer or headers are configured") - } - } - - return nil -} - -// 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" - -// authHeaderVersion is the version of the auth header format -var authHeaderVersion = "1" - -// deriveAuthHeaders creates the auth header value to be included on requests. -// The current format for the header is: -// -// :: -// -// where the byte value of is what's being signed -func deriveAuthHeaders(config Config) map[string]string { - messageBytes := config.AuthenticatorPublicKey - signature := config.AuthenticatorSigner(messageBytes) - headerValue := fmt.Sprintf("%s:%x:%x", authHeaderVersion, messageBytes, signature) - - return map[string]string{authHeaderKey: headerValue} -} diff --git a/pkg/beholder/authenticator_test.go b/pkg/beholder/authenticator_test.go deleted file mode 100644 index f35e1c0be..000000000 --- a/pkg/beholder/authenticator_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package beholder - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func defaultTestingConfig() Config { - return Config{ - AuthenticatorPublicKey: []byte("test-public-key"), - AuthenticatorSigner: func([]byte) []byte { return []byte("test-signature") }, - AuthenticatorHeaders: nil, - } -} -func TestAuthenticator_SignerAuth(t *testing.T) { - // Authenticator should derive headers if the signer is set - c := defaultTestingConfig() - a, err := newAuthenticator(c) - assert.NoError(t, err) - - expectedHeaders := map[string]string{ - "X-Beholder-Node-Auth-Token": "1:746573742d7075626c69632d6b6579:746573742d7369676e6174757265", - } - assert.Equal(t, expectedHeaders, a.GetHeaders()) - assert.Equal(t, c.AuthenticatorPublicKey, a.GetPubKey()) -} - -func TestAuthenticator_HeadersAuth(t *testing.T) { - // Authenticator should use the headers if they are set - expectedHeaders := map[string]string{"test-header-key": "test-header-value"} - c := Config{ - AuthenticatorHeaders: expectedHeaders, - AuthenticatorPublicKey: []byte("test-public-key"), - } - - a, err := newAuthenticator(c) - assert.NoError(t, err) - assert.Equal(t, expectedHeaders, a.GetHeaders()) - assert.Equal(t, c.AuthenticatorPublicKey, a.GetPubKey()) -} - -func TestAuthenticator_NoAuth(t *testing.T) { - // Authenticator should not set any headers if neither signer nor headers are set - c := Config{} - - a, err := newAuthenticator(c) - assert.NoError(t, err) - expectedHeaders := map[string]string{} - assert.Equal(t, expectedHeaders, a.GetHeaders()) - assert.Equal(t, []byte(nil), a.GetPubKey()) -} - -func TestAuthenticator_Config(t *testing.T) { - // Configuring both auth signer and auth header should error - c := defaultTestingConfig() - c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} - _, err := newAuthenticator(c) - assert.Error(t, err) - - // Configuring signer with no public key should error - c = defaultTestingConfig() - c.AuthenticatorPublicKey = nil - _, err = newAuthenticator(c) - assert.Error(t, err) - - // Configuring headers with no public key should error - c = defaultTestingConfig() - c.AuthenticatorSigner = nil - c.AuthenticatorHeaders = map[string]string{"test-header-key": "test-header-value"} - c.AuthenticatorPublicKey = nil - _, err = newAuthenticator(c) - assert.Error(t, err) - - // Configuring neither auth signer, nor auth heade, nor pub key should not error - c = defaultTestingConfig() - c.AuthenticatorSigner = nil - c.AuthenticatorPublicKey = nil - c.AuthenticatorHeaders = nil - _, err = newAuthenticator(c) - assert.NoError(t, err) -} diff --git a/pkg/beholder/client.go b/pkg/beholder/client.go index cc31e55b7..098b17452 100644 --- a/pkg/beholder/client.go +++ b/pkg/beholder/client.go @@ -3,7 +3,6 @@ package beholder import ( "context" "errors" - "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" @@ -48,9 +47,6 @@ type Client struct { MeterProvider otelmetric.MeterProvider MessageLoggerProvider otellog.LoggerProvider - // Authenticator - Authenticator *Authenticator - // OnClose OnClose func() error } @@ -94,14 +90,10 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro return nil, err } } - authenticator, err := newAuthenticator(cfg) - if err != nil { - return nil, err - } sharedLogExporter, err := otlploggrpcNew( otlploggrpc.WithTLSCredentials(creds), otlploggrpc.WithEndpoint(cfg.OtelExporterGRPCEndpoint), - otlploggrpc.WithHeaders(authenticator.GetHeaders()), + otlploggrpc.WithHeaders(cfg.AuthHeaders), ) if err != nil { return nil, err @@ -134,14 +126,14 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro logger := loggerProvider.Logger(defaultPackageName) // Tracer - tracerProvider, err := newTracerProvider(cfg, baseResource, creds, authenticator) + tracerProvider, err := newTracerProvider(cfg, baseResource, creds) if err != nil { return nil, err } tracer := tracerProvider.Tracer(defaultPackageName) // Meter - meterProvider, err := newMeterProvider(cfg, baseResource, creds, authenticator) + meterProvider, err := newMeterProvider(cfg, baseResource, creds) if err != nil { return nil, err } @@ -185,7 +177,7 @@ func newGRPCClient(cfg Config, otlploggrpcNew otlploggrpcFactory) (*Client, erro } return } - return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, authenticator, onClose}, nil + return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, onClose}, nil } // Closes all providers, flushes all data and stops all background processes @@ -236,8 +228,8 @@ func newOtelResource(cfg Config) (resource *sdkresource.Resource, err error) { // Add csa public key resource attribute csaPublicKeyHex := "not-configured" - if len(cfg.AuthenticatorPublicKey) > 0 { - csaPublicKeyHex = fmt.Sprintf("%x", cfg.AuthenticatorPublicKey) + if len(cfg.AuthPublicKeyHex) > 0 { + csaPublicKeyHex = cfg.AuthPublicKeyHex } csaPublicKeyAttr := attribute.String("csa_public_key", csaPublicKeyHex) resource, err = sdkresource.Merge( @@ -282,14 +274,14 @@ type shutdowner interface { Shutdown(ctx context.Context) error } -func newTracerProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials, authenticator *Authenticator) (*sdktrace.TracerProvider, error) { +func newTracerProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials) (*sdktrace.TracerProvider, error) { ctx := context.Background() // note: context is used internally exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithTLSCredentials(creds), otlptracegrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), - otlptracegrpc.WithHeaders(authenticator.GetHeaders()), + otlptracegrpc.WithHeaders(config.AuthHeaders), ) if err != nil { return nil, err @@ -310,7 +302,7 @@ func newTracerProvider(config Config, resource *sdkresource.Resource, creds cred return sdktrace.NewTracerProvider(opts...), nil } -func newMeterProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials, authenticator *Authenticator) (*sdkmetric.MeterProvider, error) { +func newMeterProvider(config Config, resource *sdkresource.Resource, creds credentials.TransportCredentials) (*sdkmetric.MeterProvider, error) { ctx := context.Background() // note: context is unused internally @@ -318,7 +310,7 @@ func newMeterProvider(config Config, resource *sdkresource.Resource, creds crede ctx, otlpmetricgrpc.WithTLSCredentials(creds), otlpmetricgrpc.WithEndpoint(config.OtelExporterGRPCEndpoint), - otlpmetricgrpc.WithHeaders(authenticator.GetHeaders()), + otlpmetricgrpc.WithHeaders(config.AuthHeaders), ) if err != nil { return nil, err diff --git a/pkg/beholder/config.go b/pkg/beholder/config.go index 264d3f203..0b9c9108d 100644 --- a/pkg/beholder/config.go +++ b/pkg/beholder/config.go @@ -32,9 +32,8 @@ type Config struct { // Disable it only for testing LogBatchProcessor bool - AuthenticatorPublicKey []byte - AuthenticatorSigner func([]byte) []byte - AuthenticatorHeaders map[string]string + AuthPublicKeyHex string + AuthHeaders map[string]string } const ( diff --git a/pkg/beholder/config_test.go b/pkg/beholder/config_test.go index 8f35b5d7f..abee2cabc 100644 --- a/pkg/beholder/config_test.go +++ b/pkg/beholder/config_test.go @@ -38,5 +38,5 @@ func ExampleConfig() { } fmt.Printf("%+v", config) // 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 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: MetricReaderInterval:1s LogExportTimeout:1s LogBatchProcessor:true AuthenticatorPublicKey:[] AuthenticatorSigner: AuthenticatorHeaders: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 EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter: MetricReaderInterval:1s LogExportTimeout:1s LogBatchProcessor:true AuthPublicKeyHex: AuthHeaders:map[]} } diff --git a/pkg/beholder/global.go b/pkg/beholder/global.go index aa605b818..9ec318c87 100644 --- a/pkg/beholder/global.go +++ b/pkg/beholder/global.go @@ -43,10 +43,6 @@ func GetEmitter() Emitter { return GetClient().Emitter } -func GetAuthenticator() *Authenticator { - return GetClient().Authenticator -} - // Sets global OTel logger, tracer, meter providers from Client. // Makes them accessible from anywhere in the code via global otel getters. // Any package that relies on go.opentelemetry.io will be able to pick up configured global providers diff --git a/pkg/beholder/global_test.go b/pkg/beholder/global_test.go index b5e122c3d..1dbc9e373 100644 --- a/pkg/beholder/global_test.go +++ b/pkg/beholder/global_test.go @@ -25,14 +25,13 @@ import ( func TestGlobal(t *testing.T) { // Get global logger, tracer, meter, messageEmitter // If not initialized with beholder.SetClient will return noop client - logger, tracer, meter, messageEmitter, authenticator := beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter(), beholder.GetAuthenticator() + logger, tracer, meter, messageEmitter := beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter() noopClient := beholder.NewNoopClient() assert.IsType(t, otellognoop.Logger{}, logger) assert.IsType(t, oteltracenoop.Tracer{}, tracer) assert.IsType(t, otelmetricnoop.Meter{}, meter) expectedMessageEmitter := beholder.NewNoopClient().Emitter assert.IsType(t, expectedMessageEmitter, messageEmitter) - assert.IsType(t, &beholder.Authenticator{}, authenticator) var noopClientPtr *beholder.Client = noopClient assert.IsType(t, noopClientPtr, beholder.GetClient()) @@ -42,8 +41,8 @@ func TestGlobal(t *testing.T) { beholder.SetClient(noopClientPtr) assert.Same(t, noopClientPtr, beholder.GetClient()) - // After that use beholder functions to get logger, tracer, meter, messageEmitter, authenticator - logger, tracer, meter, messageEmitter, authenticator = beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter(), beholder.GetAuthenticator() + // After that use beholder functions to get logger, tracer, meter, messageEmitter + logger, tracer, meter, messageEmitter = beholder.GetLogger(), beholder.GetTracer(), beholder.GetMeter(), beholder.GetEmitter() // Emit otel log record logger.Emit(tests.Context(t), otellog.Record{}) @@ -61,10 +60,6 @@ func TestGlobal(t *testing.T) { if err != nil { t.Fatalf("Error emitting message: %v", err) } - - // Get the auth headers - headers := authenticator.GetHeaders() - assert.Equal(t, map[string]string{"X-Beholder-Node-Auth-Token": "noop-token"}, headers) } func TestClient_SetGlobalOtelProviders(t *testing.T) { diff --git a/pkg/beholder/httpclient.go b/pkg/beholder/httpclient.go index b43d78985..0e40f339f 100644 --- a/pkg/beholder/httpclient.go +++ b/pkg/beholder/httpclient.go @@ -56,14 +56,10 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro if tlsConfig != nil { tlsConfigOption = otlploghttp.WithTLSClientConfig(tlsConfig) } - authenticator, err := newAuthenticator(cfg) - if err != nil { - return nil, err - } sharedLogExporter, err := otlploghttpNew( tlsConfigOption, otlploghttp.WithEndpoint(cfg.OtelExporterHTTPEndpoint), - otlploghttp.WithHeaders(authenticator.GetHeaders()), + otlploghttp.WithHeaders(cfg.AuthHeaders), ) if err != nil { return nil, err @@ -96,14 +92,14 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro logger := loggerProvider.Logger(defaultPackageName) // Tracer - tracerProvider, err := newHTTPTracerProvider(cfg, baseResource, tlsConfig, authenticator) + tracerProvider, err := newHTTPTracerProvider(cfg, baseResource, tlsConfig) if err != nil { return nil, err } tracer := tracerProvider.Tracer(defaultPackageName) // Meter - meterProvider, err := newHTTPMeterProvider(cfg, baseResource, tlsConfig, authenticator) + meterProvider, err := newHTTPMeterProvider(cfg, baseResource, tlsConfig) if err != nil { return nil, err } @@ -147,10 +143,10 @@ func newHTTPClient(cfg Config, otlploghttpNew otlploghttpFactory) (*Client, erro } return } - return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, authenticator, onClose}, nil + return &Client{cfg, logger, tracer, meter, emitter, loggerProvider, tracerProvider, meterProvider, messageLoggerProvider, onClose}, nil } -func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config, authenticator *Authenticator) (*sdktrace.TracerProvider, error) { +func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config) (*sdktrace.TracerProvider, error) { ctx := context.Background() tlsConfigOption := otlptracehttp.WithInsecure() @@ -161,7 +157,7 @@ func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsCon exporter, err := otlptracehttp.New(ctx, tlsConfigOption, otlptracehttp.WithEndpoint(config.OtelExporterHTTPEndpoint), - otlptracehttp.WithHeaders(authenticator.GetHeaders()), + otlptracehttp.WithHeaders(config.AuthHeaders), ) if err != nil { return nil, err @@ -182,7 +178,7 @@ func newHTTPTracerProvider(config Config, resource *sdkresource.Resource, tlsCon return sdktrace.NewTracerProvider(opts...), nil } -func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config, authenticator *Authenticator) (*sdkmetric.MeterProvider, error) { +func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConfig *tls.Config) (*sdkmetric.MeterProvider, error) { ctx := context.Background() tlsConfigOption := otlpmetrichttp.WithInsecure() @@ -193,7 +189,7 @@ func newHTTPMeterProvider(config Config, resource *sdkresource.Resource, tlsConf exporter, err := otlpmetrichttp.New(ctx, tlsConfigOption, otlpmetrichttp.WithEndpoint(config.OtelExporterHTTPEndpoint), - otlpmetrichttp.WithHeaders(authenticator.GetHeaders()), + otlpmetrichttp.WithHeaders(config.AuthHeaders), ) if err != nil { return nil, err diff --git a/pkg/beholder/noop.go b/pkg/beholder/noop.go index 84a30d2dc..3012c0304 100644 --- a/pkg/beholder/noop.go +++ b/pkg/beholder/noop.go @@ -35,12 +35,7 @@ func NewNoopClient() *Client { // MessageEmitter messageEmitter := noopMessageEmitter{} - // Authenticator - authenticator := &Authenticator{ - headers: map[string]string{"X-Beholder-Node-Auth-Token": "noop-token"}, - } - - return &Client{cfg, logger, tracer, meter, messageEmitter, loggerProvider, tracerProvider, meterProvider, loggerProvider, authenticator, noopOnClose} + return &Client{cfg, logger, tracer, meter, messageEmitter, loggerProvider, tracerProvider, meterProvider, loggerProvider, noopOnClose} } // NewStdoutClient creates a new Client with exporters which send telemetry data to standard output diff --git a/pkg/loop/server.go b/pkg/loop/server.go index 4e471a161..3d1eba76b 100644 --- a/pkg/loop/server.go +++ b/pkg/loop/server.go @@ -1,7 +1,6 @@ package loop import ( - "encoding/hex" "fmt" "os" @@ -92,18 +91,14 @@ func (s *Server) start() error { attributes = tracingConfig.Attributes() } - authPubKey, err := hex.DecodeString(envCfg.TelemetryAuthPubKeyHex) - if err != nil { - return fmt.Errorf("failed to decode telemetry auth public key hex: %w", err) - } beholderCfg := beholder.Config{ InsecureConnection: envCfg.TelemetryInsecureConnection, CACertFile: envCfg.TelemetryCACertFile, OtelExporterGRPCEndpoint: envCfg.TelemetryEndpoint, ResourceAttributes: append(attributes, envCfg.TelemetryAttributes.AsStringAttributes()...), TraceSampleRatio: envCfg.TelemetryTraceSampleRatio, - AuthenticatorHeaders: envCfg.TelemetryAuthHeaders, - AuthenticatorPublicKey: authPubKey, + AuthHeaders: envCfg.TelemetryAuthHeaders, + AuthPublicKeyHex: envCfg.TelemetryAuthPubKeyHex, } if tracingConfig.Enabled { From e42e6e3d803b6cdfd5ab3157a602d2cc50a6243a Mon Sep 17 00:00:00 2001 From: Geert G <117188496+cll-gg@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:51:10 +0000 Subject: [PATCH 14/16] Remove duplicate test --- pkg/loop/config_test.go | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index 56b9861bc..a61305f53 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -187,35 +187,6 @@ func TestGetMap(t *testing.T) { } } -func TestGetMap(t *testing.T) { - os.Setenv("TEST_PREFIX_KEY1", "value1") - os.Setenv("TEST_PREFIX_KEY2", "value2") - os.Setenv("OTHER_KEY", "othervalue") - - defer func() { - os.Unsetenv("TEST_PREFIX_KEY1") - os.Unsetenv("TEST_PREFIX_KEY2") - os.Unsetenv("OTHER_KEY") - }() - - result := getMap("TEST_PREFIX_") - - expected := map[string]string{ - "KEY1": "value1", - "KEY2": "value2", - } - - if len(result) != len(expected) { - t.Errorf("Expected map length %d, got %d", len(expected), len(result)) - } - - for k, v := range expected { - if result[k] != v { - t.Errorf("Expected key %s to have value %s, but got %s", k, v, result[k]) - } - } -} - func TestManagedGRPCClientConfig(t *testing.T) { t.Parallel() From 88df7ab02bd80b886d7840ef0161a23b2031e6d8 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:33:40 -0500 Subject: [PATCH 15/16] Use ed25519 keys instead of signer --- pkg/beholder/auth.go | 9 ++++++--- pkg/beholder/auth_test.go | 15 +++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pkg/beholder/auth.go b/pkg/beholder/auth.go index e698299a4..d49d41af1 100644 --- a/pkg/beholder/auth.go +++ b/pkg/beholder/auth.go @@ -1,6 +1,9 @@ package beholder -import "fmt" +import ( + "crypto/ed25519" + "fmt" +) // 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" @@ -14,9 +17,9 @@ var authHeaderVersion = "1" // :: // // where the byte value of is what's being signed -func BuildAuthHeaders(signer func([]byte) []byte, pubKey []byte) map[string]string { +func BuildAuthHeaders(privKey ed25519.PrivateKey, pubKey ed25519.PublicKey) map[string]string { messageBytes := pubKey - signature := signer(messageBytes) + signature := ed25519.Sign(privKey, messageBytes) headerValue := fmt.Sprintf("%s:%x:%x", authHeaderVersion, messageBytes, signature) return map[string]string{authHeaderKey: headerValue} diff --git a/pkg/beholder/auth_test.go b/pkg/beholder/auth_test.go index 51fc433e2..61ac1c70a 100644 --- a/pkg/beholder/auth_test.go +++ b/pkg/beholder/auth_test.go @@ -1,20 +1,23 @@ package beholder import ( + "crypto/ed25519" + "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func TestBuildAuthHeaders(t *testing.T) { - mockPubKey := []byte("test-public-key") - mockSigner := func(data []byte) []byte { - return append(data, []byte("__test-signature")...) - } + csaPrivKeyHex := "1ac84741fa51c633845fa65c06f37a700303619135630a01f2d22fb98eb1c54ecab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159" + csaPrivKeyBytes, err := hex.DecodeString(csaPrivKeyHex) + assert.NoError(t, err) + csaPrivKey := ed25519.PrivateKey(csaPrivKeyBytes) + csaPubKey := csaPrivKey.Public().(ed25519.PublicKey) expectedHeaders := map[string]string{ - "X-Beholder-Node-Auth-Token": "1:746573742d7075626c69632d6b6579:746573742d7075626c69632d6b65795f5f746573742d7369676e6174757265", + "X-Beholder-Node-Auth-Token": "1:cab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159:4403178e299e9acc5b48ae97de617d3975c5d431b794cfab1d23eda01c194119b2360f5f74cfb3e4f706237ab57a0ba88ffd3f8addbc1e5197b3d3e13a1fc409", } - assert.Equal(t, expectedHeaders, BuildAuthHeaders(mockSigner, mockPubKey)) + assert.Equal(t, expectedHeaders, BuildAuthHeaders(csaPrivKey, csaPubKey)) } From b969b66e6141c80565d51ecd64a8bb39d4c09811 Mon Sep 17 00:00:00 2001 From: 4of9 <177086174+4of9@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:49:26 -0500 Subject: [PATCH 16/16] Remove pub key from args --- pkg/beholder/auth.go | 3 ++- pkg/beholder/auth_test.go | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/beholder/auth.go b/pkg/beholder/auth.go index d49d41af1..ae944ab0f 100644 --- a/pkg/beholder/auth.go +++ b/pkg/beholder/auth.go @@ -17,7 +17,8 @@ var authHeaderVersion = "1" // :: // // where the byte value of is what's being signed -func BuildAuthHeaders(privKey ed25519.PrivateKey, pubKey ed25519.PublicKey) map[string]string { +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) diff --git a/pkg/beholder/auth_test.go b/pkg/beholder/auth_test.go index 61ac1c70a..fd0e2c86c 100644 --- a/pkg/beholder/auth_test.go +++ b/pkg/beholder/auth_test.go @@ -13,11 +13,10 @@ func TestBuildAuthHeaders(t *testing.T) { csaPrivKeyBytes, err := hex.DecodeString(csaPrivKeyHex) assert.NoError(t, err) csaPrivKey := ed25519.PrivateKey(csaPrivKeyBytes) - csaPubKey := csaPrivKey.Public().(ed25519.PublicKey) expectedHeaders := map[string]string{ "X-Beholder-Node-Auth-Token": "1:cab39509e63cfaa81c70e2c907391f96803aacb00db5619a5ace5588b4b08159:4403178e299e9acc5b48ae97de617d3975c5d431b794cfab1d23eda01c194119b2360f5f74cfb3e4f706237ab57a0ba88ffd3f8addbc1e5197b3d3e13a1fc409", } - assert.Equal(t, expectedHeaders, BuildAuthHeaders(csaPrivKey, csaPubKey)) + assert.Equal(t, expectedHeaders, BuildAuthHeaders(csaPrivKey)) }