Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/admin-guide/logging/formatting.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ ppd Proxy Protocol Destination IP received via Proxy Protocol context from the
Dest IP to the |TS|
ppa Proxy Protocol The Authority TLV from Proxy Protocol context from the LB
Authority to the |TS|
pptc Proxy Protocol The TLS cipher from Proxy Protocol context from the LB
TLS Cipher to the |TS|
pptv Proxy Protocol The TLS version from Proxy Protocol context from the LB
TLS version to the |TS|
===== ============== ==========================================================

.. note::
Expand Down
4 changes: 4 additions & 0 deletions include/iocore/net/ProxyProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ class ProxyProtocol
void set_ipv6_addrs(const in6_addr &src_addr, uint16_t src_port, const in6_addr &dst_addr, uint16_t dst_port);

std::optional<std::string_view> get_tlv(const uint8_t tlvCode) const;
std::optional<std::string_view> get_tlv_ssl_version() const;
std::optional<std::string_view> get_tlv_ssl_cipher() const;

ProxyProtocolVersion version = ProxyProtocolVersion::UNDEFINED;
uint16_t ip_family = AF_UNSPEC;
Expand Down Expand Up @@ -134,6 +136,8 @@ class ProxyProtocol

private:
std::string additional_data;

std::optional<std::string_view> _get_tlv_ssl_subtype(uint8_t subtype) const;
};

const size_t PPv1_CONNECTION_HEADER_LEN_MAX = 108;
Expand Down
2 changes: 2 additions & 0 deletions include/proxy/logging/LogAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ class LogAccess
int marshal_proxy_protocol_src_ip(char *); // STR
int marshal_proxy_protocol_dst_ip(char *); // STR
int marshal_proxy_protocol_authority(char *); // STR
int marshal_proxy_protocol_tls_cipher(char *); // STR
int marshal_proxy_protocol_tls_version(char *); // STR

// named fields from within a http header
//
Expand Down
78 changes: 78 additions & 0 deletions src/iocore/net/ProxyProtocol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,84 @@ ProxyProtocol::get_tlv(const uint8_t tlvCode) const
return std::nullopt;
}

/*
* PP2_TYPE_SSL
* struct pp2_tlv_ssl {
* uint8_t client;
* uint32_t verify;
* struct pp2_tlv sub_tlv[0];
* };
*/

std::optional<std::string_view>
ProxyProtocol::_get_tlv_ssl_subtype(uint8_t subtype) const
{
if (auto v = tlv.find(PP2_TYPE_SSL); v != tlv.end() && v->second.length() != 0) {
auto ssl = v->second;

// Is the client connected over TLS
if ((ssl.data()[0] & 0x01) == 0) {
// Not over TLS
return std::nullopt;
}

if (ssl.length() < 5) {
return std::nullopt;
}

// Find the given subtype
uint16_t len = ssl.length();
const char *p = ssl.data() + 5; // Skip client (uint8_t) + verify (uint32_t)
const char *end = ssl.data() + len;
while (p != end) {
if (end - p < 3) {
// The size of a sub TLV entry must be 3 bytes or more
Dbg(dbg_ctl_proxyprotocol_v2, "Remaining data (%ld bytes) is not enough for a sub TLV field", end - p);
return std::nullopt;
}

// Type
uint8_t type = *p;
p += 1;

// Length
uint16_t length = ntohs(*reinterpret_cast<const uint16_t *>(p));
p += 2;

// Value
if (end - p < length) {
// Does not have enough data
Dbg(dbg_ctl_proxyprotocol_v2, "Remaining data (%ld bytes) is not enough for a TLV field (ID:%u LEN:%hu)", end - p, type,
length);
return std::nullopt;
}

// Found it?
if (type == subtype) {
Dbg(dbg_ctl_proxyprotocol, "TLV: ID=%u LEN=%hu", type, length);
return std::string_view(p, length);
}

p += length;
}
}
return std::nullopt;
}

std::optional<std::string_view>
ProxyProtocol::get_tlv_ssl_version() const
{
// The specification only says "the US-ASCII string representation of the TLS version".
// HAProxy sends a string returned by SSL_get_version.
return this->_get_tlv_ssl_subtype(PP2_SUBTYPE_SSL_VERSION);
}

std::optional<std::string_view>
ProxyProtocol::get_tlv_ssl_cipher() const
{
return this->_get_tlv_ssl_subtype(PP2_SUBTYPE_SSL_CIPHER);
}

int
ProxyProtocol::set_additional_data(std::string_view data)
{
Expand Down
42 changes: 40 additions & 2 deletions src/iocore/net/unit_tests/test_ProxyProtocol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,16 @@ TEST_CASE("PROXY Protocol v2 Parser", "[ProxyProtocol][ProxyProtocolv2]")
0x55, 0x49, 0x54, 0x0A, ///<
0x21, ///< version & command
0x11, ///< protocol & family
0x00, 0x17, ///< len
0x00, 0x2B, ///< len
0xC0, 0x00, 0x02, 0x01, ///< src_addr
0xC6, 0x33, 0x64, 0x01, ///< dst_addr
0xC3, 0x50, ///< src_port
0x01, 0xBB, ///< dst_port
0x01, 0x00, 0x02, 0x68, 0x32, /// PP2_TYPE_ALPN (h2)
0x02, 0x00, 0x03, 0x61, 0x62, 0x63 /// PP2_TYPE_AUTHORITY (abc)
0x02, 0x00, 0x03, 0x61, 0x62, 0x63, /// PP2_TYPE_AUTHORITY (abc)
0x20, 0x00, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, /// PP2_TYPE_SSL (client=0x01, verify=0)
0x23, 0x00, 0x03, 0x58, 0x59, 0x5A, /// PP2_SUBTYPE_SSL_CIPHER (XYZ)
0x21, 0x00, 0x03, 0x54, 0x4C, 0x53, /// PP2_SUBTYPE_SSL_VERSION (TLS)
};

swoc::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data));
Expand All @@ -327,6 +330,41 @@ TEST_CASE("PROXY Protocol v2 Parser", "[ProxyProtocol][ProxyProtocolv2]")

CHECK(pp_info.tlv[PP2_TYPE_ALPN] == "h2");
CHECK(pp_info.tlv[PP2_TYPE_AUTHORITY] == "abc");

CHECK(pp_info.get_tlv_ssl_cipher() == "XYZ");
CHECK(pp_info.get_tlv_ssl_version() == "TLS");
}

SECTION("TLVs with PP2_TYPE_SSL but SSL is unused")
{
uint8_t raw_data[] = {
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface
0x55, 0x49, 0x54, 0x0A, ///<
0x21, ///< version & command
0x11, ///< protocol & family
0x00, 0x14, ///< len
0xC0, 0x00, 0x02, 0x01, ///< src_addr
0xC6, 0x33, 0x64, 0x01, ///< dst_addr
0xC3, 0x50, ///< src_port
0x01, 0xBB, ///< dst_port
0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, /// PP2_TYPE_SSL (client=0x00, verify=0)
};

swoc::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data));

ProxyProtocol pp_info;
REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size());

REQUIRE(ats_ip_pton("192.0.2.1:50000", src_addr) == 0);
REQUIRE(ats_ip_pton("198.51.100.1:443", dst_addr) == 0);

CHECK(pp_info.version == ProxyProtocolVersion::V2);
CHECK(pp_info.ip_family == AF_INET);
CHECK(pp_info.src_addr == src_addr);
CHECK(pp_info.dst_addr == dst_addr);

CHECK(pp_info.get_tlv_ssl_cipher().has_value() == false);
CHECK(pp_info.get_tlv_ssl_version().has_value() == false);
}

SECTION("TLVs with extra data")
Expand Down
10 changes: 10 additions & 0 deletions src/proxy/logging/Log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,16 @@ Log::init_fields()
global_field_list.add(field, false);
field_symbol_hash.emplace("ppa", field);

field = new LogField("proxy_protocol_tls_cipher", "pptc", LogField::STRING, &LogAccess::marshal_proxy_protocol_tls_cipher,
&LogAccess::unmarshal_str);
global_field_list.add(field, false);
field_symbol_hash.emplace("pptc", field);

field = new LogField("proxy_protocol_tls_version", "pptv", LogField::STRING, &LogAccess::marshal_proxy_protocol_tls_version,
&LogAccess::unmarshal_str);
global_field_list.add(field, false);
field_symbol_hash.emplace("pptv", field);

field = new LogField("version_build_number", "vbn", LogField::STRING, &LogAccess::marshal_version_build_number,
&LogAccess::unmarshal_str);
global_field_list.add(field, false);
Expand Down
42 changes: 42 additions & 0 deletions src/proxy/logging/LogAccess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,48 @@ LogAccess::marshal_proxy_protocol_authority(char *buf)
return len;
}

int
LogAccess::marshal_proxy_protocol_tls_cipher(char *buf)
{
int len = INK_MIN_ALIGN;

if (m_http_sm) {
if (auto cipher = m_http_sm->t_state.pp_info.get_tlv_ssl_cipher(); cipher) {
len = padded_length(cipher->size() + 1);
if (buf) {
marshal_mem(buf, cipher->data(), cipher->size(), len);
}
} else {
if (buf) {
// This prints the default value ("-")
marshal_mem(buf, nullptr, 0, len);
}
}
}
return len;
}

int
LogAccess::marshal_proxy_protocol_tls_version(char *buf)
{
int len = INK_MIN_ALIGN;

if (m_http_sm) {
if (auto version = m_http_sm->t_state.pp_info.get_tlv_ssl_version(); version) {
len = padded_length(version->size() + 1);
if (buf) {
marshal_mem(buf, version->data(), version->size(), len);
}
} else {
if (buf) {
// This prints the default value ("-")
marshal_mem(buf, nullptr, 0, len);
}
}
}
return len;
}

/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
int
Expand Down