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

Using rustls-0.23 and related dependencies #9552

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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,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(CertificateDer::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
7 changes: 3 additions & 4 deletions components/sup-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,10 @@ 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());
debug!("Upgrading ctl-gateway to TLS with domain '{}'", domain);
TcpOrTlsStream::new_tls_client(tcp_stream, tls_config, domain).await
.map_err(|e| e.0)?
TcpOrTlsStream::new_tls_client(tcp_stream, tls_config, &domain).await
.map_err(|e| e.0)?
} else {
TcpOrTlsStream::new(tcp_stream)
};
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
Loading