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 91f05dd
Show file tree
Hide file tree
Showing 13 changed files with 1,821 additions and 1,390 deletions.
1,006 changes: 632 additions & 374 deletions Cargo.lock

Large diffs are not rendered by default.

78 changes: 41 additions & 37 deletions components/common/src/cli_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@
//! for more than simply CLI configuration. If the opportunity arose it would be useful to rename
//! this to convey that it is general configuration.
use crate::types::ResolvedListenCtlAddr;
use habitat_core::{fs::{am_i_root,
FS_ROOT_PATH},
origin::Origin,
tls::rustls_wrapper::{CertificateChainCli,
PrivateKeyCli,
RootCertificateStoreCli}};
use habitat_core::{
fs::{am_i_root, FS_ROOT_PATH},
origin::Origin,
tls::rustls_wrapper::{CertificateChainCli, PrivateKeyCli, RootCertificateStoreCli},
};
use log::debug;
use rustls::{ClientConfig as TlsClientConfig,
Error as TLSError};
use serde::{Deserialize,
Serialize};
use std::{fs,
io,
path::{Path,
PathBuf}};
use rustls::pki_types::PrivateKeyDer;
use rustls::{ClientConfig as TlsClientConfig, Error as TLSError};
use serde::{Deserialize, Serialize};
use std::{
fs, io,
path::{Path, PathBuf},
};

const CLI_CONFIG_PATH_POSTFIX: &str = "hab/etc/cli.toml";

Expand All @@ -36,25 +34,25 @@ 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),
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct CliConfig {
pub auth_token: Option<String>,
pub origin: Option<Origin>,
pub ctl_secret: Option<String>,
pub listen_ctl: Option<ResolvedListenCtlAddr>,
pub ctl_client_certificate: Option<CertificateChainCli>,
pub ctl_client_key: Option<PrivateKeyCli>,
pub ctl_server_ca_certificate: Option<RootCertificateStoreCli>,
pub auth_token: Option<String>,
pub origin: Option<Origin>,
pub ctl_secret: Option<String>,
pub listen_ctl: Option<ResolvedListenCtlAddr>,
pub ctl_client_certificate: Option<CertificateChainCli>,
pub ctl_client_key: Option<PrivateKeyCli>,
pub ctl_server_ca_certificate: Option<RootCertificateStoreCli>,
pub ctl_server_name_indication: Option<String>,
pub bldr_url: Option<String>,
pub bldr_url: Option<String>,
}

impl CliConfig {
Expand All @@ -64,7 +62,9 @@ impl CliConfig {
}

/// Get a reference to the `CliConfig` cached at startup
pub fn cache() -> &'static Self { &CACHED_CLI_CONFIG }
pub fn cache() -> &'static Self {
&CACHED_CLI_CONFIG
}

/// Load an up to date `CliConfig` from disk
pub fn load() -> Result<Self, Error> {
Expand All @@ -87,19 +87,23 @@ 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);
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
98 changes: 54 additions & 44 deletions components/core/src/tls/ctl_gateway.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
//! Utilities for generating and reading the self-signed certificate for use with the control
//! gateway.
use crate::{crypto::keys::NamedRevision,
tls::rustls_wrapper::{self,
Error as RustlsReadersError}};
use rcgen::{CertificateParams,
DistinguishedName,
DnType,
Error as RcgenError,
KeyPair,
PKCS_ECDSA_P256_SHA256};
use rustls::{Certificate,
PrivateKey,
RootCertStore};
use std::{fs::{self,
File},
io::{Error as IoError,
Write},
path::{Path,
PathBuf}};
use crate::{
crypto::keys::NamedRevision,
tls::rustls_wrapper::{self, Error as RustlsReadersError},
};
use rcgen::{
CertificateParams, DistinguishedName, DnType, Error as RcgenError, KeyPair,
PKCS_ECDSA_P256_SHA256,
};

use rustls::pki_types::CertificateDer;
use rustls::pki_types::PrivatePkcs8KeyDer;
use rustls::RootCertStore;
use std::{
fs::{self, File},
io::{Error as IoError, Write},
path::{Path, PathBuf},
};
use thiserror::Error;
use webpki::types::DnsName;

Expand All @@ -38,15 +37,19 @@ pub enum Error {
CertificateWrite(#[from] IoError),
}

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(),])?;
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 distinguished_name = DistinguishedName::new();
distinguished_name.push(DnType::OrganizationName,
"Habitat Supervisor Control Gateway");
distinguished_name.push(
DnType::OrganizationName,
"Habitat Supervisor Control Gateway",
);
params.distinguished_name = distinguished_name;

let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
Expand All @@ -57,13 +60,15 @@ pub fn generate_self_signed_certificate_and_key(subject_alternate_name: &DnsName
fs::create_dir_all(&path)?;
let named_revision = NamedRevision::new(NAME_PREFIX.to_string());

let crt_path = path.as_ref()
.join(format!("{}.{}", named_revision, CRT_EXTENSION));
let crt_path = path
.as_ref()
.join(format!("{}.{}", named_revision, CRT_EXTENSION));
let mut crt_file = File::create(crt_path)?;
crt_file.write_all(crt.as_bytes())?;

let key_path = path.as_ref()
.join(format!("{}.{}", named_revision, KEY_EXTENSION));
let key_path = path
.as_ref()
.join(format!("{}.{}", named_revision, KEY_EXTENSION));
let mut key_file = File::create(key_path)?;
key_file.write_all(key.as_bytes())?;

Expand All @@ -74,19 +79,20 @@ pub fn generate_self_signed_certificate_and_key(subject_alternate_name: &DnsName
fn get_last_path(search_directory: impl AsRef<Path>, file_pattern: &str) -> Result<PathBuf, Error> {
let pattern = search_directory.as_ref().join(file_pattern);
let pattern = pattern.to_string_lossy();
glob::glob(&pattern).expect("valid pattern")
.filter_map(std::result::Result::ok)
.filter(|p| p.metadata().map(|m| m.is_file()).unwrap_or(false))
.max()
.ok_or_else(|| Error::FailedToMatchPattern(pattern.to_string()))
glob::glob(&pattern)
.expect("valid pattern")
.filter_map(std::result::Result::ok)
.filter(|p| p.metadata().map(|m| m.is_file()).unwrap_or(false))
.max()
.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 All @@ -99,18 +105,19 @@ pub fn latest_root_certificate_store(path: impl AsRef<Path>) -> Result<RootCertS
#[cfg(test)]
mod tests {
use super::*;
use std::{convert::TryFrom,
fs,
time::Duration};
use std::{convert::TryFrom, fs, time::Duration};
use tempfile::TempDir;
use webpki::types::DnsName;

#[test]
fn ctl_gateway_generate_and_read_tls_files() {
let tmpdir = TempDir::new().unwrap();

generate_self_signed_certificate_and_key(&DnsName::try_from("a_test_domain").unwrap(),
&tmpdir).unwrap();
generate_self_signed_certificate_and_key(
&DnsName::try_from("a_test_domain").unwrap(),
&tmpdir,
)
.unwrap();
assert_eq!(fs::read_dir(&tmpdir).unwrap().count(), 2);
let first_path =
get_last_path(&tmpdir, &format!("{}-*.{}", NAME_PREFIX, CRT_EXTENSION)).unwrap();
Expand All @@ -124,8 +131,11 @@ mod tests {
// name.
std::thread::sleep(Duration::from_secs(2));

generate_self_signed_certificate_and_key(&DnsName::try_from("another_domain").unwrap(),
&tmpdir).unwrap();
generate_self_signed_certificate_and_key(
&DnsName::try_from("another_domain").unwrap(),
&tmpdir,
)
.unwrap();
assert_eq!(fs::read_dir(&tmpdir).unwrap().count(), 4);
let second_path =
get_last_path(&tmpdir, &format!("{}-*.{}", NAME_PREFIX, CRT_EXTENSION)).unwrap();
Expand Down
62 changes: 32 additions & 30 deletions components/core/src/tls/rustls_wrapper/readers.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! Utility functions to standardize reading certificates, private keys, and root certificate stores
//! using `rustls`
use rustls::{Certificate,
PrivateKey,
RootCertStore};
use std::{fs::File,
io::{self,
BufReader},
path::{Path,
PathBuf}};
use rustls::pki_types::CertificateDer;
use rustls::pki_types::PrivatePkcs8KeyDer;
use rustls::RootCertStore;
use std::{
fs::File,
io::{self, BufReader},
path::{Path, PathBuf},
};
use thiserror::Error;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -36,40 +36,42 @@ 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()))
})
.collect()
rustls_pemfile::certs(&mut buf)
.map(|c| {
c.map_err(|_| Error::FailedToReadCerts(path.as_ref().into()))
.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> {
private_keys_from_file(path.as_ref())?.pop()
.ok_or_else(|| Error::NoPrivateKey(path.as_ref().into()))
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()))
Err(Error::FailedToReadCertificatesFromRootCertificateStore(
failed,
path.as_ref().into(),
))
} else {
Ok(root_certificate_store)
}
Expand Down
Loading

0 comments on commit 91f05dd

Please sign in to comment.