Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS configuration for Zipkin #3676

Merged
merged 1 commit into from
May 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cmd/collector/app/builder_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ var tlsHTTPFlagsConfig = tlscfg.ServerFlagsConfig{
Prefix: "collector.http",
}

var tlsZipkinFlagsConfig = tlscfg.ServerFlagsConfig{
Prefix: "collector.zipkin",
}

// CollectorOptions holds configuration for collector
type CollectorOptions struct {
// DynQueueSizeMemory determines how much memory to use for the queue
Expand All @@ -66,6 +70,8 @@ type CollectorOptions struct {
TLSGRPC tlscfg.Options
// TLSHTTP configures secure transport for HTTP endpoint to collect spans
TLSHTTP tlscfg.Options
// TLSZipkin configures secure transport for Zipkin endpoint to collect spans
TLSZipkin tlscfg.Options
// CollectorTags is the string representing collector tags to append to each and every span
CollectorTags map[string]string
// CollectorZipkinHTTPHostPort is the host:port address that the Zipkin collector service listens in on for http requests
Expand Down Expand Up @@ -101,6 +107,7 @@ func AddFlags(flags *flag.FlagSet) {

tlsGRPCFlagsConfig.AddFlags(flags)
tlsHTTPFlagsConfig.AddFlags(flags)
tlsZipkinFlagsConfig.AddFlags(flags)
}

// InitFromViper initializes CollectorOptions with properties from viper
Expand All @@ -127,6 +134,11 @@ func (cOpts *CollectorOptions) InitFromViper(v *viper.Viper) (*CollectorOptions,
} else {
return cOpts, fmt.Errorf("failed to parse HTTP TLS options: %w", err)
}
if tlsZipkin, err := tlsZipkinFlagsConfig.InitFromViper(v); err == nil {
cOpts.TLSZipkin = tlsZipkin
} else {
return cOpts, fmt.Errorf("failed to parse Zipkin TLS options: %w", err)
}

return cOpts, nil
}
13 changes: 13 additions & 0 deletions cmd/collector/app/builder_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ func TestCollectorOptionsWithFailedGRPCFlags(t *testing.T) {
assert.Contains(t, err.Error(), "failed to parse gRPC TLS options")
}

func TestCollectorOptionsWithFailedZipkinFlags(t *testing.T) {
c := &CollectorOptions{}
v, command := config.Viperize(AddFlags)
err := command.ParseFlags([]string{
"--collector.zipkin.tls.enabled=false",
"--collector.zipkin.tls.cert=blah", // invalid unless tls.enabled
})
require.NoError(t, err)
_, err = c.InitFromViper(v)
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to parse Zipkin TLS options")
}

func TestCollectorOptionsWithFlags_CheckMaxReceiveMessageLength(t *testing.T) {
c := &CollectorOptions{}
v, command := config.Viperize(AddFlags)
Expand Down
14 changes: 9 additions & 5 deletions cmd/collector/app/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ type Collector struct {
spanHandlers *SpanHandlers

// state, read only
hServer *http.Server
zkServer *http.Server
grpcServer *grpc.Server
tlsGRPCCertWatcherCloser io.Closer
tlsHTTPCertWatcherCloser io.Closer
hServer *http.Server
zkServer *http.Server
grpcServer *grpc.Server
tlsGRPCCertWatcherCloser io.Closer
tlsHTTPCertWatcherCloser io.Closer
tlsZipkinCertWatcherCloser io.Closer
}

// CollectorParams to construct a new Jaeger Collector.
Expand Down Expand Up @@ -125,9 +126,11 @@ func (c *Collector) Start(builderOpts *CollectorOptions) error {

c.tlsGRPCCertWatcherCloser = &builderOpts.TLSGRPC
c.tlsHTTPCertWatcherCloser = &builderOpts.TLSHTTP
c.tlsZipkinCertWatcherCloser = &builderOpts.TLSZipkin
zkServer, err := server.StartZipkinServer(&server.ZipkinServerParams{
HostPort: builderOpts.CollectorZipkinHTTPHostPort,
Handler: c.spanHandlers.ZipkinSpansHandler,
TLSConfig: builderOpts.TLSZipkin,
HealthCheck: c.hCheck,
AllowedHeaders: builderOpts.CollectorZipkinAllowedHeaders,
AllowedOrigins: builderOpts.CollectorZipkinAllowedOrigins,
Expand Down Expand Up @@ -189,6 +192,7 @@ func (c *Collector) Close() error {
// watchers actually never return errors from Close
_ = c.tlsGRPCCertWatcherCloser.Close()
_ = c.tlsHTTPCertWatcherCloser.Close()
_ = c.tlsZipkinCertWatcherCloser.Close()

return nil
}
Expand Down
17 changes: 16 additions & 1 deletion cmd/collector/app/server/zipkin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ import (

"github.com/jaegertracing/jaeger/cmd/collector/app/handler"
"github.com/jaegertracing/jaeger/cmd/collector/app/zipkin"
"github.com/jaegertracing/jaeger/pkg/config/tlscfg"
"github.com/jaegertracing/jaeger/pkg/healthcheck"
"github.com/jaegertracing/jaeger/pkg/httpmetrics"
"github.com/jaegertracing/jaeger/pkg/recoveryhandler"
)

// ZipkinServerParams to construct a new Jaeger Collector Zipkin Server
type ZipkinServerParams struct {
TLSConfig tlscfg.Options
HostPort string
Handler handler.ZipkinSpansHandler
AllowedOrigins string
Expand Down Expand Up @@ -62,6 +64,13 @@ func StartZipkinServer(params *ZipkinServerParams) (*http.Server, error) {
Addr: params.HostPort,
ErrorLog: errorLog,
}
if params.TLSConfig.Enabled {
tlsCfg, err := params.TLSConfig.Config(params.Logger) // This checks if the certificates are correctly provided
if err != nil {
return nil, err
}
server.TLSConfig = tlsCfg
}
serveZipkin(server, listener, params)

return server, nil
Expand All @@ -84,7 +93,13 @@ func serveZipkin(server *http.Server, listener net.Listener, params *ZipkinServe
recoveryHandler := recoveryhandler.NewRecoveryHandler(params.Logger, true)
server.Handler = cors.Handler(httpmetrics.Wrap(recoveryHandler(r), params.MetricsFactory))
go func(listener net.Listener, server *http.Server) {
if err := server.Serve(listener); err != nil {
var err error
if params.TLSConfig.Enabled {
err = server.ServeTLS(listener, "", "")
} else {
err = server.Serve(listener)
}
if err != nil {
if err != http.ErrServerClosed {
params.Logger.Error("Could not launch Zipkin server", zap.Error(err))
}
Expand Down
190 changes: 190 additions & 0 deletions cmd/collector/app/server/zipkin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@
package server

import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/uber/jaeger-lib/metrics/metricstest"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/cmd/collector/app/handler"
"github.com/jaegertracing/jaeger/pkg/config/tlscfg"
"github.com/jaegertracing/jaeger/pkg/healthcheck"
"github.com/jaegertracing/jaeger/ports"
)

// test wrong port number
Expand Down Expand Up @@ -57,3 +63,187 @@ func TestSpanCollectorZipkin(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, response)
}

func TestSpanCollectorZipkinTLS(t *testing.T) {
testCases := []struct {
name string
serverTLS tlscfg.Options
clientTLS tlscfg.Options
expectTLSClientErr bool
expectZipkinClientErr bool
expectServerFail bool
}{
{
name: "should fail with TLS client to untrusted TLS server",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
},
clientTLS: tlscfg.Options{
Enabled: true,
ServerName: "example.com",
},
expectTLSClientErr: true,
expectZipkinClientErr: true,
expectServerFail: false,
},
{
name: "should fail with TLS client to trusted TLS server with incorrect hostname",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "nonEmpty",
},
expectTLSClientErr: true,
expectZipkinClientErr: true,
expectServerFail: false,
},
{
name: "should pass with TLS client to trusted TLS server with correct hostname",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "example.com",
},
expectTLSClientErr: false,
expectZipkinClientErr: false,
expectServerFail: false,
},
{
name: "should fail with TLS client without cert to trusted TLS server requiring cert",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem",
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "example.com",
},
expectTLSClientErr: false,
expectServerFail: false,
expectZipkinClientErr: true,
},
{
name: "should pass with TLS client with cert to trusted TLS server requiring cert",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem",
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "example.com",
CertPath: testCertKeyLocation + "/example-client-cert.pem",
KeyPath: testCertKeyLocation + "/example-client-key.pem",
},
expectTLSClientErr: false,
expectServerFail: false,
expectZipkinClientErr: false,
},
{
name: "should fail with TLS client without cert to trusted TLS server requiring cert from a different CA",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
ClientCAPath: testCertKeyLocation + "/wrong-CA-cert.pem", // NB: wrong CA
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "example.com",
CertPath: testCertKeyLocation + "/example-client-cert.pem",
KeyPath: testCertKeyLocation + "/example-client-key.pem",
},
expectTLSClientErr: false,
expectServerFail: false,
expectZipkinClientErr: true,
},
{
name: "should fail with TLS client with cert to trusted TLS server with incorrect TLS min",
serverTLS: tlscfg.Options{
Enabled: true,
CertPath: testCertKeyLocation + "/example-server-cert.pem",
KeyPath: testCertKeyLocation + "/example-server-key.pem",
ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem",
MinVersion: "1.5",
},
clientTLS: tlscfg.Options{
Enabled: true,
CAPath: testCertKeyLocation + "/example-CA-cert.pem",
ServerName: "example.com",
CertPath: testCertKeyLocation + "/example-client-cert.pem",
KeyPath: testCertKeyLocation + "/example-client-key.pem",
},
expectTLSClientErr: true,
expectServerFail: true,
expectZipkinClientErr: false,
},
}

for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
logger, _ := zap.NewDevelopment()
params := &ZipkinServerParams{
HostPort: fmt.Sprintf(":%d", ports.CollectorZipkin),
Handler: handler.NewZipkinSpanHandler(logger, nil, nil),
MetricsFactory: metricstest.NewFactory(time.Hour),
HealthCheck: healthcheck.New(),
Logger: logger,
TLSConfig: test.serverTLS,
}

server, err := StartZipkinServer(params)

if test.expectServerFail {
require.Error(t, err)
mmorel-35 marked this conversation as resolved.
Show resolved Hide resolved
return
}
require.NoError(t, err)
defer server.Close()

clientTLSCfg, err0 := test.clientTLS.Config(zap.NewNop())
require.NoError(t, err0)
dialer := &net.Dialer{Timeout: 2 * time.Second}
conn, clientError := tls.DialWithDialer(dialer, "tcp", fmt.Sprintf("localhost:%d", ports.CollectorZipkin), clientTLSCfg)

if test.expectTLSClientErr {
require.Error(t, clientError)
} else {
require.NoError(t, clientError)
require.Nil(t, conn.Close())
}

client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: clientTLSCfg,
},
}

response, requestError := client.Post(fmt.Sprintf("https://localhost:%d", ports.CollectorZipkin), "", nil)

if test.expectZipkinClientErr {
require.Error(t, requestError)
} else {
require.NoError(t, requestError)
require.NotNil(t, response)
}
})
}
}
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ require (
github.com/bsm/sarama-cluster v2.1.13+incompatible
github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b
github.com/dgraph-io/badger/v3 v3.2103.2
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.4
github.com/go-openapi/errors v0.20.2
github.com/go-openapi/loads v0.21.1
Expand Down Expand Up @@ -45,6 +44,7 @@ require (
github.com/uber/jaeger-lib v2.4.1+incompatible
github.com/xdg-go/scram v1.1.1
go.opentelemetry.io/collector/model v0.49.0
go.opentelemetry.io/collector/pdata v0.49.0
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.1
go.uber.org/zap v1.21.0
Expand All @@ -55,8 +55,6 @@ require (
gopkg.in/yaml.v2 v2.4.0
)

require go.opentelemetry.io/collector/pdata v0.49.0

require (
github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect
github.com/VividCortex/gohistogram v1.0.0 // indirect
Expand All @@ -67,6 +65,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/eapache/go-resiliency v1.2.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
Expand Down
2 changes: 2 additions & 0 deletions ports/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const (
CollectorHTTP = 14268
// CollectorAdminHTTP is the default admin HTTP port (health check, metrics, etc.)
CollectorAdminHTTP = 14269
// CollectorZipkin is the port for Zipkin server for sending spans
CollectorZipkin = 9411

// QueryGRPC is the default port of GRPC requests for Query trace retrieval
QueryGRPC = 16685
Expand Down