Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MqttOptions::parse_url uses rustls-native-certs::load_native_certs() for encrypted connections #436

Merged
merged 6 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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