Skip to content

Commit b8ed611

Browse files
committed
Add support for TLS protocol tracing
Signed-off-by: Dom Del Nano <[email protected]>
1 parent 1f6d18b commit b8ed611

19 files changed

+406
-7
lines changed

src/stirling/binaries/stirling_wrapper.cc

+1-1
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

+21
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,27 @@ pl_cc_bpf_test(
516516
],
517517
)
518518

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

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

+1
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

+50-1
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

+24
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

+1
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

+1
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

+4
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

+1
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
)

src/stirling/source_connectors/socket_tracer/protocols/stitchers.h

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@
3131
#include "src/stirling/source_connectors/socket_tracer/protocols/nats/stitcher.h" // IWYU pragma: export
3232
#include "src/stirling/source_connectors/socket_tracer/protocols/pgsql/stitcher.h" // IWYU pragma: export
3333
#include "src/stirling/source_connectors/socket_tracer/protocols/redis/stitcher.h" // IWYU pragma: export
34+
#include "src/stirling/source_connectors/socket_tracer/protocols/tls/stitcher.h" // IWYU pragma: export

src/stirling/source_connectors/socket_tracer/protocols/types.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "src/stirling/source_connectors/socket_tracer/protocols/nats/types.h"
3535
#include "src/stirling/source_connectors/socket_tracer/protocols/pgsql/types.h"
3636
#include "src/stirling/source_connectors/socket_tracer/protocols/redis/types.h"
37+
#include "src/stirling/source_connectors/socket_tracer/protocols/tls/types.h"
3738

3839
namespace px {
3940
namespace stirling {
@@ -53,7 +54,8 @@ using FrameDequeVariant = std::variant<std::monostate,
5354
absl::flat_hash_map<kafka::correlation_id_t, std::deque<kafka::Packet>>,
5455
absl::flat_hash_map<nats::stream_id_t, std::deque<nats::Message>>,
5556
absl::flat_hash_map<amqp::channel_id, std::deque<amqp::Frame>>,
56-
absl::flat_hash_map<mongodb::stream_id_t, std::deque<mongodb::Frame>>>;
57+
absl::flat_hash_map<mongodb::stream_id_t, std::deque<mongodb::Frame>>,
58+
absl::flat_hash_map<tls::stream_id_t, std::deque<tls::Frame>>>;
5759
// clang-format off
5860

5961
} // namespace protocols

src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc

+39
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ DEFINE_int32(stirling_enable_mongodb_tracing,
117117
gflags::Int32FromEnv("PX_STIRLING_ENABLE_MONGODB_TRACING",
118118
px::stirling::TraceMode::OnForNewerKernel),
119119
"If true, stirling will trace and process MongoDB messages");
120+
DEFINE_int32(
121+
stirling_enable_tls_tracing,
122+
gflags::Int32FromEnv("PX_STIRLING_ENABLE_TLS_TRACING", px::stirling::TraceMode::Off),
123+
"If true, stirling will trace and process TLS protocol (not the TLS payload) messages. Note: "
124+
"this disables tracing the plaintext within encrypted connections until gh#2095 is addressed.");
120125
DEFINE_bool(stirling_disable_golang_tls_tracing,
121126
gflags::BoolFromEnv("PX_STIRLING_DISABLE_GOLANG_TLS_TRACING", false),
122127
"If true, stirling will not trace TLS traffic for Go applications. This implies "
@@ -283,6 +288,10 @@ void SocketTraceConnector::InitProtocolTransferSpecs() {
283288
kAMQPTableNum,
284289
{kRoleClient, kRoleServer},
285290
TRANSFER_STREAM_PROTOCOL(amqp)}},
291+
{kProtocolTLS, TransferSpec{FLAGS_stirling_enable_tls_tracing,
292+
kTLSTableNum,
293+
{kRoleClient, kRoleServer},
294+
TRANSFER_STREAM_PROTOCOL(tls)}},
286295
{kProtocolUnknown, TransferSpec{/* trace_mode */ px::stirling::TraceMode::Off,
287296
/* table_num */ static_cast<uint32_t>(-1),
288297
/* trace_roles */ {},
@@ -491,6 +500,7 @@ Status SocketTraceConnector::InitBPF() {
491500
absl::StrCat("-DENABLE_NATS_TRACING=", protocol_transfer_specs_[kProtocolNATS].enabled),
492501
absl::StrCat("-DENABLE_AMQP_TRACING=", protocol_transfer_specs_[kProtocolAMQP].enabled),
493502
absl::StrCat("-DENABLE_MONGO_TRACING=", protocol_transfer_specs_[kProtocolMongo].enabled),
503+
absl::StrCat("-DENABLE_TLS_TRACING=", protocol_transfer_specs_[kProtocolTLS].enabled),
494504
absl::StrCat("-DBPF_LOOP_LIMIT=", FLAGS_stirling_bpf_loop_limit),
495505
absl::StrCat("-DBPF_CHUNK_LIMIT=", FLAGS_stirling_bpf_chunk_limit),
496506
};
@@ -1686,6 +1696,35 @@ void SocketTraceConnector::AppendMessage(ConnectorContext* ctx, const ConnTracke
16861696
#endif
16871697
}
16881698

1699+
template <>
1700+
void SocketTraceConnector::AppendMessage(ConnectorContext* ctx, const ConnTracker& conn_tracker,
1701+
protocols::tls::Record record, DataTable* data_table) {
1702+
protocols::tls::Frame& req_message = record.req;
1703+
protocols::tls::Frame& resp_message = record.resp;
1704+
1705+
md::UPID upid(ctx->GetASID(), conn_tracker.conn_id().upid.pid,
1706+
conn_tracker.conn_id().upid.start_time_ticks);
1707+
1708+
DataTable::RecordBuilder<&kTLSTable> r(data_table, resp_message.timestamp_ns);
1709+
r.Append<r.ColIndex("time_")>(resp_message.timestamp_ns);
1710+
r.Append<r.ColIndex("upid")>(upid.value());
1711+
// Note that there is a string copy here,
1712+
// But std::move is not allowed because we re-use conn object.
1713+
r.Append<r.ColIndex("remote_addr")>(conn_tracker.remote_endpoint().AddrStr());
1714+
r.Append<r.ColIndex("remote_port")>(conn_tracker.remote_endpoint().port());
1715+
r.Append<r.ColIndex("local_addr")>(conn_tracker.local_endpoint().AddrStr());
1716+
r.Append<r.ColIndex("local_port")>(conn_tracker.local_endpoint().port());
1717+
r.Append<r.ColIndex("trace_role")>(conn_tracker.role());
1718+
r.Append<r.ColIndex("req_type")>(static_cast<uint64_t>(req_message.content_type));
1719+
r.Append<r.ColIndex("version")>(static_cast<uint64_t>(req_message.legacy_version));
1720+
r.Append<r.ColIndex("extensions")>(ToJSONString(req_message.extensions), kMaxHTTPHeadersBytes);
1721+
r.Append<r.ColIndex("latency")>(
1722+
CalculateLatency(req_message.timestamp_ns, resp_message.timestamp_ns));
1723+
#ifndef NDEBUG
1724+
r.Append<r.ColIndex("px_info_")>(PXInfoString(conn_tracker, record));
1725+
#endif
1726+
}
1727+
16891728
void SocketTraceConnector::SetupOutput(const std::filesystem::path& path) {
16901729
DCHECK(!path.empty());
16911730

src/stirling/source_connectors/socket_tracer/socket_trace_connector.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ DECLARE_int32(stirling_enable_kafka_tracing);
6666
DECLARE_int32(stirling_enable_mux_tracing);
6767
DECLARE_int32(stirling_enable_amqp_tracing);
6868
DECLARE_int32(stirling_enable_mongodb_tracing);
69+
DECLARE_int32(stirling_enable_tls_tracing);
6970
DECLARE_bool(stirling_disable_self_tracing);
7071
DECLARE_string(stirling_role_to_trace);
7172

@@ -95,9 +96,9 @@ class SocketTraceConnector : public BCCSourceConnector {
9596
public:
9697
static constexpr std::string_view kName = "socket_tracer";
9798
// PROTOCOL_LIST
98-
static constexpr auto kTables =
99-
MakeArray(kConnStatsTable, kHTTPTable, kMySQLTable, kCQLTable, kPGSQLTable, kDNSTable,
100-
kRedisTable, kNATSTable, kKafkaTable, kMuxTable, kAMQPTable, kMongoDBTable);
99+
static constexpr auto kTables = MakeArray(
100+
kConnStatsTable, kHTTPTable, kMySQLTable, kCQLTable, kPGSQLTable, kDNSTable, kRedisTable,
101+
kNATSTable, kKafkaTable, kMuxTable, kAMQPTable, kMongoDBTable, kTLSTable);
101102

102103
static constexpr uint32_t kConnStatsTableNum = TableNum(kTables, kConnStatsTable);
103104
static constexpr uint32_t kHTTPTableNum = TableNum(kTables, kHTTPTable);
@@ -111,6 +112,7 @@ class SocketTraceConnector : public BCCSourceConnector {
111112
static constexpr uint32_t kMuxTableNum = TableNum(kTables, kMuxTable);
112113
static constexpr uint32_t kAMQPTableNum = TableNum(kTables, kAMQPTable);
113114
static constexpr uint32_t kMongoDBTableNum = TableNum(kTables, kMongoDBTable);
115+
static constexpr uint32_t kTLSTableNum = TableNum(kTables, kTLSTable);
114116

115117
static constexpr auto kSamplingPeriod = std::chrono::milliseconds{200};
116118
// TODO(yzhao): This is not used right now. Eventually use this to control data push frequency.

src/stirling/source_connectors/socket_tracer/socket_trace_tables.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@
3232
#include "src/stirling/source_connectors/socket_tracer/nats_table.h"
3333
#include "src/stirling/source_connectors/socket_tracer/pgsql_table.h"
3434
#include "src/stirling/source_connectors/socket_tracer/redis_table.h"
35+
#include "src/stirling/source_connectors/socket_tracer/tls_table.h"

src/stirling/source_connectors/socket_tracer/testing/containers/ssl/nginx.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ http{
1111
ssl_certificate /etc/ssl/server.crt;
1212
ssl_certificate_key /etc/ssl/server.key;
1313

14-
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
14+
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
1515
ssl_prefer_server_ciphers on;
1616
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
1717

src/stirling/source_connectors/socket_tracer/testing/protocol_checkers.cc

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "src/stirling/source_connectors/socket_tracer/testing/protocol_checkers.h"
2020

2121
#include "src/stirling/source_connectors/socket_tracer/http_table.h"
22+
#include "src/stirling/source_connectors/socket_tracer/tls_table.h"
2223
#include "src/stirling/testing/common.h"
2324

2425
namespace px {
@@ -28,6 +29,7 @@ namespace testing {
2829
namespace http = protocols::http;
2930
namespace mux = protocols::mux;
3031
namespace mongodb = protocols::mongodb;
32+
namespace tls = protocols::tls;
3133

3234
//-----------------------------------------------------------------------------
3335
// HTTP Checkers
@@ -105,6 +107,20 @@ std::vector<mongodb::Record> GetTargetRecords(const types::ColumnWrapperRecordBa
105107
return ToRecordVector<mongodb::Record>(record_batch, target_record_indices);
106108
}
107109

110+
template <>
111+
std::vector<tls::Record> ToRecordVector(const types::ColumnWrapperRecordBatch& rb,
112+
const std::vector<size_t>& indices) {
113+
std::vector<tls::Record> result;
114+
115+
for (const auto& idx : indices) {
116+
auto version = rb[kTLSVersionIdx]->Get<types::Int64Value>(idx);
117+
tls::Record r;
118+
r.req.legacy_version = static_cast<tls::LegacyVersion>(version.val);
119+
result.push_back(r);
120+
}
121+
return result;
122+
}
123+
108124
} // namespace testing
109125
} // namespace stirling
110126
} // namespace px

src/stirling/source_connectors/socket_tracer/testing/protocol_checkers.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "src/stirling/source_connectors/socket_tracer/protocols/http/types.h"
3333
#include "src/stirling/source_connectors/socket_tracer/protocols/mongodb/types.h"
3434
#include "src/stirling/source_connectors/socket_tracer/protocols/mux/types.h"
35+
#include "src/stirling/source_connectors/socket_tracer/protocols/tls/types.h"
3536

3637
namespace px {
3738
namespace stirling {

0 commit comments

Comments
 (0)