From 7919684c92716670deac6d07d68842ca0c63ce3c Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Tue, 8 Oct 2019 14:16:54 -0700 Subject: [PATCH] Extract gRPC TLS configuration into a shared package (#1840) * Extract TLS flags and cert loading logic Signed-off-by: Yuri Shkuro * Rename package Signed-off-by: Yuri Shkuro * Refactor grpc Signed-off-by: Yuri Shkuro * Repair tests Signed-off-by: Yuri Shkuro * Refactor gRPC server in collector Signed-off-by: Yuri Shkuro * Add ShowCA option Signed-off-by: Yuri Shkuro * Switch options order Signed-off-by: Yuri Shkuro * Separate client and server TLS options Signed-off-by: Yuri Shkuro * Update usage Signed-off-by: Yuri Shkuro * Rename test, add filepath.Clean Signed-off-by: Yuri Shkuro Signed-off-by: Jonah Back --- cmd/agent/app/reporter/grpc/builder.go | 60 +---- cmd/agent/app/reporter/grpc/builder_test.go | 225 ++++++++---------- .../app/reporter/grpc/collector_proxy.go | 4 - .../app/reporter/grpc/collector_proxy_test.go | 15 -- cmd/agent/app/reporter/grpc/flags.go | 35 ++- cmd/collector/app/builder/builder_flags.go | 31 +-- cmd/collector/app/grpcserver/grpc_server.go | 38 --- cmd/collector/main.go | 7 +- pkg/config/tlscfg/flags.go | 102 ++++++++ pkg/config/tlscfg/flags_test.go | 108 +++++++++ pkg/config/tlscfg/options.go | 97 ++++++++ pkg/config/tlscfg/options_test.go | 142 +++++++++++ pkg/config/tlscfg/testdata/test-cert.pem | 25 ++ pkg/config/tlscfg/testdata/test-key.pem | 28 +++ pkg/config/tlscfg/testdata/testCA-bad.txt | 3 + pkg/config/tlscfg/testdata/testCA.pem | 13 + 16 files changed, 658 insertions(+), 275 deletions(-) create mode 100644 pkg/config/tlscfg/flags.go create mode 100644 pkg/config/tlscfg/flags_test.go create mode 100644 pkg/config/tlscfg/options.go create mode 100644 pkg/config/tlscfg/options_test.go create mode 100644 pkg/config/tlscfg/testdata/test-cert.pem create mode 100644 pkg/config/tlscfg/testdata/test-key.pem create mode 100644 pkg/config/tlscfg/testdata/testCA-bad.txt create mode 100644 pkg/config/tlscfg/testdata/testCA.pem diff --git a/cmd/agent/app/reporter/grpc/builder.go b/cmd/agent/app/reporter/grpc/builder.go index d78424ba886..13644c34b71 100644 --- a/cmd/agent/app/reporter/grpc/builder.go +++ b/cmd/agent/app/reporter/grpc/builder.go @@ -15,20 +15,17 @@ package grpc import ( - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io/ioutil" "strings" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" + "github.com/pkg/errors" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/discovery" "github.com/jaegertracing/jaeger/pkg/discovery/grpcresolver" ) @@ -38,12 +35,8 @@ type ConnBuilder struct { // CollectorHostPorts is list of host:port Jaeger Collectors. CollectorHostPorts []string `yaml:"collectorHostPorts"` - MaxRetry uint - TLS bool - TLSCA string - TLSServerName string - TLSCert string - TLSKey string + MaxRetry uint + TLS tlscfg.Options DiscoveryMinPeers int Notifier discovery.Notifier @@ -59,49 +52,14 @@ func NewConnBuilder() *ConnBuilder { func (b *ConnBuilder) CreateConnection(logger *zap.Logger) (*grpc.ClientConn, error) { var dialOptions []grpc.DialOption var dialTarget string - if b.TLS { // user requested a secure connection + if b.TLS.Enabled { // user requested a secure connection logger.Info("Agent requested secure grpc connection to collector(s)") - var err error - var certPool *x509.CertPool - if len(b.TLSCA) == 0 { // no truststore given, use SystemCertPool - certPool, err = systemCertPool() - if err != nil { - return nil, err - } - } else { // setup user specified truststore - caPEM, err := ioutil.ReadFile(b.TLSCA) - if err != nil { - return nil, fmt.Errorf("reading client CA failed, %v", err) - } - - certPool = x509.NewCertPool() - if !certPool.AppendCertsFromPEM(caPEM) { - return nil, fmt.Errorf("building client CA failed, %v", err) - } - } - - tlsCfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - RootCAs: certPool, - ServerName: b.TLSServerName, - } - - if (b.TLSKey == "" || b.TLSCert == "") && - (b.TLSKey != "" || b.TLSCert != "") { - return nil, fmt.Errorf("for client auth, both client certificate and key must be supplied") - } - - if b.TLSKey != "" && b.TLSCert != "" { - tlsCert, err := tls.LoadX509KeyPair(b.TLSCert, b.TLSKey) - if err != nil { - return nil, fmt.Errorf("could not load server TLS cert and key, %v", err) - } - - logger.Info("client TLS authentication enabled") - tlsCfg.Certificates = []tls.Certificate{tlsCert} + tlsConf, err := b.TLS.Config() + if err != nil { + return nil, errors.Wrap(err, "failed to load TLS config") } - creds := credentials.NewTLS(tlsCfg) + creds := credentials.NewTLS(tlsConf) dialOptions = append(dialOptions, grpc.WithTransportCredentials(creds)) } else { // insecure connection logger.Info("Agent requested insecure grpc connection to collector(s)") diff --git a/cmd/agent/app/reporter/grpc/builder_test.go b/cmd/agent/app/reporter/grpc/builder_test.go index b7a811b8c1f..41be83dd72b 100644 --- a/cmd/agent/app/reporter/grpc/builder_test.go +++ b/cmd/agent/app/reporter/grpc/builder_test.go @@ -14,7 +14,6 @@ package grpc import ( - "errors" "net" "strings" "testing" @@ -29,7 +28,7 @@ import ( "google.golang.org/grpc/credentials" yaml "gopkg.in/yaml.v2" - "github.com/jaegertracing/jaeger/cmd/collector/app/grpcserver" + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/pkg/discovery" "github.com/jaegertracing/jaeger/proto-gen/api_v2" "github.com/jaegertracing/jaeger/thrift-gen/jaeger" @@ -70,7 +69,7 @@ func TestBuilderWithCollectors(t *testing.T) { checkSuffixOnly bool notifier discovery.Notifier discoverer discovery.Discoverer - err error + expectedError string }{ { target: "///round_robin", @@ -103,7 +102,7 @@ func TestBuilderWithCollectors(t *testing.T) { checkSuffixOnly: false, notifier: nil, discoverer: nil, - err: errors.New("at least one collector hostPort address is required when resolver is not available"), + expectedError: "at least one collector hostPort address is required when resolver is not available", }, } @@ -116,16 +115,18 @@ func TestBuilderWithCollectors(t *testing.T) { cfg.Discoverer = test.discoverer conn, err := cfg.CreateConnection(zap.NewNop()) - if err != nil { - assert.Equal(t, test.err, err) - } else { - assert.NotNil(t, conn) + if test.expectedError == "" { + require.NoError(t, err) + require.NotNil(t, conn) if test.checkSuffixOnly { assert.True(t, strings.HasSuffix(conn.Target(), test.target)) } else { assert.True(t, conn.Target() == test.target) } + } else { + require.Error(t, err) + assert.Contains(t, err.Error(), test.expectedError) } }) } @@ -138,57 +139,35 @@ func TestProxyBuilder(t *testing.T) { expectError bool }{ { - name: "with insecure grpc connection", - grpcBuilder: &ConnBuilder{CollectorHostPorts: []string{"localhost:0000"}}, - expectError: false, - }, - { - name: "with secure grpc connection", - grpcBuilder: &ConnBuilder{CollectorHostPorts: []string{"localhost:0000"}, TLS: true}, - expectError: false, - }, - { - name: "with secure grpc connection and own CA", - grpcBuilder: &ConnBuilder{CollectorHostPorts: []string{"localhost:0000"}, TLS: true, TLSCA: "testdata/testCA.pem"}, - expectError: false, - }, - { - name: "with secure grpc connection and a CA file which does not exist", - grpcBuilder: &ConnBuilder{CollectorHostPorts: []string{"localhost:0000"}, TLS: true, TLSCA: "/not/valid"}, - expectError: true, - }, - { - name: "with secure grpc connection and valid TLS Client settings", + name: "should pass with insecure grpc connection", grpcBuilder: &ConnBuilder{ CollectorHostPorts: []string{"localhost:0000"}, - TLS: true, - TLSCA: "testdata/testCA.pem", - TLSCert: "testdata/client.jaeger.io-client.pem", - TLSKey: "testdata/client.jaeger.io-client-key.pem", }, expectError: false, }, { - name: "with secure grpc connection and valid TLS Client settings", + name: "should fail with secure grpc connection and a CA file which does not exist", grpcBuilder: &ConnBuilder{ CollectorHostPorts: []string{"localhost:0000"}, - TLS: true, - TLSCA: "testdata/testCA.pem", - TLSCert: "testdata/client.jaeger.io-client.pem", - TLSKey: "", + TLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/not/valid", + }, }, expectError: true, }, { - name: "with secure grpc connection and valid TLS Client key setting", + name: "should pass with secure grpc connection and valid TLS Client settings", grpcBuilder: &ConnBuilder{ CollectorHostPorts: []string{"localhost:0000"}, - TLS: true, - TLSCA: "testdata/testCA.pem", - TLSCert: "testdata/client.jaeger.io-client.pem", - TLSKey: "/not/valid", + TLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/testCA.pem", + CertPath: "testdata/client.jaeger.io-client.pem", + KeyPath: "testdata/client.jaeger.io-client-key.pem", + }, }, - expectError: true, + expectError: false, }, } @@ -213,101 +192,106 @@ func TestProxyBuilder(t *testing.T) { func TestProxyClientTLS(t *testing.T) { tests := []struct { - name string - grpcBuilder *ConnBuilder - serverTLS bool - serverTLSCert string - serverTLSKey string - serverTLSClientCA string - expectError bool + name string + clientTLS tlscfg.Options + serverTLS tlscfg.Options + expectError bool }{ { - name: "insecure grpc connection", - serverTLS: false, - grpcBuilder: &ConnBuilder{}, + name: "should pass with insecure grpc connection", expectError: false, }, { - name: "TLS client to non-TLS server should fail", - grpcBuilder: &ConnBuilder{ - TLS: true, - }, + name: "should fail with TLS client to non-TLS server", + clientTLS: tlscfg.Options{Enabled: true}, expectError: true, }, { - name: "TLS client to untrusted TLS server should fail", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSServerName: "server.jaeger.io", + name: "should fail with TLS client to untrusted TLS server", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + ServerName: "server.jaeger.io", }, expectError: true, }, { - name: "TLS client to trusted TLS server with incorrect hostname should fail", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSCA: "testdata/rootCA.pem", + name: "should fail with TLS client to trusted TLS server with incorrect hostname", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/rootCA.pem", }, expectError: true, }, { - name: "TLS client to trusted TLS server with correct hostname", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSCA: "testdata/rootCA.pem", - TLSServerName: "server.jaeger.io", + name: "should pass with TLS client to trusted TLS server with correct hostname", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/rootCA.pem", + ServerName: "server.jaeger.io", }, expectError: false, }, { - name: "TLS client without cert to trusted TLS server requiring cert should fail", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - serverTLSClientCA: "testdata/rootCA.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSCA: "testdata/rootCA.pem", - TLSServerName: "server.jaeger.io", + name: "should fail with TLS client without cert to trusted TLS server requiring cert", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + ClientCAPath: "testdata/rootCA.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/rootCA.pem", + ServerName: "server.jaeger.io", }, expectError: true, }, { - name: "TLS client without cert to trusted TLS server requiring cert from a differe CA should fail", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - serverTLSClientCA: "testdata/testCA.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSCA: "testdata/rootCA.pem", - TLSServerName: "server.jaeger.io", - TLSCert: "testdata/client.jaeger.io-client.pem", - TLSKey: "testdata/client.jaeger.io-client-key.pem", + name: "should fail with TLS client without cert to trusted TLS server requiring cert from a different CA", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + ClientCAPath: "testdata/testCA.pem", // NB: wrong CA + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/rootCA.pem", + ServerName: "server.jaeger.io", + CertPath: "testdata/client.jaeger.io-client.pem", + KeyPath: "testdata/client.jaeger.io-client-key.pem", }, expectError: true, }, { - name: "TLS client without cert to trusted TLS server requiring cert should fail", - serverTLS: true, - serverTLSCert: "testdata/server.jaeger.io.pem", - serverTLSKey: "testdata/server.jaeger.io-key.pem", - serverTLSClientCA: "testdata/rootCA.pem", - grpcBuilder: &ConnBuilder{ - TLS: true, - TLSCA: "testdata/rootCA.pem", - TLSServerName: "server.jaeger.io", - TLSCert: "testdata/client.jaeger.io-client.pem", - TLSKey: "testdata/client.jaeger.io-client-key.pem", + name: "should pass with TLS client with cert to trusted TLS server requiring cert", + serverTLS: tlscfg.Options{ + Enabled: true, + CertPath: "testdata/server.jaeger.io.pem", + KeyPath: "testdata/server.jaeger.io-key.pem", + ClientCAPath: "testdata/rootCA.pem", + }, + clientTLS: tlscfg.Options{ + Enabled: true, + CAPath: "testdata/rootCA.pem", + ServerName: "server.jaeger.io", + CertPath: "testdata/client.jaeger.io-client.pem", + KeyPath: "testdata/client.jaeger.io-client-key.pem", }, expectError: false, }, @@ -315,15 +299,9 @@ func TestProxyClientTLS(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { var opts []grpc.ServerOption - if test.serverTLS { - tlsCfg, err := grpcserver.TLSConfig( - test.serverTLSCert, - test.serverTLSKey, - test.serverTLSClientCA) - if err != nil { - require.NoError(t, err) - } - + if test.serverTLS.Enabled { + tlsCfg, err := test.serverTLS.Config() + require.NoError(t, err) opts = []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} } @@ -336,9 +314,12 @@ func TestProxyClientTLS(t *testing.T) { mFactory := metricstest.NewFactory(time.Microsecond) _, port, _ := net.SplitHostPort(addr.String()) - test.grpcBuilder.CollectorHostPorts = []string{net.JoinHostPort("localhost", port)} + grpcBuilder := &ConnBuilder{ + CollectorHostPorts: []string{net.JoinHostPort("localhost", port)}, + TLS: test.clientTLS, + } proxy, err := NewCollectorProxy( - test.grpcBuilder, + grpcBuilder, nil, mFactory, zap.NewNop()) diff --git a/cmd/agent/app/reporter/grpc/collector_proxy.go b/cmd/agent/app/reporter/grpc/collector_proxy.go index 9a91ee722c2..058dd4b0f1a 100644 --- a/cmd/agent/app/reporter/grpc/collector_proxy.go +++ b/cmd/agent/app/reporter/grpc/collector_proxy.go @@ -15,8 +15,6 @@ package grpc import ( - "crypto/x509" - "github.com/uber/jaeger-lib/metrics" "go.uber.org/zap" "google.golang.org/grpc" @@ -34,8 +32,6 @@ type ProxyBuilder struct { conn *grpc.ClientConn } -var systemCertPool = x509.SystemCertPool // to allow overriding in unit test - // NewCollectorProxy creates ProxyBuilder func NewCollectorProxy(builder *ConnBuilder, agentTags map[string]string, mFactory metrics.Factory, logger *zap.Logger) (*ProxyBuilder, error) { conn, err := builder.CreateConnection(logger) diff --git a/cmd/agent/app/reporter/grpc/collector_proxy_test.go b/cmd/agent/app/reporter/grpc/collector_proxy_test.go index 09b088ac910..f80febbd593 100644 --- a/cmd/agent/app/reporter/grpc/collector_proxy_test.go +++ b/cmd/agent/app/reporter/grpc/collector_proxy_test.go @@ -15,8 +15,6 @@ package grpc import ( - "crypto/x509" - "errors" "net" "testing" "time" @@ -31,19 +29,6 @@ import ( "github.com/jaegertracing/jaeger/thrift-gen/jaeger" ) -// This test is only for coverage. -func TestSystemCertPoolError(t *testing.T) { - fakeErr := errors.New("fake error") - systemCertPool = func() (*x509.CertPool, error) { - return nil, fakeErr - } - _, err := NewCollectorProxy(&ConnBuilder{ - CollectorHostPorts: []string{"foo", "bar"}, - TLS: true, - }, nil, nil, zap.NewNop()) - assert.Equal(t, fakeErr, err) -} - func TestMultipleCollectors(t *testing.T) { spanHandler1 := &mockSpanHandler{} s1, addr1 := initializeGRPCTestServer(t, func(s *grpc.Server) { diff --git a/cmd/agent/app/reporter/grpc/flags.go b/cmd/agent/app/reporter/grpc/flags.go index 127fe50d6a8..ce6a2571d79 100644 --- a/cmd/agent/app/reporter/grpc/flags.go +++ b/cmd/agent/app/reporter/grpc/flags.go @@ -19,31 +19,30 @@ import ( "strings" "github.com/spf13/viper" + + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" ) const ( - gRPCPrefix = "reporter.grpc." - collectorHostPort = gRPCPrefix + "host-port" - retry = gRPCPrefix + "retry.max" - defaultMaxRetry = 3 - collectorTLS = gRPCPrefix + "tls" - collectorTLSCA = gRPCPrefix + "tls.ca" - agentCert = gRPCPrefix + "tls.cert" - agentKey = gRPCPrefix + "tls.key" - collectorTLSServerName = gRPCPrefix + "tls.server-name" - discoveryMinPeers = gRPCPrefix + "discovery.min-peers" + gRPCPrefix = "reporter.grpc." + collectorHostPort = gRPCPrefix + "host-port" + retry = gRPCPrefix + "retry.max" + defaultMaxRetry = 3 + discoveryMinPeers = gRPCPrefix + "discovery.min-peers" ) +var tlsFlagsConfig = tlscfg.ClientFlagsConfig{ + Prefix: gRPCPrefix, + ShowEnabled: true, + ShowServerName: true, +} + // AddFlags adds flags for Options. func AddFlags(flags *flag.FlagSet) { flags.String(collectorHostPort, "", "Comma-separated string representing host:port of a static list of collectors to connect to directly") flags.Uint(retry, defaultMaxRetry, "Sets the maximum number of retries for a call") - flags.Bool(collectorTLS, false, "Use TLS when talking to the remote collector") - flags.String(collectorTLSCA, "", "Path to a TLS CA file used to verify the remote server. (default use the systems truststore)") - flags.String(collectorTLSServerName, "", "Override the TLS server name we expected in the remote certificate") - flags.String(agentCert, "", "Path to a TLS client certificate file, used to identify this agent to the collector") - flags.String(agentKey, "", "Path to the TLS client key for the client certificate") flags.Int(discoveryMinPeers, 3, "Max number of collectors to which the agent will try to connect at any given time") + tlsFlagsConfig.AddFlags(flags) } // InitFromViper initializes Options with properties retrieved from Viper. @@ -53,11 +52,7 @@ func (b *ConnBuilder) InitFromViper(v *viper.Viper) *ConnBuilder { b.CollectorHostPorts = strings.Split(hostPorts, ",") } b.MaxRetry = uint(v.GetInt(retry)) - b.TLS = v.GetBool(collectorTLS) - b.TLSCA = v.GetString(collectorTLSCA) - b.TLSServerName = v.GetString(collectorTLSServerName) - b.TLSCert = v.GetString(agentCert) - b.TLSKey = v.GetString(agentKey) + b.TLS = tlsFlagsConfig.InitFromViper(v) b.DiscoveryMinPeers = v.GetInt(discoveryMinPeers) return b } diff --git a/cmd/collector/app/builder/builder_flags.go b/cmd/collector/app/builder/builder_flags.go index 9787c3b04ca..04c80a4d2bc 100644 --- a/cmd/collector/app/builder/builder_flags.go +++ b/cmd/collector/app/builder/builder_flags.go @@ -21,6 +21,7 @@ import ( "github.com/spf13/viper" "github.com/jaegertracing/jaeger/cmd/collector/app" + "github.com/jaegertracing/jaeger/pkg/config/tlscfg" "github.com/jaegertracing/jaeger/ports" ) @@ -30,15 +31,17 @@ const ( collectorPort = "collector.port" collectorHTTPPort = "collector.http-port" collectorGRPCPort = "collector.grpc-port" - collectorGRPCTLS = "collector.grpc.tls" - collectorGRPCCert = "collector.grpc.tls.cert" - collectorGRPCKey = "collector.grpc.tls.key" - collectorGRPCClientCA = "collector.grpc.tls.client.ca" collectorZipkinHTTPort = "collector.zipkin.http-port" collectorZipkinAllowedOrigins = "collector.zipkin.allowed-origins" collectorZipkinAllowedHeaders = "collector.zipkin.allowed-headers" ) +var tlsFlagsConfig = tlscfg.ServerFlagsConfig{ + Prefix: "collector.grpc.", + ShowEnabled: true, + ShowClientCA: true, +} + // CollectorOptions holds configuration for collector type CollectorOptions struct { // QueueSize is the size of collector's queue @@ -51,14 +54,8 @@ type CollectorOptions struct { CollectorHTTPPort int // CollectorGRPCPort is the port that the collector service listens in on for gRPC requests CollectorGRPCPort int - // CollectorGRPCTLS defines if the server is setup with TLS - CollectorGRPCTLS bool - // CollectorGRPCCert is the path to a TLS certificate file for the server - CollectorGRPCCert string - // CollectorGRPCClientCA is the path to a TLS certificate file for authenticating clients - CollectorGRPCClientCA string - // CollectorGRPCKey is the path to a TLS key file for the server - CollectorGRPCKey string + // TLS configures secure transport + TLS tlscfg.Options // CollectorZipkinHTTPPort is the port that the Zipkin collector service listens in on for http requests CollectorZipkinHTTPPort int // CollectorZipkinAllowedOrigins is a list of origins a cross-domain request to the Zipkin collector service can be executed from @@ -75,12 +72,9 @@ func AddFlags(flags *flag.FlagSet) { flags.Int(collectorHTTPPort, ports.CollectorHTTP, "The HTTP port for the collector service") flags.Int(collectorGRPCPort, ports.CollectorGRPC, "The gRPC port for the collector service") flags.Int(collectorZipkinHTTPort, 0, "The HTTP port for the Zipkin collector service e.g. 9411") - flags.Bool(collectorGRPCTLS, false, "Enable TLS for the gRPC collector port") - flags.String(collectorGRPCCert, "", "Path to TLS certificate for the gRPC collector TLS service") - flags.String(collectorGRPCKey, "", "Path to TLS key for the gRPC collector TLS cert") - flags.String(collectorGRPCClientCA, "", "Path to a TLS CA to verify certificates presented by clients (if unset, all clients are permitted)") flags.String(collectorZipkinAllowedOrigins, "*", "Comma separated list of allowed origins for the Zipkin collector service, default accepts all") flags.String(collectorZipkinAllowedHeaders, "content-type", "Comma separated list of allowed headers for the Zipkin collector service, default content-type") + tlsFlagsConfig.AddFlags(flags) } // InitFromViper initializes CollectorOptions with properties from viper @@ -90,12 +84,9 @@ func (cOpts *CollectorOptions) InitFromViper(v *viper.Viper) *CollectorOptions { cOpts.CollectorPort = v.GetInt(collectorPort) cOpts.CollectorHTTPPort = v.GetInt(collectorHTTPPort) cOpts.CollectorGRPCPort = v.GetInt(collectorGRPCPort) - cOpts.CollectorGRPCTLS = v.GetBool(collectorGRPCTLS) - cOpts.CollectorGRPCCert = v.GetString(collectorGRPCCert) - cOpts.CollectorGRPCClientCA = v.GetString(collectorGRPCClientCA) - cOpts.CollectorGRPCKey = v.GetString(collectorGRPCKey) cOpts.CollectorZipkinHTTPPort = v.GetInt(collectorZipkinHTTPort) cOpts.CollectorZipkinAllowedOrigins = v.GetString(collectorZipkinAllowedOrigins) cOpts.CollectorZipkinAllowedHeaders = v.GetString(collectorZipkinAllowedHeaders) + cOpts.TLS = tlsFlagsConfig.InitFromViper(v) return cOpts } diff --git a/cmd/collector/app/grpcserver/grpc_server.go b/cmd/collector/app/grpcserver/grpc_server.go index 5150c1e4e84..745af9f49bd 100644 --- a/cmd/collector/app/grpcserver/grpc_server.go +++ b/cmd/collector/app/grpcserver/grpc_server.go @@ -15,13 +15,9 @@ package grpcserver import ( - "crypto/tls" - "crypto/x509" - "fmt" "io/ioutil" "net" "os" - "path/filepath" "strconv" "github.com/pkg/errors" @@ -35,40 +31,6 @@ import ( "github.com/jaegertracing/jaeger/proto-gen/api_v2" ) -// TLSConfig creates a *tls.Config from the user specified file paths. -func TLSConfig(cert, key, clientCA string) (*tls.Config, error) { - if cert == "" || key == "" { - return nil, fmt.Errorf("you requested TLS but configuration does not include a path to cert and/or key") - } - - tlsCfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - } - - tlsCert, err := tls.LoadX509KeyPair(filepath.Clean(cert), filepath.Clean(key)) - if err != nil { - return nil, fmt.Errorf("could not load server TLS cert and key, %v", err) - } - - tlsCfg.Certificates = []tls.Certificate{tlsCert} - - if clientCA != "" { - caPEM, err := ioutil.ReadFile(filepath.Clean(clientCA)) - if err != nil { - return nil, fmt.Errorf("load TLS client CA, %v", err) - } - - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(caPEM) { - return nil, fmt.Errorf("building TLS client CA, %v", err) - } - tlsCfg.ClientCAs = certPool - tlsCfg.ClientAuth = tls.RequireAndVerifyClientCert - } - - return tlsCfg, nil -} - // StartGRPCCollector configures and starts gRPC endpoints exposed by collector. func StartGRPCCollector( port int, diff --git a/cmd/collector/main.go b/cmd/collector/main.go index 0e7c818e7c8..d2f7e77e522 100644 --- a/cmd/collector/main.go +++ b/cmd/collector/main.go @@ -193,11 +193,8 @@ func startGRPCServer( ) (*grpc.Server, error) { var server *grpc.Server - if opts.CollectorGRPCTLS { // user requested a server with TLS, setup creds - tlsCfg, err := grpcserver.TLSConfig( - opts.CollectorGRPCCert, - opts.CollectorGRPCKey, - opts.CollectorGRPCClientCA) + if opts.TLS.Enabled { // user requested a server with TLS, setup creds + tlsCfg, err := opts.TLS.Config() if err != nil { return nil, err } diff --git a/pkg/config/tlscfg/flags.go b/pkg/config/tlscfg/flags.go new file mode 100644 index 00000000000..5de93a08f6a --- /dev/null +++ b/pkg/config/tlscfg/flags.go @@ -0,0 +1,102 @@ +// Copyright (c) 2019 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 tlscfg + +import ( + "flag" + + "github.com/spf13/viper" +) + +const ( + tlsEnabled = "tls" + tlsCA = "tls.ca" + tlsCert = "tls.cert" + tlsKey = "tls.key" + tlsServerName = "tls.server-name" + tlsClientCA = "tls.client-ca" + tlsClientCAOld = "tls.client.ca" +) + +// ClientFlagsConfig describes which CLI flags for TLS client should be generated. +type ClientFlagsConfig struct { + Prefix string + ShowEnabled bool + ShowServerName bool +} + +// ServerFlagsConfig describes which CLI flags for TLS server should be generated. +type ServerFlagsConfig struct { + Prefix string + ShowEnabled bool + ShowClientCA bool +} + +// AddFlags adds flags for TLS to the FlagSet. +func (c ClientFlagsConfig) AddFlags(flags *flag.FlagSet) { + if c.ShowEnabled { + flags.Bool(c.Prefix+tlsEnabled, false, "Enable TLS when talking to the remote server(s)") + } + flags.String(c.Prefix+tlsCA, "", "Path to a TLS CA (Certification Authority) file used to verify the remote server(s) (by default will use the system truststore)") + flags.String(c.Prefix+tlsCert, "", "Path to a TLS Certificate file, used to identify this process to the remote server(s)") + flags.String(c.Prefix+tlsKey, "", "Path to a TLS Private Key file, used to identify this process to the remote server(s)") + if c.ShowServerName { + flags.String(c.Prefix+tlsServerName, "", "Override the TLS server name we expect in the certificate of the remove server(s)") + } +} + +// AddFlags adds flags for TLS to the FlagSet. +func (c ServerFlagsConfig) AddFlags(flags *flag.FlagSet) { + if c.ShowEnabled { + flags.Bool(c.Prefix+tlsEnabled, false, "Enable TLS on the server") + } + flags.String(c.Prefix+tlsCert, "", "Path to a TLS Certificate file, used to identify this server to clients") + flags.String(c.Prefix+tlsKey, "", "Path to a TLS Private Key file, used to identify this server to clients") + flags.String(c.Prefix+tlsClientCA, "", "Path to a TLS CA (Certification Authority) file used to verify certificates presented by clients (if unset, all clients are permitted)") + flags.String(c.Prefix+tlsClientCAOld, "", "(deprecated) see --"+c.Prefix+tlsClientCA) +} + +// InitFromViper creates tls.Config populated with values retrieved from Viper. +func (c ClientFlagsConfig) InitFromViper(v *viper.Viper) Options { + var p Options + if c.ShowEnabled { + p.Enabled = v.GetBool(c.Prefix + tlsEnabled) + } + p.CAPath = v.GetString(c.Prefix + tlsCA) + p.CertPath = v.GetString(c.Prefix + tlsCert) + p.KeyPath = v.GetString(c.Prefix + tlsKey) + if c.ShowServerName { + p.ServerName = v.GetString(c.Prefix + tlsServerName) + } + return p +} + +// InitFromViper creates tls.Config populated with values retrieved from Viper. +func (c ServerFlagsConfig) InitFromViper(v *viper.Viper) Options { + var p Options + if c.ShowEnabled { + p.Enabled = v.GetBool(c.Prefix + tlsEnabled) + } + p.CertPath = v.GetString(c.Prefix + tlsCert) + p.KeyPath = v.GetString(c.Prefix + tlsKey) + if c.ShowClientCA { + p.ClientCAPath = v.GetString(c.Prefix + tlsClientCA) + if s := v.GetString(c.Prefix + tlsClientCAOld); s != "" { + // using legacy flag + p.ClientCAPath = s + } + } + return p +} diff --git a/pkg/config/tlscfg/flags_test.go b/pkg/config/tlscfg/flags_test.go new file mode 100644 index 00000000000..001dfddf83c --- /dev/null +++ b/pkg/config/tlscfg/flags_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2019 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 tlscfg + +import ( + "flag" + "testing" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClientFlags(t *testing.T) { + cmdLine := []string{ + "--prefix.tls=true", + "--prefix.tls.ca=ca-file", + "--prefix.tls.cert=cert-file", + "--prefix.tls.key=key-file", + "--prefix.tls.server-name=HAL1", + } + + v := viper.New() + command := cobra.Command{} + flagSet := &flag.FlagSet{} + flagCfg := ClientFlagsConfig{ + Prefix: "prefix.", + ShowEnabled: true, + ShowServerName: true, + } + flagCfg.AddFlags(flagSet) + command.PersistentFlags().AddGoFlagSet(flagSet) + v.BindPFlags(command.PersistentFlags()) + + err := command.ParseFlags(cmdLine) + require.NoError(t, err) + tlsOpts := flagCfg.InitFromViper(v) + assert.Equal(t, Options{ + Enabled: true, + CAPath: "ca-file", + CertPath: "cert-file", + KeyPath: "key-file", + ServerName: "HAL1", + }, tlsOpts) +} + +func TestServerFlags(t *testing.T) { + cmdLine := []string{ + "##placeholder##", // replaced in each test below + "--prefix.tls=true", + "--prefix.tls.cert=cert-file", + "--prefix.tls.key=key-file", + } + + tests := []struct { + option string + file string + }{ + { + option: "--prefix.tls.client-ca=client-ca-file", + file: "client-ca-file", + }, + { + option: "--prefix.tls.client.ca=legacy-client-ca-file", + file: "legacy-client-ca-file", + }, + } + + for _, test := range tests { + t.Run(test.file, func(t *testing.T) { + v := viper.New() + command := cobra.Command{} + flagSet := &flag.FlagSet{} + flagCfg := ServerFlagsConfig{ + Prefix: "prefix.", + ShowEnabled: true, + ShowClientCA: true, + } + flagCfg.AddFlags(flagSet) + command.PersistentFlags().AddGoFlagSet(flagSet) + v.BindPFlags(command.PersistentFlags()) + + cmdLine[0] = test.option + err := command.ParseFlags(cmdLine) + require.NoError(t, err) + tlsOpts := flagCfg.InitFromViper(v) + assert.Equal(t, Options{ + Enabled: true, + CertPath: "cert-file", + KeyPath: "key-file", + ClientCAPath: test.file, + }, tlsOpts) + }) + } +} diff --git a/pkg/config/tlscfg/options.go b/pkg/config/tlscfg/options.go new file mode 100644 index 00000000000..b4cb48ca8f4 --- /dev/null +++ b/pkg/config/tlscfg/options.go @@ -0,0 +1,97 @@ +// Copyright (c) 2019 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 tlscfg + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/pkg/errors" +) + +// Options describes the configuration properties for TLS Connections. +type Options struct { + Enabled bool + CAPath string + CertPath string + KeyPath string + ServerName string // only for client-side TLS config + ClientCAPath string // only for server-side TLS config for client auth +} + +var systemCertPool = x509.SystemCertPool // to allow overriding in unit test + +// Config loads TLS certificates and returns a TLS Config. +func (p Options) Config() (*tls.Config, error) { + certPool, err := p.loadCertPool() + if err != nil { + return nil, errors.Wrap(err, "failed to load CA CertPool") + } + + tlsCfg := &tls.Config{ + RootCAs: certPool, + ServerName: p.ServerName, + } + + if (p.CertPath == "" && p.KeyPath != "") || (p.CertPath != "" && p.KeyPath == "") { + return nil, fmt.Errorf("for client auth via TLS, either both client certificate and key must be supplied, or neither") + } + if p.CertPath != "" && p.KeyPath != "" { + tlsCert, err := tls.LoadX509KeyPair(filepath.Clean(p.CertPath), filepath.Clean(p.KeyPath)) + if err != nil { + return nil, errors.Wrap(err, "failed to load server TLS cert and key") + } + tlsCfg.Certificates = append(tlsCfg.Certificates, tlsCert) + } + + if p.ClientCAPath != "" { + certPool, err := p.loadCert(p.ClientCAPath) + if err != nil { + return nil, err + } + tlsCfg.ClientCAs = certPool + tlsCfg.ClientAuth = tls.RequireAndVerifyClientCert + } + + return tlsCfg, nil +} + +func (p Options) loadCertPool() (*x509.CertPool, error) { + if len(p.CAPath) == 0 { // no truststore given, use SystemCertPool + certPool, err := systemCertPool() + if err != nil { + return nil, errors.Wrap(err, "failed to load SystemCertPool") + } + return certPool, nil + } + // setup user specified truststore + return p.loadCert(p.CAPath) +} + +func (p Options) loadCert(caPath string) (*x509.CertPool, error) { + caPEM, err := ioutil.ReadFile(filepath.Clean(caPath)) + if err != nil { + return nil, errors.Wrapf(err, "failed to load CA %s", caPath) + } + + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(caPEM) { + return nil, fmt.Errorf("failed to parse CA %s", caPath) + } + return certPool, nil +} diff --git a/pkg/config/tlscfg/options_test.go b/pkg/config/tlscfg/options_test.go new file mode 100644 index 00000000000..0ba261b3949 --- /dev/null +++ b/pkg/config/tlscfg/options_test.go @@ -0,0 +1,142 @@ +// Copyright (c) 2019 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 tlscfg + +import ( + "crypto/x509" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOptionsToConfig(t *testing.T) { + tests := []struct { + name string + options Options + fakeSysPool bool + expectError string + }{ + { + name: "should load system CA", + options: Options{CAPath: ""}, + }, + { + name: "should fail with fake system CA", + fakeSysPool: true, + options: Options{CAPath: ""}, + expectError: "fake system pool", + }, + { + name: "should load custom CA", + options: Options{CAPath: "testdata/testCA.pem"}, + }, + { + name: "should fail with invalid CA file path", + options: Options{CAPath: "testdata/not/valid"}, + expectError: "failed to load CA", + }, + { + name: "should fail with invalid CA file content", + options: Options{CAPath: "testdata/testCA-bad.txt"}, + expectError: "failed to parse CA", + }, + { + name: "should load valid TLS Client settings", + options: Options{ + CAPath: "testdata/testCA.pem", + CertPath: "testdata/test-cert.pem", + KeyPath: "testdata/test-key.pem", + }, + }, + { + name: "should fail with missing TLS Client Key", + options: Options{ + CAPath: "testdata/testCA.pem", + CertPath: "testdata/test-cert.pem", + }, + expectError: "both client certificate and key must be supplied", + }, + { + name: "should fail with invalid TLS Client Key", + options: Options{ + CAPath: "testdata/testCA.pem", + CertPath: "testdata/test-cert.pem", + KeyPath: "testdata/not/valid", + }, + expectError: "failed to load server TLS cert and key", + }, + { + name: "should fail with missing TLS Client Cert", + options: Options{ + CAPath: "testdata/testCA.pem", + KeyPath: "testdata/test-key.pem", + }, + expectError: "both client certificate and key must be supplied", + }, + { + name: "should fail with invalid TLS Client Cert", + options: Options{ + CAPath: "testdata/testCA.pem", + CertPath: "testdata/not/valid", + KeyPath: "testdata/test-key.pem", + }, + expectError: "failed to load server TLS cert and key", + }, + { + name: "should fail with invalid TLS Client CA", + options: Options{ + ClientCAPath: "testdata/not/valid", + }, + expectError: "failed to load CA", + }, + { + name: "should fail with invalid Client CA pool", + options: Options{ + ClientCAPath: "testdata/testCA-bad.txt", + }, + expectError: "failed to parse CA", + }, + { + name: "should pass with valid Client CA pool", + options: Options{ + ClientCAPath: "testdata/testCA.pem", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.fakeSysPool { + saveSystemCertPool := systemCertPool + systemCertPool = func() (*x509.CertPool, error) { + return nil, fmt.Errorf("fake system pool") + } + defer func() { + systemCertPool = saveSystemCertPool + }() + } + cfg, err := test.options.Config() + if test.expectError != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), test.expectError) + } else { + require.NoError(t, err) + assert.NotNil(t, cfg) + } + }) + } +} diff --git a/pkg/config/tlscfg/testdata/test-cert.pem b/pkg/config/tlscfg/testdata/test-cert.pem new file mode 100644 index 00000000000..627628866fa --- /dev/null +++ b/pkg/config/tlscfg/testdata/test-cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHjCCAoagAwIBAgIQTUSsPHdq9Uhu2bMD29ThkDANBgkqhkiG9w0BAQsFADBd +MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGTAXBgNVBAsMEHRyaXN0 +YW5AdHJpc3RhbnMxIDAeBgNVBAMMF21rY2VydCB0cmlzdGFuQHRyaXN0YW5zMB4X +DTE5MDYxMTA3NTA0NloXDTI5MDYxMTA3NTA0NlowRDEnMCUGA1UEChMebWtjZXJ0 +IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMRkwFwYDVQQLDBB0cmlzdGFuQHRyaXN0 +YW5zMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6zf2JlSdKTdiYDZV +i1yPnP65/CgqlxMflTP9N2P1W7F1SbvQgiGiSfUyc7NicffLGqqDbK3Q4hvANkRC +wOYc+nXZLL6IAxsZ/QBfud3GG2XhuETT2p84Wlqo55I3wFF+Efb89FRp+IiAy2gj +c275hmie6zDRYNJticmZwBIXfnYvwY66V8Y2jKEAjtf6BEmB8yPxWLhxdgY3FjWR +y3kRLfr6BhxVM2qYtl/gXbyGTFjAv7LgFQa/25OXRevs+VjBWFQiQ89b+YIZPpJB +y8y+02nsRLt9Oy9lWMq1/pEqySDV6T3rrw5rV7TLj2RGNkxbnjk+qmf5mWxYzO5X +QaBqeQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH +AwIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQbdcIL3c/Yr+lR9wLU2FLPJSfk +ZjAbBgNVHREEFDASghBjbGllbnQuamFlZ2VyLmlvMA0GCSqGSIb3DQEBCwUAA4IB +gQBx/tQKqGLQGv90TyQOdKFPPOQ0iU/pXrM0t1Gn55UuvSz6G66IufPQuV+MeW1B +CGcSm12QAjwIvFVPFiBurygQ9eYU/tZW1NCaTSoSRa8KzYMBuDlfqYdS3/7gq2+L +L3b9QZt4rLgaQp0StTlpgCZuKa6N4aK25HQpu+lZ/ZxL1cvLlTGtI2VEWrx9hZ9q +5ImLy063iSc/YqD51XR0LJTkjSdep4sBEGtl5V582ncZAGZQim90hiaPrf3TXVnN +HQuzJNE5pwS637nCcyzmXn07Wh4qcT5vWDmySeN9eDJjfrbM9il11mkGZ9JQYf8Z +S+1562KvxjVVlsegnXaR27tAGkJ40X/OZRC28jLEXIjManDhClZD3uwqlSRtb6/M +ux4+8kqL90msVRlZR5VnUCR5/rZr4ji07NMDVJijI99lRQ5rDbf7Z9CMUpLTXcfd +jJBweUKlFEe3HZ9BfZOU3tLbAdQa2/I420lFVo8mEdu6cpKQpW8fITDvl/71OpQu +FsI= +-----END CERTIFICATE----- diff --git a/pkg/config/tlscfg/testdata/test-key.pem b/pkg/config/tlscfg/testdata/test-key.pem new file mode 100644 index 00000000000..dc7766f9363 --- /dev/null +++ b/pkg/config/tlscfg/testdata/test-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDrN/YmVJ0pN2Jg +NlWLXI+c/rn8KCqXEx+VM/03Y/VbsXVJu9CCIaJJ9TJzs2Jx98saqoNsrdDiG8A2 +RELA5hz6ddksvogDGxn9AF+53cYbZeG4RNPanzhaWqjnkjfAUX4R9vz0VGn4iIDL +aCNzbvmGaJ7rMNFg0m2JyZnAEhd+di/BjrpXxjaMoQCO1/oESYHzI/FYuHF2BjcW +NZHLeREt+voGHFUzapi2X+BdvIZMWMC/suAVBr/bk5dF6+z5WMFYVCJDz1v5ghk+ +kkHLzL7TaexEu307L2VYyrX+kSrJINXpPeuvDmtXtMuPZEY2TFueOT6qZ/mZbFjM +7ldBoGp5AgMBAAECgf854ouw4yHKAtcy1iw3H5A4Eneyli/k/c/H6ANonjDDX+h9 +PLsTSzOk/7JqxrpzUYeqCExPcnb1Ld8fe6zxy69V86p+WGUgXosGuBDWrL0UAP6L +WmTIaGZ11dm7I0CVE3jy8tVNS3jIsM8BP595yNWfPh/dwSXFrgNG5VXw7oLZm8Nd +q4+yybeRT/1dhlz+toV44x1GjfKkxqhnTPZvnyqvg8jYMVQmbsnUlvAyfRr3fh3g +zEnzuBW0KPPkNbMyL1Q3QU/8LVf4lQ37pI1887edJmlXtbEuh8QjTVDB/5oi7O5/ +2wxdGDTGIad4kXYG2vsuTVunZZq15BfMVyGkHoECgYEA9h1ROB6AfoxkykvQyJEb +1rOxQVz0tAgwzb25aThkSEXVZ6GgdZabgH4aArCGOEXrVt5hlDoDHC8ZEcyQ+yuF ++wFa2C6SorUkGnBJH9J9umWW+bOa5XigqgMHnpjM9yhNH34UnMSm+VarqczQhVx5 +QqIsbCsT+hbAkhwAgJo64kkCgYEA9KqbQ8YTRMc58n3juX8PIFYrOXsUGhWPG2jo +YoiUXgHSZDvxAsp6AtU2jUXjzjTCaF+h4zhxND3FD2yBLRt/Xx/GYXzmDf+Wx68B +4G0ZW4a+huoIEhsM6WGs7oT/sQxluMFb6G/rOaZEWDNzhYtVGNZTxnxCsd4eWj1j +9zy6RrECgYEA4qWTAyxLxr6Bny58ogfH7Evk4723+AdG8mFS2ww8hbYR1fKpM0C0 +CXuXdnybzjzNgl0e3YMjFBRncNXDehrVspbH0yfokABitBpNrQmKEVq201NMRSB2 +TLqnjK1IrB+oDmVslAYhgqMHSUK9kOLdJLj2UdLF/dxwEN3KtKPTsEkCgYEAhPPU +rY6MV/qfDZvFTL6z3JGWqYStVsNSYcWvSiQH49G/n4JHJIocpT9xhnFtKlfXMNqO +4SeBtK7AT/JZe8aOf4WHyuARL5gtOlNqhKckeW0OScgRHK2gZY4TaAXT4ETpXe2M +4RE4VLp6Nye2ZeJiGr4VBi3uHDOkcMsdcHOKkfECgYEAwEizw5kfhQ79bl9SwPbl +euE0wxUyEu+1lNqqAr6ty+BtfGufOxupzejNKghdpgB/bmuK77G8ikbDh6Ya6pQ1 +++Oes8NSFNiKq7pZOpjOeXRRo/OncBFKRDOX/i4ARWeJ/ZvjYz1fPyQuQiylaeDx +IYDJ4/DyVeyPiVrSQKJ5YLk= +-----END PRIVATE KEY----- diff --git a/pkg/config/tlscfg/testdata/testCA-bad.txt b/pkg/config/tlscfg/testdata/testCA-bad.txt new file mode 100644 index 00000000000..2f4aad65c7f --- /dev/null +++ b/pkg/config/tlscfg/testdata/testCA-bad.txt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +bad certificate +-----END CERTIFICATE----- diff --git a/pkg/config/tlscfg/testdata/testCA.pem b/pkg/config/tlscfg/testdata/testCA.pem new file mode 100644 index 00000000000..0a986020542 --- /dev/null +++ b/pkg/config/tlscfg/testdata/testCA.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICBzCCAXCgAwIBAgIQNkTaUtOczDHvL2YT/kqScTANBgkqhkiG9w0BAQsFADAX +MRUwEwYDVQQKEwxqYWdlcnRyYWNpbmcwHhcNMTkwMjA4MDYyODAyWhcNMTkwMjA4 +MDcyODAyWjAXMRUwEwYDVQQKEwxqYWdlcnRyYWNpbmcwgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAMcOLYflHGbqC1f7+tbnsdfcpd0rEuX65+ab0WzelAgvo988 +yD+j7LDLPIE8IPk/tfqaETZ8h0LRUUTn8F2rW/wgrl/G8Onz0utog38N0elfTifG +Mu7GJCr/+aYM5xbQMDj4Brb4vhnkJF8UBe49fWILhIltUcm1SeKqVX3d1FvpAgMB +AAGjVDBSMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV +HRMBAf8EBTADAQH/MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG +9w0BAQsFAAOBgQCreFjwpAn1HqJT812JOwoWKrt1NjOKGcz7pvIs1k3DfQVLH2aZ +iPKnCkzNgxMzQtwdgpAOXIAqXyNibvyOAv1C+3QSMLKbuPEHaIxlCuvl1suX/g25 +17x1o3Q64AnPCWOLpN2wjkfZqX7gZ84nsxpqb9Sbw1+2+kqX7dSZ3mfVxQ== +-----END CERTIFICATE-----