diff --git a/backend/src/bin/embassyd.rs b/backend/src/bin/embassyd.rs index 8292fc929..0a227077f 100644 --- a/backend/src/bin/embassyd.rs +++ b/backend/src/bin/embassyd.rs @@ -82,8 +82,8 @@ async fn inner_main(cfg_path: Option<&str>) -> Result, Error> { }); let mut db = rpc_ctx.db.handle(); - embassy::hostname::sync_hostname(&mut db).await?; let receipts = embassy::context::rpc::RpcSetNginxReceipts::new(&mut db).await?; + embassy::hostname::sync_hostname(&mut db, &receipts.hostname_receipts).await?; rpc_ctx.set_nginx_conf(&mut db, receipts).await?; drop(db); diff --git a/backend/src/context/rpc.rs b/backend/src/context/rpc.rs index c2394fea9..55e64f0d0 100644 --- a/backend/src/context/rpc.rs +++ b/backend/src/context/rpc.rs @@ -23,6 +23,7 @@ use tracing::instrument; use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation}; use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry}; +use crate::hostname::HostNameReceipt; use crate::init::{init_postgres, pgloader}; use crate::install::cleanup::{cleanup_failed, uninstall, CleanupFailedReceipts}; use crate::manager::ManagerMap; @@ -175,6 +176,7 @@ impl RpcCleanReceipts { } pub struct RpcSetNginxReceipts { + pub hostname_receipts: HostNameReceipt, server_info: LockReceipt, } @@ -189,12 +191,14 @@ impl RpcSetNginxReceipts { pub fn setup( locks: &mut Vec, ) -> impl FnOnce(&patch_db::Verifier) -> Result { + let hostname_receipts = HostNameReceipt::setup(locks); let server_info = crate::db::DatabaseModel::new() .server_info() .make_locker(LockType::Read) .add_to_keys(locks); move |skeleton_key| { Ok(Self { + hostname_receipts: hostname_receipts(skeleton_key)?, server_info: server_info.verify(skeleton_key)?, }) } diff --git a/backend/src/hostname.rs b/backend/src/hostname.rs index a390cc6e1..1290d5ede 100644 --- a/backend/src/hostname.rs +++ b/backend/src/hostname.rs @@ -56,36 +56,83 @@ pub async fn set_hostname(hostname: &Hostname) -> Result<(), Error> { Ok(()) } -#[instrument(skip(handle))] -pub async fn get_id(handle: &mut Db) -> Result { - let id = crate::db::DatabaseModel::new() - .server_info() - .id() - .get(handle, false) - .await?; - Ok(id.to_string()) +#[instrument(skip(handle, receipts))] +pub async fn get_id( + handle: &mut Db, + receipts: &HostNameReceipt, +) -> Result { + let id = receipts.id.get(handle).await?; + Ok(id) } -pub async fn get_hostname(handle: &mut Db) -> Result { - if let Ok(hostname) = crate::db::DatabaseModel::new() - .server_info() - .hostname() - .get(handle, false) - .await - { +pub async fn get_hostname( + handle: &mut Db, + receipts: &HostNameReceipt, +) -> Result { + if let Ok(hostname) = receipts.hostname.get(handle).await { if let Some(hostname) = hostname.to_owned() { return Ok(Hostname(hostname)); } } - let id = get_id(handle).await?; + let id = get_id(handle, receipts).await?; if id.len() != 8 { return Ok(generate_hostname()); } return Ok(Hostname(format!("embassy-{}", id))); } -#[instrument(skip(handle))] -pub async fn sync_hostname(handle: &mut Db) -> Result<(), Error> { - set_hostname(&get_hostname(handle).await?).await?; + +pub async fn ensure_hostname_is_set( + handle: &mut Db, + receipts: &HostNameReceipt, +) -> Result<(), Error> { + let hostname = get_hostname(handle, &receipts).await?; + receipts.hostname.set(handle, Some(hostname.0)).await?; + Ok(()) +} + +#[derive(Clone)] +pub struct HostNameReceipt { + hostname: patch_db::LockReceipt, ()>, + pub id: patch_db::LockReceipt, +} + +impl HostNameReceipt { + pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result { + let mut locks = Vec::new(); + + let setup = Self::setup(&mut locks); + setup(&db.lock_all(locks).await?) + } + + pub fn setup( + locks: &mut Vec, + ) -> impl FnOnce(&patch_db::Verifier) -> Result { + use patch_db::LockType; + let hostname = crate::db::DatabaseModel::new() + .server_info() + .hostname() + .make_locker(LockType::Write) + .add_to_keys(locks); + let id = crate::db::DatabaseModel::new() + .server_info() + .id() + .make_locker(LockType::Write) + .add_to_keys(locks); + move |skeleton_key| { + Ok(Self { + hostname: hostname.verify(skeleton_key)?, + id: id.verify(skeleton_key)?, + }) + } + } +} + +#[instrument(skip(handle, receipts))] +pub async fn sync_hostname( + handle: &mut Db, + receipts: &HostNameReceipt, +) -> Result<(), Error> { + set_hostname(&get_hostname(handle, receipts).await?).await?; Command::new("systemctl") .arg("restart") .arg("avahi-daemon") diff --git a/backend/src/net/mod.rs b/backend/src/net/mod.rs index d882500cb..c6a9787a0 100644 --- a/backend/src/net/mod.rs +++ b/backend/src/net/mod.rs @@ -66,7 +66,9 @@ impl NetController { None => SslManager::init(db, handle).await, Some(a) => SslManager::import_root_ca(db, a.0, a.1).await, }?; - let hostname = get_hostname(handle).await?; + + let hostname_receipts = crate::hostname::HostNameReceipt::new(handle).await?; + let hostname = get_hostname(handle, &hostname_receipts).await?; Ok(Self { tor: TorController::init(embassyd_addr, embassyd_tor_key, tor_control).await?, #[cfg(feature = "avahi")] diff --git a/backend/src/net/ssl.rs b/backend/src/net/ssl.rs index 42f40a869..f0730e77b 100644 --- a/backend/src/net/ssl.rs +++ b/backend/src/net/ssl.rs @@ -165,7 +165,8 @@ impl SslManager { #[instrument(skip(db, handle))] pub async fn init(db: PgPool, handle: &mut Db) -> Result { let store = SslStore::new(db)?; - let id = crate::hostname::get_id(handle).await?; + let receipts = crate::hostname::HostNameReceipt::new(handle).await?; + let id = crate::hostname::get_id(handle, &receipts).await?; let (root_key, root_cert) = match store.load_root_certificate().await? { None => { let root_key = generate_key()?; @@ -528,7 +529,7 @@ fn make_leaf_cert( // let root_cert1 = mgr.root_cert; // let int_key1 = mgr.int_key; // let int_cert1 = mgr.int_cert; -// +// // assert_eq!(root_cert0.to_pem()?, root_cert1.to_pem()?); // assert_eq!( // int_key0.private_key_to_pem_pkcs8()?, @@ -537,7 +538,7 @@ fn make_leaf_cert( // assert_eq!(int_cert0.to_pem()?, int_cert1.to_pem()?); // Ok(()) // } -// +// // #[tokio::test] // async fn certificate_details_persist() -> Result<(), Error> { // let pool = sqlx::Pool::::connect("postgres::memory:").await?; @@ -549,7 +550,7 @@ fn make_leaf_cert( // let package_id = "bitcoind".parse().unwrap(); // let (key0, cert_chain0) = mgr.certificate_for("start9", &package_id).await?; // let (key1, cert_chain1) = mgr.certificate_for("start9", &package_id).await?; -// +// // assert_eq!( // key0.private_key_to_pem_pkcs8()?, // key1.private_key_to_pem_pkcs8()? diff --git a/backend/src/setup.rs b/backend/src/setup.rs index 778e18c7c..684eeecb6 100644 --- a/backend/src/setup.rs +++ b/backend/src/setup.rs @@ -38,7 +38,7 @@ use crate::disk::mount::filesystem::ReadOnly; use crate::disk::mount::guard::TmpMountGuard; use crate::disk::util::{pvscan, recovery_info, DiskInfo, EmbassyOsRecoveryInfo}; use crate::disk::REPAIR_DISK_PATH; -use crate::hostname::{get_hostname, Hostname}; +use crate::hostname::{get_hostname, HostNameReceipt, Hostname}; use crate::id::Id; use crate::init::init; use crate::install::PKG_PUBLIC_DIR; @@ -158,7 +158,9 @@ pub async fn attach( db_tx.commit().await?; secrets_tx.commit().await?; - let hostname = get_hostname(&mut db_handle).await?; + + let hostname_receipts = HostNameReceipt::new(&mut db_handle).await?; + let hostname = get_hostname(&mut db_handle, &hostname_receipts).await?; let (_, root_ca) = SslManager::init(secrets, &mut db_handle) .await? @@ -321,9 +323,10 @@ pub async fn complete(#[context] ctx: SetupContext) -> Result(&self, db: &mut Db) -> Result<(), Error> { - let hostname = get_hostname(db).await?; - crate::db::DatabaseModel::new() - .server_info() - .hostname() - .put(db, &Some(hostname.0)) - .await?; - crate::db::DatabaseModel::new() - .server_info() - .id() - .put(db, &generate_id()) - .await?; + let receipts = crate::hostname::HostNameReceipt::new(db).await?; + crate::hostname::ensure_hostname_is_set(db, &receipts).await?; + receipts.id.set(db, generate_id()).await?; - sync_hostname(db).await?; + sync_hostname(db, &receipts).await?; Ok(()) } async fn down(&self, _db: &mut Db) -> Result<(), Error> {