Feature/always gen certs (#784)

* move SslManager to NetController and out of NginxController

* always generate the certs, move that functionality to the net controller before any nginx configs are written

* add info log

* refactor to make net controller not responsible for nginx stuff

* diff minimization

* diff minimization

* diff minimization

* diff minimization

* use net controller cert path instead of nginx controller cert path

* remove unused imports

* move location of cert mounts
This commit is contained in:
Keagan McClelland
2021-11-12 11:58:06 -07:00
committed by Aiden McClelland
parent 9f3909188a
commit c723ee6a15
6 changed files with 87 additions and 65 deletions

View File

@@ -331,12 +331,7 @@ async fn perform_backup<Db: DbHandle>(
tx.save().await?;
}
let (root_ca_key, root_ca_cert) = ctx
.net_controller
.nginx
.ssl_manager
.export_root_ca()
.await?;
let (root_ca_key, root_ca_cert) = ctx.net_controller.ssl.export_root_ca().await?;
let mut os_backup_file = AtomicFile::new(backup_guard.as_ref().join("os-backup.cbor")).await?;
os_backup_file
.write_all(

View File

@@ -27,6 +27,8 @@ pub mod ssl;
pub mod tor;
pub mod wifi;
const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
#[command(subcommands(tor::tor))]
pub fn net() -> Result<(), Error> {
Ok(())
@@ -37,6 +39,7 @@ pub struct NetController {
#[cfg(feature = "avahi")]
pub mdns: MdnsController,
pub nginx: NginxController,
pub ssl: SslManager,
}
impl NetController {
#[instrument(skip(db))]
@@ -47,7 +50,7 @@ impl NetController {
db: SqlitePool,
import_root_ca: Option<(PKey<Private>, X509)>,
) -> Result<Self, Error> {
let ssl_manager = match import_root_ca {
let ssl = match import_root_ca {
None => SslManager::init(db).await,
Some(a) => SslManager::import_root_ca(db, a.0, a.1).await,
}?;
@@ -55,20 +58,29 @@ impl NetController {
tor: TorController::init(embassyd_addr, embassyd_tor_key, tor_control).await?,
#[cfg(feature = "avahi")]
mdns: MdnsController::init(),
nginx: NginxController::init(PathBuf::from("/etc/nginx"), ssl_manager).await?,
nginx: NginxController::init(PathBuf::from("/etc/nginx"), &ssl).await?,
ssl,
})
}
pub fn ssl_directory_for(&self, pkg_id: &PackageId) -> PathBuf {
PathBuf::from(format!("{}/{}", PACKAGE_CERT_PATH, pkg_id))
}
#[instrument(skip(self, interfaces))]
pub async fn add<
'a,
I: IntoIterator<Item = (InterfaceId, &'a Interface, TorSecretKeyV3)> + Clone,
>(
pub async fn add<'a, I>(
&self,
pkg_id: &PackageId,
ip: Ipv4Addr,
interfaces: I,
) -> Result<(), Error> {
) -> Result<(), Error>
where
I: IntoIterator<Item = (InterfaceId, &'a Interface, TorSecretKeyV3)> + Clone,
for<'b> &'b I: IntoIterator<Item = &'b (InterfaceId, &'a Interface, TorSecretKeyV3)>,
{
self.generate_certificate_mountpoint(pkg_id, &interfaces)
.await?;
let interfaces_tor = interfaces
.clone()
.into_iter()
@@ -107,7 +119,7 @@ impl NetController {
},
)),
});
self.nginx.add(pkg_id.clone(), ip, interfaces)
self.nginx.add(&self.ssl, pkg_id.clone(), ip, interfaces)
}
);
tor_res?;
@@ -138,7 +150,31 @@ impl NetController {
Ok(())
}
async fn generate_certificate_mountpoint<'a, I>(
&self,
pkg_id: &PackageId,
interfaces: &I,
) -> Result<(), Error>
where
I: IntoIterator<Item = (InterfaceId, &'a Interface, TorSecretKeyV3)> + Clone,
for<'b> &'b I: IntoIterator<Item = &'b (InterfaceId, &'a Interface, TorSecretKeyV3)>,
{
tracing::info!("Generating SSL Certificate mountpoints for {}", pkg_id);
let package_path = PathBuf::from(PACKAGE_CERT_PATH).join(pkg_id);
tokio::fs::create_dir_all(&package_path).await?;
Ok(for (id, _, key) in interfaces {
let dns_base = OnionAddressV3::from(&key.public()).get_address_without_dot_onion();
let ssl_path_key = package_path.join(format!("{}.key.pem", id));
let ssl_path_cert = package_path.join(format!("{}.cert.pem", id));
let (key, chain) = self.ssl.certificate_for(&dns_base).await?;
tokio::try_join!(
crate::net::ssl::export_key(&key, &ssl_path_key),
crate::net::ssl::export_cert(&chain, &ssl_path_cert)
)?;
})
}
pub async fn export_root_ca(&self) -> Result<(PKey<Private>, X509), Error> {
self.nginx.ssl_manager.export_root_ca().await
self.ssl.export_root_ca().await
}
}

View File

@@ -15,23 +15,19 @@ use crate::util::{Invoke, Port};
use crate::{Error, ErrorKind, ResultExt};
pub struct NginxController {
nginx_root: PathBuf,
pub ssl_manager: SslManager,
pub nginx_root: PathBuf,
inner: Mutex<NginxControllerInner>,
}
impl NginxController {
pub async fn init(nginx_root: PathBuf, ssl_manager: SslManager) -> Result<Self, Error> {
pub async fn init(nginx_root: PathBuf, ssl_manager: &SslManager) -> Result<Self, Error> {
Ok(NginxController {
inner: Mutex::new(NginxControllerInner::init(&nginx_root, &ssl_manager).await?),
ssl_manager,
inner: Mutex::new(NginxControllerInner::init(&nginx_root, ssl_manager).await?),
nginx_root,
})
}
pub fn ssl_directory_for(&self, package: &PackageId) -> PathBuf {
self.nginx_root.join("ssl").join(package)
}
pub async fn add<I: IntoIterator<Item = (InterfaceId, InterfaceMetadata)>>(
&self,
ssl_manager: &SslManager,
package: PackageId,
ipv4: Ipv4Addr,
interfaces: I,
@@ -39,13 +35,7 @@ impl NginxController {
self.inner
.lock()
.await
.add(
&self.nginx_root,
&self.ssl_manager,
package,
ipv4,
interfaces,
)
.add(&self.nginx_root, ssl_manager, package, ipv4, interfaces)
.await
}
pub async fn remove(&self, package: &PackageId) -> Result<(), Error> {
@@ -66,17 +56,13 @@ impl NginxControllerInner {
let inner = NginxControllerInner {
interfaces: BTreeMap::new(),
};
// write main ssl key/cert to fs location
let (key, cert) = ssl_manager.certificate_for(&get_hostname().await?).await?;
let ssl_path_key = nginx_root.join(format!("ssl/embassy_main.key.pem"));
let ssl_path_cert = nginx_root.join(format!("ssl/embassy_main.cert.pem"));
tokio::try_join!(
tokio::fs::write(&ssl_path_key, key.private_key_to_pem_pkcs8()?),
tokio::fs::write(
&ssl_path_cert,
cert.into_iter()
.flat_map(|c| c.to_pem().unwrap())
.collect::<Vec<u8>>()
)
crate::net::ssl::export_key(&key, &ssl_path_key),
crate::net::ssl::export_cert(&cert, &ssl_path_cert),
)?;
Ok(inner)
}
@@ -104,34 +90,15 @@ impl NginxControllerInner {
// get ssl certificate chain
let (listen_args, ssl_certificate_line, ssl_certificate_key_line) =
if lan_port_config.ssl {
// these have already been written by the net controller
let package_path = nginx_root.join(format!("ssl/{}", package));
tokio::fs::create_dir_all(package_path).await?;
let ssl_path_key =
nginx_root.join(format!("ssl/{}/{}.key.pem", package, id));
let ssl_path_cert =
nginx_root.join(format!("ssl/{}/{}.cert.pem", package, id));
let ssl_path_key = package_path.join(format!("{}.key.pem", id));
let ssl_path_cert = package_path.join(format!("{}.cert.pem", id));
let (key, chain) = ssl_manager.certificate_for(&meta.dns_base).await?;
// write nginx ssl certs
tokio::try_join!(
tokio::fs::write(&ssl_path_key, key.private_key_to_pem_pkcs8()?).map(
|res| res.with_ctx(|_| (
ErrorKind::Filesystem,
ssl_path_key.display().to_string()
))
),
tokio::fs::write(
&ssl_path_cert,
chain
.into_iter()
.flat_map(|c| c.to_pem().unwrap())
.collect::<Vec<u8>>()
)
.map(|res| res.with_ctx(|_| (
ErrorKind::Filesystem,
ssl_path_cert.display().to_string()
))),
crate::net::ssl::export_key(&key, &ssl_path_key),
crate::net::ssl::export_cert(&chain, &ssl_path_cert)
)?;
(
format!("{} ssl", lan_port_config.mapping),
format!("ssl_certificate {};", ssl_path_cert.to_str().unwrap()),

View File

@@ -1,6 +1,8 @@
use std::cmp::Ordering;
use std::path::Path;
use color_eyre::eyre::eyre;
use futures::FutureExt;
use openssl::asn1::{Asn1Integer, Asn1Time};
use openssl::bn::{BigNum, MsbOption};
use openssl::ec::{EcGroup, EcKey};
@@ -13,7 +15,7 @@ use sqlx::SqlitePool;
use tokio::sync::Mutex;
use tracing::instrument;
use crate::{Error, ErrorKind};
use crate::{Error, ErrorKind, ResultExt};
static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.
@@ -256,6 +258,23 @@ impl SslManager {
}
}
pub async fn export_key(key: &PKey<Private>, target: &Path) -> Result<(), Error> {
tokio::fs::write(target, key.private_key_to_pem_pkcs8()?)
.map(|res| res.with_ctx(|_| (ErrorKind::Filesystem, target.display().to_string())))
.await?;
Ok(())
}
pub async fn export_cert(chain: &Vec<X509>, target: &Path) -> Result<(), Error> {
tokio::fs::write(
target,
chain
.into_iter()
.flat_map(|c| c.to_pem().unwrap())
.collect::<Vec<u8>>(),
)
.await?;
Ok(())
}
#[instrument]
fn rand_serial() -> Result<Asn1Integer, Error> {
let mut bn = BigNum::new()?;

View File

@@ -215,9 +215,7 @@ impl Volume {
} else {
path.as_ref()
}),
Volume::Certificate { interface_id: _ } => {
ctx.net_controller.nginx.ssl_directory_for(pkg_id)
}
Volume::Certificate { interface_id: _ } => ctx.net_controller.ssl_directory_for(pkg_id),
Volume::Backup { .. } => backup_dir(pkg_id),
}
}

View File

@@ -889,6 +889,7 @@ dependencies = [
"patch-db",
"pbkdf2",
"pin-project",
"platforms",
"prettytable-rs",
"proptest",
"proptest-derive",
@@ -2267,6 +2268,12 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "platforms"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325"
[[package]]
name = "ppv-lite86"
version = "0.2.10"