Upstream envoy has included the envoy.reloadable_features.tls_async_cert_validation runtime guard since 1.24. This guard is set to true by default in the upstream code, meaning that upstream envoy performs async certificate validation by default.
The flip side of this is that the “old” synchronous certificate validation code still exists upstream, and can be used by setting the envoy.reloadable_features.tls_async_cert_validation runtime guard to false.
Up until this point, Maistra envoy has only included the code to support the synchronous certificate validation mechanism, as we have never pulled in the async code nor the runtime guard from upstream.
The problem we face now is that support for synchronous certificate validation was removed from upstream envoy in 1.27, leaving only the asynchronous mechanism. Therefore, maistra envoy will have to move to the asynchronous mechanism.
- Synchronous Call Flow
- ContextImpl::ContextImpl()
- SSL_CTX_set_verify(ctx, verify_mode, nullptr);
- SSL_CTX_set_cert_verify_callback(ctx, ContextImpl::verifyCallback, this);
- int ContextImpl::verifyCallback(X509_STORE_CTX* store_ctx, void* arg)
- ContextImpl* impl = reinterpret_cast<ContextImpl*>(arg);
- impl->cert_validator_->doSynchronousVerifyCertChain()
- CertValidator::doSynchronousVerifyCertChain()
- Asynchronous Call Flow
- ContextImpl::ContextImpl()
- SSL_CTX_set_custom_verify(ctx, verify_mode, ContextImpl::customVerifyCallback);
- SSL_CTX_set_reverify_on_resume(ctx, /reverify_on_resume_enabled)=/1);
- enum ssl_verify_result_t ContextImpl::customVerifyCallback(SSL* ssl, uint8_t* out_alert)
- This callback function implements the BoringSSL signature, and maps to Envoy’s own
- SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
- ContextImpl* context_impl = static_cast<ContextImpl*>(SSL_CTX_get_app_data(ssl_ctx));
- ValidationResults result = context_impl->customVerifyCertChain(...)
In the async case, the ContextImpl::customVerifyCallback(SSL ssl, uint8_t* out_alert)* method returns BoringSSL’s ssl_verify_result_t value, which has the following enumerators:
- ssl_verify_ok - if the certificate is valid
- ssl_verify_invalid - if the certificate is invalid
- ssl_verify_retry - to verify a certificate asynchronously, in which case the handshake will then pause with SSL_get_error() returning SSL_ERROR_WANT_CERTIFICATE_VERIFY
OpenSSL Async Support
These are the functions used in “Ismo’s async handshaker”:
- typedef int ({}SSL_async_callback_fn{*})(SSL *s, void *arg);
- int SSL_CTX_set_mode(SSL_CTX ctx, {}SSL_MODE_ASYNC{*});
- Enable asynchronous processing. TLS I/O operations may indicate a retry with SSL_ERROR_WANT_ASYNC with this mode set if an asynchronous capable engine is used to perform cryptographic operations.
- int SSL_waiting_for_async(SSL *s)
- int SSL_get_all_async_fds(SSL *s, OSSL_ASYNC_FD *fds, size_t *numfds)
- int SSL_get_changed_async_fds(SSL *s, OSSL_ASYNC_FD *addfd, size_t *numaddfds, OSSL_ASYNC_FD *delfd, size_t *numdelfds)
- int SSL_CTX_set_async_callback(SSL_CTX *ctx, SSL_async_callback_fn callback)
- int SSL_CTX_set_async_callback_arg(SSL_CTX *ctx, void *arg)
- int SSL_get_async_status(SSL *s, int *status)
**
See also ASYNC_start_job() for a description of how OpenSSL implements asynchronous capabilities through an ASYNC_JOB.