Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing upstream string formatters #33857

Merged
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
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%
keithmattix marked this conversation as resolved.
Show resolved Hide resolved
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(), ",");
adisuissa marked this conversation as resolved.
Show resolved Hide resolved
});
}}},
{"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;
keithmattix marked this conversation as resolved.
Show resolved Hide resolved
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
Loading