Skip to content

Commit

Permalink
Add support to pass through and route untrusted certificates (#9172)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Hargreaves <mik.hargreaves@gmail.com>
Co-authored-by: owlbreeze <mik.hargreaves@gmail.com>
  • Loading branch information
jimini-lumox and owlbreeze authored Feb 3, 2020
1 parent 2b20da3 commit abb1f96
Show file tree
Hide file tree
Showing 28 changed files with 449 additions and 24 deletions.
17 changes: 16 additions & 1 deletion api/envoy/api/v2/auth/cert.proto
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,19 @@ message TlsSessionTicketKeys {
[(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true];
}

// [#next-free-field: 10]
// [#next-free-field: 11]
message CertificateValidationContext {
// Peer certificate verification mode.
enum TrustChainVerification {
// Perform default certificate verification (e.g., against CA / verification lists)
VERIFY_TRUST_CHAIN = 0;

// Connections where the certificate fails verification will be permitted.
// For HTTP connections, the result of certificate verification can be used in route matching. (
// see :ref:`validated <envoy_api_field_route.RouteMatch.TlsContextMatchOptions.validated>` ).
ACCEPT_UNTRUSTED = 1;
}

// TLS certificate data containing certificate authority certificates to use in verifying
// a presented peer certificate (e.g. server certificate for clusters or client certificate
// for listeners). If not specified and a peer certificate is presented it will not be
Expand Down Expand Up @@ -300,6 +311,10 @@ message CertificateValidationContext {

// If specified, Envoy will not reject expired certificates.
bool allow_expired_certificate = 8;

// Certificate trust chain verification mode.
TrustChainVerification trust_chain_verification = 10
[(validate.rules).enum = {defined_only: true}];
}

// TLS context shared by both client and server TLS contexts.
Expand Down
5 changes: 5 additions & 0 deletions api/envoy/api/v2/route/route_components.proto
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,12 @@ message RouteMatch {

message TlsContextMatchOptions {
// If specified, the route will match against whether or not a certificate is presented.
// If not specified, certificate presentation status (true or false) will not be considered when route matching.
google.protobuf.BoolValue presented = 1;

// If specified, the route will match against whether or not a certificate is validated.
// If not specified, certificate validation status (true or false) will not be considered when route matching.
google.protobuf.BoolValue validated = 2;
}

reserved 5;
Expand Down
5 changes: 5 additions & 0 deletions api/envoy/config/route/v3/route_components.proto
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,12 @@ message RouteMatch {
"envoy.api.v2.route.RouteMatch.TlsContextMatchOptions";

// If specified, the route will match against whether or not a certificate is presented.
// If not specified, certificate presentation status (true or false) will not be considered when route matching.
google.protobuf.BoolValue presented = 1;

// If specified, the route will match against whether or not a certificate is validated.
// If not specified, certificate validation status (true or false) will not be considered when route matching.
google.protobuf.BoolValue validated = 2;
}

reserved 5, 3;
Expand Down
17 changes: 16 additions & 1 deletion api/envoy/extensions/transport_sockets/tls/v3/cert.proto
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,22 @@ message TlsSessionTicketKeys {
[(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true];
}

// [#next-free-field: 10]
// [#next-free-field: 11]
message CertificateValidationContext {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.auth.CertificateValidationContext";

// Peer certificate verification mode.
enum TrustChainVerification {
// Perform default certificate verification (e.g., against CA / verification lists)
VERIFY_TRUST_CHAIN = 0;

// Connections where the certificate fails verification will be permitted.
// For HTTP connections, the result of certificate verification can be used in route matching. (
// see :ref:`validated <envoy_api_field_config.route.v3.RouteMatch.TlsContextMatchOptions.validated>` ).
ACCEPT_UNTRUSTED = 1;
}

reserved 4;

reserved "verify_subject_alt_name";
Expand Down Expand Up @@ -307,6 +318,10 @@ message CertificateValidationContext {

// If specified, Envoy will not reject expired certificates.
bool allow_expired_certificate = 8;

// Certificate trust chain verification mode.
TrustChainVerification trust_chain_verification = 10
[(validate.rules).enum = {defined_only: true}];
}

// TLS context shared by both client and server TLS contexts.
Expand Down
2 changes: 2 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Version history
* config: use type URL to select an extension whenever the config type URL (or its previous versions) uniquely identify a typed extension, see :ref:`extension configuration <config_overview_extension_configuration>`.
* http: fixing a bug in HTTP/1.0 responses where Connection: keep-alive was not appended for connections which were kept alive.
* retry: added a retry predicate that :ref:`rejects hosts based on metadata. <envoy_api_field_route.RetryPolicy.retry_host_predicate>`
* router: added the ability to match a route based on whether a downstream TLS connection certificate has been
:ref:`validated <envoy_api_field_route.RouteMatch.TlsContextMatchOptions.validated>`.
* upstream: combined HTTP/1 and HTTP/2 connection pool code. This means that circuit breaker
limits for both requests and connections apply to both pool types. Also, HTTP/2 now has
the option to limit concurrent requests on a connection, and allow multiple draining
Expand Down
17 changes: 16 additions & 1 deletion generated_api_shadow/envoy/api/v2/auth/cert.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions include/envoy/router/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,11 +530,23 @@ class MetadataMatchCriteria {
filterMatchCriteria(const std::set<std::string>& names) const PURE;
};

/**
* Criterion that a route entry uses for matching TLS connection context.
*/
class TlsContextMatchCriteria {
public:
virtual ~TlsContextMatchCriteria() = default;

/**
* @return bool indicating whether the client presented credentials.
*/
virtual const absl::optional<bool>& presented() const PURE;

/**
* @return bool indicating whether the client credentials successfully validated against the TLS
* context validation context.
*/
virtual const absl::optional<bool>& validated() const PURE;
};

using TlsContextMatchCriteriaConstPtr = std::unique_ptr<const TlsContextMatchCriteria>;
Expand Down
8 changes: 8 additions & 0 deletions include/envoy/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ envoy_cc_library(
hdrs = ["certificate_validation_context_config.h"],
deps = [
"//source/common/common:matchers_lib",
"@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto",
"@envoy_api//envoy/type/matcher/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "ssl_socket_extended_info_interface",
hdrs = ["ssl_socket_extended_info.h"],
deps = [
],
)
8 changes: 8 additions & 0 deletions include/envoy/ssl/certificate_validation_context_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>

#include "envoy/common/pure.h"
#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h"
#include "envoy/type/matcher/v3/string.pb.h"

namespace Envoy {
Expand Down Expand Up @@ -61,6 +62,13 @@ class CertificateValidationContextConfig {
* @return whether to ignore expired certificates (both too new and too old).
*/
virtual bool allowExpiredCertificate() const PURE;

/**
* @return client certificate validation configuration.
*/
virtual envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext::
TrustChainVerification
trustChainVerification() const PURE;
};

using CertificateValidationContextConfigPtr = std::unique_ptr<CertificateValidationContextConfig>;
Expand Down
5 changes: 5 additions & 0 deletions include/envoy/ssl/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class ConnectionInfo {
**/
virtual bool peerCertificatePresented() const PURE;

/**
* @return bool whether the peer certificate was validated.
**/
virtual bool peerCertificateValidated() const PURE;

/**
* @return std::string the URIs in the SAN field of the local certificate. Returns {} if there is
* no local certificate, or no SAN field, or no URI.
Expand Down
30 changes: 30 additions & 0 deletions include/envoy/ssl/ssl_socket_extended_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <memory>
#include <string>
#include <vector>

#include "envoy/common/pure.h"

namespace Envoy {
namespace Ssl {

enum class ClientValidationStatus { NotValidated, NoClientCertificate, Validated, Failed };

class SslExtendedSocketInfo {
public:
virtual ~SslExtendedSocketInfo() = default;

/**
* Set the peer certificate validation status.
**/
virtual void setCertificateValidationStatus(ClientValidationStatus validated) PURE;

/**
* @return ClientValidationStatus The peer certificate validation status.
**/
virtual ClientValidationStatus certificateValidationStatus() const PURE;
};

} // namespace Ssl
} // namespace Envoy
6 changes: 6 additions & 0 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@ bool RouteEntryImplBase::evaluateTlsContextMatch(const StreamInfo::StreamInfo& s
matches &= criteria.presented().value() == peer_presented;
}

if (criteria.validated().has_value()) {
const bool peer_validated = stream_info.downstreamSslConnection() &&
stream_info.downstreamSslConnection()->peerCertificateValidated();
matches &= criteria.validated().value() == peer_validated;
}

return matches;
}

Expand Down
4 changes: 4 additions & 0 deletions source/common/router/tls_context_match_criteria_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ TlsContextMatchCriteriaImpl::TlsContextMatchCriteriaImpl(
if (options.has_presented()) {
presented_ = options.presented().value();
}

if (options.has_validated()) {
validated_ = options.validated().value();
}
}

} // namespace Router
Expand Down
2 changes: 2 additions & 0 deletions source/common/router/tls_context_match_criteria_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ class TlsContextMatchCriteriaImpl : public TlsContextMatchCriteria {
const envoy::config::route::v3::RouteMatch::TlsContextMatchOptions& options);

const absl::optional<bool>& presented() const override { return presented_; }
const absl::optional<bool>& validated() const override { return validated_; }

private:
absl::optional<bool> presented_;
absl::optional<bool> validated_;
};

} // namespace Router
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl(
config.verify_certificate_hash().end()),
verify_certificate_spki_list_(config.verify_certificate_spki().begin(),
config.verify_certificate_spki().end()),
allow_expired_certificate_(config.allow_expired_certificate()) {
allow_expired_certificate_(config.allow_expired_certificate()),
trust_chain_verification_(config.trust_chain_verification()) {
if (ca_cert_.empty()) {
if (!certificate_revocation_list_.empty()) {
throw EnvoyException(fmt::format("Failed to load CRL from {} without trusted CA",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte
return verify_certificate_spki_list_;
}
bool allowExpiredCertificate() const override { return allow_expired_certificate_; }
envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext::
TrustChainVerification
trustChainVerification() const override {
return trust_chain_verification_;
}

private:
const std::string ca_cert_;
Expand All @@ -49,6 +54,8 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte
const std::vector<std::string> verify_certificate_hash_list_;
const std::vector<std::string> verify_certificate_spki_list_;
const bool allow_expired_certificate_;
const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext::
TrustChainVerification trust_chain_verification_;
};

} // namespace Ssl
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/transport_sockets/tls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ envoy_cc_library(
":utility_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/network:transport_socket_interface",
"//include/envoy/ssl:ssl_socket_extended_info_interface",
"//include/envoy/ssl/private_key:private_key_callbacks_interface",
"//include/envoy/ssl/private_key:private_key_interface",
"//include/envoy/stats:stats_macros",
Expand Down Expand Up @@ -96,6 +97,7 @@ envoy_cc_library(
"//include/envoy/ssl:context_config_interface",
"//include/envoy/ssl:context_interface",
"//include/envoy/ssl:context_manager_interface",
"//include/envoy/ssl:ssl_socket_extended_info_interface",
"//include/envoy/ssl/private_key:private_key_interface",
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
Expand Down
Loading

0 comments on commit abb1f96

Please sign in to comment.