Skip to content

Commit

Permalink
feat: MqttOptions::parse_url uses `rustls-native-certs::load_native…
Browse files Browse the repository at this point in the history
…_certs()` for encrypted connections (#436)

* `MqttOptions::parse_url` when passed the url for an encrypted protocol, e.g. `mqtts://`, `ssl://` or `wss://` will from now on use `rustls-native-certs::load_native_certs()` to load the certificates from the native cert store.

* Updated documentation

Issue: #435 

Attribute: @de-sh
  • Loading branch information
de-sh authored Aug 26, 2022
1 parent 2f11546 commit 4935b9d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 6 deletions.
1 change: 1 addition & 0 deletions rumqttc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ rustls-pemfile = { version = "0.3", optional = true }
thiserror = "1"
tokio = { version = "1", features = ["rt", "macros", "io-util", "net", "time"] }
tokio-rustls = { version = "=0.23.3", optional = true }
rustls-native-certs = "0.6"
url = { version = "2", default-features = false, optional = true }
ws_stream_tungstenite = { version = "0.7", default-features = false, features = ["tokio_io"], optional = true }

Expand Down
4 changes: 2 additions & 2 deletions rumqttc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl From<TrySendError<Request>> for ClientError {
/// An asynchronous client, communicates with MQTT `EventLoop`.
///
/// This is cloneable and can be used to asynchronously [`publish`](`AsyncClient::publish`),
/// [`subscribe`](`AsynClient::subscribe`) through the `EventLoop`, which is to be polled parallelly.
/// [`subscribe`](`AsyncClient::subscribe`) through the `EventLoop`, which is to be polled parallelly.
///
/// **NOTE**: The `EventLoop` must be regularly polled in order to send, receive and process packets
/// from the broker, i.e. move ahead.
Expand Down Expand Up @@ -217,7 +217,7 @@ fn get_ack_req(publish: &Publish) -> Option<Request> {
/// A synchronous client, communicates with MQTT `EventLoop`.
///
/// This is cloneable and can be used to synchronously [`publish`](`AsyncClient::publish`),
/// [`subscribe`](`AsynClient::subscribe`) through the `EventLoop`/`Connection`, which is to be polled in parallel
/// [`subscribe`](`AsyncClient::subscribe`) through the `EventLoop`/`Connection`, which is to be polled in parallel
/// by iterating over the object returned by [`Connection.iter()`](Connection::iter) in a separate thread.
///
/// **NOTE**: The `EventLoop`/`Connection` must be regularly polled(`.next()` in case of `Connection`) in order
Expand Down
55 changes: 51 additions & 4 deletions rumqttc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,16 @@ pub use eventloop::{ConnectionError, Event, EventLoop};
pub use flume::{SendError, Sender, TrySendError};
pub use mqttbytes::v4::*;
pub use mqttbytes::*;
#[cfg(feature = "use-rustls")]
pub use rustls_native_certs::load_native_certs;
pub use state::{MqttState, StateError};
#[cfg(feature = "use-rustls")]
pub use tls::Error as TlsError;

#[cfg(feature = "use-rustls")]
pub use tokio_rustls;
#[cfg(feature = "use-rustls")]
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore};

pub type Incoming = Packet;

Expand Down Expand Up @@ -226,6 +228,11 @@ impl Transport {
Self::Tcp
}

#[cfg(feature = "use-rustls")]
pub fn tls_with_default_config() -> Self {
Self::tls_with_config(Default::default())
}

/// Use secure tcp with tls as transport
#[cfg(feature = "use-rustls")]
pub fn tls(
Expand Down Expand Up @@ -281,6 +288,12 @@ impl Transport {
pub fn wss_with_config(tls_config: TlsConfiguration) -> Self {
Self::Wss(tls_config)
}

#[cfg(all(feature = "use-rustls", feature = "websocket"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-rustls", feature = "websocket"))))]
pub fn wss_with_default_config() -> Self {
Self::Wss(Default::default())
}
}

/// TLS configuration method
Expand All @@ -299,6 +312,22 @@ pub enum TlsConfiguration {
Rustls(Arc<ClientConfig>),
}

#[cfg(feature = "use-rustls")]
impl Default for TlsConfiguration {
fn default() -> Self {
let mut root_cert_store = RootCertStore::empty();
for cert in load_native_certs().expect("could not load platform certs") {
root_cert_store.add(&Certificate(cert.0)).unwrap();
}
let tls_config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_cert_store)
.with_no_client_auth();

Self::Rustls(Arc::new(tls_config))
}
}

#[cfg(feature = "use-rustls")]
impl From<ClientConfig> for TlsConfiguration {
fn from(config: ClientConfig) -> Self {
Expand Down Expand Up @@ -402,8 +431,24 @@ impl MqttOptions {
/// let options = MqttOptions::parse_url("mqtt://example.com:1883?client_id=123").unwrap();
/// ```
///
/// NOTE: A url must be prefixed with one of either `tcp://`, `mqtt://`, `ssl://`,`mqtts://`,
/// **NOTE:** A url must be prefixed with one of either `tcp://`, `mqtt://`, `ssl://`,`mqtts://`,
/// `ws://` or `wss://` to denote the protocol for establishing a connection with the broker.
///
/// **NOTE:** Encrypted connections(i.e. `mqtts://`, `ssl://`, `wss://`) by default use the
/// system's root certificates. To configure with custom certificates, one may use the
/// [`set_transport`](MqttOptions::set_transport) method.
///
/// ```
/// # use rumqttc::{MqttOptions, Transport};
/// # use tokio_rustls::rustls::ClientConfig;
/// # let root_cert_store = rustls::RootCertStore::empty();
/// # let client_config = ClientConfig::builder()
/// # .with_safe_defaults()
/// # .with_root_certificates(root_cert_store)
/// # .with_no_client_auth();
/// let mut options = MqttOptions::parse_url("mqtts://example.com?client_id=123").unwrap();
/// options.set_transport(Transport::tls_with_config(client_config.into()));
/// ```
pub fn parse_url<S: Into<String>>(url: S) -> Result<MqttOptions, OptionError> {
use std::convert::TryFrom;

Expand Down Expand Up @@ -612,10 +657,12 @@ impl std::convert::TryFrom<url::Url> for MqttOptions {
// Encrypted connections are supported, but require explicit TLS configuration. We fall
// back to the unencrypted transport layer, so that `set_transport` can be used to
// configure the encrypted transport layer with the provided TLS configuration.
"mqtts" | "ssl" => (Transport::Tcp, 8883),
"mqtts" | "ssl" => (Transport::tls_with_default_config(), 8883),
"mqtt" | "tcp" => (Transport::Tcp, 1883),
#[cfg(feature = "websocket")]
"ws" | "wss" => (Transport::Ws, 8000),
"ws" => (Transport::Ws, 8000),
#[cfg(feature = "websocket")]
"wss" => (Transport::wss_with_default_config(), 8000),
_ => return Err(OptionError::Scheme),
};

Expand Down

0 comments on commit 4935b9d

Please sign in to comment.