Skip to content

Commit

Permalink
Using rustls-0.23 and related dependencies
Browse files Browse the repository at this point in the history
Signed-off-by: Abhijit Gadgil <[email protected]>
  • Loading branch information
agadgil-progress committed Jan 7, 2025
1 parent 9fa2690 commit 702c65d
Show file tree
Hide file tree
Showing 13 changed files with 795 additions and 512 deletions.
1,006 changes: 632 additions & 374 deletions Cargo.lock

Large diffs are not rendered by default.

30 changes: 17 additions & 13 deletions components/common/src/cli_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use habitat_core::{fs::{am_i_root,
PrivateKeyCli,
RootCertificateStoreCli}};
use log::debug;
use rustls::{ClientConfig as TlsClientConfig,
use rustls::{pki_types::PrivateKeyDer,
ClientConfig as TlsClientConfig,
Error as TLSError};
use serde::{Deserialize,
Serialize};
Expand All @@ -36,11 +37,11 @@ static ref CACHED_CLI_CONFIG: CliConfig = CliConfig::load().unwrap_or_default();

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("'{}' io failure, err: {0}", CLI_CONFIG_PATH.display())]
#[error("'{cfg_path}' io failure, err: {0}", cfg_path = CLI_CONFIG_PATH.display())]
Io(#[from] io::Error),
#[error("deserializing '{}' failed, err: {0}", CLI_CONFIG_PATH.display())]
#[error("deserializing '{cfg_path}' failed, err: {0}", cfg_path = CLI_CONFIG_PATH.display())]
Deserialize(#[from] toml::de::Error),
#[error("serializing '{}' failed, err: {0}", CLI_CONFIG_PATH.display())]
#[error("serializing '{cfg_path}' failed, err: {0}", cfg_path = CLI_CONFIG_PATH.display())]
Serialize(#[from] toml::ser::Error),
}

Expand Down Expand Up @@ -87,19 +88,22 @@ impl CliConfig {
}

pub fn maybe_tls_client_config(self) -> Result<Option<TlsClientConfig>, TLSError> {
let client_certificates = self.ctl_client_certificate
.map(CertificateChainCli::into_inner);
let client_key = self.ctl_client_key.map(PrivateKeyCli::into_inner);
let server_ca_certificates = self.ctl_server_ca_certificate
.map(RootCertificateStoreCli::into_inner);
if let Some(server_certificates) = server_ca_certificates {
let tls_config = TlsClientConfig::builder().with_safe_defaults()
.with_root_certificates(server_certificates);
if let Some(client_key) = client_key {
let tls_config = TlsClientConfig::builder() //.with_safe_default_protocol_versions()
.with_root_certificates(server_certificates);
if let Some(client_key) = self.ctl_client_key {
debug!("Configuring ctl-gateway TLS with client certificate");
let config =
tls_config.with_client_auth_cert(client_certificates.unwrap_or_default(),
client_key)?;
let certs = if let Some(certs) = self.ctl_client_certificate.clone() {
certs.into_inner()
} else {
vec![]
};
let config = tls_config.with_client_auth_cert(
certs,
PrivateKeyDer::Pkcs8(client_key.into_inner().clone_key()),
)?;
Ok(Some(config))
} else {
Ok(Some(tls_config.with_no_client_auth()))
Expand Down
2 changes: 1 addition & 1 deletion components/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pin-project = "*"
rand = "*"
regex = "*"
rcgen = "*"
rustls = "*"
rustls = "0.23"
rustls-pemfile = "*"
serde = { version = "*", features = ["derive"] }
serde_json = { version = "*", features = ["preserve_order"] }
Expand Down
16 changes: 9 additions & 7 deletions components/core/src/tls/ctl_gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use rcgen::{CertificateParams,
Error as RcgenError,
KeyPair,
PKCS_ECDSA_P256_SHA256};
use rustls::{Certificate,
PrivateKey,

use rustls::{pki_types::{CertificateDer,
PrivatePkcs8KeyDer},
RootCertStore};
use std::{fs::{self,
File},
Expand Down Expand Up @@ -41,9 +42,10 @@ pub enum Error {
pub fn generate_self_signed_certificate_and_key(subject_alternate_name: &DnsName,
path: impl AsRef<Path>)
-> Result<(), Error> {
let mut params =
CertificateParams::new(vec![Into::<&str>::into(subject_alternate_name.as_ref()).to_string(),
"localhost".to_string(),])?;
let mut params = CertificateParams::new(vec![
Into::<&str>::into(subject_alternate_name.as_ref()).to_string(),
"localhost".to_string(),
])?;
let mut distinguished_name = DistinguishedName::new();
distinguished_name.push(DnType::OrganizationName,
"Habitat Supervisor Control Gateway");
Expand Down Expand Up @@ -81,12 +83,12 @@ fn get_last_path(search_directory: impl AsRef<Path>, file_pattern: &str) -> Resu
.ok_or_else(|| Error::FailedToMatchPattern(pattern.to_string()))
}

pub fn latest_certificates(path: impl AsRef<Path>) -> Result<Vec<Certificate>, Error> {
pub fn latest_certificates(path: impl AsRef<Path>) -> Result<Vec<CertificateDer<'static>>, Error> {
let path = get_last_path(path, &format!("{}-*.{}", NAME_PREFIX, CRT_EXTENSION))?;
Ok(rustls_wrapper::certificates_from_file(path)?)
}

pub fn latest_private_key(path: impl AsRef<Path>) -> Result<PrivateKey, Error> {
pub fn latest_private_key(path: impl AsRef<Path>) -> Result<PrivatePkcs8KeyDer<'static>, Error> {
let path = get_last_path(path, &format!("{}-*.{}", NAME_PREFIX, KEY_EXTENSION))?;
Ok(rustls_wrapper::private_key_from_file(path)?)
}
Expand Down
29 changes: 12 additions & 17 deletions components/core/src/tls/rustls_wrapper/readers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Utility functions to standardize reading certificates, private keys, and root certificate stores
//! using `rustls`
use rustls::{Certificate,
PrivateKey,
use rustls::{pki_types::{CertificateDer,
PrivatePkcs8KeyDer},
RootCertStore};
use std::{fs::File,
io::{self,
Expand Down Expand Up @@ -36,37 +36,32 @@ fn buf_from_file(path: impl AsRef<Path>) -> Result<BufReader<File>, Error> {
Ok(BufReader::new(file))
}

pub fn certificates_from_file(path: impl AsRef<Path>) -> Result<Vec<Certificate>, Error> {
pub fn certificates_from_file(path: impl AsRef<Path>)
-> Result<Vec<CertificateDer<'static>>, Error> {
let mut buf = buf_from_file(path.as_ref())?;
rustls_pemfile::certs(&mut buf).map(|c| {
c.map_err(|_| Error::FailedToReadCerts(path.as_ref().into()))
.map(|c| Certificate(c.as_ref().to_vec()))
.map(|x| x.into_owned())
})
.collect()
}

fn private_keys_from_file(path: impl AsRef<Path>) -> Result<Vec<PrivateKey>, Error> {
fn private_keys_from_file(path: impl AsRef<Path>)
-> Result<Vec<PrivatePkcs8KeyDer<'static>>, Error> {
let mut buf = buf_from_file(path.as_ref())?;
rustls_pemfile::pkcs8_private_keys(&mut buf).map(|k| {
k.map_err(|_| Error::FailedToReadPrivateKeys(path.as_ref().into()))
.map(|k| PrivateKey(k.secret_pkcs8_der().to_vec()))
})
.collect()
rustls_pemfile::pkcs8_private_keys(&mut buf)
.map(|k| k.map_err(|_| Error::FailedToReadPrivateKeys(path.as_ref().into())))
.collect()
}

pub fn private_key_from_file(path: impl AsRef<Path>) -> Result<PrivateKey, Error> {
pub fn private_key_from_file(path: impl AsRef<Path>) -> Result<PrivatePkcs8KeyDer<'static>, Error> {
private_keys_from_file(path.as_ref())?.pop()
.ok_or_else(|| Error::NoPrivateKey(path.as_ref().into()))
}

pub fn root_certificate_store_from_file(path: impl AsRef<Path>) -> Result<RootCertStore, Error> {
let mut buf = buf_from_file(path.as_ref())?;
let mut root_certificate_store = RootCertStore::empty();
let certs = &rustls_pemfile::certs(&mut buf).map(|c| {
c.map_err(|_| Error::FailedToReadRootCertificateStore(path.as_ref().into()))
.map(|c| c.as_ref().to_vec())
})
.collect::<Result<Vec<_>, _>>()?;
let certs = certificates_from_file(path.as_ref())?;
let (_, failed) = root_certificate_store.add_parsable_certificates(certs);
if failed > 0 {
Err(Error::FailedToReadCertificatesFromRootCertificateStore(failed, path.as_ref().into()))
Expand Down
8 changes: 4 additions & 4 deletions components/core/src/tls/rustls_wrapper/tcp_or_tls_stream.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use pin_project::pin_project;
use rustls::{ClientConfig as TlsClientConfig,
ServerConfig as TlsServerConfig,
ServerName};
use rustls::{pki_types::ServerName,
ClientConfig as TlsClientConfig,
ServerConfig as TlsServerConfig};
use std::{convert::TryFrom,
pin::Pin,
sync::Arc,
Expand Down Expand Up @@ -69,7 +69,7 @@ impl TcpOrTlsStream {
let tls_client_stream = match self {
Self::TcpStream(stream) => {
let tls_connector = TlsConnector::from(tls_config);
let server_name = match ServerName::try_from(domain) {
let server_name = match ServerName::try_from(domain.to_string()) {
Ok(name) => name,
Err(_) => {
let error = io::Error::new(io::ErrorKind::InvalidInput,
Expand Down
21 changes: 14 additions & 7 deletions components/core/src/tls/rustls_wrapper/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
use crate::{error::Error,
tls::{ctl_gateway,
rustls_wrapper}};
use rustls::{Certificate,
PrivateKey as RustlsPrivateKey,
use rustls::{pki_types::{CertificateDer,
PrivatePkcs8KeyDer},
RootCertStore};
use serde::{Deserialize,
Serialize};
Expand All @@ -21,11 +21,11 @@ use std::{path::PathBuf,
#[serde(try_from = "String", into = "String")]
pub struct CertificateChainCli {
path: PathBuf,
certificates: Vec<Certificate>,
certificates: Vec<CertificateDer<'static>>,
}

impl CertificateChainCli {
pub fn into_inner(self) -> Vec<Certificate> { self.certificates }
pub fn into_inner(self) -> Vec<CertificateDer<'static>> { self.certificates }
}

impl FromStr for CertificateChainCli {
Expand All @@ -48,15 +48,22 @@ impl std::fmt::Display for CertificateChainCli {
}
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
#[serde(try_from = "String", into = "String")]
pub struct PrivateKeyCli {
path: PathBuf,
private_key: RustlsPrivateKey,
private_key: PrivatePkcs8KeyDer<'static>,
}

impl PrivateKeyCli {
pub fn into_inner(self) -> RustlsPrivateKey { self.private_key }
pub fn into_inner(self) -> PrivatePkcs8KeyDer<'static> { self.private_key }
}

impl Clone for PrivateKeyCli {
fn clone(&self) -> Self {
Self { path: self.path.clone(),
private_key: self.private_key.clone_key(), }
}
}

impl FromStr for PrivateKeyCli {
Expand Down
4 changes: 2 additions & 2 deletions components/sup-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ impl SrvClient {
let config = CliConfig::load()?;
let server_name_indication = config.ctl_server_name_indication.clone();
let tcp_stream = if let Some(tls_config) = config.maybe_tls_client_config()?.map(Arc::new) {
let domain = server_name_indication.as_deref()
.unwrap_or_else(|| addr.domain());
let domain = server_name_indication.unwrap_or_else(|| addr.domain().to_string());
let domain: &str = &*domain;
debug!("Upgrading ctl-gateway to TLS with domain '{}'", domain);
TcpOrTlsStream::new_tls_client(tcp_stream, tls_config, domain).await
.map_err(|e| e.0)?
Expand Down
3 changes: 2 additions & 1 deletion components/sup/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ doc = false
[dependencies]
bytes = "*"
actix-web = { version = "*", default-features = false, features = [
"rustls-0_21",
"rustls-0_23",
] }
actix-http = { version = "3", features = [ "rustls-0_23" ]}
actix-rt = "*"
byteorder = "*"
clap = { git = "https://github.com/habitat-sh/clap.git", branch = "v2-master", features = [
Expand Down
75 changes: 44 additions & 31 deletions components/sup/src/ctl_gateway/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ use prometheus::{register_histogram_vec,
HistogramTimer,
HistogramVec,
IntCounterVec};

use rustls::{self,
server::{AllowAnyAuthenticatedClient,
NoClientAuth},
Certificate,
PrivateKey,
pki_types::{CertificateDer,
PrivateKeyDer,
PrivatePkcs8KeyDer},
server::WebPkiClientVerifier,
RootCertStore,
ServerConfig as TlsServerConfig};
use std::{error,
Expand Down Expand Up @@ -203,7 +204,9 @@ impl Client {
}
Err(err) => {
warn!("Handshake error, {:?}", err);
return Err(HandlerError::from(io::Error::from(io::ErrorKind::ConnectionAborted)));
return Err(HandlerError::from(io::Error::from(
io::ErrorKind::ConnectionAborted,
)));
}
}
};
Expand Down Expand Up @@ -478,8 +481,8 @@ pub(crate) struct CtlGatewayServer {
pub(crate) listen_addr: SocketAddr,
pub(crate) secret_key: String,
pub(crate) mgr_sender: MgrSender,
pub(crate) server_certificates: Option<Vec<Certificate>>,
pub(crate) server_key: Option<PrivateKey>,
pub(crate) server_certificates: Option<Vec<CertificateDer<'static>>>,
pub(crate) server_key: Option<PrivatePkcs8KeyDer<'static>>,
pub(crate) client_certificates: Option<RootCertStore>,
}

Expand Down Expand Up @@ -520,26 +523,36 @@ impl CtlGatewayServer {

// Upgrade to a TLS connection if necessary
let tcp_stream = if let Some(tls_config) = &maybe_tls_config {
match TcpOrTlsStream::new_tls_server(tcp_stream, Arc::clone(tls_config)).await
{
Ok(tcp_stream) => tcp_stream,
Err((e, tcp_stream)) => {
error!("Failed to accept TLS client connection, err {}", e);
// If the client sent a corrupt TLS message it is a good indicator that
// they did not upgrade to TLS. In this case send back an error response.
// We do not always send back an error response because it can lead to
// confusing error messages on the client.
#[allow(clippy::redundant_closure_for_method_calls)]
if let Some(&rustls::Error::InvalidMessage(_)) = e.get_ref().and_then(|e| e.downcast_ref()) {
let mut srv_codec = SrvCodec::new().framed(tcp_stream);
let net_err = net::err(ErrCode::TlsHandshakeFailed, format!("TLS handshake failed, err: {}", e));
if let Err(e) = srv_codec.send(SrvMessage::from(net_err)).await {
error!("Failed to send TLS failure message to client, err {}", e);
match TcpOrTlsStream::new_tls_server(tcp_stream, Arc::clone(tls_config))
.await
{
Ok(tcp_stream) => tcp_stream,
Err((e, tcp_stream)) => {
error!("Failed to accept TLS client connection, err {}", e);
// If the client sent a corrupt TLS message it is a good indicator that
// they did not upgrade to TLS. In this case send back an error response.
// We do not always send back an error response because it can lead to
// confusing error messages on the client.
#[allow(clippy::redundant_closure_for_method_calls)]
if let Some(&rustls::Error::InvalidMessage(_)) =
e.get_ref().and_then(|e| e.downcast_ref())
{
let mut srv_codec = SrvCodec::new().framed(tcp_stream);
let net_err = net::err(
ErrCode::TlsHandshakeFailed,
format!("TLS handshake failed, err: {}", e),
);
if let Err(e) = srv_codec.send(SrvMessage::from(net_err)).await
{
error!(
"Failed to send TLS failure message to client, err {}",
e
);
}
}
continue;
}
continue;
}
}
} else {
TcpOrTlsStream::new(tcp_stream)
};
Expand All @@ -556,23 +569,23 @@ impl CtlGatewayServer {
}
}

fn maybe_tls_config(server_certificates: Option<Vec<Certificate>>,
server_key: Option<PrivateKey>,
fn maybe_tls_config(server_certificates: Option<Vec<CertificateDer<'static>>>,
server_key: Option<PrivatePkcs8KeyDer<'static>>,
client_certificates: Option<RootCertStore>)
-> Option<TlsServerConfig> {
if let Some(server_key) = server_key {
let client_auth = if let Some(client_certificates) = client_certificates {
debug!("Upgrading ctl-gateway to TLS with client authentication");
AllowAnyAuthenticatedClient::new(client_certificates).boxed()
WebPkiClientVerifier::builder(client_certificates.into()).build()
.unwrap()
} else {
debug!("Upgrading ctl-gateway to TLS");
NoClientAuth::boxed()
WebPkiClientVerifier::no_client_auth()
};
let tls_config =
TlsServerConfig::builder().with_safe_defaults()
.with_client_cert_verifier(client_auth)
TlsServerConfig::builder().with_client_cert_verifier(client_auth)
.with_single_cert(server_certificates.unwrap_or_default(),
server_key)
PrivateKeyDer::Pkcs8(server_key))
.expect("Could not set certificate for ctl gateway!");

Some(tls_config)
Expand Down
2 changes: 1 addition & 1 deletion components/sup/src/http_gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ impl Server {
debug!("http_gateway server configured");

let bind = match tls_config {
Some(c) => server.bind_rustls_021(listen_addr.to_string(), c),
Some(c) => server.bind_rustls_0_23(listen_addr.to_string(), c),
None => server.bind(listen_addr.to_string()),
};
debug!("http_gateway server port bound");
Expand Down
Loading

0 comments on commit 702c65d

Please sign in to comment.