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/forward_client_cert.h"
14
#include "source/common/http/header_utility.h"
15
#include "source/common/http/headers.h"
16
#include "source/common/http/http1/codec_impl.h"
17
#include "source/common/http/http2/codec_impl.h"
18
#include "source/common/http/matching/data_impl.h"
19
#include "source/common/http/path_utility.h"
20
#include "source/common/http/utility.h"
21
#include "source/common/matcher/matcher.h"
22
#include "source/common/network/utility.h"
23
#include "source/common/runtime/runtime_features.h"
24
#include "source/common/stream_info/utility.h"
25
#include "source/common/tracing/http_tracer_impl.h"
26

            
27
#include "absl/strings/str_cat.h"
28
#include "absl/strings/str_join.h"
29

            
30
namespace Envoy {
31
namespace Http {
32
namespace {
33

            
34
14696
absl::string_view getScheme(absl::string_view forwarded_proto, bool is_ssl) {
35
14696
  if (Utility::schemeIsValid(forwarded_proto)) {
36
14695
    return forwarded_proto;
37
14695
  }
38
1
  return is_ssl ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http;
39
14696
}
40

            
41
// Determines the scheme (http/https) based on PROXY protocol destination port if configured,
42
// otherwise falls back to connection's TLS status.
43
absl::string_view getSchemeFromProxyProtocolOrConnection(const Network::Connection& connection,
44
90068
                                                         const ConnectionManagerConfig& config) {
45
90068
  const auto& https_ports = config.httpsDestinationPorts();
46
90068
  const auto& http_ports = config.httpDestinationPorts();
47

            
48
  // If the feature is configured and the local address was restored from PROXY protocol,
49
  // try to infer the scheme from the destination port.
50
90068
  if ((!https_ports.empty() || !http_ports.empty()) &&
51
90068
      connection.connectionInfoProvider().localAddressRestored()) {
52
4
    const Envoy::Network::Address::Ip* ip =
53
4
        connection.connectionInfoProvider().localAddress()->ip();
54
4
    if (ip != nullptr) {
55
4
      uint32_t port = ip->port();
56
4
      if (https_ports.contains(port)) {
57
2
        return Headers::get().SchemeValues.Https;
58
2
      }
59
2
      if (http_ports.contains(port)) {
60
1
        return Headers::get().SchemeValues.Http;
61
1
      }
62
2
    }
63
4
  }
64

            
65
  // Fall back to connection's TLS status.
66
90065
  return connection.ssl() ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http;
67
90068
}
68

            
69
} // namespace
70
std::string ConnectionManagerUtility::determineNextProtocol(Network::Connection& connection,
71
470
                                                            const Buffer::Instance& data) {
72
470
  std::string next_protocol = connection.nextProtocol();
73
470
  if (!next_protocol.empty()) {
74
2
    return next_protocol;
75
2
  }
76

            
77
  // See if the data we have so far shows the HTTP/2 prefix. We ignore the case where someone sends
78
  // us the first few bytes of the HTTP/2 prefix since in all public cases we use SSL/ALPN. For
79
  // internal cases this should practically never happen.
80
468
  if (data.startsWith(Http2::CLIENT_MAGIC_PREFIX)) {
81
305
    return Utility::AlpnNames::get().Http2;
82
305
  }
83

            
84
163
  return "";
85
468
}
86

            
87
ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
88
    Network::Connection& connection, const Buffer::Instance& data,
89
    ServerConnectionCallbacks& callbacks, Stats::Scope& scope, Random::RandomGenerator& random,
90
    Http1::CodecStats::AtomicPtr& http1_codec_stats,
91
    Http2::CodecStats::AtomicPtr& http2_codec_stats, const Http1Settings& http1_settings,
92
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
93
    uint32_t max_request_headers_kb, uint32_t max_request_headers_count,
94
    envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
95
        headers_with_underscores_action,
96
463
    Server::OverloadManager& overload_manager) {
97
463
  if (determineNextProtocol(connection, data) == Utility::AlpnNames::get().Http2) {
98
304
    Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope);
99
304
    return std::make_unique<Http2::ServerConnectionImpl>(
100
304
        connection, callbacks, stats, random, http2_options, max_request_headers_kb,
101
304
        max_request_headers_count, headers_with_underscores_action, overload_manager);
102
364
  } else {
103
159
    Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope);
104
159
    return std::make_unique<Http1::ServerConnectionImpl>(
105
159
        connection, stats, callbacks, http1_settings, max_request_headers_kb,
106
159
        max_request_headers_count, headers_with_underscores_action, overload_manager);
107
159
  }
108
463
}
109

            
110
void ConnectionManagerUtility::appendXff(RequestHeaderMap& request_headers,
111
                                         Network::Connection& connection,
112
793
                                         ConnectionManagerConfig& config) {
113
793
  if (Network::Utility::isLoopbackAddress(*connection.connectionInfoProvider().remoteAddress())) {
114
447
    Utility::appendXff(request_headers, config.localAddress());
115
788
  } else {
116
346
    Utility::appendXff(request_headers, *connection.connectionInfoProvider().remoteAddress());
117
346
  }
118
793
}
119

            
120
ConnectionManagerUtility::MutateRequestHeadersResult ConnectionManagerUtility::mutateRequestHeaders(
121
    RequestHeaderMap& request_headers, Network::Connection& connection,
122
    ConnectionManagerConfig& config, const Router::Config& route_config,
123
90133
    const LocalInfo::LocalInfo& local_info, const StreamInfo::StreamInfo& stream_info) {
124

            
125
90133
  for (const auto& extension : config.earlyHeaderMutationExtensions()) {
126
4
    if (!extension->mutate(request_headers, stream_info)) {
127
2
      break;
128
2
    }
129
4
  }
130

            
131
  // If this is a Upgrade request, do not remove the Connection and Upgrade headers,
132
  // as we forward them verbatim to the upstream hosts.
133
90133
  if (!Utility::isUpgrade(request_headers)) {
134
89948
    request_headers.removeConnection();
135
89948
    request_headers.removeUpgrade();
136

            
137
89948
    sanitizeTEHeader(request_headers);
138
89948
  }
139

            
140
  // Clean proxy headers.
141
90133
  request_headers.removeEnvoyInternalRequest();
142
90133
  request_headers.removeKeepAlive();
143
90133
  request_headers.removeProxyConnection();
144
90133
  request_headers.removeTransferEncoding();
145

            
146
  // Sanitize referer field if exists.
147
90133
  auto result = request_headers.get(Http::CustomHeaders::get().Referer);
148
90133
  if (!result.empty()) {
149
15
    if (result.size() > 1 || !Utility::isValidRefererValue(result[0]->value().getStringView())) {
150
      // A request header shouldn't have multiple referer field.
151
5
      request_headers.remove(Http::CustomHeaders::get().Referer);
152
5
    }
153
15
  }
154

            
155
  // If we are "using remote address" this means that we create/append to XFF with our immediate
156
  // peer. Cases where we don't "use remote address" include trusted double proxy where we expect
157
  // our peer to have already properly set XFF, etc.
158
90133
  Network::Address::InstanceConstSharedPtr final_remote_address;
159
90133
  bool allow_trusted_address_checks = false;
160
90133
  const uint32_t xff_num_trusted_hops = config.xffNumTrustedHops();
161

            
162
90133
  if (config.useRemoteAddress()) {
163
771
    allow_trusted_address_checks = request_headers.ForwardedFor() == nullptr;
164
    // If there are any trusted proxies in front of this Envoy instance (as indicated by
165
    // the xff_num_trusted_hops configuration option), get the trusted client address
166
    // from the XFF before we append to XFF.
167
771
    if (xff_num_trusted_hops > 0) {
168
7
      final_remote_address =
169
7
          Utility::getLastAddressFromXFF(request_headers, xff_num_trusted_hops - 1).address_;
170
7
    }
171
    // If there aren't any trusted proxies in front of this Envoy instance, or there
172
    // are but they didn't populate XFF properly, the trusted client address is the
173
    // source address of the immediate downstream's connection to us.
174
771
    if (final_remote_address == nullptr) {
175
768
      final_remote_address = connection.connectionInfoProvider().remoteAddress();
176
768
    }
177
771
    if (!config.skipXffAppend()) {
178
767
      appendXff(request_headers, connection, config);
179
767
    }
180
    // If the prior hop is not a trusted proxy, overwrite any
181
    // x-forwarded-proto/x-forwarded-port value it set as untrusted. Alternately if no
182
    // x-forwarded-proto/x-forwarded-port header exists, add one if configured.
183
771
    if (xff_num_trusted_hops == 0 || request_headers.ForwardedProto() == nullptr) {
184
769
      request_headers.setReferenceForwardedProto(
185
769
          getSchemeFromProxyProtocolOrConnection(connection, config));
186
769
    }
187
771
    if (config.appendXForwardedPort() &&
188
771
        (xff_num_trusted_hops == 0 || request_headers.ForwardedPort() == nullptr)) {
189
2
      const Envoy::Network::Address::Ip* ip =
190
2
          connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
191
2
      if (ip) {
192
2
        request_headers.setForwardedPort(ip->port());
193
2
      }
194
2
    }
195
89943
  } else {
196
    // If we are not using remote address, attempt to pull a valid IPv4 or IPv6 address out of XFF
197
    // or through an extension. An extension might be needed when XFF doesn't work (e.g. an
198
    // irregular network).
199
    //
200
    // If we find one, it will be used as the downstream address for logging. It may or may not be
201
    // used for determining internal/external status (see below).
202
89362
    OriginalIPDetectionParams params = {request_headers,
203
89362
                                        connection.connectionInfoProvider().remoteAddress()};
204
89363
    for (const auto& detection_extension : config.originalIpDetectionExtensions()) {
205
89363
      const auto result = detection_extension->detect(params);
206

            
207
89363
      if (result.reject_options.has_value()) {
208
1
        return {nullptr, result.reject_options};
209
1
      }
210
89362
      if (!result.skip_xff_append) {
211
26
        appendXff(request_headers, connection, config);
212
26
      }
213

            
214
89362
      if (result.detected_remote_address) {
215
1429
        final_remote_address = result.detected_remote_address;
216
1429
        allow_trusted_address_checks = result.allow_trusted_address_checks;
217
1429
        break;
218
1429
      }
219
89362
    }
220
89362
  }
221

            
222
  // If the x-forwarded-proto header is not set, set it here, since Envoy uses it for determining
223
  // scheme and communicating it upstream.
224
90132
  if (!request_headers.ForwardedProto()) {
225
89299
    request_headers.setReferenceForwardedProto(
226
89299
        getSchemeFromProxyProtocolOrConnection(connection, config));
227
89299
  }
228

            
229
  // Usually, the x-forwarded-port header comes with x-forwarded-proto header. If the
230
  // x-forwarded-proto header is not set, set it here if append-x-forwarded-port is configured.
231
90132
  if (config.appendXForwardedPort() && !request_headers.ForwardedPort()) {
232
2
    const Envoy::Network::Address::Ip* ip =
233
2
        connection.streamInfo().downstreamAddressProvider().localAddress()->ip();
234
2
    if (ip) {
235
2
      request_headers.setForwardedPort(ip->port());
236
2
    }
237
2
  }
238

            
239
90132
  if (config.schemeToSet().has_value()) {
240
13
    request_headers.setScheme(config.schemeToSet().value());
241
13
    request_headers.setForwardedProto(config.schemeToSet().value());
242
13
  }
243

            
244
  // If :scheme is not set, sets :scheme based on X-Forwarded-Proto if a valid scheme,
245
  // else encryption level.
246
  // X-Forwarded-Proto and :scheme may still differ if different values are sent from downstream.
247
90132
  if (!request_headers.Scheme()) {
248
14696
    request_headers.setScheme(
249
14696
        getScheme(request_headers.getForwardedProtoValue(), connection.ssl() != nullptr));
250
14696
  }
251
90132
  request_headers.setScheme(absl::AsciiStrToLower(request_headers.getSchemeValue()));
252

            
253
  // At this point we can determine whether this is an internal or external request. The
254
  // determination of internal status uses the following:
255
  // 1) After remote address/XFF appending, the XFF header must contain a *single* address.
256
  // 2) The single address must be an internal address.
257
  // 3) If configured to not use remote address, but no XFF header is available, even if the real
258
  //    remote is internal, the request is considered external.
259
  // HUGE WARNING: The way we do this is not optimal but is how it worked "from the beginning" so
260
  //               we can't change it at this point. In the future we will likely need to add
261
  //               additional inference modes and make this mode legacy.
262
90132
  const bool internal_request =
263
90132
      allow_trusted_address_checks && final_remote_address != nullptr &&
264
90132
      config.internalAddressConfig().isInternalAddress(*final_remote_address);
265

            
266
  // After determining internal request status, if there is no final remote address, due to no XFF,
267
  // busted XFF, etc., use the direct connection remote address for logging.
268
90132
  if (final_remote_address == nullptr) {
269
87932
    final_remote_address = connection.connectionInfoProvider().remoteAddress();
270
87932
  }
271

            
272
  // Edge request is the request from external clients to front Envoy.
273
  // Request from front Envoy to the internal service will be treated as not edge request.
274
90132
  const bool edge_request = !internal_request && config.useRemoteAddress();
275

            
276
  // If internal request, set header and do other internal only modifications.
277
90132
  if (internal_request) {
278
1396
    request_headers.setReferenceEnvoyInternalRequest(
279
1396
        Headers::get().EnvoyInternalRequestValues.True);
280
89740
  } else {
281
88736
    cleanInternalHeaders(request_headers, edge_request, route_config.internalOnlyHeaders());
282
88736
  }
283

            
284
90132
  if (config.userAgent()) {
285
3
    request_headers.setEnvoyDownstreamServiceCluster(config.userAgent().value());
286
3
    const HeaderEntry* user_agent_header = request_headers.UserAgent();
287
3
    if (!user_agent_header || user_agent_header->value().empty()) {
288
      // Following setReference() is safe because user agent is constant for the life of the
289
      // listener.
290
2
      request_headers.setReferenceUserAgent(config.userAgent().value());
291
2
    }
292

            
293
    // TODO(htuch): should this be under the config.userAgent() condition or in the outer scope?
294
3
    if (!local_info.nodeName().empty()) {
295
      // Following setReference() is safe because local info is constant for the life of the server.
296
2
      request_headers.setReferenceEnvoyDownstreamServiceNode(local_info.nodeName());
297
2
    }
298
3
  }
299

            
300
90132
  if (!config.via().empty()) {
301
103
    Utility::appendVia(request_headers, config.via());
302
103
  }
303

            
304
  // If we are an external request, AND we are "using remote address" (see above), we set
305
  // x-envoy-external-address since this is our first ingress point into the trusted network.
306
90132
  if (edge_request && final_remote_address->type() == Network::Address::Type::Ip) {
307
746
    request_headers.setEnvoyExternalAddress(final_remote_address->ip()->addressAsString());
308
746
  }
309

            
310
  // Generate x-request-id for all edge requests, or if there is none.
311
90132
  if (config.generateRequestId()) {
312
89693
    config.requestIDExtension()->set(request_headers, edge_request,
313
89693
                                     config.preserveExternalRequestId());
314
89693
  }
315

            
316
90132
  if (connection.connecting() && request_headers.get(Headers::get().EarlyData).empty()) {
317
    // Add an Early-Data header to indicate that this is a 0-RTT request according to
318
    // https://datatracker.ietf.org/doc/html/rfc8470#section-5.1.
319
2
    HeaderString value;
320
2
    value.setCopy("1");
321
2
    request_headers.addViaMove(HeaderString(Headers::get().EarlyData), std::move(value));
322
2
  }
323
90132
  mutateXfccRequestHeader(request_headers, stream_info, connection, config);
324

            
325
90132
  return {final_remote_address, absl::nullopt};
326
90133
}
327

            
328
89948
void ConnectionManagerUtility::sanitizeTEHeader(RequestHeaderMap& request_headers) {
329
89948
  absl::string_view te_header = request_headers.getTEValue();
330
89948
  if (te_header.empty()) {
331
89930
    return;
332
89930
  }
333

            
334
  // If the TE header contains the "trailers" value, set the TE header to "trailers" only.
335
18
  std::vector<absl::string_view> te_values = absl::StrSplit(te_header, ',');
336
21
  for (const absl::string_view& te_value : te_values) {
337
21
    bool is_trailers =
338
21
        absl::StripAsciiWhitespace(te_value) == Http::Headers::get().TEValues.Trailers;
339

            
340
21
    if (is_trailers) {
341
15
      request_headers.setTE(Http::Headers::get().TEValues.Trailers);
342
15
      return;
343
15
    }
344
21
  }
345

            
346
  // If the TE header does not contain the "trailers" value, remove the TE header.
347
3
  request_headers.removeTE();
348
3
}
349

            
350
void ConnectionManagerUtility::cleanInternalHeaders(
351
    RequestHeaderMap& request_headers, bool edge_request,
352
88736
    const std::vector<Http::LowerCaseString>& internal_only_headers) {
353
88736
  if (edge_request) {
354
    // Headers to be stripped from edge requests, i.e. to sanitize so
355
    // clients can't inject values.
356
746
    request_headers.removeEnvoyDecoratorOperation();
357
746
    request_headers.removeEnvoyDownstreamServiceCluster();
358
746
    request_headers.removeEnvoyDownstreamServiceNode();
359

            
360
    // TODO(wbpcode): Envoy may should always remove these headers from client because
361
    // these headers are hop by hop headers and should not be sent to upstream.
362
746
    request_headers.removeEnvoyOriginalPath();
363
746
    request_headers.removeEnvoyOriginalHost();
364
746
  }
365

            
366
  // Headers to be stripped from edge *and* intermediate-hop external requests.
367
  // TODO: some of these should only be stripped at edge, i.e. moved into
368
  // the block above.
369
88736
  request_headers.removeEnvoyRetriableStatusCodes();
370
88736
  request_headers.removeEnvoyRetriableHeaderNames();
371
88736
  request_headers.removeEnvoyRetryOn();
372
88736
  request_headers.removeEnvoyRetryGrpcOn();
373
88736
  request_headers.removeEnvoyMaxRetries();
374
88736
  request_headers.removeEnvoyUpstreamAltStatName();
375
88736
  request_headers.removeEnvoyUpstreamRequestTimeoutMs();
376
88736
  request_headers.removeEnvoyUpstreamRequestPerTryTimeoutMs();
377
88736
  request_headers.removeEnvoyUpstreamRequestTimeoutAltResponse();
378
88736
  request_headers.removeEnvoyExpectedRequestTimeoutMs();
379
88736
  request_headers.removeEnvoyForceTrace();
380
88736
  request_headers.removeEnvoyIpTags();
381
88736
  request_headers.removeEnvoyOriginalUrl();
382
88736
  request_headers.removeEnvoyHedgeOnPerTryTimeout();
383

            
384
88736
  for (const LowerCaseString& header : internal_only_headers) {
385
2
    request_headers.remove(header);
386
2
  }
387
88736
}
388

            
389
Tracing::Reason ConnectionManagerUtility::mutateTracingRequestHeader(
390
    RequestHeaderMap& request_headers, Runtime::Loader& runtime, ConnectionManagerConfig& config,
391
1090134
    const Router::Route* route) {
392
1090134
  Tracing::Reason final_reason = Tracing::Reason::NotTraceable;
393
1090134
  if (!config.tracingConfig()) {
394
89729
    return final_reason;
395
89729
  }
396

            
397
1000405
  auto rid_extension = config.requestIDExtension();
398
1000405
  if (!rid_extension->useRequestIdForTraceSampling()) {
399
1
    return Tracing::Reason::Sampling;
400
1
  }
401
1000404
  const auto rid_to_integer = rid_extension->getInteger(request_headers);
402
  // Skip if request-id is corrupted, or non-existent
403
1000404
  if (!rid_to_integer.has_value()) {
404
8
    return final_reason;
405
8
  }
406
1000396
  const uint64_t result = rid_to_integer.value() % 10000;
407

            
408
1000396
  const envoy::type::v3::FractionalPercent* client_sampling =
409
1000396
      &config.tracingConfig()->client_sampling_;
410
1000396
  const envoy::type::v3::FractionalPercent* random_sampling =
411
1000396
      &config.tracingConfig()->random_sampling_;
412
1000396
  const envoy::type::v3::FractionalPercent* overall_sampling =
413
1000396
      &config.tracingConfig()->overall_sampling_;
414

            
415
1000396
  const Router::RouteTracing* route_tracing = route ? route->tracingConfig() : nullptr;
416
1000396
  if (route_tracing != nullptr) {
417
5
    client_sampling = &route_tracing->getClientSampling();
418
5
    random_sampling = &route_tracing->getRandomSampling();
419
5
    overall_sampling = &route_tracing->getOverallSampling();
420
5
  }
421

            
422
  // Do not apply tracing transformations if we are currently tracing.
423
1000396
  final_reason = rid_extension->getTraceReason(request_headers);
424
1000396
  if (Tracing::Reason::NotTraceable == final_reason) {
425
378
    if (request_headers.ClientTraceId() &&
426
378
        runtime.snapshot().featureEnabled("tracing.client_enabled", *client_sampling)) {
427
2
      final_reason = Tracing::Reason::ClientForced;
428
2
      rid_extension->setTraceReason(request_headers, final_reason);
429
376
    } else if (request_headers.EnvoyForceTrace()) {
430
3
      final_reason = Tracing::Reason::ServiceForced;
431
3
      rid_extension->setTraceReason(request_headers, final_reason);
432
373
    } else if (runtime.snapshot().featureEnabled("tracing.random_sampling", *random_sampling,
433
373
                                                 result)) {
434
42
      final_reason = Tracing::Reason::Sampling;
435
42
      rid_extension->setTraceReason(request_headers, final_reason);
436
42
    }
437
378
  }
438

            
439
1000396
  if (final_reason != Tracing::Reason::NotTraceable &&
440
1000396
      !runtime.snapshot().featureEnabled("tracing.global_enabled", *overall_sampling, result)) {
441
998971
    final_reason = Tracing::Reason::NotTraceable;
442
998971
    rid_extension->setTraceReason(request_headers, final_reason);
443
998971
  }
444

            
445
1000396
  return final_reason;
446
1000404
}
447

            
448
namespace {
449

            
450
// Helper functions to apply forward client cert logic.
451

            
452
// Base implementation that takes the forward client cert type and details directly.
453
void applyForwardClientCertConfig(
454
    RequestHeaderMap& request_headers, Network::Connection& connection,
455
    ForwardClientCertType forward_client_cert,
456
90132
    const std::vector<ClientCertDetailsType>& set_current_client_cert_details) {
457
  // When AlwaysForwardOnly is set, always forward the XFCC header without modification.
458
90132
  if (forward_client_cert == ForwardClientCertType::AlwaysForwardOnly) {
459
5
    return;
460
5
  }
461
  // When Sanitize is set, or the connection is not mutual TLS, remove the XFCC header.
462
90127
  if (forward_client_cert == ForwardClientCertType::Sanitize ||
463
90127
      !(connection.ssl() && connection.ssl()->peerCertificatePresented())) {
464
90103
    request_headers.removeForwardedClientCert();
465
90103
    return;
466
90103
  }
467

            
468
  // When ForwardOnly is set, always forward the XFCC header without modification.
469
24
  if (forward_client_cert == ForwardClientCertType::ForwardOnly) {
470
2
    return;
471
2
  }
472

            
473
  // TODO(myidpt): Handle the special characters in By and URI fields.
474
  // TODO: Optimize client_cert_details based on perf analysis (direct string appending may be more
475
  // preferable).
476
22
  std::vector<std::string> client_cert_details;
477
  // When AppendForward or SanitizeSet is set, the client certificate information should be set into
478
  // the XFCC header.
479
22
  if (forward_client_cert == ForwardClientCertType::AppendForward ||
480
22
      forward_client_cert == ForwardClientCertType::SanitizeSet) {
481
22
    const auto uri_sans_local_cert = connection.ssl()->uriSanLocalCertificate();
482
22
    if (!uri_sans_local_cert.empty()) {
483
39
      for (const std::string& uri : uri_sans_local_cert) {
484
39
        client_cert_details.push_back(absl::StrCat("By=", uri));
485
39
      }
486
21
    }
487
22
    const std::string cert_digest = connection.ssl()->sha256PeerCertificateDigest();
488
22
    if (!cert_digest.empty()) {
489
22
      client_cert_details.push_back(absl::StrCat("Hash=", cert_digest));
490
22
    }
491
39
    for (const auto& detail : set_current_client_cert_details) {
492
39
      switch (detail) {
493
3
      case ClientCertDetailsType::Cert: {
494
3
        const std::string peer_cert = connection.ssl()->urlEncodedPemEncodedPeerCertificate();
495
3
        if (!peer_cert.empty()) {
496
3
          client_cert_details.push_back(absl::StrCat("Cert=\"", peer_cert, "\""));
497
3
        }
498
3
        break;
499
      }
500
3
      case ClientCertDetailsType::Chain: {
501
3
        const std::string peer_chain = connection.ssl()->urlEncodedPemEncodedPeerCertificateChain();
502
3
        if (!peer_chain.empty()) {
503
3
          client_cert_details.push_back(absl::StrCat("Chain=\"", peer_chain, "\""));
504
3
        }
505
3
        break;
506
      }
507
10
      case ClientCertDetailsType::Subject:
508
        // The "Subject" key still exists even if the subject is empty.
509
10
        client_cert_details.push_back(
510
10
            absl::StrCat("Subject=\"", connection.ssl()->subjectPeerCertificate(), "\""));
511
10
        break;
512
14
      case ClientCertDetailsType::URI: {
513
        // The "URI" key still exists even if the URI is empty.
514
14
        const auto sans = connection.ssl()->uriSanPeerCertificate();
515
14
        if (!sans.empty()) {
516
23
          for (const std::string& uri : sans) {
517
23
            client_cert_details.push_back(absl::StrCat("URI=", uri));
518
23
          }
519
13
        } else {
520
1
          client_cert_details.push_back("URI=");
521
1
        }
522
14
        break;
523
      }
524
9
      case ClientCertDetailsType::DNS: {
525
9
        auto dns_sans = connection.ssl()->dnsSansPeerCertificate();
526
9
        if (!dns_sans.empty()) {
527
16
          for (const std::string& dns : dns_sans) {
528
16
            client_cert_details.push_back(absl::StrCat("DNS=", dns));
529
16
          }
530
9
        }
531
9
        break;
532
      }
533
39
      }
534
39
    }
535
22
  }
536

            
537
22
  const std::string client_cert_details_str = absl::StrJoin(client_cert_details, ";");
538

            
539
22
  ENVOY_BUG(forward_client_cert == ForwardClientCertType::AppendForward ||
540
22
                forward_client_cert == ForwardClientCertType::SanitizeSet,
541
22
            "error in client cert logic");
542
22
  if (forward_client_cert == ForwardClientCertType::AppendForward) {
543
13
    request_headers.appendForwardedClientCert(client_cert_details_str, ",");
544
13
  } else if (forward_client_cert == ForwardClientCertType::SanitizeSet) {
545
9
    request_headers.setForwardedClientCert(client_cert_details_str);
546
9
  }
547
22
}
548

            
549
} // namespace
550

            
551
void ConnectionManagerUtility::mutateXfccRequestHeader(RequestHeaderMap& request_headers,
552
                                                       const StreamInfo::StreamInfo& stream_info,
553
                                                       Network::Connection& connection,
554
90132
                                                       ConnectionManagerConfig& config) {
555
  // If a matcher is configured, evaluate it to get per-request forward client cert config.
556
90132
  if (const auto& matcher = config.forwardClientCertMatcher(); matcher != nullptr) {
557
2
    Matching::HttpMatchingDataImpl data(stream_info);
558
2
    data.onRequestHeaders(request_headers);
559
2
    auto match_result = Matcher::evaluateMatch<HttpMatchingData>(*matcher, data);
560
2
    if (match_result.isMatch() && match_result.action() != nullptr) {
561
      // Use the matched action's config via the ForwardClientCertActionConfig interface.
562
1
      const auto& forward_client_cert_action =
563
1
          match_result.action()->getTyped<ForwardClientCertActionConfig>();
564
1
      applyForwardClientCertConfig(request_headers, connection,
565
1
                                   forward_client_cert_action.forwardClientCertType(),
566
1
                                   forward_client_cert_action.setCurrentClientCertDetails());
567
1
      return;
568
1
    }
569
2
  }
570

            
571
  // Fall back to static config if no matcher or no match.
572
90131
  applyForwardClientCertConfig(request_headers, connection, config.forwardClientCert(),
573
90131
                               config.setCurrentClientCertDetails());
574
90131
}
575

            
576
void ConnectionManagerUtility::mutateResponseHeaders(ResponseHeaderMap& response_headers,
577
                                                     const RequestHeaderMap* request_headers,
578
                                                     ConnectionManagerConfig& config,
579
                                                     const std::string& via,
580
                                                     const StreamInfo::StreamInfo& stream_info,
581
                                                     absl::string_view proxy_name,
582
47400
                                                     bool clear_hop_by_hop) {
583
47400
  if (request_headers != nullptr && Utility::isUpgrade(*request_headers) &&
584
47400
      Utility::isUpgrade(response_headers)) {
585
    // As in mutateRequestHeaders, Upgrade responses have special handling.
586
    //
587
    // Unlike mutateRequestHeaders there is no explicit protocol check. If Envoy is proxying an
588
    // upgrade response it has already passed the protocol checks.
589
86
    const bool no_body =
590
86
        (!response_headers.TransferEncoding() && !response_headers.ContentLength());
591

            
592
86
    const bool is_1xx = CodeUtility::is1xx(Utility::getResponseStatus(response_headers));
593

            
594
    // We are explicitly forbidden from setting content-length for 1xx responses
595
    // (RFC7230, Section 3.3.2). We ignore 204 because this is an upgrade.
596
86
    if (no_body && !is_1xx) {
597
1
      response_headers.setContentLength(uint64_t(0));
598
1
    }
599
47328
  } else {
600
    // Only clear these hop by hop headers for non-upgrade connections.
601
47314
    if (clear_hop_by_hop) {
602
47313
      response_headers.removeConnection();
603
47313
      response_headers.removeUpgrade();
604
47313
    }
605
47314
  }
606
47400
  if (clear_hop_by_hop) {
607
47399
    response_headers.removeTransferEncoding();
608
47399
    response_headers.removeKeepAlive();
609
47399
    response_headers.removeProxyConnection();
610
47399
  }
611

            
612
47400
  if (request_headers != nullptr &&
613
47400
      (config.alwaysSetRequestIdInResponse() || request_headers->EnvoyForceTrace())) {
614
2
    config.requestIDExtension()->setInResponse(response_headers, *request_headers);
615
2
  }
616
47400
  if (!via.empty()) {
617
103
    Utility::appendVia(response_headers, via);
618
103
  }
619

            
620
47400
  setProxyStatusHeader(response_headers, config, stream_info, proxy_name);
621
47400
}
622

            
623
void ConnectionManagerUtility::setProxyStatusHeader(ResponseHeaderMap& response_headers,
624
                                                    const ConnectionManagerConfig& config,
625
                                                    const StreamInfo::StreamInfo& stream_info,
626
47400
                                                    absl::string_view proxy_name) {
627
47400
  if (auto* proxy_status_config = config.proxyStatusConfig(); proxy_status_config != nullptr) {
628
    // Writing the Proxy-Status header is gated on the existence of
629
    // |proxy_status_config|. The |details| field and other internals are generated in
630
    // fromStreamInfo().
631
2212
    if (absl::optional<StreamInfo::ProxyStatusError> proxy_status =
632
2212
            StreamInfo::ProxyStatusUtils::fromStreamInfo(stream_info);
633
2212
        proxy_status.has_value()) {
634
2128
      response_headers.appendProxyStatus(
635
2128
          StreamInfo::ProxyStatusUtils::makeProxyStatusHeader(stream_info, *proxy_status,
636
2128
                                                              proxy_name, *proxy_status_config),
637
2128
          ", ");
638
      // Apply the recommended response code, if configured and applicable.
639
2128
      if (proxy_status_config->set_recommended_response_code()) {
640
2
        if (absl::optional<Http::Code> response_code =
641
2
                StreamInfo::ProxyStatusUtils::recommendedHttpStatusCode(*proxy_status);
642
2
            response_code.has_value()) {
643
2
          response_headers.setStatus(std::to_string(enumToInt(*response_code)));
644
2
        }
645
2
      }
646
2128
    }
647
2212
  }
648
47400
}
649

            
650
ConnectionManagerUtility::NormalizePathAction
651
ConnectionManagerUtility::maybeNormalizePath(RequestHeaderMap& request_headers,
652
90400
                                             const ConnectionManagerConfig& config) {
653
90400
  if (!request_headers.Path()) {
654
448
    return NormalizePathAction::Continue; // It's as valid as it is going to get.
655
448
  }
656

            
657
89952
  auto fragment_pos = request_headers.getPathValue().find('#');
658
89952
  if (fragment_pos != absl::string_view::npos) {
659
815
    if (Runtime::runtimeFeatureEnabled(
660
815
            "envoy.reloadable_features.http_reject_path_with_fragment")) {
661
21
      return NormalizePathAction::Reject;
662
21
    }
663
    // Check runtime override and throw away fragment from URI path
664
794
    request_headers.setPath(request_headers.getPathValue().substr(0, fragment_pos));
665
794
  }
666

            
667
89931
  NormalizePathAction final_action = NormalizePathAction::Continue;
668
89931
  const auto escaped_slashes_action = config.pathWithEscapedSlashesAction();
669
89931
  ASSERT(escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::
670
89931
                                       v3::HttpConnectionManager::IMPLEMENTATION_SPECIFIC_DEFAULT);
671
89931
  if (escaped_slashes_action != envoy::extensions::filters::network::http_connection_manager::v3::
672
89931
                                    HttpConnectionManager::KEEP_UNCHANGED) {
673
31
    auto escaped_slashes_result = PathUtil::unescapeSlashes(request_headers);
674
31
    if (escaped_slashes_result == PathUtil::UnescapeSlashesResult::FoundAndUnescaped) {
675
29
      if (escaped_slashes_action == envoy::extensions::filters::network::http_connection_manager::
676
29
                                        v3::HttpConnectionManager::REJECT_REQUEST) {
677
14
        return NormalizePathAction::Reject;
678
23
      } else if (escaped_slashes_action ==
679
15
                 envoy::extensions::filters::network::http_connection_manager::v3::
680
15
                     HttpConnectionManager::UNESCAPE_AND_REDIRECT) {
681
9
        final_action = NormalizePathAction::Redirect;
682
10
      } else {
683
6
        ASSERT(escaped_slashes_action ==
684
6
               envoy::extensions::filters::network::http_connection_manager::v3::
685
6
                   HttpConnectionManager::UNESCAPE_AND_FORWARD);
686
6
      }
687
29
    }
688
31
  }
689

            
690
89917
  if (config.shouldNormalizePath() && !PathUtil::canonicalPath(request_headers)) {
691
12
    return NormalizePathAction::Reject;
692
12
  }
693

            
694
  // Merge slashes after path normalization to catch potential edge cases with percent encoding.
695
89905
  if (config.shouldMergeSlashes()) {
696
446
    PathUtil::mergeSlashes(request_headers);
697
446
  }
698

            
699
89905
  return final_action;
700
89917
}
701

            
702
absl::optional<uint32_t>
703
ConnectionManagerUtility::maybeNormalizeHost(RequestHeaderMap& request_headers,
704
90331
                                             const ConnectionManagerConfig& config, uint32_t port) {
705
90331
  if (config.shouldStripTrailingHostDot()) {
706
18
    HeaderUtility::stripTrailingHostDot(request_headers);
707
18
  }
708
90331
  if (config.stripPortType() == Http::StripPortType::Any) {
709
15
    return HeaderUtility::stripPortFromHost(request_headers, absl::nullopt);
710
90316
  } else if (config.stripPortType() == Http::StripPortType::MatchingHost) {
711
7
    return HeaderUtility::stripPortFromHost(request_headers, port);
712
7
  }
713
90309
  return absl::nullopt;
714
90331
}
715

            
716
} // namespace Http
717
} // namespace Envoy