mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
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:
committed by
Aiden McClelland
parent
9f3909188a
commit
c723ee6a15
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
7
system-images/compat/Cargo.lock
generated
7
system-images/compat/Cargo.lock
generated
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user