Skip to content

Commit d5f52c4

Browse files
committed
feat: add opentracing + jaeger
This adds support for opentracing with Jaeger. It is disabled by default, and can be turned on with a configurable sample rate. The bulk of the changes here are adding `context.Context` as the first parameter to the internal functions to allow for span context propogation. This does change the protocol, adding a new optional field to allow for span propogation between keyless instances. #276 allows this.
1 parent ddafcb2 commit d5f52c4

File tree

424 files changed

+62002
-20204
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

424 files changed

+62002
-20204
lines changed

client/client.go

+33-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"context"
45
"crypto"
56
"crypto/ecdsa"
67
"crypto/rsa"
@@ -18,7 +19,9 @@ import (
1819

1920
"github.com/cloudflare/cfssl/log"
2021
"github.com/cloudflare/gokeyless/protocol"
22+
"github.com/cloudflare/gokeyless/tracing"
2123
"github.com/lziest/ttlcache"
24+
"github.com/opentracing/opentracing-go"
2225
)
2326

2427
const (
@@ -190,8 +193,10 @@ func (c *Client) getRemote(server string) (Remote, error) {
190193
// NewRemoteSignerWithCertID returns a remote keyserver based crypto.Signer
191194
// ski, sni, serverIP, and certID are used to identify the key by the remote
192195
// keyserver.
193-
func NewRemoteSignerWithCertID(c *Client, keyserver string, ski protocol.SKI,
196+
func NewRemoteSignerWithCertID(ctx context.Context, c *Client, keyserver string, ski protocol.SKI,
194197
pub crypto.PublicKey, sni string, certID string, serverIP net.IP) (crypto.Signer, error) {
198+
span, _ := opentracing.StartSpanFromContext(ctx, "client.NewRemoteSignerWithCertID")
199+
defer span.Finish()
195200
priv := PrivateKey{
196201
public: pub,
197202
client: c,
@@ -201,6 +206,11 @@ func NewRemoteSignerWithCertID(c *Client, keyserver string, ski protocol.SKI,
201206
keyserver: keyserver,
202207
certID: certID,
203208
}
209+
var err error
210+
priv.JaegerSpan, err = tracing.SpanContextToBinary(span.Context())
211+
if err != nil {
212+
log.Errorf("failed to inject span: %v", err)
213+
}
204214

205215
// This is due to an issue in crypto/tls, where an ECDSA key is not allowed to
206216
// implement Decrypt.
@@ -213,8 +223,11 @@ func NewRemoteSignerWithCertID(c *Client, keyserver string, ski protocol.SKI,
213223
// NewRemoteSigner returns a remote keyserver based crypto.Signer,
214224
// ski, sni, and serverIP are used to identified the key by the remote
215225
// keyserver.
216-
func NewRemoteSigner(c *Client, keyserver string, ski protocol.SKI,
226+
func NewRemoteSigner(ctx context.Context, c *Client, keyserver string, ski protocol.SKI,
217227
pub crypto.PublicKey, sni string, serverIP net.IP) (crypto.Signer, error) {
228+
229+
span, _ := opentracing.StartSpanFromContext(ctx, "client.NewRemoteSignerWithCertID")
230+
defer span.Finish()
218231
priv := PrivateKey{
219232
public: pub,
220233
client: c,
@@ -223,6 +236,11 @@ func NewRemoteSigner(c *Client, keyserver string, ski protocol.SKI,
223236
serverIP: serverIP,
224237
keyserver: keyserver,
225238
}
239+
var err error
240+
priv.JaegerSpan, err = tracing.SpanContextToBinary(span.Context())
241+
if err != nil {
242+
log.Errorf("failed to inject span: %v", err)
243+
}
226244

227245
// This is due to an issue in crypto/tls, where an ECDSA key is not allowed to
228246
// implement Decrypt.
@@ -237,42 +255,42 @@ func NewRemoteSigner(c *Client, keyserver string, ski protocol.SKI,
237255
// SKI is computed from the public key and along with sni and serverIP,
238256
// the remote Signer uses those key identification info to contact the
239257
// remote keyserver for keyless operations.
240-
func (c *Client) NewRemoteSignerTemplate(keyserver string, pub crypto.PublicKey, sni string, serverIP net.IP) (crypto.Signer, error) {
258+
func (c *Client) NewRemoteSignerTemplate(ctx context.Context, keyserver string, pub crypto.PublicKey, sni string, serverIP net.IP) (crypto.Signer, error) {
241259
ski, err := protocol.GetSKI(pub)
242260
if err != nil {
243261
return nil, err
244262
}
245-
return NewRemoteSigner(c, keyserver, ski, pub, sni, serverIP)
263+
return NewRemoteSigner(ctx, c, keyserver, ski, pub, sni, serverIP)
246264
}
247265

248266
// NewRemoteSignerTemplateWithCertID returns a remote keyserver
249267
// based crypto.Signer with the public key.
250268
// SKI is computed from public key, and along with sni, serverIP, and
251269
// certID the remote signer uses these to contact the remote keyserver.
252-
func (c *Client) NewRemoteSignerTemplateWithCertID(keyserver string, pub crypto.PublicKey, sni string, serverIP net.IP, certID string) (crypto.Signer, error) {
270+
func (c *Client) NewRemoteSignerTemplateWithCertID(ctx context.Context, keyserver string, pub crypto.PublicKey, sni string, serverIP net.IP, certID string) (crypto.Signer, error) {
253271
ski, err := protocol.GetSKI(pub)
254272
if err != nil {
255273
return nil, err
256274
}
257-
return NewRemoteSignerWithCertID(c, keyserver, ski, pub, sni, certID, serverIP)
275+
return NewRemoteSignerWithCertID(ctx, c, keyserver, ski, pub, sni, certID, serverIP)
258276
}
259277

260278
// NewRemoteSignerByPublicKey returns a remote keyserver based signer
261279
// with the the public key.
262-
func (c *Client) NewRemoteSignerByPublicKey(server string, pub crypto.PublicKey) (crypto.Signer, error) {
263-
return c.NewRemoteSignerTemplate(server, pub, "", nil)
280+
func (c *Client) NewRemoteSignerByPublicKey(ctx context.Context, server string, pub crypto.PublicKey) (crypto.Signer, error) {
281+
return c.NewRemoteSignerTemplate(ctx, server, pub, "", nil)
264282
}
265283

266284
// NewRemoteSignerByCert returns a remote keyserver based signer
267285
// with the the public key contained in a x509.Certificate.
268-
func (c *Client) NewRemoteSignerByCert(server string, cert *x509.Certificate) (crypto.Signer, error) {
269-
return c.NewRemoteSignerTemplate(server, cert.PublicKey, "", nil)
286+
func (c *Client) NewRemoteSignerByCert(ctx context.Context, server string, cert *x509.Certificate) (crypto.Signer, error) {
287+
return c.NewRemoteSignerTemplate(ctx, server, cert.PublicKey, "", nil)
270288
}
271289

272290
// NewRemoteSignerByCertPEM returns a remote keyserver based signer
273291
// with the public key extracted from a single PEM cert
274292
// (possibly the leaf of a chain of certs).
275-
func (c *Client) NewRemoteSignerByCertPEM(server string, certsPEM []byte) (crypto.Signer, error) {
293+
func (c *Client) NewRemoteSignerByCertPEM(ctx context.Context, server string, certsPEM []byte) (crypto.Signer, error) {
276294
block, _ := pem.Decode(certsPEM)
277295
if block == nil {
278296
return nil, errors.New("couldn't parse PEM bytes")
@@ -283,7 +301,7 @@ func (c *Client) NewRemoteSignerByCertPEM(server string, certsPEM []byte) (crypt
283301
return nil, err
284302
}
285303

286-
return c.NewRemoteSignerTemplate(server, cert.PublicKey, "", nil)
304+
return c.NewRemoteSignerTemplate(ctx, server, cert.PublicKey, "", nil)
287305
}
288306

289307
var (
@@ -318,11 +336,11 @@ func (c *Client) ScanDir(server, dir string, LoadPubKey func([]byte) (crypto.Pub
318336
return err
319337
}
320338

321-
if priv, err = c.NewRemoteSignerByPublicKey(server, pub); err != nil {
339+
if priv, err = c.NewRemoteSignerByPublicKey(context.Background(), server, pub); err != nil {
322340
return err
323341
}
324342
} else {
325-
if priv, err = c.NewRemoteSignerByCertPEM(server, in); err != nil {
343+
if priv, err = c.NewRemoteSignerByCertPEM(context.Background(), server, in); err != nil {
326344
return err
327345
}
328346
}
@@ -363,7 +381,7 @@ func (c *Client) LoadTLSCertificate(server, certFile string) (cert tls.Certifica
363381
return fail(err)
364382
}
365383

366-
cert.PrivateKey, err = c.NewRemoteSignerByCert(server, cert.Leaf)
384+
cert.PrivateKey, err = c.NewRemoteSignerByCert(context.TODO(), server, cert.Leaf)
367385
if err != nil {
368386
return fail(err)
369387
}

client/keys.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"context"
45
"crypto"
56
"crypto/ecdsa"
67
"crypto/rsa"
@@ -12,6 +13,9 @@ import (
1213

1314
"github.com/cloudflare/cfssl/log"
1415
"github.com/cloudflare/gokeyless/protocol"
16+
"github.com/cloudflare/gokeyless/tracing"
17+
"github.com/opentracing/opentracing-go"
18+
"github.com/opentracing/opentracing-go/ext"
1519
"golang.org/x/crypto/ed25519"
1620
)
1721

@@ -87,6 +91,10 @@ type PrivateKey struct {
8791
keyserver string
8892
sni string
8993
certID string
94+
95+
// We have shove the span context inside PrivateKey because
96+
// it's used by calling functions on the `crypto.Signer` interface, which don't take ctx as a parameter.
97+
JaegerSpan []byte
9098
}
9199

92100
// Public returns the public key corresponding to the opaque private key.
@@ -96,7 +104,9 @@ func (key *PrivateKey) Public() crypto.PublicKey {
96104

97105
// execute performs an opaque cryptographic operation on a server associated
98106
// with the key.
99-
func (key *PrivateKey) execute(op protocol.Op, msg []byte) ([]byte, error) {
107+
func (key *PrivateKey) execute(ctx context.Context, op protocol.Op, msg []byte) ([]byte, error) {
108+
span, ctx := opentracing.StartSpanFromContext(ctx, "PrivateKey.execute")
109+
defer span.Finish()
100110
var result *protocol.Operation
101111
// retry once if connection returned by remote Dial is problematic.
102112
for attempts := 2; attempts > 0; attempts-- {
@@ -110,7 +120,11 @@ func (key *PrivateKey) execute(op protocol.Op, msg []byte) ([]byte, error) {
110120
return nil, err
111121
}
112122

113-
result, err = conn.Conn.DoOperation(protocol.Operation{
123+
// We explicitly do NOT want to fill in JaegerSpan here, since the remote keyless server
124+
// will error if it does know how to handle that Tag
125+
// https://github.com/cloudflare/gokeyless/pull/276 makes it safe to fill it in,
126+
// but there's no way to know the version of the remote keyserver
127+
result, err = conn.Conn.DoOperation(ctx, protocol.Operation{
114128
Opcode: op,
115129
Payload: msg,
116130
SKI: key.ski,
@@ -149,6 +163,13 @@ func (key *PrivateKey) execute(op protocol.Op, msg []byte) ([]byte, error) {
149163

150164
// Sign implements the crypto.Signer operation for the given key.
151165
func (key *PrivateKey) Sign(r io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
166+
spanCtx, err := tracing.SpanContextFromBinary(key.JaegerSpan)
167+
if err != nil {
168+
log.Errorf("failed to extract span: %v", err)
169+
}
170+
span, ctx := opentracing.StartSpanFromContext(context.Background(), "client: PrivateKey.Sign", ext.RPCServerOption(spanCtx))
171+
defer span.Finish()
172+
152173
// If opts specifies a hash function, then the message is expected to be the
153174
// length of the output of that hash function.
154175
if opts.HashFunc() != 0 && len(msg) != opts.HashFunc().Size() {
@@ -159,7 +180,7 @@ func (key *PrivateKey) Sign(r io.Reader, msg []byte, opts crypto.SignerOpts) ([]
159180
if op == protocol.OpError {
160181
return nil, errors.New("invalid key type, hash or options")
161182
}
162-
return key.execute(op, msg)
183+
return key.execute(ctx, op, msg)
163184
}
164185

165186
// Decrypter implements the Decrypt method on a PrivateKey.
@@ -169,12 +190,18 @@ type Decrypter struct {
169190

170191
// Decrypt implements the crypto.Decrypter operation for the given key.
171192
func (key *Decrypter) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error) {
193+
spanCtx, err := tracing.SpanContextFromBinary(key.JaegerSpan)
194+
if err != nil {
195+
log.Errorf("failed to extract span: %v", err)
196+
}
197+
span, ctx := opentracing.StartSpanFromContext(context.Background(), "client: Decrypter.Decrypt", ext.RPCServerOption(spanCtx))
198+
defer span.Finish()
172199
opts1v15, ok := opts.(*rsa.PKCS1v15DecryptOptions)
173200
if opts != nil && !ok {
174201
return nil, errors.New("invalid options for Decrypt")
175202
}
176203

177-
ptxt, err := key.execute(protocol.OpRSADecrypt, msg)
204+
ptxt, err := key.execute(ctx, protocol.OpRSADecrypt, msg)
178205
if err != nil {
179206
return nil, err
180207
}

client/remote.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"context"
45
"crypto/tls"
56
"errors"
67
"fmt"
@@ -120,7 +121,7 @@ func healthchecker(c *Conn) {
120121
return
121122
}
122123

123-
err := c.Conn.Ping(nil)
124+
err := c.Conn.Ping(context.Background(), nil)
124125
if err != nil {
125126
if err == conn.ErrClosed {
126127
// somebody else closed the connection while we were sleeping
@@ -314,7 +315,7 @@ func (s *singleRemote) PingAll(c *Client, concurrency int) {
314315
return
315316
}
316317

317-
err = cn.Conn.Ping(nil)
318+
err = cn.Conn.Ping(context.Background(), nil)
318319
if err != nil {
319320
cn.Close()
320321
}
@@ -470,7 +471,7 @@ func (g *Group) PingAll(c *Client, concurrency int) {
470471
}
471472

472473
start := time.Now()
473-
err = cn.Conn.Ping(nil)
474+
err = cn.Conn.Ping(context.Background(), nil)
474475
duration := time.Since(start)
475476

476477
if err != nil {

client/remote_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"context"
45
"crypto"
56
"crypto/x509"
67
"encoding/pem"
@@ -244,7 +245,7 @@ func NewRemoteSignerByCertFile(filepath string) (crypto.Signer, error) {
244245
if err != nil {
245246
return nil, err
246247
}
247-
s, err := c.NewRemoteSignerByPublicKey("", pub)
248+
s, err := c.NewRemoteSignerByPublicKey(context.Background(), "", pub)
248249
if err != nil {
249250
return nil, err
250251
}

cmd/gokeyless/gokeyless.go

+27
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
"strings"
1313
"time"
1414

15+
"github.com/opentracing/opentracing-go"
16+
"github.com/uber/jaeger-client-go"
17+
1518
"github.com/spf13/pflag"
1619
"github.com/spf13/viper"
1720
"gopkg.in/yaml.v2"
@@ -48,6 +51,10 @@ type Config struct {
4851
PidFile string `yaml:"pid_file" mapstructure:"pid_file"`
4952

5053
CurrentTime string `yaml:"current_time" mapstructure:"current_time"`
54+
55+
TracingEnabled bool `yaml:"tracing_enabled" mapstructure:"tracing_enabled"`
56+
TracingAddress string `yaml:"tracing_address" mapstructure:"tracing_address"`
57+
TracingSampleRate float64 `yaml:"tracing_sample_rate" mapstructure:"tracing_sample_rate"` // between 0 and 1
5158
}
5259

5360
// PrivateKeyStoreConfig defines a key store.
@@ -106,6 +113,10 @@ func init() {
106113
viper.SetDefault("metrics_port", 2406)
107114
flagset.String("pid-file", "", "File to store PID of running server")
108115
flagset.String("current-time", "", "Current time used for certificate validation (for testing only)")
116+
flagset.Bool("tracing-enabled", false, "")
117+
flagset.String("tracing-address", "", "")
118+
viper.SetDefault("tracing-address", "localhost:6831")
119+
flagset.Float64("tracing-sample-rate", 0, "")
109120
// These override the private_key_stores value from the config file.
110121
flagset.StringVar(&privateKeyDirs, "private-key-dirs", "", "Comma-separated list of directories in which private keys are stored with .key extension")
111122
flagset.StringVar(&privateKeyFiles, "private-key-files", "", "Comma-separated list of private key files")
@@ -236,6 +247,22 @@ func main() {
236247
fmt.Print(string(b))
237248
os.Exit(0)
238249
}
250+
if config.TracingEnabled {
251+
// jaeger failing to connect to the agent / initializing shouldn't prevent keyless from starting,
252+
// so if we encounter an error we should log it but move on.
253+
jaegerTransport, err := jaeger.NewUDPTransport(config.TracingAddress, 0)
254+
if err != nil {
255+
log.Errorf("failed to enable tracing: %s", err)
256+
}
257+
sampler, err := jaeger.NewProbabilisticSampler(config.TracingSampleRate)
258+
if err != nil {
259+
log.Errorf("failed to enable tracing: %s", err)
260+
}
261+
tracer, closer := jaeger.NewTracer("gokeyless", sampler, jaeger.NewRemoteReporter(jaegerTransport))
262+
defer closer.Close()
263+
opentracing.SetGlobalTracer(tracer)
264+
log.Infof("tracing enabled: %s", sampler.String())
265+
}
239266

240267
// If we make it here we need to ask for user input, so we need to give up
241268
// and log an error instead (in case the server is running as a daemon).

0 commit comments

Comments
 (0)