mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +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?;
|
tx.save().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (root_ca_key, root_ca_cert) = ctx
|
let (root_ca_key, root_ca_cert) = ctx.net_controller.ssl.export_root_ca().await?;
|
||||||
.net_controller
|
|
||||||
.nginx
|
|
||||||
.ssl_manager
|
|
||||||
.export_root_ca()
|
|
||||||
.await?;
|
|
||||||
let mut os_backup_file = AtomicFile::new(backup_guard.as_ref().join("os-backup.cbor")).await?;
|
let mut os_backup_file = AtomicFile::new(backup_guard.as_ref().join("os-backup.cbor")).await?;
|
||||||
os_backup_file
|
os_backup_file
|
||||||
.write_all(
|
.write_all(
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ pub mod ssl;
|
|||||||
pub mod tor;
|
pub mod tor;
|
||||||
pub mod wifi;
|
pub mod wifi;
|
||||||
|
|
||||||
|
const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
|
||||||
|
|
||||||
#[command(subcommands(tor::tor))]
|
#[command(subcommands(tor::tor))]
|
||||||
pub fn net() -> Result<(), Error> {
|
pub fn net() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -37,6 +39,7 @@ pub struct NetController {
|
|||||||
#[cfg(feature = "avahi")]
|
#[cfg(feature = "avahi")]
|
||||||
pub mdns: MdnsController,
|
pub mdns: MdnsController,
|
||||||
pub nginx: NginxController,
|
pub nginx: NginxController,
|
||||||
|
pub ssl: SslManager,
|
||||||
}
|
}
|
||||||
impl NetController {
|
impl NetController {
|
||||||
#[instrument(skip(db))]
|
#[instrument(skip(db))]
|
||||||
@@ -47,7 +50,7 @@ impl NetController {
|
|||||||
db: SqlitePool,
|
db: SqlitePool,
|
||||||
import_root_ca: Option<(PKey<Private>, X509)>,
|
import_root_ca: Option<(PKey<Private>, X509)>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let ssl_manager = match import_root_ca {
|
let ssl = match import_root_ca {
|
||||||
None => SslManager::init(db).await,
|
None => SslManager::init(db).await,
|
||||||
Some(a) => SslManager::import_root_ca(db, a.0, a.1).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?,
|
tor: TorController::init(embassyd_addr, embassyd_tor_key, tor_control).await?,
|
||||||
#[cfg(feature = "avahi")]
|
#[cfg(feature = "avahi")]
|
||||||
mdns: MdnsController::init(),
|
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))]
|
#[instrument(skip(self, interfaces))]
|
||||||
pub async fn add<
|
pub async fn add<'a, I>(
|
||||||
'a,
|
|
||||||
I: IntoIterator<Item = (InterfaceId, &'a Interface, TorSecretKeyV3)> + Clone,
|
|
||||||
>(
|
|
||||||
&self,
|
&self,
|
||||||
pkg_id: &PackageId,
|
pkg_id: &PackageId,
|
||||||
ip: Ipv4Addr,
|
ip: Ipv4Addr,
|
||||||
interfaces: I,
|
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
|
let interfaces_tor = interfaces
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.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?;
|
tor_res?;
|
||||||
@@ -138,7 +150,31 @@ impl NetController {
|
|||||||
Ok(())
|
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> {
|
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};
|
use crate::{Error, ErrorKind, ResultExt};
|
||||||
|
|
||||||
pub struct NginxController {
|
pub struct NginxController {
|
||||||
nginx_root: PathBuf,
|
pub nginx_root: PathBuf,
|
||||||
pub ssl_manager: SslManager,
|
|
||||||
inner: Mutex<NginxControllerInner>,
|
inner: Mutex<NginxControllerInner>,
|
||||||
}
|
}
|
||||||
impl NginxController {
|
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 {
|
Ok(NginxController {
|
||||||
inner: Mutex::new(NginxControllerInner::init(&nginx_root, &ssl_manager).await?),
|
inner: Mutex::new(NginxControllerInner::init(&nginx_root, ssl_manager).await?),
|
||||||
ssl_manager,
|
|
||||||
nginx_root,
|
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)>>(
|
pub async fn add<I: IntoIterator<Item = (InterfaceId, InterfaceMetadata)>>(
|
||||||
&self,
|
&self,
|
||||||
|
ssl_manager: &SslManager,
|
||||||
package: PackageId,
|
package: PackageId,
|
||||||
ipv4: Ipv4Addr,
|
ipv4: Ipv4Addr,
|
||||||
interfaces: I,
|
interfaces: I,
|
||||||
@@ -39,13 +35,7 @@ impl NginxController {
|
|||||||
self.inner
|
self.inner
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.add(
|
.add(&self.nginx_root, ssl_manager, package, ipv4, interfaces)
|
||||||
&self.nginx_root,
|
|
||||||
&self.ssl_manager,
|
|
||||||
package,
|
|
||||||
ipv4,
|
|
||||||
interfaces,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
pub async fn remove(&self, package: &PackageId) -> Result<(), Error> {
|
pub async fn remove(&self, package: &PackageId) -> Result<(), Error> {
|
||||||
@@ -66,17 +56,13 @@ impl NginxControllerInner {
|
|||||||
let inner = NginxControllerInner {
|
let inner = NginxControllerInner {
|
||||||
interfaces: BTreeMap::new(),
|
interfaces: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
// write main ssl key/cert to fs location
|
||||||
let (key, cert) = ssl_manager.certificate_for(&get_hostname().await?).await?;
|
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_key = nginx_root.join(format!("ssl/embassy_main.key.pem"));
|
||||||
let ssl_path_cert = nginx_root.join(format!("ssl/embassy_main.cert.pem"));
|
let ssl_path_cert = nginx_root.join(format!("ssl/embassy_main.cert.pem"));
|
||||||
tokio::try_join!(
|
tokio::try_join!(
|
||||||
tokio::fs::write(&ssl_path_key, key.private_key_to_pem_pkcs8()?),
|
crate::net::ssl::export_key(&key, &ssl_path_key),
|
||||||
tokio::fs::write(
|
crate::net::ssl::export_cert(&cert, &ssl_path_cert),
|
||||||
&ssl_path_cert,
|
|
||||||
cert.into_iter()
|
|
||||||
.flat_map(|c| c.to_pem().unwrap())
|
|
||||||
.collect::<Vec<u8>>()
|
|
||||||
)
|
|
||||||
)?;
|
)?;
|
||||||
Ok(inner)
|
Ok(inner)
|
||||||
}
|
}
|
||||||
@@ -104,34 +90,15 @@ impl NginxControllerInner {
|
|||||||
// get ssl certificate chain
|
// get ssl certificate chain
|
||||||
let (listen_args, ssl_certificate_line, ssl_certificate_key_line) =
|
let (listen_args, ssl_certificate_line, ssl_certificate_key_line) =
|
||||||
if lan_port_config.ssl {
|
if lan_port_config.ssl {
|
||||||
|
// these have already been written by the net controller
|
||||||
let package_path = nginx_root.join(format!("ssl/{}", package));
|
let package_path = nginx_root.join(format!("ssl/{}", package));
|
||||||
tokio::fs::create_dir_all(package_path).await?;
|
let ssl_path_key = package_path.join(format!("{}.key.pem", id));
|
||||||
let ssl_path_key =
|
let ssl_path_cert = package_path.join(format!("{}.cert.pem", id));
|
||||||
nginx_root.join(format!("ssl/{}/{}.key.pem", package, id));
|
|
||||||
let ssl_path_cert =
|
|
||||||
nginx_root.join(format!("ssl/{}/{}.cert.pem", package, id));
|
|
||||||
let (key, chain) = ssl_manager.certificate_for(&meta.dns_base).await?;
|
let (key, chain) = ssl_manager.certificate_for(&meta.dns_base).await?;
|
||||||
// write nginx ssl certs
|
|
||||||
tokio::try_join!(
|
tokio::try_join!(
|
||||||
tokio::fs::write(&ssl_path_key, key.private_key_to_pem_pkcs8()?).map(
|
crate::net::ssl::export_key(&key, &ssl_path_key),
|
||||||
|res| res.with_ctx(|_| (
|
crate::net::ssl::export_cert(&chain, &ssl_path_cert)
|
||||||
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()
|
|
||||||
))),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
(
|
(
|
||||||
format!("{} ssl", lan_port_config.mapping),
|
format!("{} ssl", lan_port_config.mapping),
|
||||||
format!("ssl_certificate {};", ssl_path_cert.to_str().unwrap()),
|
format!("ssl_certificate {};", ssl_path_cert.to_str().unwrap()),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
|
use futures::FutureExt;
|
||||||
use openssl::asn1::{Asn1Integer, Asn1Time};
|
use openssl::asn1::{Asn1Integer, Asn1Time};
|
||||||
use openssl::bn::{BigNum, MsbOption};
|
use openssl::bn::{BigNum, MsbOption};
|
||||||
use openssl::ec::{EcGroup, EcKey};
|
use openssl::ec::{EcGroup, EcKey};
|
||||||
@@ -13,7 +15,7 @@ use sqlx::SqlitePool;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::instrument;
|
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.
|
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]
|
#[instrument]
|
||||||
fn rand_serial() -> Result<Asn1Integer, Error> {
|
fn rand_serial() -> Result<Asn1Integer, Error> {
|
||||||
let mut bn = BigNum::new()?;
|
let mut bn = BigNum::new()?;
|
||||||
|
|||||||
@@ -215,9 +215,7 @@ impl Volume {
|
|||||||
} else {
|
} else {
|
||||||
path.as_ref()
|
path.as_ref()
|
||||||
}),
|
}),
|
||||||
Volume::Certificate { interface_id: _ } => {
|
Volume::Certificate { interface_id: _ } => ctx.net_controller.ssl_directory_for(pkg_id),
|
||||||
ctx.net_controller.nginx.ssl_directory_for(pkg_id)
|
|
||||||
}
|
|
||||||
Volume::Backup { .. } => backup_dir(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",
|
"patch-db",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
|
"platforms",
|
||||||
"prettytable-rs",
|
"prettytable-rs",
|
||||||
"proptest",
|
"proptest",
|
||||||
"proptest-derive",
|
"proptest-derive",
|
||||||
@@ -2267,6 +2268,12 @@ version = "0.3.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platforms"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
|
|||||||
Reference in New Issue
Block a user