Line data Source code
1 : #include "source/common/http/conn_manager_utility.h"
2 :
3 : #include <atomic>
4 : #include <cstdint>
5 : #include <string>
6 :
7 : #include "envoy/type/v3/percent.pb.h"
8 :
9 : #include "source/common/common/empty_string.h"
10 : #include "source/common/common/enum_to_int.h"
11 : #include "source/common/common/utility.h"
12 : #include "source/common/http/conn_manager_config.h"
13 : #include "source/common/http/header_utility.h"
14 : #include "source/common/http/headers.h"
15 : #include "source/common/http/http1/codec_impl.h"
16 : #include "source/common/http/http2/codec_impl.h"
17 : #include "source/common/http/path_utility.h"
18 : #include "source/common/http/utility.h"
19 : #include "source/common/network/utility.h"
20 : #include "source/common/runtime/runtime_features.h"
21 : #include "source/common/stream_info/utility.h"
22 : #include "source/common/tracing/http_tracer_impl.h"
23 :
24 : #include "absl/strings/str_cat.h"
25 : #include "absl/strings/str_join.h"
26 :
27 : namespace Envoy {
28 : namespace Http {
29 : namespace {
30 :
31 302 : absl::string_view getScheme(absl::string_view forwarded_proto, bool is_ssl) {
32 302 : if (Utility::schemeIsValid(forwarded_proto)) {
33 302 : return forwarded_proto;
34 302 : }
35 0 : return is_ssl ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http;
36 302 : }
37 :
38 : } // namespace
39 : std::string ConnectionManagerUtility::determineNextProtocol(Network::Connection& connection,
40 101 : const Buffer::Instance& data) {
41 101 : const std::string next_protocol = connection.nextProtocol();
42 101 : if (!next_protocol.empty()) {
43 0 : return next_protocol;
44 0 : }
45 :
46 : // See if the data we have so far shows the HTTP/2 prefix. We ignore the case where someone sends
47 : // us the first few bytes of the HTTP/2 prefix since in all public cases we use SSL/ALPN. For
48 : // internal cases this should practically never happen.
49 101 : if (data.startsWith(Http2::CLIENT_MAGIC_PREFIX)) {
50 0 : return Utility::AlpnNames::get().Http2;
51 0 : }
52 :
53 101 : return "";
54 101 : }
55 :
56 : ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
57 : Network::Connection& connection, const Buffer::Instance& data,
58 : ServerConnectionCallbacks& callbacks, Stats::Scope& scope, Random::RandomGenerator& random,
59 : Http1::CodecStats::AtomicPtr& http1_codec_stats,
60 : Http2::CodecStats::AtomicPtr& http2_codec_stats, const Http1Settings& http1_settings,
61 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
62 : uint32_t max_request_headers_kb, uint32_t max_request_headers_count,
63 : envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
64 : headers_with_underscores_action,
65 101 : Server::OverloadManager& overload_manager) {
66 101 : if (determineNextProtocol(connection, data) == Utility::AlpnNames::get().Http2) {
67 0 : Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope);
68 0 : return std::make_unique<Http2::ServerConnectionImpl>(
69 0 : connection, callbacks, stats, random, http2_options, max_request_headers_kb,
70 0 : max_request_headers_count, headers_with_underscores_action, overload_manager);
71 101 : } else {
72 101 : Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope);
73 101 : return std::make_unique<Http1::ServerConnectionImpl>(
74 101 : connection, stats, callbacks, http1_settings, max_request_headers_kb,
75 101 : max_request_headers_count, headers_with_underscores_action, overload_manager);
76 101 : }
77 101 : }
78 :
79 : ConnectionManagerUtility::MutateRequestHeadersResult ConnectionManagerUtility::mutateRequestHeaders(
80 : RequestHeaderMap& request_headers, Network::Connection& connection,
81 : ConnectionManagerConfig& config, const Router::Config& route_config,
82 507 : const LocalInfo::LocalInfo& local_info, const StreamInfo::StreamInfo& stream_info) {
83 :
84 507 : for (const auto& extension : config.earlyHeaderMutationExtensions()) {
85 0 : if (!extension->mutate(request_headers, stream_info)) {
86 0 : break;
87 0 : }
88 0 : }
89 :
90 : // If this is a Upgrade request, do not remove the Connection and Upgrade headers,
91 : // as we forward them verbatim to the upstream hosts.
92 507 : if (!Utility::isUpgrade(request_headers)) {
93 506 : request_headers.removeConnection();
94 506 : request_headers.removeUpgrade();
95 506 : if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.sanitize_te")) {
96 506 : request_headers.removeTE();
97 506 : }
98 506 : }
99 :
100 : // Clean proxy headers.
101 507 : request_headers.removeEnvoyInternalRequest();
102 507 : request_headers.removeKeepAlive();
103 507 : request_headers.removeProxyConnection();
104 507 : request_headers.removeTransferEncoding();
105 :
106 : // Sanitize referer field if exists.
107 507 : auto result = request_headers.get(Http::CustomHeaders::get().Referer);
108 507 : if (!result.empty()) {
109 0 : if (result.size() > 1 || !Utility::isValidRefererValue(result[0]->value().getStringView())) {
110 : // A request header shouldn't have multiple referer field.
111 0 : request_headers.remove(Http::CustomHeaders::get().Referer);
112 0 : }
113 0 : }
114 :
115 : // If we are "using remote address" this means that we create/append to XFF with our immediate
116 : // peer. Cases where we don't "use remote address" include trusted double proxy where we expect
117 : // our peer to have already properly set XFF, etc.
118 507 : Network::Address::InstanceConstSharedPtr final_remote_address;
119 507 : bool allow_trusted_address_checks = false;
120 507 : const uint32_t xff_num_trusted_hops = config.xffNumTrustedHops();
121 :
122 507 : if (config.useRemoteAddress()) {
123 120 : allow_trusted_address_checks = request_headers.ForwardedFor() == nullptr;
124 : // If there are any trusted proxies in front of this Envoy instance (as indicated by
125 : // the xff_num_trusted_hops configuration option), get the trusted client address
126 : // from the XFF before we append to XFF.
127 120 : if (xff_num_trusted_hops > 0) {
128 0 : final_remote_address =
129 0 : Utility::getLastAddressFromXFF(request_headers, xff_num_trusted_hops - 1).address_;
130 0 : }
131 : // If there aren't any trusted proxies in front of this Envoy instance, or there
132 : // are but they didn't populate XFF properly, the trusted client address is the
133 : // source address of the immediate downstream's connection to us.
134 120 : if (final_remote_address == nullptr) {
135 120 : final_remote_address = connection.connectionInfoProvider().remoteAddress();
136 120 : }
137 120 : if (!config.skipXffAppend()) {
138 120 : if (Network::Utility::isLoopbackAddress(
139 120 : *connection.connectionInfoProvider().remoteAddress())) {
140 98 : Utility::appendXff(request_headers, config.localAddress());
141 120 : } else {
142 22 : Utility::appendXff(request_headers, *connection.connectionInfoProvider().remoteAddress());
143 22 : }
144 120 : }
145 : // If the prior hop is not a trusted proxy, overwrite any
146 : // x-forwarded-proto/x-forwarded-port value it set as untrusted. Alternately if no
147 : // x-forwarded-proto/x-forwarded-port header exists, add one if configured.
148 120 : if (xff_num_trusted_hops == 0 || request_headers.ForwardedProto() == nullptr) {
149 120 : request_headers.setReferenceForwardedProto(
150 120 : connection.ssl() ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http);
151 120 : }
152 120 : if (config.appendXForwardedPort() &&
153 120 : (xff_num_trusted_hops == 0 || request_headers.ForwardedPort() == nullptr)) {
154 0 : const Envoy::Network::Address::Ip* ip =
155 0 : connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
156 0 : if (ip) {
157 0 : request_headers.setForwardedPort(ip->port());
158 0 : }
159 0 : }
160 449 : } else {
161 : // If we are not using remote address, attempt to pull a valid IPv4 or IPv6 address out of XFF
162 : // or through an extension. An extension might be needed when XFF doesn't work (e.g. an
163 : // irregular network).
164 : //
165 : // If we find one, it will be used as the downstream address for logging. It may or may not be
166 : // used for determining internal/external status (see below).
167 387 : OriginalIPDetectionParams params = {request_headers,
168 387 : connection.connectionInfoProvider().remoteAddress()};
169 387 : for (const auto& detection_extension : config.originalIpDetectionExtensions()) {
170 387 : const auto result = detection_extension->detect(params);
171 :
172 387 : if (result.reject_options.has_value()) {
173 0 : return {nullptr, result.reject_options};
174 0 : }
175 :
176 387 : if (result.detected_remote_address) {
177 179 : final_remote_address = result.detected_remote_address;
178 179 : allow_trusted_address_checks = result.allow_trusted_address_checks;
179 179 : break;
180 179 : }
181 387 : }
182 387 : }
183 :
184 : // If the x-forwarded-proto header is not set, set it here, since Envoy uses it for determining
185 : // scheme and communicating it upstream.
186 507 : if (!request_headers.ForwardedProto()) {
187 387 : request_headers.setReferenceForwardedProto(connection.ssl() ? Headers::get().SchemeValues.Https
188 387 : : Headers::get().SchemeValues.Http);
189 387 : }
190 :
191 : // Usually, the x-forwarded-port header comes with x-forwarded-proto header. If the
192 : // x-forwarded-proto header is not set, set it here if append-x-forwarded-port is configured.
193 507 : if (config.appendXForwardedPort() && !request_headers.ForwardedPort()) {
194 0 : const Envoy::Network::Address::Ip* ip =
195 0 : connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
196 0 : if (ip) {
197 0 : request_headers.setForwardedPort(ip->port());
198 0 : }
199 0 : }
200 :
201 507 : if (config.schemeToSet().has_value()) {
202 0 : request_headers.setScheme(config.schemeToSet().value());
203 0 : request_headers.setForwardedProto(config.schemeToSet().value());
204 0 : }
205 :
206 : // If :scheme is not set, sets :scheme based on X-Forwarded-Proto if a valid scheme,
207 : // else encryption level.
208 : // X-Forwarded-Proto and :scheme may still differ if different values are sent from downstream.
209 507 : if (!request_headers.Scheme()) {
210 302 : request_headers.setScheme(
211 302 : getScheme(request_headers.getForwardedProtoValue(), connection.ssl() != nullptr));
212 302 : }
213 507 : if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.lowercase_scheme")) {
214 507 : request_headers.setScheme(absl::AsciiStrToLower(request_headers.getSchemeValue()));
215 507 : }
216 :
217 : // At this point we can determine whether this is an internal or external request. The
218 : // determination of internal status uses the following:
219 : // 1) After remote address/XFF appending, the XFF header must contain a *single* address.
220 : // 2) The single address must be an internal address.
221 : // 3) If configured to not use remote address, but no XFF header is available, even if the real
222 : // remote is internal, the request is considered external.
223 : // HUGE WARNING: The way we do this is not optimal but is how it worked "from the beginning" so
224 : // we can't change it at this point. In the future we will likely need to add
225 : // additional inference modes and make this mode legacy.
226 507 : const bool internal_request =
227 507 : allow_trusted_address_checks && final_remote_address != nullptr &&
228 507 : config.internalAddressConfig().isInternalAddress(*final_remote_address);
229 :
230 : // After determining internal request status, if there is no final remote address, due to no XFF,
231 : // busted XFF, etc., use the direct connection remote address for logging.
232 507 : if (final_remote_address == nullptr) {
233 208 : final_remote_address = connection.connectionInfoProvider().remoteAddress();
234 208 : }
235 :
236 : // Edge request is the request from external clients to front Envoy.
237 : // Request from front Envoy to the internal service will be treated as not edge request.
238 507 : const bool edge_request = !internal_request && config.useRemoteAddress();
239 :
240 : // If internal request, set header and do other internal only modifications.
241 507 : if (internal_request) {
242 179 : request_headers.setReferenceEnvoyInternalRequest(
243 179 : Headers::get().EnvoyInternalRequestValues.True);
244 475 : } else {
245 328 : cleanInternalHeaders(request_headers, edge_request, route_config.internalOnlyHeaders());
246 328 : }
247 :
248 507 : if (config.userAgent()) {
249 0 : request_headers.setEnvoyDownstreamServiceCluster(config.userAgent().value());
250 0 : const HeaderEntry* user_agent_header = request_headers.UserAgent();
251 0 : if (!user_agent_header || user_agent_header->value().empty()) {
252 : // Following setReference() is safe because user agent is constant for the life of the
253 : // listener.
254 0 : request_headers.setReferenceUserAgent(config.userAgent().value());
255 0 : }
256 :
257 : // TODO(htuch): should this be under the config.userAgent() condition or in the outer scope?
258 0 : if (!local_info.nodeName().empty()) {
259 : // Following setReference() is safe because local info is constant for the life of the server.
260 0 : request_headers.setReferenceEnvoyDownstreamServiceNode(local_info.nodeName());
261 0 : }
262 0 : }
263 :
264 507 : if (!config.via().empty()) {
265 0 : Utility::appendVia(request_headers, config.via());
266 0 : }
267 :
268 : // If we are an external request, AND we are "using remote address" (see above), we set
269 : // x-envoy-external-address since this is our first ingress point into the trusted network.
270 507 : if (edge_request && final_remote_address->type() == Network::Address::Type::Ip) {
271 120 : request_headers.setEnvoyExternalAddress(final_remote_address->ip()->addressAsString());
272 120 : }
273 :
274 : // Generate x-request-id for all edge requests, or if there is none.
275 507 : if (config.generateRequestId()) {
276 409 : auto rid_extension = config.requestIDExtension();
277 : // Unconditionally set a request ID if we are allowed to override it from
278 : // the edge. Otherwise just ensure it is set.
279 409 : const bool force_set = !config.preserveExternalRequestId() && edge_request;
280 409 : rid_extension->set(request_headers, force_set);
281 409 : }
282 :
283 507 : if (connection.connecting() && request_headers.get(Headers::get().EarlyData).empty()) {
284 : // Add an Early-Data header to indicate that this is a 0-RTT request according to
285 : // https://datatracker.ietf.org/doc/html/rfc8470#section-5.1.
286 0 : HeaderString value;
287 0 : value.setCopy("1");
288 0 : request_headers.addViaMove(HeaderString(Headers::get().EarlyData), std::move(value));
289 0 : }
290 507 : mutateXfccRequestHeader(request_headers, connection, config);
291 :
292 507 : return {final_remote_address, absl::nullopt};
293 507 : }
294 :
295 : void ConnectionManagerUtility::cleanInternalHeaders(
296 : RequestHeaderMap& request_headers, bool edge_request,
297 328 : const std::list<Http::LowerCaseString>& internal_only_headers) {
298 328 : if (edge_request) {
299 : // Headers to be stripped from edge requests, i.e. to sanitize so
300 : // clients can't inject values.
301 120 : request_headers.removeEnvoyDecoratorOperation();
302 120 : request_headers.removeEnvoyDownstreamServiceCluster();
303 120 : request_headers.removeEnvoyDownstreamServiceNode();
304 120 : request_headers.removeEnvoyOriginalPath();
305 120 : }
306 :
307 : // Headers to be stripped from edge *and* intermediate-hop external requests.
308 : // TODO: some of these should only be stripped at edge, i.e. moved into
309 : // the block above.
310 328 : request_headers.removeEnvoyRetriableStatusCodes();
311 328 : request_headers.removeEnvoyRetriableHeaderNames();
312 328 : request_headers.removeEnvoyRetryOn();
313 328 : request_headers.removeEnvoyRetryGrpcOn();
314 328 : request_headers.removeEnvoyMaxRetries();
315 328 : request_headers.removeEnvoyUpstreamAltStatName();
316 328 : request_headers.removeEnvoyUpstreamRequestTimeoutMs();
317 328 : request_headers.removeEnvoyUpstreamRequestPerTryTimeoutMs();
318 328 : request_headers.removeEnvoyUpstreamRequestTimeoutAltResponse();
319 328 : request_headers.removeEnvoyExpectedRequestTimeoutMs();
320 328 : request_headers.removeEnvoyForceTrace();
321 328 : request_headers.removeEnvoyIpTags();
322 328 : request_headers.removeEnvoyOriginalUrl();
323 328 : request_headers.removeEnvoyHedgeOnPerTryTimeout();
324 :
325 328 : for (const LowerCaseString& header : internal_only_headers) {
326 0 : request_headers.remove(header);
327 0 : }
328 328 : }
329 :
330 : Tracing::Reason ConnectionManagerUtility::mutateTracingRequestHeader(
331 : RequestHeaderMap& request_headers, Runtime::Loader& runtime, ConnectionManagerConfig& config,
332 507 : const Router::Route* route) {
333 507 : Tracing::Reason final_reason = Tracing::Reason::NotTraceable;
334 507 : if (!config.tracingConfig()) {
335 507 : return final_reason;
336 507 : }
337 :
338 0 : auto rid_extension = config.requestIDExtension();
339 0 : if (!rid_extension->useRequestIdForTraceSampling()) {
340 0 : return Tracing::Reason::Sampling;
341 0 : }
342 0 : const auto rid_to_integer = rid_extension->getInteger(request_headers);
343 : // Skip if request-id is corrupted, or non-existent
344 0 : if (!rid_to_integer.has_value()) {
345 0 : return final_reason;
346 0 : }
347 0 : const uint64_t result = rid_to_integer.value() % 10000;
348 :
349 0 : const envoy::type::v3::FractionalPercent* client_sampling =
350 0 : &config.tracingConfig()->client_sampling_;
351 0 : const envoy::type::v3::FractionalPercent* random_sampling =
352 0 : &config.tracingConfig()->random_sampling_;
353 0 : const envoy::type::v3::FractionalPercent* overall_sampling =
354 0 : &config.tracingConfig()->overall_sampling_;
355 :
356 0 : if (route && route->tracingConfig()) {
357 0 : client_sampling = &route->tracingConfig()->getClientSampling();
358 0 : random_sampling = &route->tracingConfig()->getRandomSampling();
359 0 : overall_sampling = &route->tracingConfig()->getOverallSampling();
360 0 : }
361 :
362 : // Do not apply tracing transformations if we are currently tracing.
363 0 : final_reason = rid_extension->getTraceReason(request_headers);
364 0 : if (Tracing::Reason::NotTraceable == final_reason) {
365 0 : if (request_headers.ClientTraceId() &&
366 0 : runtime.snapshot().featureEnabled("tracing.client_enabled", *client_sampling)) {
367 0 : final_reason = Tracing::Reason::ClientForced;
368 0 : rid_extension->setTraceReason(request_headers, final_reason);
369 0 : } else if (request_headers.EnvoyForceTrace()) {
370 0 : final_reason = Tracing::Reason::ServiceForced;
371 0 : rid_extension->setTraceReason(request_headers, final_reason);
372 0 : } else if (runtime.snapshot().featureEnabled("tracing.random_sampling", *random_sampling,
373 0 : result)) {
374 0 : final_reason = Tracing::Reason::Sampling;
375 0 : rid_extension->setTraceReason(request_headers, final_reason);
376 0 : }
377 0 : }
378 :
379 0 : if (final_reason != Tracing::Reason::NotTraceable &&
380 0 : !runtime.snapshot().featureEnabled("tracing.global_enabled", *overall_sampling, result)) {
381 0 : final_reason = Tracing::Reason::NotTraceable;
382 0 : rid_extension->setTraceReason(request_headers, final_reason);
383 0 : }
384 :
385 0 : return final_reason;
386 0 : }
387 :
388 : void ConnectionManagerUtility::mutateXfccRequestHeader(RequestHeaderMap& request_headers,
389 : Network::Connection& connection,
390 507 : ConnectionManagerConfig& config) {
391 : // When AlwaysForwardOnly is set, always forward the XFCC header without modification.
392 507 : if (config.forwardClientCert() == ForwardClientCertType::AlwaysForwardOnly) {
393 0 : return;
394 0 : }
395 : // When Sanitize is set, or the connection is not mutual TLS, remove the XFCC header.
396 507 : if (config.forwardClientCert() == ForwardClientCertType::Sanitize ||
397 507 : !(connection.ssl() && connection.ssl()->peerCertificatePresented())) {
398 507 : request_headers.removeForwardedClientCert();
399 507 : return;
400 507 : }
401 :
402 : // When ForwardOnly is set, always forward the XFCC header without modification.
403 0 : if (config.forwardClientCert() == ForwardClientCertType::ForwardOnly) {
404 0 : return;
405 0 : }
406 :
407 : // TODO(myidpt): Handle the special characters in By and URI fields.
408 : // TODO: Optimize client_cert_details based on perf analysis (direct string appending may be more
409 : // preferable).
410 0 : std::vector<std::string> client_cert_details;
411 : // When AppendForward or SanitizeSet is set, the client certificate information should be set into
412 : // the XFCC header.
413 0 : if (config.forwardClientCert() == ForwardClientCertType::AppendForward ||
414 0 : config.forwardClientCert() == ForwardClientCertType::SanitizeSet) {
415 0 : const auto uri_sans_local_cert = connection.ssl()->uriSanLocalCertificate();
416 0 : if (!uri_sans_local_cert.empty()) {
417 0 : for (const std::string& uri : uri_sans_local_cert) {
418 0 : client_cert_details.push_back(absl::StrCat("By=", uri));
419 0 : }
420 0 : }
421 0 : const std::string cert_digest = connection.ssl()->sha256PeerCertificateDigest();
422 0 : if (!cert_digest.empty()) {
423 0 : client_cert_details.push_back(absl::StrCat("Hash=", cert_digest));
424 0 : }
425 0 : for (const auto& detail : config.setCurrentClientCertDetails()) {
426 0 : switch (detail) {
427 0 : case ClientCertDetailsType::Cert: {
428 0 : const std::string peer_cert = connection.ssl()->urlEncodedPemEncodedPeerCertificate();
429 0 : if (!peer_cert.empty()) {
430 0 : client_cert_details.push_back(absl::StrCat("Cert=\"", peer_cert, "\""));
431 0 : }
432 0 : break;
433 0 : }
434 0 : case ClientCertDetailsType::Chain: {
435 0 : const std::string peer_chain = connection.ssl()->urlEncodedPemEncodedPeerCertificateChain();
436 0 : if (!peer_chain.empty()) {
437 0 : client_cert_details.push_back(absl::StrCat("Chain=\"", peer_chain, "\""));
438 0 : }
439 0 : break;
440 0 : }
441 0 : case ClientCertDetailsType::Subject:
442 : // The "Subject" key still exists even if the subject is empty.
443 0 : client_cert_details.push_back(
444 0 : absl::StrCat("Subject=\"", connection.ssl()->subjectPeerCertificate(), "\""));
445 0 : break;
446 0 : case ClientCertDetailsType::URI: {
447 : // The "URI" key still exists even if the URI is empty.
448 0 : const auto sans = connection.ssl()->uriSanPeerCertificate();
449 0 : if (!sans.empty()) {
450 0 : for (const std::string& uri : sans) {
451 0 : client_cert_details.push_back(absl::StrCat("URI=", uri));
452 0 : }
453 0 : } else {
454 0 : client_cert_details.push_back("URI=");
455 0 : }
456 0 : break;
457 0 : }
458 0 : case ClientCertDetailsType::DNS: {
459 0 : auto dns_sans = connection.ssl()->dnsSansPeerCertificate();
460 0 : if (!dns_sans.empty()) {
461 0 : for (const std::string& dns : dns_sans) {
462 0 : client_cert_details.push_back(absl::StrCat("DNS=", dns));
463 0 : }
464 0 : }
465 0 : break;
466 0 : }
467 0 : }
468 0 : }
469 0 : }
470 :
471 0 : const std::string client_cert_details_str = absl::StrJoin(client_cert_details, ";");
472 :
473 0 : ENVOY_BUG(config.forwardClientCert() == ForwardClientCertType::AppendForward ||
474 0 : config.forwardClientCert() == ForwardClientCertType::SanitizeSet,
475 0 : "error in client cert logic");
476 0 : if (config.forwardClientCert() == ForwardClientCertType::AppendForward) {
477 0 : request_headers.appendForwardedClientCert(client_cert_details_str, ",");
478 0 : } else if (config.forwardClientCert() == ForwardClientCertType::SanitizeSet) {
479 0 : request_headers.setForwardedClientCert(client_cert_details_str);
480 0 : }
481 0 : }
482 :
483 : void ConnectionManagerUtility::mutateResponseHeaders(ResponseHeaderMap& response_headers,
484 : const RequestHeaderMap* request_headers,
485 : ConnectionManagerConfig& config,
486 : const std::string& via,
487 : const StreamInfo::StreamInfo& stream_info,
488 : absl::string_view proxy_name,
489 611 : bool clear_hop_by_hop) {
490 611 : if (request_headers != nullptr && Utility::isUpgrade(*request_headers) &&
491 611 : Utility::isUpgrade(response_headers)) {
492 : // As in mutateRequestHeaders, Upgrade responses have special handling.
493 : //
494 : // Unlike mutateRequestHeaders there is no explicit protocol check. If Envoy is proxying an
495 : // upgrade response it has already passed the protocol checks.
496 4 : const bool no_body =
497 4 : (!response_headers.TransferEncoding() && !response_headers.ContentLength());
498 :
499 4 : const bool is_1xx = CodeUtility::is1xx(Utility::getResponseStatus(response_headers));
500 :
501 : // We are explicitly forbidden from setting content-length for 1xx responses
502 : // (RFC7230, Section 3.3.2). We ignore 204 because this is an upgrade.
503 4 : if (no_body && !is_1xx) {
504 2 : response_headers.setContentLength(uint64_t(0));
505 2 : }
506 607 : } else {
507 : // Only clear these hop by hop headers for non-upgrade connections.
508 607 : if (clear_hop_by_hop) {
509 607 : response_headers.removeConnection();
510 607 : response_headers.removeUpgrade();
511 607 : }
512 607 : }
513 611 : if (clear_hop_by_hop) {
514 611 : response_headers.removeTransferEncoding();
515 611 : response_headers.removeKeepAlive();
516 611 : response_headers.removeProxyConnection();
517 611 : }
518 :
519 611 : if (request_headers != nullptr &&
520 611 : (config.alwaysSetRequestIdInResponse() || request_headers->EnvoyForceTrace())) {
521 0 : config.requestIDExtension()->setInResponse(response_headers, *request_headers);
522 0 : }
523 611 : if (!via.empty()) {
524 1 : Utility::appendVia(response_headers, via);
525 1 : }
526 :
527 611 : setProxyStatusHeader(response_headers, config, stream_info, proxy_name);
528 611 : }
529 :
530 : void ConnectionManagerUtility::setProxyStatusHeader(ResponseHeaderMap& response_headers,
531 : const ConnectionManagerConfig& config,
532 : const StreamInfo::StreamInfo& stream_info,
533 611 : absl::string_view proxy_name) {
534 611 : if (auto* proxy_status_config = config.proxyStatusConfig(); proxy_status_config != nullptr) {
535 : // Writing the Proxy-Status header is gated on the existence of
536 : // |proxy_status_config|. The |details| field and other internals are generated in
537 : // fromStreamInfo().
538 0 : if (absl::optional<StreamInfo::ProxyStatusError> proxy_status =
539 0 : StreamInfo::ProxyStatusUtils::fromStreamInfo(stream_info);
540 0 : proxy_status.has_value()) {
541 0 : response_headers.appendProxyStatus(
542 0 : StreamInfo::ProxyStatusUtils::makeProxyStatusHeader(stream_info, *proxy_status,
543 0 : proxy_name, *proxy_status_config),
544 0 : ", ");
545 : // Apply the recommended response code, if configured and applicable.
546 0 : if (proxy_status_config->set_recommended_response_code()) {
547 0 : if (absl::optional<Http::Code> response_code =
548 0 : StreamInfo::ProxyStatusUtils::recommendedHttpStatusCode(*proxy_status);
549 0 : response_code.has_value()) {
550 0 : response_headers.setStatus(std::to_string(enumToInt(*response_code)));
551 0 : }
552 0 : }
553 0 : }
554 0 : }
555 611 : }
556 :
557 : ConnectionManagerUtility::NormalizePathAction
558 : ConnectionManagerUtility::maybeNormalizePath(RequestHeaderMap& request_headers,
559 507 : const ConnectionManagerConfig& config) {
560 507 : if (!request_headers.Path()) {
561 0 : return NormalizePathAction::Continue; // It's as valid as it is going to get.
562 0 : }
563 :
564 507 : auto fragment_pos = request_headers.getPathValue().find('#');
565 507 : if (fragment_pos != absl::string_view::npos) {
566 0 : if (Runtime::runtimeFeatureEnabled(
567 0 : "envoy.reloadable_features.http_reject_path_with_fragment")) {
568 0 : return NormalizePathAction::Reject;
569 0 : }
570 : // Check runtime override and throw away fragment from URI path
571 0 : request_headers.setPath(request_headers.getPathValue().substr(0, fragment_pos));
572 0 : }
573 :
574 507 : NormalizePathAction final_action = NormalizePathAction::Continue;
575 507 : const auto escaped_slashes_action = config.pathWithEscapedSlashesAction();
576 507 : ASSERT(escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::
577 507 : v3::HttpConnectionManager::IMPLEMENTATION_SPECIFIC_DEFAULT);
578 507 : if (escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::v3::
579 507 : HttpConnectionManager::KEEP_UNCHANGED) {
580 0 : auto escaped_slashes_result = PathUtil::unescapeSlashes(request_headers);
581 0 : if (escaped_slashes_result == PathUtil::UnescapeSlashesResult::FoundAndUnescaped) {
582 0 : if (escaped_slashes_action == envoy::extensions::filters::network::http_connection_manager::
583 0 : v3::HttpConnectionManager::REJECT_REQUEST) {
584 0 : return NormalizePathAction::Reject;
585 0 : } else if (escaped_slashes_action ==
586 0 : envoy::extensions::filters::network::http_connection_manager::v3::
587 0 : HttpConnectionManager::UNESCAPE_AND_REDIRECT) {
588 0 : final_action = NormalizePathAction::Redirect;
589 0 : } else {
590 0 : ASSERT(escaped_slashes_action ==
591 0 : envoy::extensions::filters::network::http_connection_manager::v3::
592 0 : HttpConnectionManager::UNESCAPE_AND_FORWARD);
593 0 : }
594 0 : }
595 0 : }
596 :
597 507 : if (config.shouldNormalizePath() && !PathUtil::canonicalPath(request_headers)) {
598 0 : return NormalizePathAction::Reject;
599 0 : }
600 :
601 : // Merge slashes after path normalization to catch potential edge cases with percent encoding.
602 507 : if (config.shouldMergeSlashes()) {
603 98 : PathUtil::mergeSlashes(request_headers);
604 98 : }
605 :
606 507 : return final_action;
607 507 : }
608 :
609 : absl::optional<uint32_t>
610 : ConnectionManagerUtility::maybeNormalizeHost(RequestHeaderMap& request_headers,
611 507 : const ConnectionManagerConfig& config, uint32_t port) {
612 507 : if (config.shouldStripTrailingHostDot()) {
613 0 : HeaderUtility::stripTrailingHostDot(request_headers);
614 0 : }
615 507 : if (config.stripPortType() == Http::StripPortType::Any) {
616 0 : return HeaderUtility::stripPortFromHost(request_headers, absl::nullopt);
617 507 : } else if (config.stripPortType() == Http::StripPortType::MatchingHost) {
618 0 : return HeaderUtility::stripPortFromHost(request_headers, port);
619 0 : }
620 507 : return absl::nullopt;
621 507 : }
622 :
623 : } // namespace Http
624 : } // namespace Envoy
|