diff --git a/cmd/collector/app/collector.go b/cmd/collector/app/collector.go index 5432904740b..6ecfd8d0bf9 100644 --- a/cmd/collector/app/collector.go +++ b/cmd/collector/app/collector.go @@ -146,8 +146,7 @@ func (c *Collector) Start(options *flags.CollectorOptions) error { Handler: c.spanHandlers.ZipkinSpansHandler, TLSConfig: options.Zipkin.TLS, HealthCheck: c.hCheck, - AllowedHeaders: options.Zipkin.AllowedHeaders, - AllowedOrigins: options.Zipkin.AllowedOrigins, + CORSConfig: options.Zipkin.CORS, Logger: c.logger, MetricsFactory: c.metricsFactory, KeepAlive: options.Zipkin.KeepAlive, diff --git a/cmd/collector/app/flags/flags.go b/cmd/collector/app/flags/flags.go index e5d5a36272a..b5190cf27d6 100644 --- a/cmd/collector/app/flags/flags.go +++ b/cmd/collector/app/flags/flags.go @@ -24,6 +24,7 @@ import ( "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/flags" + "github.com/jaegertracing/jaeger/pkg/config/corscfg" "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/ports" @@ -49,8 +50,6 @@ const ( flagCollectorOTLPEnabled = "collector.otlp.enabled" flagZipkinHTTPHostPort = "collector.zipkin.host-port" - flagZipkinAllowedHeaders = "collector.zipkin.allowed-headers" - flagZipkinAllowedOrigins = "collector.zipkin.allowed-origins" flagZipkinKeepAliveEnabled = "collector.zipkin.keep-alive" // DefaultNumWorkers is the default number of workers consuming from the processor queue @@ -99,6 +98,14 @@ var tlsZipkinFlagsConfig = tlscfg.ServerFlagsConfig{ Prefix: "collector.zipkin", } +var corsZipkinFlags = corscfg.Flags{ + Prefix: "collector.zipkin", +} + +var corsOTLPFlags = corscfg.Flags{ + Prefix: "collector.otlp.http", +} + // CollectorOptions holds configuration for collector type CollectorOptions struct { // DynQueueSizeMemory determines how much memory to use for the queue @@ -121,12 +128,10 @@ type CollectorOptions struct { Zipkin struct { // HTTPHostPort is the host:port address that the Zipkin collector service listens in on for http requests HTTPHostPort string - // ZipkinAllowedOrigins is a list of origins a cross-domain request to the Zipkin collector service can be executed from - AllowedOrigins string - // ZipkinAllowedHeaders is a list of headers that the Zipkin collector service allowes the client to use with cross-domain requests - AllowedHeaders string // TLS configures secure transport for Zipkin endpoint to collect spans TLS tlscfg.Options + // CORS allows CORS requests , sets the values for Allowed Headers and Allowed Origins. + CORS corscfg.Options // KeepAlive configures allow Keep-Alive for Zipkin HTTP server KeepAlive bool } @@ -153,6 +158,8 @@ type HTTPOptions struct { ReadHeaderTimeout time.Duration // IdleTimeout sets the respective parameter of http.Server IdleTimeout time.Duration + // CORS allows CORS requests , sets the values for Allowed Headers and Allowed Origins. + CORS corscfg.Options } // GRPCOptions defines options for a gRPC server @@ -186,13 +193,13 @@ func AddFlags(flags *flag.FlagSet) { flags.Bool(flagCollectorOTLPEnabled, true, "Enables OpenTelemetry OTLP receiver on dedicated HTTP and gRPC ports") addHTTPFlags(flags, otlpServerFlagsCfg.HTTP, "") + corsOTLPFlags.AddFlags(flags) addGRPCFlags(flags, otlpServerFlagsCfg.GRPC, "") - flags.String(flagZipkinAllowedHeaders, "content-type", "Comma separated list of allowed headers for the Zipkin collector service, default content-type") - flags.String(flagZipkinAllowedOrigins, "*", "Comma separated list of allowed origins for the Zipkin collector service, default accepts all") flags.String(flagZipkinHTTPHostPort, "", "The host:port (e.g. 127.0.0.1:9411 or :9411) of the collector's Zipkin server (disabled by default)") flags.Bool(flagZipkinKeepAliveEnabled, true, "KeepAlive configures allow Keep-Alive for Zipkin HTTP server (enabled by default)") tlsZipkinFlagsConfig.AddFlags(flags) + corsZipkinFlags.AddFlags(flags) tenancy.AddFlags(flags) } @@ -273,12 +280,11 @@ func (cOpts *CollectorOptions) InitFromViper(v *viper.Viper, logger *zap.Logger) if err := cOpts.OTLP.HTTP.initFromViper(v, logger, otlpServerFlagsCfg.HTTP); err != nil { return cOpts, fmt.Errorf("failed to parse OTLP/HTTP server options: %w", err) } + cOpts.OTLP.HTTP.CORS = corsOTLPFlags.InitFromViper(v) if err := cOpts.OTLP.GRPC.initFromViper(v, logger, otlpServerFlagsCfg.GRPC); err != nil { return cOpts, fmt.Errorf("failed to parse OTLP/gRPC server options: %w", err) } - cOpts.Zipkin.AllowedHeaders = v.GetString(flagZipkinAllowedHeaders) - cOpts.Zipkin.AllowedOrigins = v.GetString(flagZipkinAllowedOrigins) cOpts.Zipkin.KeepAlive = v.GetBool(flagZipkinKeepAliveEnabled) cOpts.Zipkin.HTTPHostPort = ports.FormatHostPort(v.GetString(flagZipkinHTTPHostPort)) if tlsZipkin, err := tlsZipkinFlagsConfig.InitFromViper(v); err == nil { @@ -286,6 +292,7 @@ func (cOpts *CollectorOptions) InitFromViper(v *viper.Viper, logger *zap.Logger) } else { return cOpts, fmt.Errorf("failed to parse Zipkin TLS options: %w", err) } + cOpts.Zipkin.CORS = corsZipkinFlags.InitFromViper(v) return cOpts, nil } diff --git a/cmd/collector/app/handler/otlp_receiver.go b/cmd/collector/app/handler/otlp_receiver.go index 4ec09d712d0..5583dbdce62 100644 --- a/cmd/collector/app/handler/otlp_receiver.go +++ b/cmd/collector/app/handler/otlp_receiver.go @@ -128,6 +128,11 @@ func applyHTTPSettings(cfg *confighttp.HTTPServerSettings, opts *flags.HTTPOptio if opts.TLS.Enabled { cfg.TLSSetting = applyTLSSettings(&opts.TLS) } + + cfg.CORS = &confighttp.CORSSettings{ + AllowedOrigins: opts.CORS.AllowedOrigins, + AllowedHeaders: opts.CORS.AllowedHeaders, + } } func applyTLSSettings(opts *tlscfg.Options) *configtls.TLSServerSetting { diff --git a/cmd/collector/app/handler/otlp_receiver_test.go b/cmd/collector/app/handler/otlp_receiver_test.go index 842270a49c3..3e6384fa3e8 100644 --- a/cmd/collector/app/handler/otlp_receiver_test.go +++ b/cmd/collector/app/handler/otlp_receiver_test.go @@ -30,6 +30,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/collector/app/flags" "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/pkg/config/corscfg" "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/pkg/testutils" @@ -204,11 +205,16 @@ func TestApplyOTLPHTTPServerSettings(t *testing.T) { MinVersion: "1.1", MaxVersion: "1.3", }, + CORS: corscfg.Options{ + AllowedOrigins: []string{"http://example.domain.com", "http://*.domain.com"}, + AllowedHeaders: []string{"Content-Type", "Accept", "X-Requested-With"}, + }, } applyHTTPSettings(otlpReceiverConfig.HTTP, httpOpts) out := otlpReceiverConfig.HTTP + assert.Equal(t, out.Endpoint, ":12345") require.NotNil(t, out.TLSSetting) assert.Equal(t, out.TLSSetting.CAFile, "ca") @@ -217,4 +223,6 @@ func TestApplyOTLPHTTPServerSettings(t *testing.T) { assert.Equal(t, out.TLSSetting.ClientCAFile, "clientca") assert.Equal(t, out.TLSSetting.MinVersion, "1.1") assert.Equal(t, out.TLSSetting.MaxVersion, "1.3") + assert.Equal(t, out.CORS.AllowedHeaders, []string{"Content-Type", "Accept", "X-Requested-With"}) + assert.Equal(t, out.CORS.AllowedOrigins, []string{"http://example.domain.com", "http://*.domain.com"}) } diff --git a/cmd/collector/app/server/zipkin.go b/cmd/collector/app/server/zipkin.go index 44ba865c85d..1abe7a43b3b 100644 --- a/cmd/collector/app/server/zipkin.go +++ b/cmd/collector/app/server/zipkin.go @@ -17,7 +17,6 @@ package server import ( "net" "net/http" - "strings" "time" "github.com/gorilla/mux" @@ -27,6 +26,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/collector/app/handler" "github.com/jaegertracing/jaeger/cmd/collector/app/zipkin" + "github.com/jaegertracing/jaeger/pkg/config/corscfg" "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/healthcheck" "github.com/jaegertracing/jaeger/pkg/httpmetrics" @@ -39,8 +39,7 @@ type ZipkinServerParams struct { TLSConfig tlscfg.Options HostPort string Handler handler.ZipkinSpansHandler - AllowedOrigins string - AllowedHeaders string + CORSConfig corscfg.Options HealthCheck *healthcheck.HealthCheck Logger *zap.Logger MetricsFactory metrics.Factory @@ -86,13 +85,10 @@ func serveZipkin(server *http.Server, listener net.Listener, params *ZipkinServe zHandler := zipkin.NewAPIHandler(params.Handler) zHandler.RegisterRoutes(r) - origins := strings.Split(strings.ReplaceAll(params.AllowedOrigins, " ", ""), ",") - headers := strings.Split(strings.ReplaceAll(params.AllowedHeaders, " ", ""), ",") - cors := cors.New(cors.Options{ - AllowedOrigins: origins, + AllowedOrigins: params.CORSConfig.AllowedOrigins, AllowedMethods: []string{"POST"}, // Allowing only POST, because that's the only handled one - AllowedHeaders: headers, + AllowedHeaders: params.CORSConfig.AllowedHeaders, }) recoveryHandler := recoveryhandler.NewRecoveryHandler(params.Logger, true) diff --git a/pkg/config/corscfg/flags.go b/pkg/config/corscfg/flags.go new file mode 100644 index 00000000000..7ccaffe7e1c --- /dev/null +++ b/pkg/config/corscfg/flags.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package corscfg + +import ( + "flag" + "strings" + + "github.com/spf13/viper" +) + +const ( + corsPrefix = ".cors" + corsAllowedHeaders = corsPrefix + ".allowed-headers" + corsAllowedOrigins = corsPrefix + ".allowed-origins" +) + +type Flags struct { + Prefix string +} + +func (c Flags) AddFlags(flags *flag.FlagSet) { + flags.String(c.Prefix+corsAllowedHeaders, "", "Comma-separated CORS allowed headers. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers") + flags.String(c.Prefix+corsAllowedOrigins, "", "Comma-separated CORS allowed origins. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin") +} + +func (c Flags) InitFromViper(v *viper.Viper) Options { + var p Options + + allowedHeaders := v.GetString(c.Prefix + corsAllowedHeaders) + allowedOrigins := v.GetString(c.Prefix + corsAllowedOrigins) + + p.AllowedOrigins = strings.Split(strings.ReplaceAll(allowedOrigins, " ", ""), ",") + p.AllowedHeaders = strings.Split(strings.ReplaceAll(allowedHeaders, " ", ""), ",") + + return p +} diff --git a/pkg/config/corscfg/flags_test.go b/pkg/config/corscfg/flags_test.go new file mode 100644 index 00000000000..94a9fce5fef --- /dev/null +++ b/pkg/config/corscfg/flags_test.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package corscfg + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/jaegertracing/jaeger/pkg/config" +) + +func TestCORSFlags(t *testing.T) { + cmdFlags := []string{ + "--prefix.cors.allowed-headers=Content-Type, Accept, X-Requested-With", + "--prefix.cors.allowed-origins=http://example.domain.com, http://*.domain.com", + } + t.Run("CORS Flags", func(t *testing.T) { + flagCfg := Flags{ + Prefix: "prefix", + } + v, command := config.Viperize(flagCfg.AddFlags) + + err := command.ParseFlags(cmdFlags) + require.NoError(t, err) + + corsOpts := flagCfg.InitFromViper(v) + fmt.Println(corsOpts) + + assert.Equal(t, Options{ + AllowedHeaders: []string{"Content-Type", "Accept", "X-Requested-With"}, + AllowedOrigins: []string{"http://example.domain.com", "http://*.domain.com"}, + }, corsOpts) + }) +} diff --git a/pkg/config/corscfg/options.go b/pkg/config/corscfg/options.go new file mode 100644 index 00000000000..7deffbf4e1e --- /dev/null +++ b/pkg/config/corscfg/options.go @@ -0,0 +1,20 @@ +// Copyright (c) 2023 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package corscfg + +type Options struct { + AllowedOrigins []string + AllowedHeaders []string +}