remove product key from setup flow (#1750)

* remove product key flow from setup

* feat: backend turned off encryption + new Id + no package id

* implement new encryption scheme in FE

* decode response string

* crypto not working

* update setup wizard closes #1762

* feat: Get the encryption key

* fix: Get to recovery

* remove old code

* fix build

* fix: Install works for now

* fix bug in config for adding new list items

* dismiss action modal on success

* clear button in config

* wip: Currently broken in avahi mdns

* include headers with req/res and refactor patchDB init and usage

* fix: Can now run in the main

* flatline on failed init

* update patch DB

* add last-wifi-region to data model even though not used by FE

* chore: Fix the start.

* wip: Fix wrong order for getting hostname before sql has been
created

* fix edge case where union keys displayed as new when not new

* fix: Can start

* last backup color, markdown links always new tab, fix bug with login

* refactor to remove WithRevision

* resolve circular dep issue

* update submodule

* fix patch-db

* update patchDB

* update patch again

* escape error

* decodeuricomponent

* increase proxy buffer size

* increase proxy buffer size

* fix nginx

Co-authored-by: BluJ <mogulslayer@gmail.com>
Co-authored-by: BluJ <dragondef@gmail.com>
Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Matt Hill
2022-09-07 09:25:01 -06:00
committed by GitHub
parent 76682ebef0
commit 50111e37da
175 changed files with 11436 additions and 2906 deletions

View File

@@ -10,6 +10,7 @@ use digest::generic_array::GenericArray;
use digest::OutputSizeUser;
use futures::future::BoxFuture;
use futures::{FutureExt, TryFutureExt, TryStreamExt};
use josekit::jwk::Jwk;
use nix::unistd::{Gid, Uid};
use openssl::x509::X509;
use patch_db::{DbHandle, LockType};
@@ -37,7 +38,7 @@ use crate::disk::mount::filesystem::ReadOnly;
use crate::disk::mount::guard::TmpMountGuard;
use crate::disk::util::{pvscan, recovery_info, DiskListResponse, EmbassyOsRecoveryInfo};
use crate::disk::REPAIR_DISK_PATH;
use crate::hostname::PRODUCT_KEY_PATH;
use crate::hostname::{get_hostname, Hostname};
use crate::id::Id;
use crate::init::init;
use crate::install::PKG_PUBLIC_DIR;
@@ -62,7 +63,7 @@ where
Ok(password)
}
#[command(subcommands(status, disk, attach, execute, recovery, cifs, complete))]
#[command(subcommands(status, disk, attach, execute, recovery, cifs, complete, get_secret))]
pub fn setup() -> Result<(), Error> {
Ok(())
}
@@ -70,14 +71,12 @@ pub fn setup() -> Result<(), Error> {
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct StatusRes {
product_key: bool,
migrating: bool,
}
#[command(rpc_only, metadata(authenticated = false))]
pub async fn status(#[context] ctx: SetupContext) -> Result<StatusRes, Error> {
Ok(StatusRes {
product_key: tokio::fs::metadata(PRODUCT_KEY_PATH).await.is_ok(),
migrating: ctx.recovery_status.read().await.is_some(),
})
}
@@ -123,31 +122,7 @@ pub async fn attach(
ErrorKind::DiskManagement,
));
}
let product_key = ctx.product_key().await?;
let product_key_path = Path::new("/embassy-data/main/product_key.txt");
if tokio::fs::metadata(product_key_path).await.is_ok() {
let pkey = Arc::new(
tokio::fs::read_to_string(product_key_path)
.await?
.trim()
.to_owned(),
);
if pkey != product_key {
crate::disk::main::export(&*guid, &ctx.datadir).await?;
return Err(Error::new(
eyre!(
"The EmbassyOS product key does not match the supplied drive: {}",
pkey
),
ErrorKind::ProductKeyMismatch,
));
}
}
init(
&RpcContextConfig::load(ctx.config_path.as_ref()).await?,
&*product_key,
)
.await?;
init(&RpcContextConfig::load(ctx.config_path.as_ref()).await?).await?;
let secrets = ctx.secret_store().await?;
let db = ctx.db(&secrets).await?;
let mut secrets_handle = secrets.acquire().await?;
@@ -168,16 +143,17 @@ pub async fn attach(
let tor_key = crate::net::tor::os_key(&mut secrets_tx).await?;
db_tx.commit(None).await?;
db_tx.commit().await?;
secrets_tx.commit().await?;
let hostname = get_hostname(&mut db_handle).await?;
let (_, root_ca) = SslManager::init(secrets).await?.export_root_ca().await?;
let (_, root_ca) = SslManager::init(secrets, &mut db_handle)
.await?
.export_root_ca()
.await?;
let setup_result = SetupResult {
tor_address: format!("http://{}", tor_key.public().get_onion_address()),
lan_address: format!(
"https://embassy-{}.local",
crate::hostname::derive_id(&*product_key)
),
lan_address: hostname.lan_address(),
root_ca: String::from_utf8(root_ca.to_pem()?)?,
};
*ctx.setup_result.write().await = Some((guid, setup_result.clone()));
@@ -222,6 +198,29 @@ pub async fn recovery_status(
ctx.recovery_status.read().await.clone().transpose()
}
/// We want to be able to get a secret, a shared private key with the frontend
/// This way the frontend can send a secret, like the password for the setup/ recovory
/// without knowing the password over clearnet. We use the public key shared across the network
/// since it is fine to share the public, and encrypt against the public.
#[command(rename = "get-secret", rpc_only, metadata(authenticated = false))]
pub async fn get_secret(
#[context] ctx: SetupContext,
#[arg] pubkey: Jwk,
) -> Result<String, RpcError> {
let secret = ctx.update_secret().await?;
let mut header = josekit::jwe::JweHeader::new();
header.set_algorithm("ECDH-ES");
header.set_content_encryption("A256GCM");
let encrypter = josekit::jwe::alg::ecdh_es::EcdhEsJweAlgorithm::EcdhEs
.encrypter_from_jwk(&pubkey)
.unwrap();
Ok(josekit::jwe::serialize_compact(secret.as_bytes(), &header, &encrypter).unwrap())
// Need to encrypt from the public key sent
// then encode via hex
}
#[command(subcommands(verify_cifs))]
pub fn cifs() -> Result<(), Error> {
Ok(())
@@ -269,14 +268,11 @@ pub async fn execute(
)
.await
{
Ok((tor_addr, root_ca)) => {
Ok((hostname, tor_addr, root_ca)) => {
tracing::info!("Setup Successful! Tor Address: {}", tor_addr);
Ok(SetupResult {
tor_address: format!("http://{}", tor_addr),
lan_address: format!(
"https://embassy-{}.local",
crate::hostname::derive_id(&ctx.product_key().await?)
),
lan_address: hostname.lan_address(),
root_ca: String::from_utf8(root_ca.to_pem()?)?,
})
}
@@ -299,33 +295,14 @@ pub async fn complete(#[context] ctx: SetupContext) -> Result<SetupResult, Error
crate::ErrorKind::InvalidRequest,
));
};
if tokio::fs::metadata(PRODUCT_KEY_PATH).await.is_err() {
crate::hostname::set_product_key(&*ctx.product_key().await?).await?;
} else {
let key_on_disk = crate::hostname::get_product_key().await?;
let key_in_cache = ctx.product_key().await?;
if *key_in_cache != key_on_disk {
crate::hostname::set_product_key(&*ctx.product_key().await?).await?;
}
}
tokio::fs::write(
Path::new("/embassy-data/main/product_key.txt"),
&*ctx.product_key().await?,
)
.await?;
let secrets = ctx.secret_store().await?;
let mut db = ctx.db(&secrets).await?.handle();
let hostname = crate::hostname::get_hostname().await?;
let hostname = crate::hostname::get_hostname(&mut db).await?;
let si = crate::db::DatabaseModel::new().server_info();
si.clone()
.id()
.put(&mut db, &crate::hostname::get_id().await?)
.await?;
let id = crate::hostname::get_id(&mut db).await?;
si.clone().id().put(&mut db, &id).await?;
si.lan_address()
.put(
&mut db,
&format!("https://{}.local", &hostname).parse().unwrap(),
)
.put(&mut db, &hostname.lan_address().parse().unwrap())
.await?;
let mut guid_file = File::create("/embassy-os/disk.guid").await?;
guid_file.write_all(guid.as_bytes()).await?;
@@ -341,7 +318,7 @@ pub async fn execute_inner(
embassy_password: String,
recovery_source: Option<BackupTargetFS>,
recovery_password: Option<String>,
) -> Result<(OnionAddressV3, X509), Error> {
) -> Result<(Hostname, OnionAddressV3, X509), Error> {
if ctx.recovery_status.read().await.is_some() {
return Err(Error::new(
eyre!("Cannot execute setup while in recovery!"),
@@ -374,12 +351,11 @@ pub async fn execute_inner(
recovery_password,
)
.await?;
init(
&RpcContextConfig::load(ctx.config_path.as_ref()).await?,
&ctx.product_key().await?,
)
.await?;
let res = (tor_addr, root_ca.clone());
let db = init(&RpcContextConfig::load(ctx.config_path.as_ref()).await?)
.await?
.db;
let hostname = get_hostname(&mut db.handle()).await?;
let res = (hostname.clone(), tor_addr, root_ca.clone());
tokio::spawn(async move {
if let Err(e) = recover_fut
.and_then(|_| async {
@@ -387,10 +363,7 @@ pub async fn execute_inner(
guid,
SetupResult {
tor_address: format!("http://{}", tor_addr),
lan_address: format!(
"https://embassy-{}.local",
crate::hostname::derive_id(&ctx.product_key().await?)
),
lan_address: hostname.lan_address(),
root_ca: String::from_utf8(root_ca.to_pem()?)?,
},
));
@@ -412,23 +385,19 @@ pub async fn execute_inner(
res
} else {
let (tor_addr, root_ca) = fresh_setup(&ctx, &embassy_password).await?;
init(
&RpcContextConfig::load(ctx.config_path.as_ref()).await?,
&ctx.product_key().await?,
)
.await?;
let db = init(&RpcContextConfig::load(ctx.config_path.as_ref()).await?)
.await?
.db;
*ctx.setup_result.write().await = Some((
guid,
SetupResult {
tor_address: format!("http://{}", tor_addr),
lan_address: format!(
"https://embassy-{}.local",
crate::hostname::derive_id(&ctx.product_key().await?)
),
lan_address: get_hostname(&mut db.handle()).await?.lan_address(),
root_ca: String::from_utf8(root_ca.to_pem()?)?,
},
));
(tor_addr, root_ca)
let hostname = get_hostname(&mut db.handle()).await?;
(hostname, tor_addr, root_ca)
};
Ok(res)
@@ -455,7 +424,8 @@ async fn fresh_setup(
)
.execute(&mut sqlite_pool.acquire().await?)
.await?;
let (_, root_ca) = SslManager::init(sqlite_pool.clone())
let db = ctx.db(&sqlite_pool).await?;
let (_, root_ca) = SslManager::init(sqlite_pool.clone(), &mut db.handle())
.await?
.export_root_ca()
.await?;