Refactor/networking (#2189)

* refactor networking and account

* add interfaces from manifest automatically

* use nistp256 to satisfy firefox

* use ed25519 if available

* fix ip signing

* fix SQL error

* update prettytable to fix segfault

* fix migration

* fix migration

* bump welcome-ack

* add redirect if connecting to https over http

* misc rebase fixes

* fix compression

* bump rustc version
This commit is contained in:
Aiden McClelland
2023-03-08 19:30:46 -07:00
committed by GitHub
parent da55d6f7cd
commit bbb9980941
79 changed files with 3577 additions and 3587 deletions

View File

@@ -4,6 +4,7 @@ use async_trait::async_trait;
use color_eyre::eyre::eyre;
use patch_db::DbHandle;
use rpc_toolkit::command;
use sqlx::PgPool;
use crate::init::InitReceipts;
use crate::Error;
@@ -76,8 +77,8 @@ where
fn new() -> Self;
fn semver(&self) -> emver::Version;
fn compat(&self) -> &'static emver::VersionRange;
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error>;
async fn up<Db: DbHandle>(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error>;
async fn down<Db: DbHandle>(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error>;
async fn commit<Db: DbHandle>(
&self,
db: &mut Db,
@@ -98,11 +99,19 @@ where
&self,
version: &V,
db: &mut Db,
secrets: &PgPool,
receipts: &InitReceipts,
) -> Result<(), Error> {
match self.semver().cmp(&version.semver()) {
Ordering::Greater => self.rollback_to_unchecked(version, db, receipts).await,
Ordering::Less => version.migrate_from_unchecked(self, db, receipts).await,
Ordering::Greater => {
self.rollback_to_unchecked(version, db, secrets, receipts)
.await
}
Ordering::Less => {
version
.migrate_from_unchecked(self, db, secrets, receipts)
.await
}
Ordering::Equal => Ok(()),
}
}
@@ -110,12 +119,13 @@ where
&self,
version: &V,
db: &mut Db,
secrets: &PgPool,
receipts: &InitReceipts,
) -> Result<(), Error> {
let previous = Self::Previous::new();
if version.semver() < previous.semver() {
previous
.migrate_from_unchecked(version, db, receipts)
.migrate_from_unchecked(version, db, secrets, receipts)
.await?;
} else if version.semver() > previous.semver() {
return Err(Error::new(
@@ -127,7 +137,7 @@ where
));
}
tracing::info!("{} -> {}", previous.semver(), self.semver(),);
self.up(db).await?;
self.up(db, secrets).await?;
self.commit(db, receipts).await?;
Ok(())
}
@@ -135,15 +145,16 @@ where
&self,
version: &V,
db: &mut Db,
secrets: &PgPool,
receipts: &InitReceipts,
) -> Result<(), Error> {
let previous = Self::Previous::new();
tracing::info!("{} -> {}", self.semver(), previous.semver(),);
self.down(db).await?;
self.down(db, secrets).await?;
previous.commit(db, receipts).await?;
if version.semver() < previous.semver() {
previous
.rollback_to_unchecked(version, db, receipts)
.rollback_to_unchecked(version, db, secrets, receipts)
.await?;
} else if version.semver() > previous.semver() {
return Err(Error::new(
@@ -184,21 +195,55 @@ where
pub async fn init<Db: DbHandle>(
db: &mut Db,
secrets: &PgPool,
receipts: &crate::init::InitReceipts,
) -> Result<(), Error> {
let version = Version::from_util_version(receipts.server_version.get(db).await?);
match version {
Version::V0_3_0(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_0_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_0_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_0_3(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_1_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_1_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_2_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_3(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_4(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_0(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_0_1(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_0_2(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_0_3(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_1(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_1_1(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_1_2(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_2(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_2_1(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_3(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::V0_3_4(v) => {
v.0.migrate_to(&Current::new(), db, secrets, receipts)
.await?
}
Version::Other(_) => {
return Err(Error::new(
eyre!("Cannot downgrade"),

View File

@@ -28,10 +28,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -18,10 +18,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*v0_3_0::V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -18,10 +18,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*v0_3_0::V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -18,10 +18,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*v0_3_0::V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -19,10 +19,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -19,10 +19,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -19,10 +19,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -2,8 +2,6 @@ use emver::VersionRange;
use super::v0_3_0::V0_3_0_COMPAT;
use super::*;
use crate::config::util::MergeWith;
use crate::hostname::{generate_id, sync_hostname};
const V0_3_2: emver::Version = emver::Version::new(0, 3, 2, 0);
@@ -42,19 +40,117 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
let receipts = crate::hostname::HostNameReceipt::new(db).await?;
crate::hostname::ensure_hostname_is_set(db, &receipts).await?;
receipts.id.set(db, generate_id()).await?;
async fn up<Db: DbHandle>(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
let hostname = legacy::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, &legacy::hostname::generate_id())
.await?;
let mut ui = crate::db::DatabaseModel::new().ui().get(db).await?.clone();
ui.merge_with(&DEFAULT_UI);
crate::db::DatabaseModel::new().ui().put(db, &ui).await?;
sync_hostname(db, &receipts).await?;
legacy::hostname::sync_hostname(db).await?;
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}
mod legacy {
pub mod hostname {
use patch_db::DbHandle;
use rand::{thread_rng, Rng};
use tokio::process::Command;
use tracing::instrument;
use crate::util::Invoke;
use crate::{Error, ErrorKind};
#[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]
pub struct Hostname(pub String);
lazy_static::lazy_static! {
static ref ADJECTIVES: Vec<String> = include_str!("../assets/adjectives.txt").lines().map(|x| x.to_string()).collect();
static ref NOUNS: Vec<String> = include_str!("../assets/nouns.txt").lines().map(|x| x.to_string()).collect();
}
impl AsRef<str> for Hostname {
fn as_ref(&self) -> &str {
&self.0
}
}
pub fn generate_hostname() -> Hostname {
let mut rng = thread_rng();
let adjective = &ADJECTIVES[rng.gen_range(0..ADJECTIVES.len())];
let noun = &NOUNS[rng.gen_range(0..NOUNS.len())];
Hostname(format!("embassy-{adjective}-{noun}"))
}
pub fn generate_id() -> String {
let id = uuid::Uuid::new_v4();
id.to_string()
}
#[instrument]
pub async fn get_current_hostname() -> Result<Hostname, Error> {
let out = Command::new("hostname")
.invoke(ErrorKind::ParseSysInfo)
.await?;
let out_string = String::from_utf8(out)?;
Ok(Hostname(out_string.trim().to_owned()))
}
#[instrument]
pub async fn set_hostname(hostname: &Hostname) -> Result<(), Error> {
let hostname: &String = &hostname.0;
let _out = Command::new("hostnamectl")
.arg("set-hostname")
.arg(hostname)
.invoke(ErrorKind::ParseSysInfo)
.await?;
Ok(())
}
#[instrument(skip(handle))]
pub async fn get_id<Db: DbHandle>(handle: &mut Db) -> Result<String, Error> {
let id = crate::db::DatabaseModel::new()
.server_info()
.id()
.get(handle)
.await?;
Ok(id.to_string())
}
pub async fn get_hostname<Db: DbHandle>(handle: &mut Db) -> Result<Hostname, Error> {
if let Ok(hostname) = crate::db::DatabaseModel::new()
.server_info()
.hostname()
.get(handle)
.await
{
if let Some(hostname) = hostname.to_owned() {
return Ok(Hostname(hostname));
}
}
let id = get_id(handle).await?;
if id.len() != 8 {
return Ok(generate_hostname());
}
return Ok(Hostname(format!("embassy-{}", id)));
}
#[instrument(skip(handle))]
pub async fn sync_hostname<Db: DbHandle>(handle: &mut Db) -> Result<(), Error> {
set_hostname(&get_hostname(handle).await?).await?;
Command::new("systemctl")
.arg("restart")
.arg("avahi-daemon")
.invoke(crate::ErrorKind::Network)
.await?;
Ok(())
}
}
}

View File

@@ -17,10 +17,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static emver::VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -24,7 +24,7 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?;
if let Some(Value::String(selected_url)) =
@@ -65,7 +65,7 @@ impl VersionT for Version {
Ok(())
}
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?;
let selected_url = ui["marketplace"]["selected-url"]
.as_str()

View File

@@ -1,6 +1,12 @@
use async_trait::async_trait;
use emver::VersionRange;
use itertools::Itertools;
use openssl::hash::MessageDigest;
use serde_json::{json, Value};
use ssh_key::public::Ed25519PublicKey;
use crate::account::AccountInfo;
use crate::hostname::{generate_hostname, sync_hostname, Hostname};
use super::v0_3_0::V0_3_0_COMPAT;
use super::*;
@@ -37,7 +43,44 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
async fn up<Db: DbHandle>(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error> {
let mut account = AccountInfo::load(secrets).await?;
crate::db::DatabaseModel::new()
.server_info()
.pubkey()
.put(
db,
&ssh_key::PublicKey::from(Ed25519PublicKey::from(&account.key.ssh_key()))
.to_openssh()?,
)
.await?;
crate::db::DatabaseModel::new()
.server_info()
.ca_fingerprint()
.put(
db,
&account
.root_ca_cert
.digest(MessageDigest::sha256())
.unwrap()
.iter()
.map(|x| format!("{x:X}"))
.join(":"),
)
.await?;
let server_info = crate::db::DatabaseModel::new()
.server_info()
.get(db)
.await?
.into_owned();
account.hostname = server_info
.hostname
.map(Hostname)
.unwrap_or_else(generate_hostname);
account.server_id = server_info.id;
account.save(secrets).await?;
sync_hostname(&account).await?;
let parsed_url = Some(COMMUNITY_URL.parse().unwrap());
let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?;
ui["marketplace"]["known-hosts"][COMMUNITY_URL] = json!({});
@@ -66,7 +109,7 @@ impl VersionT for Version {
ui.save(db).await?;
Ok(())
}
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
async fn down<Db: DbHandle>(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> {
let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?;
let parsed_url = Some(MAIN_REGISTRY.parse().unwrap());
for package_id in crate::db::DatabaseModel::new()