pub(crate) async fn connect<T, P>(
alpn_override: Option<ALPN>,
) -> Result<SslStream<T>>
let mut ssl_conf = tls_ctx.configure().unwrap();
ssl_set_renegotiate_mode_freely(&mut ssl_conf);
// Set up CA/verify cert store
// TODO: store X509Store in the peer directly
if let Some(ca_list) = peer.get_ca() {
let mut store_builder = X509StoreBuilder::new().unwrap();
store_builder.add_cert(ca.clone()).unwrap();
ssl_set_verify_cert_store(&mut ssl_conf, &store_builder.build())
.or_err(InternalError, "failed to load cert store")?;
// Set up client cert/key
if let Some(key_pair) = peer.get_client_cert_key() {
debug!("setting client cert and key");
ssl_use_certificate(&mut ssl_conf, key_pair.leaf())
.or_err(InternalError, "invalid client cert")?;
ssl_use_private_key(&mut ssl_conf, key_pair.key())
.or_err(InternalError, "invalid client key")?;
let intermediates = key_pair.intermediates();
if !intermediates.is_empty() {
debug!("adding intermediate certificates for mTLS chain");
for int in intermediates {
ssl_add_chain_cert(&mut ssl_conf, int)
.or_err(InternalError, "invalid intermediate client cert")?;
if let Some(curve) = peer.get_peer_options().and_then(|o| o.curves) {
ssl_set_groups_list(&mut ssl_conf, curve).or_err(InternalError, "invalid curves")?;
// second_keyshare is default true
if !peer.get_peer_options().map_or(true, |o| o.second_keyshare) {
ssl_use_second_key_share(&mut ssl_conf, false);
// disable verification if sni does not exist
// XXX: verify on empty string cause null string seg fault
if peer.sni().is_empty() {
ssl_conf.set_use_server_name_indication(false);
/* NOTE: technically we can still verify who signs the cert but turn it off to be
consistent with nginx's behavior */
ssl_conf.set_verify(SslVerifyMode::NONE);
} else if peer.verify_cert() {
if peer.verify_hostname() {
let verify_param = ssl_conf.param_mut();
add_host(verify_param, peer.sni()).or_err(InternalError, "failed to add host")?;
// if sni had underscores in leftmost label replace and add
if let Some(sni_s) = replace_leftmost_underscore(peer.sni()) {
add_host(verify_param, sni_s.as_ref()).unwrap();
if let Some(alt_cn) = peer.alternative_cn() {
add_host(verify_param, alt_cn).unwrap();
// if alt_cn had underscores in leftmost label replace and add
if let Some(alt_cn_s) = replace_leftmost_underscore(alt_cn) {
add_host(verify_param, alt_cn_s.as_ref()).unwrap();
ssl_conf.set_verify(SslVerifyMode::PEER);
ssl_conf.set_verify(SslVerifyMode::NONE);
We always set set_verify_hostname(false) here because:
- verify case.) otherwise ssl.connect calls X509_VERIFY_PARAM_set1_host
which overrides the names added by add_host. Verify is
essentially on as long as the names are added.
- off case.) the non verify hostname case should have it disabled
ssl_conf.set_verify_hostname(false);
if let Some(alpn) = alpn_override.as_ref().or(peer.get_alpn()) {
ssl_conf.set_alpn_protos(alpn.to_wire_preference()).unwrap();
let connect_future = handshake(ssl_conf, peer.sni(), stream);
match peer.connection_timeout() {
Some(t) => match pingora_timeout::timeout(t, connect_future).await {
Err(_) => Error::e_explain(
format!("connecting to server {}, timeout {:?}", peer, t),
None => connect_future.await,