Skip to content

Commit 33e3fdd

Browse files
authored
Add support for TLS protocol tracing (#2096)
Summary: Add support for TLS protocol tracing This is the final change to wire up the tls protocol parser and stitcher into stirling. I've also filed #2095 to track supporting tracing TLS handshakes and the application data. Relevant Issues: N/A Type of change: /kind feature Test Plan: New tests verify functionality works end to end Changelog Message: Added support for tracing TLS handshakes. This can be enabled with `--stirling_enable_tls_tracing=1` or through the `PX_STIRLING_ENABLE_TLS_TRACING` environment variable. Until #2095 is addressed, this will disable tracing the plaintext within encrypted connections. --------- Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent 9b05b62 commit 33e3fdd

23 files changed

+437
-25
lines changed

src/shared/protocols/protocols.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum class Protocol {
3939
kKafka = 10,
4040
kMux = 11,
4141
kAMQP = 12,
42+
kTLS = 13,
4243
};
4344

4445
} // namespace protocols

src/stirling/binaries/stirling_wrapper.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ DEFINE_string(trace, "",
6262
"Dynamic trace to deploy. Either (1) the path to a file containing PxL or IR trace "
6363
"spec, or (2) <path to object file>:<symbol_name> for full-function tracing.");
6464
DEFINE_string(print_record_batches,
65-
"http_events,mysql_events,pgsql_events,redis_events,cql_events,dns_events",
65+
"http_events,mysql_events,pgsql_events,redis_events,cql_events,dns_events,tls_events",
6666
"Comma-separated list of tables to print.");
6767
DEFINE_bool(init_only, false, "If true, only runs the init phase and exits. For testing.");
6868
DEFINE_int32(timeout_secs, -1,

src/stirling/source_connectors/socket_tracer/BUILD.bazel

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,27 @@ pl_cc_bpf_test(
518518
],
519519
)
520520

521+
pl_cc_bpf_test(
522+
name = "tls_trace_bpf_test",
523+
timeout = "long",
524+
srcs = ["tls_trace_bpf_test.cc"],
525+
flaky = True,
526+
shard_count = 2,
527+
tags = [
528+
"cpu:16",
529+
"no_asan",
530+
"requires_bpf",
531+
],
532+
deps = [
533+
":cc_library",
534+
"//src/common/testing/test_utils:cc_library",
535+
"//src/stirling/source_connectors/socket_tracer/testing:cc_library",
536+
"//src/stirling/source_connectors/socket_tracer/testing/container_images:curl_container",
537+
"//src/stirling/source_connectors/socket_tracer/testing/container_images:nginx_openssl_3_0_8_container",
538+
"//src/stirling/testing:cc_library",
539+
],
540+
)
541+
521542
pl_cc_bpf_test(
522543
name = "dyn_lib_trace_bpf_test",
523544
timeout = "moderate",

src/stirling/source_connectors/socket_tracer/bcc_bpf/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pl_cc_test(
8282
"ENABLE_NATS_TRACING=true",
8383
"ENABLE_MONGO_TRACING=true",
8484
"ENABLE_AMQP_TRACING=true",
85+
"ENABLE_TLS_TRACING=true",
8586
],
8687
deps = [
8788
"//src/stirling/bpf_tools/bcc_bpf:headers",

src/stirling/source_connectors/socket_tracer/bcc_bpf/protocol_inference.h

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,46 @@ static __inline enum message_type_t infer_http_message(const char* buf, size_t c
6060
return kUnknown;
6161
}
6262

63+
static __inline enum message_type_t infer_tls_message(const char* buf, size_t count) {
64+
if (count < 6) {
65+
return kUnknown;
66+
}
67+
68+
uint8_t content_type = buf[0];
69+
// TLS content types correspond to the following:
70+
// 0x14: ChangeCipherSpec
71+
// 0x15: Alert
72+
// 0x16: Handshake
73+
// 0x17: ApplicationData
74+
// 0x18: Heartbeat
75+
if (content_type != 0x16) {
76+
return kUnknown;
77+
}
78+
79+
uint16_t legacy_version = buf[1] << 8 | buf[2];
80+
// TLS versions correspond to the following:
81+
// 0x0300: SSL 3.0
82+
// 0x0301: TLS 1.0
83+
// 0x0302: TLS 1.1
84+
// 0x0303: TLS 1.2
85+
// 0x0304: TLS 1.3
86+
if (legacy_version < 0x0300 || legacy_version > 0x0304) {
87+
return kUnknown;
88+
}
89+
90+
uint8_t handshake_type = buf[5];
91+
// Check for ServerHello
92+
if (handshake_type == 2) {
93+
return kResponse;
94+
}
95+
// Check for ClientHello
96+
if (handshake_type == 1) {
97+
return kRequest;
98+
}
99+
100+
return kUnknown;
101+
}
102+
63103
// Cassandra frame:
64104
// 0 8 16 24 32 40
65105
// +---------+---------+---------+---------+---------+
@@ -699,7 +739,16 @@ static __inline struct protocol_message_t infer_protocol(const char* buf, size_t
699739
// role by considering which side called accept() vs connect(). Once the clean-up
700740
// above is done, the code below can be turned into a chained ternary.
701741
// PROTOCOL_LIST: Requires update on new protocols.
702-
if (ENABLE_HTTP_TRACING && (inferred_message.type = infer_http_message(buf, count)) != kUnknown) {
742+
//
743+
// TODO(ddelnano): TLS tracing should be handled differently in the future as we want to be able
744+
// to trace the handshake and the application data separately (gh#2095). The current connection
745+
// tracker model only works with one or the other, meaning if TLS tracing is enabled, tracing the
746+
// plaintext within an encrypted conn will not work. ENABLE_TLS_TRACING will default to false
747+
// until this is revisted.
748+
if (ENABLE_TLS_TRACING && (inferred_message.type = infer_tls_message(buf, count)) != kUnknown) {
749+
inferred_message.protocol = kProtocolTLS;
750+
} else if (ENABLE_HTTP_TRACING &&
751+
(inferred_message.type = infer_http_message(buf, count)) != kUnknown) {
703752
inferred_message.protocol = kProtocolHTTP;
704753
} else if (ENABLE_CQL_TRACING &&
705754
(inferred_message.type = infer_cql_message(buf, count)) != kUnknown) {

src/stirling/source_connectors/socket_tracer/bcc_bpf/protocol_inference_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,27 @@ TEST(ProtocolInferenceTest, AMQPResponse) {
482482
EXPECT_EQ(protocol_message.protocol, kProtocolAMQP);
483483
EXPECT_EQ(protocol_message.type, kResponse);
484484
}
485+
486+
TEST(ProtocolInferenceTest, TLSRequest) {
487+
struct conn_info_t conn_info = {};
488+
// TLS Client Hello
489+
constexpr uint8_t kReqFrame[] = {
490+
0x16, 0x03, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x7b, 0x7b, 0x7b,
491+
};
492+
auto protocol_message =
493+
infer_protocol(reinterpret_cast<const char*>(kReqFrame), sizeof(kReqFrame), &conn_info);
494+
EXPECT_EQ(protocol_message.protocol, kProtocolTLS);
495+
EXPECT_EQ(protocol_message.type, kRequest);
496+
}
497+
498+
TEST(ProtocolInferenceTest, TLSResponse) {
499+
struct conn_info_t conn_info = {};
500+
// TLS Server Hello
501+
constexpr uint8_t kRespFrame[] = {
502+
0x16, 0x03, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0xfc, 0x03, 0x03, 0x7b, 0x7b, 0x7b,
503+
};
504+
auto protocol_message =
505+
infer_protocol(reinterpret_cast<const char*>(kRespFrame), sizeof(kRespFrame), &conn_info);
506+
EXPECT_EQ(protocol_message.protocol, kProtocolTLS);
507+
EXPECT_EQ(protocol_message.type, kResponse);
508+
}

src/stirling/source_connectors/socket_tracer/bcc_bpf_intf/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ enum traffic_protocol_t {
5151
kProtocolKafka = 10,
5252
kProtocolMux = 11,
5353
kProtocolAMQP = 12,
54+
kProtocolTLS = 13,
5455
// We use magic enum to iterate through protocols in C++ land,
5556
// and don't want the C-enum-size trick to show up there.
5657
#ifndef __cplusplus

src/stirling/source_connectors/socket_tracer/conn_tracker.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ auto CreateTraceRoles() {
674674
res.Set(kProtocolKafka, {kRoleServer});
675675
res.Set(kProtocolMux, {kRoleServer});
676676
res.Set(kProtocolAMQP, {kRoleServer});
677+
res.Set(kProtocolTLS, {kRoleServer});
677678

678679
DCHECK(res.AreAllKeysSet());
679680
return res;

src/stirling/source_connectors/socket_tracer/data_stream.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ template void DataStream::ProcessBytesToFrames<protocols::amqp::channel_id, prot
215215
template void DataStream::ProcessBytesToFrames<
216216
protocols::mongodb::stream_id_t, protocols::mongodb::Frame, protocols::mongodb::StateWrapper>(
217217
message_type_t type, protocols::mongodb::StateWrapper* state);
218+
219+
template void DataStream::ProcessBytesToFrames<protocols::tls::stream_id_t, protocols::tls::Frame,
220+
protocols::NoState>(message_type_t type,
221+
protocols::NoState* state);
218222
void DataStream::Reset() {
219223
data_buffer_.Reset();
220224
has_new_events_ = false;

src/stirling/source_connectors/socket_tracer/protocols/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ pl_cc_library(
4646
"//src/stirling/source_connectors/socket_tracer/protocols/nats:cc_library",
4747
"//src/stirling/source_connectors/socket_tracer/protocols/pgsql:cc_library",
4848
"//src/stirling/source_connectors/socket_tracer/protocols/redis:cc_library",
49+
"//src/stirling/source_connectors/socket_tracer/protocols/tls:cc_library",
4950
],
5051
)

0 commit comments

Comments
 (0)