/
grpc.go
181 lines (157 loc) · 7.52 KB
/
grpc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.
// Package grpcsec is the gRPC instrumentation API and contract for AppSec
// defining an abstract run-time representation of gRPC handlers.
// gRPC integrations must use this package to enable AppSec features for gRPC,
// which listens to this package's operation events.
package grpcsec
import (
"encoding/json"
"reflect"
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/dyngo"
)
// Abstract gRPC server handler operation definitions. It is based on two
// operations allowing to describe every type of RPC: the HandlerOperation type
// which represents the RPC handler, and the ReceiveOperation type which
// represents the messages the RPC handler receives during its lifetime.
// This means that the ReceiveOperation(s) will happen within the
// HandlerOperation.
// Every type of RPC, unary, client streaming, server streaming, and
// bidirectional streaming RPCs, can be all represented with a HandlerOperation
// having one or several ReceiveOperation.
// The send operation is not required for now and therefore not defined, which
// means that server and bidirectional streaming RPCs currently have the same
// run-time representation as unary and client streaming RPCs.
type (
// HandlerOperation represents a gRPC server handler operation.
// It must be created with StartHandlerOperation() and finished with its
// Finish() method.
// Security events observed during the operation lifetime should be added
// to the operation using its AddSecurityEvent() method.
HandlerOperation struct {
dyngo.Operation
events []json.RawMessage
mu sync.Mutex
}
// HandlerOperationArgs is the grpc handler arguments. Empty as of today.
HandlerOperationArgs struct {
// Message received by the gRPC handler.
// Corresponds to the address `grpc.server.request.metadata`.
Metadata map[string][]string
}
// HandlerOperationRes is the grpc handler results. Empty as of today.
HandlerOperationRes struct{}
// ReceiveOperation type representing an gRPC server handler operation. It must
// be created with StartReceiveOperation() and finished with its Finish().
ReceiveOperation struct {
dyngo.Operation
}
// ReceiveOperationArgs is the gRPC handler receive operation arguments
// Empty as of today.
ReceiveOperationArgs struct{}
// ReceiveOperationRes is the gRPC handler receive operation results which
// contains the message the gRPC handler received.
ReceiveOperationRes struct {
// Message received by the gRPC handler.
// Corresponds to the address `grpc.server.request.message`.
Message interface{}
}
)
// TODO(Julio-Guerra): create a go-generate tool to generate the types, vars and methods below
// StartHandlerOperation starts an gRPC server handler operation, along with the
// given arguments and parent operation, and emits a start event up in the
// operation stack. When parent is nil, the operation is linked to the global
// root operation.
func StartHandlerOperation(args HandlerOperationArgs, parent dyngo.Operation) *HandlerOperation {
op := &HandlerOperation{Operation: dyngo.NewOperation(parent)}
dyngo.StartOperation(op, args)
return op
}
// Finish the gRPC handler operation, along with the given results, and emit a
// finish event up in the operation stack.
func (op *HandlerOperation) Finish(res HandlerOperationRes) []json.RawMessage {
dyngo.FinishOperation(op, res)
return op.events
}
// AddSecurityEvent adds the security event to the list of events observed
// during the operation lifetime.
func (op *HandlerOperation) AddSecurityEvent(events []json.RawMessage) {
op.mu.Lock()
defer op.mu.Unlock()
op.events = append(op.events, events...)
}
// gRPC handler operation's start and finish event callback function types.
type (
// OnHandlerOperationStart function type, called when an gRPC handler
// operation starts.
OnHandlerOperationStart func(*HandlerOperation, HandlerOperationArgs)
// OnHandlerOperationFinish function type, called when an gRPC handler
// operation finishes.
OnHandlerOperationFinish func(*HandlerOperation, HandlerOperationRes)
)
var (
handlerOperationArgsType = reflect.TypeOf((*HandlerOperationArgs)(nil)).Elem()
handlerOperationResType = reflect.TypeOf((*HandlerOperationRes)(nil)).Elem()
)
// ListenedType returns the type a OnHandlerOperationStart event listener
// listens to, which is the HandlerOperationArgs type.
func (OnHandlerOperationStart) ListenedType() reflect.Type { return handlerOperationArgsType }
// Call the underlying event listener function by performing the type-assertion
// on v whose type is the one returned by ListenedType().
func (f OnHandlerOperationStart) Call(op dyngo.Operation, v interface{}) {
f(op.(*HandlerOperation), v.(HandlerOperationArgs))
}
// ListenedType returns the type a OnHandlerOperationFinish event listener
// listens to, which is the HandlerOperationRes type.
func (OnHandlerOperationFinish) ListenedType() reflect.Type { return handlerOperationResType }
// Call the underlying event listener function by performing the type-assertion
// on v whose type is the one returned by ListenedType().
func (f OnHandlerOperationFinish) Call(op dyngo.Operation, v interface{}) {
f(op.(*HandlerOperation), v.(HandlerOperationRes))
}
// StartReceiveOperation starts a receive operation of a gRPC handler, along
// with the given arguments and parent operation, and emits a start event up in
// the operation stack. When parent is nil, the operation is linked to the
// global root operation.
func StartReceiveOperation(args ReceiveOperationArgs, parent dyngo.Operation) ReceiveOperation {
op := ReceiveOperation{Operation: dyngo.NewOperation(parent)}
dyngo.StartOperation(op, args)
return op
}
// Finish the gRPC handler operation, along with the given results, and emits a
// finish event up in the operation stack.
func (op ReceiveOperation) Finish(res ReceiveOperationRes) {
dyngo.FinishOperation(op, res)
}
// gRPC receive operation's start and finish event callback function types.
type (
// OnReceiveOperationStart function type, called when a gRPC receive
// operation starts.
OnReceiveOperationStart func(ReceiveOperation, ReceiveOperationArgs)
// OnReceiveOperationFinish function type, called when a grpc receive
// operation finishes.
OnReceiveOperationFinish func(ReceiveOperation, ReceiveOperationRes)
)
var (
receiveOperationArgsType = reflect.TypeOf((*ReceiveOperationArgs)(nil)).Elem()
receiveOperationResType = reflect.TypeOf((*ReceiveOperationRes)(nil)).Elem()
)
// ListenedType returns the type a OnHandlerOperationStart event listener
// listens to, which is the HandlerOperationArgs type.
func (OnReceiveOperationStart) ListenedType() reflect.Type { return receiveOperationArgsType }
// Call the underlying event listener function by performing the type-assertion
// on v whose type is the one returned by ListenedType().
func (f OnReceiveOperationStart) Call(op dyngo.Operation, v interface{}) {
f(op.(ReceiveOperation), v.(ReceiveOperationArgs))
}
// ListenedType returns the type a OnHandlerOperationFinish event listener
// listens to, which is the HandlerOperationRes type.
func (OnReceiveOperationFinish) ListenedType() reflect.Type { return receiveOperationResType }
// Call the underlying event listener function by performing the type-assertion
// on v whose type is the one returned by ListenedType().
func (f OnReceiveOperationFinish) Call(op dyngo.Operation, v interface{}) {
f(op.(ReceiveOperation), v.(ReceiveOperationRes))
}