Skip to content

Commit

Permalink
Merge 62e8a3f into 838bc86
Browse files Browse the repository at this point in the history
  • Loading branch information
keithmattix authored May 23, 2024
2 parents 838bc86 + 62e8a3f commit 3d0adae
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 1 deletion.
5 changes: 5 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ new_features:
change: |
Added support to healthcheck with ProxyProtocol in TCP Healthcheck by setting
:ref:`health_check_config <envoy_v3_api_field_config.core.v3.HealthCheck.TcpHealthCheck.proxy_protocol_config>`.
- area: access_log
change: |
added new ``access_log`` command operators to retrieve upstream connection information change: ``%UPSTREAM_PEER_URI_SAN%``,
``%UPSTREAM_PEER_IP_SAN%``, ``%UPSTREAM_PEER_DNS_SAN%``, ``%UPSTREAM_LOCAL_URI_SAN%``, ``%UPSTREAM_LOCAL_DNS_SAN%``,
``%UPSTREAM_LOCAL_IP_SAN%``.
- area: open_telemetry
change: |
added :ref:`stat_prefix
Expand Down
36 changes: 36 additions & 0 deletions docs/root/configuration/observability/access_log/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,42 @@ UDP
UPSTREAM_PEER_CERT_V_END can be customized using a `format string <https://en.cppreference.com/w/cpp/io/manip/put_time>`_.
See :ref:`START_TIME <config_access_log_format_start_time>` for additional format specifiers and examples.

%UPSTREAM_PEER_URI_SAN%
HTTP/TCP/THRIFT
The URIs present in the SAN of the peer certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%UPSTREAM_PEER_DNS_SAN%
HTTP/TCP/THRIFT
The DNS names present in the SAN of the peer certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%UPSTREAM_PEER_IP_SAN%
HTTP/TCP/THRIFT
The ip addresses present in the SAN of the peer certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%UPSTREAM_LOCAL_URI_SAN%
HTTP/TCP/THRIFT
The URIs present in the SAN of the local certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%UPSTREAM_LOCAL_DNS_SAN%
HTTP/TCP/THRIFT
The DNS names present in the SAN of the local certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%UPSTREAM_LOCAL_IP_SAN%
HTTP/TCP/THRIFT
The ip addresses present in the SAN of the local certificate used to establish the upstream TLS connection.
UDP
Not implemented ("-").

%HOSTNAME%
The system hostname.

Expand Down
48 changes: 48 additions & 0 deletions source/common/formatter/stream_info_formatter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,54 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide
return result;
});
}}},
{"UPSTREAM_PEER_URI_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.uriSanPeerCertificate(), ",");
});
}}},
{"UPSTREAM_PEER_DNS_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.dnsSansPeerCertificate(), ",");
});
}}},
{"UPSTREAM_PEER_IP_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.ipSansPeerCertificate(), ",");
});
}}},
{"UPSTREAM_LOCAL_URI_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.uriSanLocalCertificate(), ",");
});
}}},
{"UPSTREAM_LOCAL_DNS_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.dnsSansLocalCertificate(), ",");
});
}}},
{"UPSTREAM_LOCAL_IP_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
return std::make_unique<StreamInfoUpstreamSslConnectionInfoFormatterProvider>(
[](const Ssl::ConnectionInfo& connection_info) {
return absl::StrJoin(connection_info.ipSansLocalCertificate(), ",");
});
}}},
{"DOWNSTREAM_PEER_URI_SAN",
{CommandSyntaxChecker::COMMAND_ONLY,
[](const std::string&, absl::optional<size_t>) {
Expand Down
265 changes: 264 additions & 1 deletion test/common/formatter/substitution_formatter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,270 @@ TEST(SubstitutionFormatterTest, streamInfoFormatterWithSsl) {
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue(expected_cert)));
}

// Test that the upstream peer uri san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, uriSanPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that peer URI SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, uriSanPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty peer URI SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, uriSanPeerCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null peer URI SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_PEER_URI_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that the upstream peer DNS san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, dnsSansPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that peer DNS SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, dnsSansPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty peer DNS SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, dnsSansPeerCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null peer DNS SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_PEER_DNS_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that the upstream peer IP san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, ipSansPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that peer IP SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, ipSansPeerCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty peer IP SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, ipSansPeerCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null peer IP SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_PEER_IP_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that the upstream local DNS san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, dnsSansLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that local DNS SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, dnsSansLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty local DNS SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_DNS_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, dnsSansLocalCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null local DNS SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_DNS_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that the upstream local URI san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, uriSanLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that local URI SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, uriSanLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty local URI SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_URI_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, uriSanLocalCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null local URI SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_URI_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that the upstream local IP san is returned by the formatter.
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san"};
EXPECT_CALL(*connection_info, ipSansLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ("san", upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san")));
}
// Test that local IP SAN delimiter is applied correctly
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
const std::vector<std::string> sans{"san1", "san2"};
EXPECT_CALL(*connection_info, ipSansLocalCertificate()).WillRepeatedly(Return(sans));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::stringValue("san1,san2")));
}
// Test that an empty local IP SAN list returns a null value
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_IP_SAN");
auto connection_info = std::make_shared<Ssl::MockConnectionInfo>();
EXPECT_CALL(*connection_info, ipSansLocalCertificate())
.WillRepeatedly(Return(std::vector<std::string>()));
stream_info.upstreamInfo()->setUpstreamSslConnection(connection_info);
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
// Test that a null connection returns a null local IP SAN
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
stream_info.downstream_connection_info_provider_->setSslConnection(nullptr);
StreamInfoFormatter upstream_format("UPSTREAM_LOCAL_IP_SAN");
EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info));
EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
{
NiceMock<StreamInfo::MockStreamInfo> stream_info;
StreamInfoFormatter upstream_format("UPSTREAM_PEER_SUBJECT");
Expand Down

0 comments on commit 3d0adae

Please sign in to comment.