Skip to content

Commit

Permalink
Enable CORS settings on OTLP HTTP endpoint (#4586)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
- Resolves #4459 

## Short description of the changes
- Added a new package corscfg to handle the CORS requests on OTLP HTTP
Endpoint and Zipkin

---------

Signed-off-by: bugslayer-332 <[email protected]>
Co-authored-by: bugslayer-332 <[email protected]>
  • Loading branch information
pmuls99 and severussnape321 authored Jul 19, 2023
1 parent 587fb74 commit 013ec4d
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 20 deletions.
3 changes: 1 addition & 2 deletions cmd/collector/app/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 17 additions & 10 deletions cmd/collector/app/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -273,19 +280,19 @@ 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 {
cOpts.Zipkin.TLS = tlsZipkin
} else {
return cOpts, fmt.Errorf("failed to parse Zipkin TLS options: %w", err)
}
cOpts.Zipkin.CORS = corsZipkinFlags.InitFromViper(v)

return cOpts, nil
}
5 changes: 5 additions & 0 deletions cmd/collector/app/handler/otlp_receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions cmd/collector/app/handler/otlp_receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Expand All @@ -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"})
}
12 changes: 4 additions & 8 deletions cmd/collector/app/server/zipkin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package server
import (
"net"
"net/http"
"strings"
"time"

"github.com/gorilla/mux"
Expand All @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
49 changes: 49 additions & 0 deletions pkg/config/corscfg/flags.go
Original file line number Diff line number Diff line change
@@ -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
}
49 changes: 49 additions & 0 deletions pkg/config/corscfg/flags_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
20 changes: 20 additions & 0 deletions pkg/config/corscfg/options.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 013ec4d

Please sign in to comment.