Skip to content

Commit beee61e

Browse files
authored
RUST-1083 Add openssl as an optional TLS provider (#590)
1 parent 037058e commit beee61e

File tree

14 files changed

+369
-134
lines changed

14 files changed

+369
-134
lines changed

.evergreen/config.yml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,9 @@ functions:
297297
298298
export CERT_PATH=$DRIVERS_TOOLS/.evergreen/x509gen/client.pem
299299
300-
.evergreen/run-x509-tests.sh
300+
ASYNC_RUNTIME=${ASYNC_RUNTIME} \
301+
TLS_FEATURE=${TLS_FEATURE} \
302+
.evergreen/run-x509-tests.sh
301303
302304
"run plain tests":
303305
- command: shell.exec
@@ -376,6 +378,7 @@ functions:
376378
SINGLE_THREAD=${SINGLE_THREAD} \
377379
ASYNC_RUNTIME=${ASYNC_RUNTIME} \
378380
MONGODB_API_VERSION=${MONGODB_API_VERSION} \
381+
TLS_FEATURE=${TLS_FEATURE} \
379382
.evergreen/run-tests.sh
380383
381384
"run serverless tests":
@@ -403,6 +406,7 @@ functions:
403406
SINGLE_THREAD=${SINGLE_THREAD} \
404407
ASYNC_RUNTIME=${ASYNC_RUNTIME} \
405408
MONGODB_API_VERSION=${MONGODB_API_VERSION} \
409+
TLS_FEATURE=${TLS_FEATURE} \
406410
.evergreen/run-serverless-tests.sh
407411
408412
"run atlas tests":
@@ -1350,15 +1354,23 @@ axes:
13501354
display_name: Authentication and TLS
13511355
values:
13521356
- id: "auth-and-tls"
1353-
display_name: Auth TLS
1357+
display_name: Auth TLS (Default)
1358+
variables:
1359+
AUTH: "auth"
1360+
SSL: "ssl"
1361+
TLS_FEATURE: ""
1362+
- id: "openssl-auth-and-tls"
1363+
display_name: Auth TLS (OpenSSL)
13541364
variables:
13551365
AUTH: "auth"
13561366
SSL: "ssl"
1367+
TLS_FEATURE: "openssl-tls"
13571368
- id: "noauth-and-notls"
13581369
display_name: NoAuth NoTLS
13591370
variables:
13601371
AUTH: "noauth"
13611372
SSL: "nossl"
1373+
TLS_FEATURE: ""
13621374

13631375
- id: "compressor"
13641376
display_name: "Compressor"
@@ -1539,9 +1551,11 @@ buildvariants:
15391551
- ubuntu-18.04-arm64
15401552
- macos-10.14
15411553
- windows-64-vs2017
1542-
auth-and-tls: "auth-and-tls"
1554+
auth-and-tls:
1555+
- auth-and-tls
1556+
- openssl-auth-and-tls
15431557
async-runtime: "*"
1544-
display_name: "${os} X.509 auth with ${async-runtime}"
1558+
display_name: "${os} X.509 ${auth-and-tls} with ${async-runtime}"
15451559
tasks:
15461560
- "test-x509-auth"
15471561

.evergreen/env.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ if [[ "$OS" == "Windows_NT" ]]; then
1414
export NVM_SYMLINK
1515
NVM_ARTIFACTS_PATH=$(cygpath -w "$NODE_ARTIFACTS_PATH/bin")
1616
export NVM_ARTIFACTS_PATH
17+
export OPENSSL_DIR="C:\\openssl"
18+
OPENSSL_LIB_PATH=$(cygpath $OPENSSL_DIR/lib)
19+
ls $OPENSSL_LIB_PATH
1720
PATH=$(cygpath $NVM_SYMLINK):$(cygpath $NVM_HOME):$PATH
1821
export PATH
1922
echo "updated path on windows PATH=$PATH"

.evergreen/run-async-std-tests.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ echo "cargo test options: ${OPTIONS}"
1616
set +o errexit
1717
CARGO_RESULT=0
1818

19-
RUST_BACKTRACE=1 cargo test --no-default-features --features async-std-runtime $OPTIONS | tee async-tests.json
19+
RUST_BACKTRACE=1 cargo test --no-default-features --features async-std-runtime,${TLS_FEATURE} $OPTIONS | tee async-tests.json
2020
(( CARGO_RESULT = CARGO_RESULT || $? ))
2121
cat async-tests.json | cargo2junit > async-tests.xml
22-
RUST_BACKTRACE=1 cargo test sync --no-default-features --features sync $OPTIONS | tee sync-tests.json
22+
RUST_BACKTRACE=1 cargo test sync --no-default-features --features sync,${TLS_FEATURE} $OPTIONS | tee sync-tests.json
2323
(( CARGO_RESULT = CARGO_RESULT || $? ))
2424
cat sync-tests.json | cargo2junit > sync-tests.xml
25-
RUST_BACKTRACE=1 cargo test --doc sync --no-default-features --features sync $OPTIONS | tee sync-doc-tests.json
25+
RUST_BACKTRACE=1 cargo test --doc sync --no-default-features --features sync,${TLS_FEATURE} $OPTIONS | tee sync-doc-tests.json
2626
(( CARGO_RESULT = CARGO_RESULT || $? ))
2727
cat sync-doc-tests.json | cargo2junit > sync-doc-tests.xml
2828

2929
junit-report-merger results.xml async-tests.xml sync-tests.xml sync-doc-tests.xml
3030

31-
exit $CARGO_RESULT
31+
exit $CARGO_RESULT

.evergreen/run-serverless-tests.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ if [ "$SINGLE_THREAD" = true ]; then
2222
OPTIONS="$OPTIONS --test-threads=1"
2323
fi
2424

25+
FEATURE_FLAGS=${FEATURE_FLAGS},${TLS_FEATURE}
26+
2527
echo "cargo test options: ${DEFAULT_FEATURES} --features $FEATURE_FLAGS ${OPTIONS}"
2628

2729
CARGO_RESULT=0
@@ -46,4 +48,4 @@ cargo_test test::cursor > cursor.xml
4648

4749
junit-report-merger results.xml crud.xml retryable_reads.xml retryable_writes.xml versioned_api.xml sessions.xml transactions.xml load_balancers.xml cursor.xml
4850

49-
exit $CARGO_RESULT
51+
exit $CARGO_RESULT

.evergreen/run-tokio-tests.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/bin/bash
22

33
set -o errexit
4+
set -o pipefail
45

56
source ./.evergreen/env.sh
67

@@ -10,7 +11,7 @@ if [ "$SINGLE_THREAD" = true ]; then
1011
OPTIONS="$OPTIONS --test-threads=1"
1112
fi
1213

13-
FEATURE_FLAGS="zstd-compression,snappy-compression,zlib-compression"
14+
FEATURE_FLAGS="zstd-compression,snappy-compression,zlib-compression,${TLS_FEATURE}"
1415

1516
echo "cargo test options: --features $FEATURE_FLAGS ${OPTIONS}"
1617

@@ -29,4 +30,4 @@ cat sync-doc-tests.json | cargo2junit > sync-doc-tests.xml
2930

3031
junit-report-merger results.xml async-tests.xml sync-tests.xml sync-doc-tests.xml
3132

32-
exit $CARGO_RESULT
33+
exit $CARGO_RESULT

.evergreen/run-x509-tests.sh

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
set -o errexit
44
set -o xtrace
5+
set -o pipefail
56

6-
. ~/.cargo/env
7+
source ./.evergreen/env.sh
78

89
export SUBJECT=`openssl x509 -subject -nameopt RFC2253 -noout -inform PEM -in $CERT_PATH`
910

@@ -13,4 +14,19 @@ export SUBJECT=${SUBJECT#"subject="}
1314
# Remove any leading or trailing whitespace
1415
export SUBJECT=`echo "$SUBJECT" | awk '{$1=$1;print}'`
1516

16-
RUST_BACKTRACE=1 MONGO_X509_USER="$SUBJECT" cargo test x509
17+
if [ "$ASYNC_RUNTIME" = "tokio" ]; then
18+
RUNTIME_FEATURE="tokio-runtime"
19+
elif [ "$ASYNC_RUNTIME" = "async-std" ]; then
20+
RUNTIME_FEATURE="async-std-runtime"
21+
else
22+
echo "invalid async runtime: ${ASYNC_RUNTIME}" >&2
23+
exit 1
24+
fi
25+
26+
OPTIONS="--no-default-features --features ${RUNTIME_FEATURE},${TLS_FEATURE} -- -Z unstable-options --format json --report-time"
27+
28+
set +o errexit
29+
RUST_BACKTRACE=1 MONGO_X509_USER="$SUBJECT" cargo test x509 $OPTIONS | tee results.json
30+
CARGO_EXIT=$?
31+
cat results.json | cargo2junit > results.xml
32+
exit $CARGO_EXIT

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tokio-runtime = ["tokio/macros", "tokio/net", "tokio/rt", "tokio/time", "serde_b
2525
async-std-runtime = ["async-std", "async-std/attributes", "async-std-resolver", "tokio-util/compat"]
2626
sync = ["async-std-runtime"]
2727
tokio-sync = ["tokio-runtime"]
28+
openssl-tls = ["openssl", "openssl-probe", "tokio-openssl"]
2829

2930
# Enable support for v0.4 of the chrono crate in the public API of the BSON library.
3031
bson-chrono-0_4 = ["bson/chrono-0_4"]
@@ -59,6 +60,8 @@ hex = "0.4.0"
5960
hmac = "0.11"
6061
lazy_static = "1.4.0"
6162
md-5 = "0.9.1"
63+
openssl = { version = "0.10.38", optional = true }
64+
openssl-probe = { version = "0.1.5", optional = true }
6265
os_info = { version = "3.0.1", default-features = false }
6366
percent-encoding = "2.0.0"
6467
rand = { version = "0.8.3", features = ["small_rng"] }
@@ -72,6 +75,7 @@ stringprep = "0.1.2"
7275
strsim = "0.10.0"
7376
take_mut = "0.2.2"
7477
thiserror = "1.0.24"
78+
tokio-openssl = { version = "0.6.3", optional = true }
7579
trust-dns-proto = "0.20.0"
7680
trust-dns-resolver = "0.20.0"
7781
typed-builder = "0.9.0"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ features = ["sync"]
7474
| `zlib-compression` | Enable support for compressing messages with [`zlib`](https://zlib.net/) | `flate2` 1.0 | no |
7575
| `zstd-compression` | Enable support for compressing messages with [`zstd`](http://facebook.github.io/zstd/). This flag requires Rust version 1.54. | `zstd` 0.9.0 | no |
7676
| `snappy-compression` | Enable support for compressing messages with [`snappy`](http://google.github.io/snappy/) | `snap` 1.0.5 | no |
77+
| `openssl-tls` | Switch TLS connection handling to use ['openssl'](https://docs.rs/openssl/0.10.38/). | `openssl` 0.10.38 | no |
7778

7879
## Example Usage
7980
Below are simple examples of using the driver. For more specific examples and the API reference, see the driver's [docs.rs page](https://docs.rs/mongodb/latest).

src/client/options/mod.rs

Lines changed: 7 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use std::{
77
collections::HashSet,
88
convert::TryFrom,
99
fmt::{self, Display, Formatter},
10-
fs::File,
1110
hash::{Hash, Hasher},
12-
io::{BufReader, Seek, SeekFrom},
1311
path::PathBuf,
1412
str::FromStr,
1513
sync::Arc,
@@ -18,15 +16,6 @@ use std::{
1816

1917
use derivative::Derivative;
2018
use lazy_static::lazy_static;
21-
use rustls::{
22-
internal::pemfile,
23-
Certificate,
24-
RootCertStore,
25-
ServerCertVerified,
26-
ServerCertVerifier,
27-
TLSError,
28-
};
29-
use rustls_pemfile::{read_one, Item};
3019
use serde::{
3120
de::{Error, Unexpected},
3221
Deserialize,
@@ -36,7 +25,6 @@ use serde::{
3625
use serde_with::skip_serializing_none;
3726
use strsim::jaro_winkler;
3827
use typed_builder::TypedBuilder;
39-
use webpki_roots::TLS_SERVER_ROOTS;
4028

4129
#[cfg(test)]
4230
use crate::srv::LookupHosts;
@@ -788,99 +776,17 @@ pub struct TlsOptions {
788776
/// [`Client`](../struct.Client.html) will not attempt to verify its identity to the
789777
/// server.
790778
pub cert_key_file_path: Option<PathBuf>,
791-
}
792779

793-
struct NoCertVerifier {}
794-
795-
impl ServerCertVerifier for NoCertVerifier {
796-
fn verify_server_cert(
797-
&self,
798-
_: &RootCertStore,
799-
_: &[Certificate],
800-
_: webpki::DNSNameRef,
801-
_: &[u8],
802-
) -> std::result::Result<ServerCertVerified, TLSError> {
803-
Ok(ServerCertVerified::assertion())
804-
}
780+
/// Whether or not the [`Client`](../struct.Client.html) should return an error if the hostname
781+
/// is invalid.
782+
///
783+
/// The default value is to error on invalid hostnames.
784+
#[cfg(any(feature = "openssl-tls", docsrs))]
785+
#[cfg_attr(docsrs, doc(cfg(feature = "openssl-tls")))]
786+
pub allow_invalid_hostnames: Option<bool>,
805787
}
806788

807789
impl TlsOptions {
808-
/// Converts `TlsOptions` into a rustls::ClientConfig.
809-
pub(crate) fn into_rustls_config(self) -> Result<rustls::ClientConfig> {
810-
let mut config = rustls::ClientConfig::new();
811-
812-
if let Some(true) = self.allow_invalid_certificates {
813-
config
814-
.dangerous()
815-
.set_certificate_verifier(Arc::new(NoCertVerifier {}));
816-
}
817-
818-
let mut store = RootCertStore::empty();
819-
if let Some(path) = self.ca_file_path {
820-
store
821-
.add_pem_file(&mut BufReader::new(File::open(&path)?))
822-
.map_err(|_| ErrorKind::InvalidTlsConfig {
823-
message: format!(
824-
"Unable to parse PEM-encoded root certificate from {}",
825-
path.display()
826-
),
827-
})?;
828-
} else {
829-
store.add_server_trust_anchors(&TLS_SERVER_ROOTS);
830-
}
831-
832-
config.root_store = store;
833-
834-
if let Some(path) = self.cert_key_file_path {
835-
let mut file = BufReader::new(File::open(&path)?);
836-
let certs = match pemfile::certs(&mut file) {
837-
Ok(certs) => certs,
838-
Err(()) => {
839-
return Err(ErrorKind::InvalidTlsConfig {
840-
message: format!(
841-
"Unable to parse PEM-encoded client certificate from {}",
842-
path.display()
843-
),
844-
}
845-
.into())
846-
}
847-
};
848-
849-
file.seek(SeekFrom::Start(0))?;
850-
let key = loop {
851-
match read_one(&mut file) {
852-
Ok(Some(Item::PKCS8Key(bytes))) | Ok(Some(Item::RSAKey(bytes))) => {
853-
break rustls::PrivateKey(bytes)
854-
}
855-
Ok(Some(_)) => continue,
856-
Ok(None) => {
857-
return Err(ErrorKind::InvalidTlsConfig {
858-
message: format!("No PEM-encoded keys in {}", path.display()),
859-
}
860-
.into())
861-
}
862-
Err(_) => {
863-
return Err(ErrorKind::InvalidTlsConfig {
864-
message: format!(
865-
"Unable to parse PEM-encoded item from {}",
866-
path.display()
867-
),
868-
}
869-
.into())
870-
}
871-
}
872-
};
873-
874-
config
875-
.set_single_client_cert(certs, key)
876-
.map_err(|e| ErrorKind::InvalidTlsConfig {
877-
message: e.to_string(),
878-
})?;
879-
}
880-
881-
Ok(config)
882-
}
883-
884790
#[cfg(test)]
885791
pub(crate) fn serialize_for_client_options<S>(
886792
tls_options: &TlsOptions,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
//! | `zlib-compression` | Enable support for compressing messages with [`zlib`](https://zlib.net/) | `flate2` 1.0 | no |
6363
//! | `zstd-compression` | Enable support for compressing messages with [`zstd`](http://facebook.github.io/zstd/). This flag requires Rust version 1.54. | `zstd` 0.9.0 | no |
6464
//! | `snappy-compression` | Enable support for compressing messages with [`snappy`](http://google.github.io/snappy/) | `snap` 1.0.5 | no |
65+
//! | `openssl-tls` | Switch TLS connection handling to use ['openssl'](https://docs.rs/openssl/0.10.38/). | `openssl` 0.10.38 | no |
6566
//!
6667
//! # Example Usage
6768
//!

0 commit comments

Comments
 (0)