mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
2
Makefile
2
Makefile
@@ -5,7 +5,7 @@ EMBASSY_UIS := frontend/dist/ui frontend/dist/setup-wizard frontend/dist/diagnos
|
||||
EMBASSY_SRC := raspios.img product_key.txt $(EMBASSY_BINS) backend/embassyd.service backend/embassy-init.service $(EMBASSY_UIS) $(shell find build)
|
||||
COMPAT_SRC := $(shell find system-images/compat/src)
|
||||
UTILS_SRC := $(shell find system-images/utils/Dockerfile)
|
||||
BACKEND_SRC := $(shell find backend/src) $(shell find patch-db/*/src) backend/Cargo.toml backend/Cargo.lock
|
||||
BACKEND_SRC := $(shell find backend/src) $(shell find backend/migrations) $(shell find patch-db/*/src) backend/Cargo.toml backend/Cargo.lock
|
||||
FRONTEND_SHARED_SRC := $(shell find frontend/projects/shared) $(shell find frontend/assets) $(shell ls -p frontend/ | grep -v / | sed 's/^/frontend\//g') frontend/node_modules frontend/config.json patch-db/client/dist
|
||||
FRONTEND_UI_SRC := $(shell find frontend/projects/ui)
|
||||
FRONTEND_SETUP_WIZARD_SRC := $(shell find frontend/projects/setup-wizard)
|
||||
|
||||
98
backend/Cargo.lock
generated
98
backend/Cargo.lock
generated
@@ -982,6 +982,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
@@ -992,6 +1001,17 @@ dependencies = [
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.4.3",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
@@ -1302,18 +1322,6 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"spin 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -1641,6 +1649,15 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||
dependencies = [
|
||||
"hmac 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.11.0"
|
||||
@@ -2151,17 +2168,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
@@ -2213,6 +2219,15 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582"
|
||||
dependencies = [
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
@@ -2509,7 +2524,7 @@ checksum = "e7249a699cdeea261ac73f1bf9350777cb867324f44373aafb5a287365bf1771"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"byteorder",
|
||||
"md-5",
|
||||
"md-5 0.9.1",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -3219,7 +3234,7 @@ dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
@@ -3725,15 +3740,6 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.6.0"
|
||||
@@ -3773,34 +3779,39 @@ checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"atoi",
|
||||
"base64 0.13.0",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"dirs 4.0.0",
|
||||
"either",
|
||||
"event-listener",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-intrusive",
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac 0.12.1",
|
||||
"indexmap",
|
||||
"itoa 1.0.2",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"md-5 0.10.1",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha-1",
|
||||
"sha2 0.10.2",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
@@ -3810,6 +3821,7 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4356,7 +4368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dirs",
|
||||
"dirs 1.0.5",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -5167,6 +5179,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -123,7 +123,7 @@ sqlx = { version = "0.6.0", features = [
|
||||
"chrono",
|
||||
"offline",
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"postgres",
|
||||
] }
|
||||
stderrlog = "0.5.3"
|
||||
tar = "0.4.38"
|
||||
@@ -142,8 +142,11 @@ trust-dns-server = "0.21.2"
|
||||
typed-builder = "0.10.0"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
|
||||
[profile.test]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.backtrace]
|
||||
opt-level = 3
|
||||
|
||||
[profile.test]
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE IF NOT EXISTS tor
|
||||
(
|
||||
CREATE TABLE IF NOT EXISTS tor (
|
||||
package TEXT NOT NULL,
|
||||
interface TEXT NOT NULL,
|
||||
key BLOB NOT NULL CHECK (length(key) = 64),
|
||||
key BYTEA NOT NULL CHECK (length(key) = 64),
|
||||
PRIMARY KEY (package, interface)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS session
|
||||
(
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
logged_in TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
logged_out TIMESTAMP,
|
||||
@@ -15,31 +14,34 @@ CREATE TABLE IF NOT EXISTS session
|
||||
user_agent TEXT,
|
||||
metadata TEXT NOT NULL DEFAULT 'null'
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS account
|
||||
(
|
||||
id INTEGER PRIMARY KEY CHECK (id = 0),
|
||||
|
||||
CREATE TABLE IF NOT EXISTS account (
|
||||
id SERIAL PRIMARY KEY CHECK (id = 0),
|
||||
password TEXT NOT NULL,
|
||||
tor_key BLOB NOT NULL CHECK (length(tor_key) = 64)
|
||||
tor_key BYTEA NOT NULL CHECK (length(tor_key) = 64)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS ssh_keys
|
||||
(
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ssh_keys (
|
||||
fingerprint TEXT NOT NULL,
|
||||
openssh_pubkey TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (fingerprint)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS certificates
|
||||
(
|
||||
id INTEGER PRIMARY KEY, -- Root = 0, Int = 1, Other = 2..
|
||||
|
||||
CREATE TABLE IF NOT EXISTS certificates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
-- Root = 0, Int = 1, Other = 2..
|
||||
priv_key_pem TEXT NOT NULL,
|
||||
certificate_pem TEXT NOT NULL,
|
||||
lookup_string TEXT UNIQUE,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS notifications
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
ALTER SEQUENCE certificates_id_seq START 2 RESTART 2;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
package_id TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
code INTEGER NOT NULL,
|
||||
@@ -48,9 +50,9 @@ CREATE TABLE IF NOT EXISTS notifications
|
||||
message TEXT NOT NULL,
|
||||
data TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS cifs_shares
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cifs_shares (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hostname TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ use rpc_toolkit::command_helpers::prelude::{RequestParts, ResponseParts};
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
@@ -87,7 +87,7 @@ pub fn check_password(hash: &str, password: &str) -> Result<(), Error> {
|
||||
|
||||
pub async fn check_password_against_db<Ex>(secrets: &mut Ex, password: &str) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let pw_hash = sqlx::query!("SELECT password FROM account")
|
||||
.fetch_one(secrets)
|
||||
@@ -124,7 +124,7 @@ pub async fn login(
|
||||
let metadata = serde_json::to_string(&metadata).with_kind(crate::ErrorKind::Database)?;
|
||||
let hash_token_hashed = hash_token.hashed();
|
||||
sqlx::query!(
|
||||
"INSERT INTO session (id, user_agent, metadata) VALUES (?, ?, ?)",
|
||||
"INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)",
|
||||
hash_token_hashed,
|
||||
user_agent,
|
||||
metadata,
|
||||
@@ -328,7 +328,7 @@ pub async fn set_password<Db: DbHandle, Ex>(
|
||||
password: &str,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let password = argon2::hash_encoded(
|
||||
password.as_bytes(),
|
||||
@@ -337,7 +337,7 @@ where
|
||||
)
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?;
|
||||
|
||||
sqlx::query!("UPDATE account SET password = ?", password,)
|
||||
sqlx::query!("UPDATE account SET password = $1", password,)
|
||||
.execute(secrets)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use patch_db::{DbHandle, HasModel, LockType};
|
||||
use reqwest::Url;
|
||||
use rpc_toolkit::command;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tracing::instrument;
|
||||
@@ -195,7 +195,7 @@ impl BackupActions {
|
||||
volumes: &Volumes,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let mut volumes = volumes.clone();
|
||||
volumes.insert(VolumeId::Backup, Volume::Backup { readonly: true });
|
||||
@@ -231,7 +231,7 @@ impl BackupActions {
|
||||
)
|
||||
})?;
|
||||
sqlx::query!(
|
||||
"REPLACE INTO tor (package, interface, key) VALUES (?, ?, ?)",
|
||||
"INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET key = $3",
|
||||
**pkg_id,
|
||||
*iface,
|
||||
key_vec,
|
||||
|
||||
@@ -221,7 +221,7 @@ pub async fn recover_full_embassy(
|
||||
let key_vec = os_backup.tor_key.as_bytes().to_vec();
|
||||
let secret_store = ctx.secret_store().await?;
|
||||
sqlx::query!(
|
||||
"REPLACE INTO account (id, password, tor_key) VALUES (?, ?, ?)",
|
||||
"INSERT INTO account (id, password, tor_key) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3",
|
||||
0,
|
||||
password,
|
||||
key_vec,
|
||||
|
||||
@@ -4,7 +4,7 @@ use color_eyre::eyre::eyre;
|
||||
use futures::TryStreamExt;
|
||||
use rpc_toolkit::command;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
|
||||
use super::{BackupTarget, BackupTargetId};
|
||||
use crate::context::RpcContext;
|
||||
@@ -49,8 +49,8 @@ pub async fn add(
|
||||
let embassy_os = recovery_info(&guard).await?;
|
||||
guard.unmount().await?;
|
||||
let path_string = Path::new("/").join(&cifs.path).display().to_string();
|
||||
let id: u32 = sqlx::query!(
|
||||
"INSERT INTO cifs_shares (hostname, path, username, password) VALUES (?, ?, ?, ?) RETURNING id AS \"id: u32\"",
|
||||
let id: i32 = sqlx::query!(
|
||||
"INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id",
|
||||
cifs.hostname,
|
||||
path_string,
|
||||
cifs.username,
|
||||
@@ -98,7 +98,7 @@ pub async fn update(
|
||||
guard.unmount().await?;
|
||||
let path_string = Path::new("/").join(&cifs.path).display().to_string();
|
||||
if sqlx::query!(
|
||||
"UPDATE cifs_shares SET hostname = ?, path = ?, username = ?, password = ? WHERE id = ?",
|
||||
"UPDATE cifs_shares SET hostname = $1, path = $2, username = $3, password = $4 WHERE id = $5",
|
||||
cifs.hostname,
|
||||
path_string,
|
||||
cifs.username,
|
||||
@@ -137,7 +137,7 @@ pub async fn remove(#[context] ctx: RpcContext, #[arg] id: BackupTargetId) -> Re
|
||||
crate::ErrorKind::NotFound,
|
||||
));
|
||||
};
|
||||
if sqlx::query!("DELETE FROM cifs_shares WHERE id = ?", id)
|
||||
if sqlx::query!("DELETE FROM cifs_shares WHERE id = $1", id)
|
||||
.execute(&ctx.secret_store)
|
||||
.await?
|
||||
.rows_affected()
|
||||
@@ -151,12 +151,12 @@ pub async fn remove(#[context] ctx: RpcContext, #[arg] id: BackupTargetId) -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load<Ex>(secrets: &mut Ex, id: u32) -> Result<Cifs, Error>
|
||||
pub async fn load<Ex>(secrets: &mut Ex, id: i32) -> Result<Cifs, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let record = sqlx::query!(
|
||||
"SELECT hostname, path, username, password FROM cifs_shares WHERE id = ?",
|
||||
"SELECT hostname, path, username, password FROM cifs_shares WHERE id = $1",
|
||||
id
|
||||
)
|
||||
.fetch_one(secrets)
|
||||
@@ -170,13 +170,12 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn list<Ex>(secrets: &mut Ex) -> Result<Vec<(u32, CifsBackupTarget)>, Error>
|
||||
pub async fn list<Ex>(secrets: &mut Ex) -> Result<Vec<(i32, CifsBackupTarget)>, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let mut records = sqlx::query!(
|
||||
"SELECT id AS \"id: u32\", hostname, path, username, password FROM cifs_shares"
|
||||
)
|
||||
let mut records =
|
||||
sqlx::query!("SELECT id, hostname, path, username, password FROM cifs_shares")
|
||||
.fetch_many(secrets);
|
||||
|
||||
let mut cifs = Vec::new();
|
||||
|
||||
@@ -10,7 +10,7 @@ use digest::OutputSizeUser;
|
||||
use rpc_toolkit::command;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tracing::instrument;
|
||||
|
||||
use self::cifs::CifsBackupTarget;
|
||||
@@ -45,12 +45,12 @@ pub enum BackupTarget {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum BackupTargetId {
|
||||
Disk { logicalname: PathBuf },
|
||||
Cifs { id: u32 },
|
||||
Cifs { id: i32 },
|
||||
}
|
||||
impl BackupTargetId {
|
||||
pub async fn load<Ex>(self, secrets: &mut Ex) -> Result<BackupTargetFS, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
Ok(match self {
|
||||
BackupTargetId::Disk { logicalname } => {
|
||||
|
||||
@@ -18,7 +18,7 @@ use regex::Regex;
|
||||
use serde::de::{MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::{Number, Value};
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::util::{self, CharSet, NumRange, UniqueBy, STATIC_NULL};
|
||||
use super::{Config, MatchError, NoMatchWithPath, TimeoutError, TypeOf};
|
||||
@@ -2050,7 +2050,7 @@ impl TorKeyPointer {
|
||||
async fn deref(
|
||||
&self,
|
||||
source_package: &PackageId,
|
||||
secrets: &SqlitePool,
|
||||
secrets: &PgPool,
|
||||
) -> Result<Value, ConfigurationError> {
|
||||
if &self.package_id != source_package {
|
||||
return Err(ConfigurationError::PermissionDenied(
|
||||
@@ -2058,7 +2058,7 @@ impl TorKeyPointer {
|
||||
));
|
||||
}
|
||||
let x = sqlx::query!(
|
||||
"SELECT key FROM tor WHERE package = ? AND interface = ?",
|
||||
"SELECT key FROM tor WHERE package = $1 AND interface = $2",
|
||||
*self.package_id,
|
||||
*self.interface
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use bollard::Docker;
|
||||
use helpers::to_tmp_path;
|
||||
@@ -14,8 +13,8 @@ use reqwest::Url;
|
||||
use rpc_toolkit::url::Host;
|
||||
use rpc_toolkit::Context;
|
||||
use serde::Deserialize;
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::postgres::PgConnectOptions;
|
||||
use sqlx::PgPool;
|
||||
use tokio::fs::File;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
||||
@@ -24,6 +23,7 @@ use tracing::instrument;
|
||||
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation};
|
||||
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
|
||||
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
||||
use crate::init::{init_postgres, pgloader};
|
||||
use crate::install::cleanup::{cleanup_failed, uninstall, CleanupFailedReceipts};
|
||||
use crate::manager::ManagerMap;
|
||||
use crate::middleware::auth::HashSessionToken;
|
||||
@@ -71,7 +71,7 @@ impl RpcContextConfig {
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| Path::new("/embassy-data"))
|
||||
}
|
||||
pub async fn db(&self, secret_store: &SqlitePool, product_key: &str) -> Result<PatchDb, Error> {
|
||||
pub async fn db(&self, secret_store: &PgPool, product_key: &str) -> Result<PatchDb, Error> {
|
||||
let sid = derive_id(product_key);
|
||||
let hostname = derive_hostname(&sid);
|
||||
let db_path = self.datadir().join("main").join("embassy.db");
|
||||
@@ -94,18 +94,19 @@ impl RpcContextConfig {
|
||||
Ok(db)
|
||||
}
|
||||
#[instrument]
|
||||
pub async fn secret_store(&self) -> Result<SqlitePool, Error> {
|
||||
let secret_store = SqlitePool::connect_with(
|
||||
SqliteConnectOptions::new()
|
||||
.filename(self.datadir().join("main").join("secrets.db"))
|
||||
.create_if_missing(true)
|
||||
.busy_timeout(Duration::from_secs(30)),
|
||||
)
|
||||
pub async fn secret_store(&self) -> Result<PgPool, Error> {
|
||||
init_postgres(self.datadir()).await?;
|
||||
let secret_store =
|
||||
PgPool::connect_with(PgConnectOptions::new().database("secrets").username("root"))
|
||||
.await?;
|
||||
sqlx::migrate!()
|
||||
.run(&secret_store)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Database)?;
|
||||
let old_db_path = self.datadir().join("main/secrets.db");
|
||||
if tokio::fs::metadata(&old_db_path).await.is_ok() {
|
||||
pgloader(&old_db_path).await?;
|
||||
}
|
||||
Ok(secret_store)
|
||||
}
|
||||
}
|
||||
@@ -118,7 +119,7 @@ pub struct RpcContextSeed {
|
||||
pub datadir: PathBuf,
|
||||
pub disk_guid: Arc<String>,
|
||||
pub db: PatchDb,
|
||||
pub secret_store: SqlitePool,
|
||||
pub secret_store: PgPool,
|
||||
pub docker: Docker,
|
||||
pub net_controller: NetController,
|
||||
pub managers: ManagerMap,
|
||||
@@ -214,7 +215,7 @@ impl RpcContext {
|
||||
)));
|
||||
let (shutdown, _) = tokio::sync::broadcast::channel(1);
|
||||
let secret_store = base.secret_store().await?;
|
||||
tracing::info!("Opened Sqlite DB");
|
||||
tracing::info!("Opened Pg DB");
|
||||
let db = base.db(&secret_store, &get_product_key().await?).await?;
|
||||
tracing::info!("Opened PatchDB");
|
||||
let docker = Docker::connect_with_unix_defaults()?;
|
||||
|
||||
@@ -9,9 +9,10 @@ use patch_db::PatchDb;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::postgres::PgConnectOptions;
|
||||
use sqlx::PgPool;
|
||||
use tokio::fs::File;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::broadcast::Sender;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
@@ -19,10 +20,11 @@ use url::Host;
|
||||
|
||||
use crate::db::model::Database;
|
||||
use crate::hostname::{derive_hostname, derive_id, get_product_key};
|
||||
use crate::init::{init_postgres, pgloader};
|
||||
use crate::net::tor::os_key;
|
||||
use crate::setup::{password_hash, RecoveryStatus};
|
||||
use crate::util::io::from_yaml_async_reader;
|
||||
use crate::util::AsyncFileExt;
|
||||
use crate::util::{AsyncFileExt, Invoke};
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@@ -93,7 +95,7 @@ impl SetupContext {
|
||||
})))
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn db(&self, secret_store: &SqlitePool) -> Result<PatchDb, Error> {
|
||||
pub async fn db(&self, secret_store: &PgPool) -> Result<PatchDb, Error> {
|
||||
let db_path = self.datadir.join("main").join("embassy.db");
|
||||
let db = PatchDb::open(&db_path)
|
||||
.await
|
||||
@@ -117,18 +119,19 @@ impl SetupContext {
|
||||
Ok(db)
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn secret_store(&self) -> Result<SqlitePool, Error> {
|
||||
let secret_store = SqlitePool::connect_with(
|
||||
SqliteConnectOptions::new()
|
||||
.filename(self.datadir.join("main").join("secrets.db"))
|
||||
.create_if_missing(true)
|
||||
.busy_timeout(Duration::from_secs(30)),
|
||||
)
|
||||
pub async fn secret_store(&self) -> Result<PgPool, Error> {
|
||||
init_postgres(&self.datadir).await?;
|
||||
let secret_store =
|
||||
PgPool::connect_with(PgConnectOptions::new().database("secrets").username("root"))
|
||||
.await?;
|
||||
sqlx::migrate!()
|
||||
.run(&secret_store)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Database)?;
|
||||
let old_db_path = self.datadir.join("main/secrets.db");
|
||||
if tokio::fs::metadata(&old_db_path).await.is_ok() {
|
||||
pgloader(&old_db_path).await?;
|
||||
}
|
||||
Ok(secret_store)
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
|
||||
@@ -48,8 +48,10 @@ pub async fn unmount<P: AsRef<Path>>(mountpoint: P) -> Result<(), Error> {
|
||||
.arg(mountpoint.as_ref())
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
tokio::fs::remove_dir_all(mountpoint.as_ref())
|
||||
.await
|
||||
match tokio::fs::remove_dir(mountpoint.as_ref()).await {
|
||||
Err(e) if e.raw_os_error() == Some(39) => Ok(()), // directory not empty
|
||||
a => a,
|
||||
}
|
||||
.with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::path::Path;
|
||||
use std::process::Stdio;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use patch_db::{DbHandle, LockReceipt, LockType};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::context::rpc::RpcContextConfig;
|
||||
use crate::db::model::ServerStatus;
|
||||
use crate::disk::mount::util::unmount;
|
||||
use crate::install::PKG_DOCKER_DIR;
|
||||
use crate::util::Invoke;
|
||||
use crate::Error;
|
||||
@@ -67,10 +71,91 @@ impl InitReceipts {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pgloader(old_db_path: impl AsRef<Path>) -> Result<(), Error> {
|
||||
tokio::fs::write(
|
||||
"/etc/embassy/migrate.load",
|
||||
format!(
|
||||
include_str!("migrate.load"),
|
||||
sqlite_path = old_db_path.as_ref().display()
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Running pgloader");
|
||||
let out = Command::new("pgloader")
|
||||
.arg("-v")
|
||||
.arg("/etc/embassy/migrate.load")
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.await?;
|
||||
let stdout = String::from_utf8(out.stdout)?;
|
||||
for line in stdout.lines() {
|
||||
tracing::debug!("pgloader: {}", line);
|
||||
}
|
||||
let stderr = String::from_utf8(out.stderr)?;
|
||||
for line in stderr.lines() {
|
||||
tracing::debug!("pgloader err: {}", line);
|
||||
}
|
||||
tracing::debug!("pgloader exited with code {:?}", out.status);
|
||||
if let Some(err) = stdout.lines().chain(stderr.lines()).find_map(|l| {
|
||||
if l.split_ascii_whitespace()
|
||||
.any(|word| word == "ERROR" || word == "FATAL")
|
||||
{
|
||||
Some(l)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
return Err(Error::new(
|
||||
eyre!("pgloader error: {}", err),
|
||||
crate::ErrorKind::Database,
|
||||
));
|
||||
}
|
||||
tokio::fs::rename(
|
||||
old_db_path.as_ref(),
|
||||
old_db_path.as_ref().with_extension("bak"),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// must be idempotent
|
||||
pub async fn init_postgres(datadir: impl AsRef<Path>) -> Result<(), Error> {
|
||||
let db_dir = datadir.as_ref().join("main/postgresql");
|
||||
let is_mountpoint = || async {
|
||||
Ok::<_, Error>(
|
||||
tokio::process::Command::new("mountpoint")
|
||||
.arg("/var/lib/postgresql")
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()
|
||||
.await?
|
||||
.success(),
|
||||
)
|
||||
};
|
||||
if tokio::fs::metadata(&db_dir).await.is_err() {
|
||||
Command::new("cp")
|
||||
.arg("-ra")
|
||||
.arg("/var/lib/postgresql")
|
||||
.arg(&db_dir)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
}
|
||||
if !is_mountpoint().await? {
|
||||
crate::disk::mount::util::bind(&db_dir, "/var/lib/postgresql", false).await?;
|
||||
}
|
||||
Command::new("systemctl")
|
||||
.arg("start")
|
||||
.arg("postgresql")
|
||||
.invoke(crate::ErrorKind::Database)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error> {
|
||||
let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok();
|
||||
let secret_store = cfg.secret_store().await?;
|
||||
let log_dir = cfg.datadir().join("main").join("logs");
|
||||
let log_dir = cfg.datadir().join("main/logs");
|
||||
if tokio::fs::metadata(&log_dir).await.is_err() {
|
||||
tokio::fs::create_dir_all(&log_dir).await?;
|
||||
}
|
||||
@@ -92,7 +177,7 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
|
||||
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
||||
}
|
||||
Command::new("cp")
|
||||
.arg("-r")
|
||||
.arg("-ra")
|
||||
.arg("/var/lib/docker")
|
||||
.arg(&tmp_docker)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use bollard::image::ListImagesOptions;
|
||||
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::{PKG_ARCHIVE_DIR, PKG_DOCKER_DIR};
|
||||
@@ -337,7 +337,7 @@ pub async fn uninstall<Ex>(
|
||||
id: &PackageId,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let mut tx = db.begin().await?;
|
||||
let receipts = UninstallReceipts::new(&mut tx, id).await?;
|
||||
@@ -383,10 +383,10 @@ where
|
||||
#[instrument(skip(secrets))]
|
||||
pub async fn remove_tor_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let id_str = id.as_str();
|
||||
sqlx::query!("DELETE FROM tor WHERE package = ?", id_str)
|
||||
sqlx::query!("DELETE FROM tor WHERE package = $1", id_str)
|
||||
.execute(secrets)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub const DEFAULT_MARKETPLACE: &str = "https://marketplace.start9.com";
|
||||
pub const DEFAULT_MARKETPLACE: &str = "https://registry.start9.com";
|
||||
pub const BUFFER_SIZE: usize = 1024;
|
||||
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
|
||||
pub const TARGET: &str = current_platform::CURRENT_PLATFORM;
|
||||
|
||||
@@ -12,7 +12,7 @@ use color_eyre::eyre::eyre;
|
||||
use nix::sys::signal::Signal;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use patch_db::DbHandle;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tokio::sync::watch::error::RecvError;
|
||||
use tokio::sync::watch::{channel, Receiver, Sender};
|
||||
use tokio::sync::{Notify, RwLock};
|
||||
@@ -47,7 +47,7 @@ impl ManagerMap {
|
||||
secrets: &mut Ex,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let mut res = BTreeMap::new();
|
||||
for package in crate::db::DatabaseModel::new()
|
||||
|
||||
@@ -41,7 +41,7 @@ impl HasLoggedOutSessions {
|
||||
for session in logged_out_sessions {
|
||||
let session = session.as_logout_session_id();
|
||||
sqlx::query!(
|
||||
"UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = ?",
|
||||
"UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1",
|
||||
session
|
||||
)
|
||||
.execute(&mut sqlx_conn)
|
||||
@@ -68,7 +68,7 @@ impl HasValidSession {
|
||||
|
||||
pub async fn from_session(session: &HashSessionToken, ctx: &RpcContext) -> Result<Self, Error> {
|
||||
let session_hash = session.hashed();
|
||||
let session = sqlx::query!("UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = ? AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP", session_hash)
|
||||
let session = sqlx::query!("UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP", session_hash)
|
||||
.execute(&mut ctx.secret_store.acquire().await?)
|
||||
.await?;
|
||||
if session.rows_affected() == 0 {
|
||||
|
||||
7
backend/src/migrate.load
Normal file
7
backend/src/migrate.load
Normal file
@@ -0,0 +1,7 @@
|
||||
load database
|
||||
from sqlite://{sqlite_path}
|
||||
into postgresql://root@unix:/var/run/postgresql:5432/secrets
|
||||
|
||||
with include no drop, truncate, reset sequences, data only
|
||||
|
||||
excluding table names like '_sqlx_migrations';
|
||||
@@ -6,7 +6,7 @@ use indexmap::IndexSet;
|
||||
use itertools::Either;
|
||||
pub use models::InterfaceId;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use torut::onion::TorSecretKeyV3;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Interfaces {
|
||||
package_id: &PackageId,
|
||||
) -> Result<InterfaceAddressMap, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let mut interface_addresses = InterfaceAddressMap(BTreeMap::new());
|
||||
for (id, iface) in &self.0 {
|
||||
@@ -51,7 +51,7 @@ impl Interfaces {
|
||||
let key = TorSecretKeyV3::generate();
|
||||
let key_vec = key.as_bytes().to_vec();
|
||||
sqlx::query!(
|
||||
"INSERT OR IGNORE INTO tor (package, interface, key) VALUES (?, ?, ?)",
|
||||
"INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING",
|
||||
**package_id,
|
||||
**id,
|
||||
key_vec,
|
||||
@@ -59,7 +59,7 @@ impl Interfaces {
|
||||
.execute(&mut *secrets)
|
||||
.await?;
|
||||
let key_row = sqlx::query!(
|
||||
"SELECT key FROM tor WHERE package = ? AND interface = ?",
|
||||
"SELECT key FROM tor WHERE package = $1 AND interface = $2",
|
||||
**package_id,
|
||||
**id,
|
||||
)
|
||||
@@ -89,10 +89,10 @@ impl Interfaces {
|
||||
package_id: &PackageId,
|
||||
) -> Result<BTreeMap<InterfaceId, TorSecretKeyV3>, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
Ok(sqlx::query!(
|
||||
"SELECT interface, key FROM tor WHERE package = ?",
|
||||
"SELECT interface, key FROM tor WHERE package = $1",
|
||||
**package_id
|
||||
)
|
||||
.fetch_many(secrets)
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::X509;
|
||||
use rpc_toolkit::command;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl NetController {
|
||||
embassyd_tor_key: TorSecretKeyV3,
|
||||
tor_control: SocketAddr,
|
||||
dns_bind: &[SocketAddr],
|
||||
db: SqlitePool,
|
||||
db: PgPool,
|
||||
import_root_ca: Option<(PKey<Private>, X509)>,
|
||||
) -> Result<Self, Error> {
|
||||
let ssl = match import_root_ca {
|
||||
|
||||
@@ -11,7 +11,7 @@ use openssl::nid::Nid;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509};
|
||||
use openssl::*;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::instrument;
|
||||
@@ -33,17 +33,17 @@ pub struct SslManager {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SslStore {
|
||||
secret_store: SqlitePool,
|
||||
secret_store: PgPool,
|
||||
}
|
||||
impl SslStore {
|
||||
fn new(db: SqlitePool) -> Result<Self, Error> {
|
||||
fn new(db: PgPool) -> Result<Self, Error> {
|
||||
Ok(SslStore { secret_store: db })
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
async fn save_root_certificate(&self, key: &PKey<Private>, cert: &X509) -> Result<(), Error> {
|
||||
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
|
||||
let cert_str = String::from_utf8(cert.to_pem()?)?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (0, ?, ?, NULL, datetime('now'), datetime('now'))", key_str, cert_str).execute(&self.secret_store).await?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (0, $1, $2, NULL, now(), now())", key_str, cert_str).execute(&self.secret_store).await?;
|
||||
Ok(())
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
@@ -69,7 +69,7 @@ impl SslStore {
|
||||
) -> Result<(), Error> {
|
||||
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
|
||||
let cert_str = String::from_utf8(cert.to_pem()?)?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (1, ?, ?, NULL, datetime('now'), datetime('now'))", key_str, cert_str).execute(&self.secret_store).await?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (1, $1, $2, NULL, now(), now())", key_str, cert_str).execute(&self.secret_store).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn load_intermediate_certificate(&self) -> Result<Option<(PKey<Private>, X509)>, Error> {
|
||||
@@ -108,7 +108,7 @@ impl SslStore {
|
||||
) -> Result<(), Error> {
|
||||
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
|
||||
let cert_str = String::from_utf8(cert.to_pem()?)?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (?, ?, ?, datetime('now'), datetime('now'))", key_str, cert_str, lookup_string).execute(&self.secret_store).await?;
|
||||
let _n = sqlx::query!("INSERT INTO certificates (priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES ($1, $2, $3, now(), now())", key_str, cert_str, lookup_string).execute(&self.secret_store).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn load_certificate(
|
||||
@@ -116,7 +116,7 @@ impl SslStore {
|
||||
lookup_string: &str,
|
||||
) -> Result<Option<(PKey<Private>, X509)>, Error> {
|
||||
let m_row = sqlx::query!(
|
||||
"SELECT priv_key_pem, certificate_pem FROM certificates WHERE lookup_string = ?",
|
||||
"SELECT priv_key_pem, certificate_pem FROM certificates WHERE lookup_string = $1",
|
||||
lookup_string
|
||||
)
|
||||
.fetch_optional(&self.secret_store)
|
||||
@@ -139,7 +139,8 @@ impl SslStore {
|
||||
) -> Result<(), Error> {
|
||||
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
|
||||
let cert_str = String::from_utf8(cert.to_pem()?)?;
|
||||
let n = sqlx::query!("UPDATE certificates SET priv_key_pem = ?, certificate_pem = ?, updated_at = datetime('now') WHERE lookup_string = ?", key_str, cert_str, lookup_string).execute(&self.secret_store).await?;
|
||||
let n = sqlx::query!("UPDATE certificates SET priv_key_pem = $1, certificate_pem = $2, updated_at = now() WHERE lookup_string = $3", key_str, cert_str, lookup_string)
|
||||
.execute(&self.secret_store).await?;
|
||||
if n.rows_affected() == 0 {
|
||||
return Err(Error::new(
|
||||
eyre!(
|
||||
@@ -161,7 +162,7 @@ lazy_static::lazy_static! {
|
||||
|
||||
impl SslManager {
|
||||
#[instrument(skip(db))]
|
||||
pub async fn init(db: SqlitePool) -> Result<Self, Error> {
|
||||
pub async fn init(db: PgPool) -> Result<Self, Error> {
|
||||
let store = SslStore::new(db)?;
|
||||
let (root_key, root_cert) = match store.load_root_certificate().await? {
|
||||
None => {
|
||||
@@ -204,6 +205,10 @@ impl SslManager {
|
||||
}
|
||||
Some((key, cert)) => Ok((key, cert)),
|
||||
}?;
|
||||
|
||||
sqlx::query!("SELECT setval('certificates_id_seq', GREATEST(MAX(id) + 1, nextval('certificates_id_seq') - 1)) FROM certificates")
|
||||
.fetch_one(&store.secret_store).await?;
|
||||
|
||||
Ok(SslManager {
|
||||
store,
|
||||
root_cert,
|
||||
@@ -221,7 +226,7 @@ impl SslManager {
|
||||
// the intermediate certificate
|
||||
#[instrument(skip(db))]
|
||||
pub async fn import_root_ca(
|
||||
db: SqlitePool,
|
||||
db: PgPool,
|
||||
root_key: PKey<Private>,
|
||||
root_cert: X509,
|
||||
) -> Result<Self, Error> {
|
||||
@@ -508,10 +513,11 @@ fn make_leaf_cert(
|
||||
|
||||
#[tokio::test]
|
||||
async fn ca_details_persist() -> Result<(), Error> {
|
||||
let pool = sqlx::Pool::<sqlx::Sqlite>::connect("sqlite::memory:").await?;
|
||||
sqlx::query_file!("migrations/20210629193146_Init.sql")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
let pool = sqlx::Pool::<sqlx::Postgres>::connect("postgres::memory:").await?;
|
||||
sqlx::migrate!()
|
||||
.run(&pool)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Database)?;
|
||||
let mgr = SslManager::init(pool.clone()).await?;
|
||||
let root_cert0 = mgr.root_cert;
|
||||
let int_key0 = mgr.int_key;
|
||||
@@ -532,10 +538,11 @@ async fn ca_details_persist() -> Result<(), Error> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn certificate_details_persist() -> Result<(), Error> {
|
||||
let pool = sqlx::Pool::<sqlx::Sqlite>::connect("sqlite::memory:").await?;
|
||||
sqlx::query_file!("migrations/20210629193146_Init.sql")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
let pool = sqlx::Pool::<sqlx::Postgres>::connect("postgres::memory:").await?;
|
||||
sqlx::migrate!()
|
||||
.run(&pool)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Database)?;
|
||||
let mgr = SslManager::init(pool.clone()).await?;
|
||||
let package_id = "bitcoind".parse().unwrap();
|
||||
let (key0, cert_chain0) = mgr.certificate_for("start9", &package_id).await?;
|
||||
|
||||
@@ -9,7 +9,7 @@ use futures::FutureExt;
|
||||
use reqwest::Client;
|
||||
use rpc_toolkit::command;
|
||||
use serde_json::json;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::Mutex;
|
||||
use torut::control::{AsyncEvent, AuthenticatedConn, ConnError};
|
||||
@@ -60,7 +60,7 @@ pub async fn list_services(
|
||||
#[instrument(skip(secrets))]
|
||||
pub async fn os_key<Ex>(secrets: &mut Ex) -> Result<TorSecretKeyV3, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let key = sqlx::query!("SELECT tor_key FROM account")
|
||||
.fetch_one(secrets)
|
||||
|
||||
@@ -6,7 +6,7 @@ use chrono::{DateTime, Utc};
|
||||
use color_eyre::eyre::eyre;
|
||||
use patch_db::{DbHandle, LockType};
|
||||
use rpc_toolkit::command;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -27,7 +27,7 @@ pub async fn notification() -> Result<(), Error> {
|
||||
#[instrument(skip(ctx))]
|
||||
pub async fn list(
|
||||
#[context] ctx: RpcContext,
|
||||
#[arg] before: Option<u32>,
|
||||
#[arg] before: Option<i32>,
|
||||
#[arg] limit: Option<u32>,
|
||||
) -> Result<WithRevision<Vec<Notification>>, Error> {
|
||||
let limit = limit.unwrap_or(40);
|
||||
@@ -39,8 +39,8 @@ pub async fn list(
|
||||
.unread_notification_count();
|
||||
model.lock(&mut handle, LockType::Write).await?;
|
||||
let records = sqlx::query!(
|
||||
"SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT ?",
|
||||
limit
|
||||
"SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT $1",
|
||||
limit as i64
|
||||
).fetch_all(&ctx.secret_store).await?;
|
||||
let notifs = records
|
||||
.into_iter()
|
||||
@@ -80,9 +80,9 @@ pub async fn list(
|
||||
}
|
||||
Some(before) => {
|
||||
let records = sqlx::query!(
|
||||
"SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < ? ORDER BY id DESC LIMIT ?",
|
||||
"SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < $1 ORDER BY id DESC LIMIT $2",
|
||||
before,
|
||||
limit
|
||||
limit as i64
|
||||
).fetch_all(&ctx.secret_store).await?;
|
||||
let res = records
|
||||
.into_iter()
|
||||
@@ -122,16 +122,16 @@ pub async fn list(
|
||||
}
|
||||
|
||||
#[command(display(display_none))]
|
||||
pub async fn delete(#[context] ctx: RpcContext, #[arg] id: u32) -> Result<(), Error> {
|
||||
sqlx::query!("DELETE FROM notifications WHERE id = ?", id)
|
||||
pub async fn delete(#[context] ctx: RpcContext, #[arg] id: i32) -> Result<(), Error> {
|
||||
sqlx::query!("DELETE FROM notifications WHERE id = $1", id)
|
||||
.execute(&ctx.secret_store)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(rename = "delete-before", display(display_none))]
|
||||
pub async fn delete_before(#[context] ctx: RpcContext, #[arg] before: u32) -> Result<(), Error> {
|
||||
sqlx::query!("DELETE FROM notifications WHERE id < ?", before)
|
||||
pub async fn delete_before(#[context] ctx: RpcContext, #[arg] before: i32) -> Result<(), Error> {
|
||||
sqlx::query!("DELETE FROM notifications WHERE id < $1", before)
|
||||
.execute(&ctx.secret_store)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -218,22 +218,22 @@ pub struct Notification {
|
||||
pub trait NotificationType:
|
||||
serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug
|
||||
{
|
||||
const CODE: u32;
|
||||
const CODE: i32;
|
||||
}
|
||||
|
||||
impl NotificationType for () {
|
||||
const CODE: u32 = 0;
|
||||
const CODE: i32 = 0;
|
||||
}
|
||||
impl NotificationType for BackupReport {
|
||||
const CODE: u32 = 1;
|
||||
const CODE: i32 = 1;
|
||||
}
|
||||
|
||||
pub struct NotificationManager {
|
||||
sqlite: SqlitePool,
|
||||
sqlite: PgPool,
|
||||
cache: Mutex<HashMap<(Option<PackageId>, NotificationLevel, String), i64>>,
|
||||
}
|
||||
impl NotificationManager {
|
||||
pub fn new(sqlite: SqlitePool) -> Self {
|
||||
pub fn new(sqlite: PgPool) -> Self {
|
||||
NotificationManager {
|
||||
sqlite,
|
||||
cache: Mutex::new(HashMap::new()),
|
||||
@@ -267,9 +267,9 @@ impl NotificationManager {
|
||||
let sql_data =
|
||||
serde_json::to_string(&subtype).with_kind(crate::ErrorKind::Serialization)?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO notifications (package_id, code, level, title, message, data) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
"INSERT INTO notifications (package_id, code, level, title, message, data) VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
sql_package_id,
|
||||
sql_code,
|
||||
sql_code as i32,
|
||||
sql_level,
|
||||
title,
|
||||
message,
|
||||
|
||||
@@ -17,7 +17,7 @@ use rpc_toolkit::command;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::{Connection, Executor, Sqlite};
|
||||
use sqlx::{Connection, Executor, Postgres};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||
@@ -52,7 +52,7 @@ use crate::{ensure_code, Error, ErrorKind, ResultExt};
|
||||
#[instrument(skip(secrets))]
|
||||
pub async fn password_hash<Ex>(secrets: &mut Ex) -> Result<String, Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>,
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let password = sqlx::query!("SELECT password FROM account")
|
||||
.fetch_one(secrets)
|
||||
@@ -448,7 +448,7 @@ async fn fresh_setup(
|
||||
let key_vec = tor_key.as_bytes().to_vec();
|
||||
let sqlite_pool = ctx.secret_store().await?;
|
||||
sqlx::query!(
|
||||
"REPLACE INTO account (id, password, tor_key) VALUES (?, ?, ?)",
|
||||
"INSERT INTO account (id, password, tor_key) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3",
|
||||
0,
|
||||
password,
|
||||
key_vec,
|
||||
@@ -696,7 +696,7 @@ async fn recover_v2(
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?;
|
||||
let sqlite_pool = ctx.secret_store().await?;
|
||||
sqlx::query!(
|
||||
"REPLACE INTO account (id, password, tor_key) VALUES (?, ?, ?)",
|
||||
"INSERT INTO account (id, password, tor_key) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3",
|
||||
0,
|
||||
password,
|
||||
key_vec
|
||||
@@ -790,7 +790,7 @@ async fn recover_v2(
|
||||
);
|
||||
let key_vec = key_vec[32..].to_vec();
|
||||
sqlx::query!(
|
||||
"REPLACE INTO tor (package, interface, key) VALUES (?, 'main', ?)",
|
||||
"INSERT INTO tor (package, interface, key) VALUES ($1, 'main', $2) ON CONFLICT (package, interface) DO UPDATE SET key = $2",
|
||||
*dst_id,
|
||||
key_vec,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ use chrono::Utc;
|
||||
use clap::ArgMatches;
|
||||
use color_eyre::eyre::eyre;
|
||||
use rpc_toolkit::command;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
@@ -61,7 +61,7 @@ pub async fn add(#[context] ctx: RpcContext, #[arg] key: PubKey) -> Result<SshKe
|
||||
let pool = &ctx.secret_store;
|
||||
// check fingerprint for duplicates
|
||||
let fp = key.0.fingerprint_md5();
|
||||
match sqlx::query!("SELECT * FROM ssh_keys WHERE fingerprint = ?", fp)
|
||||
match sqlx::query!("SELECT * FROM ssh_keys WHERE fingerprint = $1", fp)
|
||||
.fetch_optional(pool)
|
||||
.await?
|
||||
{
|
||||
@@ -70,7 +70,7 @@ pub async fn add(#[context] ctx: RpcContext, #[arg] key: PubKey) -> Result<SshKe
|
||||
let raw_key = format!("{}", key.0);
|
||||
let created_at = Utc::now().to_rfc3339();
|
||||
sqlx::query!(
|
||||
"INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES (?, ?, ?)",
|
||||
"INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)",
|
||||
fp,
|
||||
raw_key,
|
||||
created_at
|
||||
@@ -96,7 +96,7 @@ pub async fn delete(#[context] ctx: RpcContext, #[arg] fingerprint: String) -> R
|
||||
let pool = &ctx.secret_store;
|
||||
// check if fingerprint is in DB
|
||||
// if in DB, remove it from DB
|
||||
let n = sqlx::query!("DELETE FROM ssh_keys WHERE fingerprint = ?", fingerprint)
|
||||
let n = sqlx::query!("DELETE FROM ssh_keys WHERE fingerprint = $1", fingerprint)
|
||||
.execute(pool)
|
||||
.await?
|
||||
.rows_affected();
|
||||
@@ -172,7 +172,10 @@ pub async fn list(
|
||||
}
|
||||
|
||||
#[instrument(skip(pool, dest))]
|
||||
pub async fn sync_keys_from_db<P: AsRef<Path>>(pool: &Pool<Sqlite>, dest: P) -> Result<(), Error> {
|
||||
pub async fn sync_keys_from_db<P: AsRef<Path>>(
|
||||
pool: &Pool<Postgres>,
|
||||
dest: P,
|
||||
) -> Result<(), Error> {
|
||||
let dest = dest.as_ref();
|
||||
let keys = sqlx::query!("SELECT openssh_pubkey FROM ssh_keys")
|
||||
.fetch_all(pool)
|
||||
|
||||
@@ -112,7 +112,7 @@ pub async fn kernel_logs_nofollow(
|
||||
_ctx: (),
|
||||
(limit, cursor, before, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogResponse, Error> {
|
||||
fetch_logs(LogSource::Service(SYSTEMD_UNIT), limit, cursor, before).await
|
||||
fetch_logs(LogSource::Kernel, limit, cursor, before).await
|
||||
}
|
||||
|
||||
#[command(rpc_only, rename = "follow", display(display_none))]
|
||||
@@ -120,7 +120,7 @@ pub async fn kernel_logs_follow(
|
||||
#[context] ctx: RpcContext,
|
||||
#[parent_data] (limit, _, _, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogFollowResponse, Error> {
|
||||
follow_logs(ctx, LogSource::Service(SYSTEMD_UNIT), limit).await
|
||||
follow_logs(ctx, LogSource::Kernel, limit).await
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
||||
22
backend/update-sqlx-data.sh
Executable file
22
backend/update-sqlx-data.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
TMP_DIR=$(mktemp -d)
|
||||
mkdir $TMP_DIR/pgdata
|
||||
docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR/pgdata:/var/lib/postgresql/data postgres
|
||||
|
||||
(
|
||||
set -e
|
||||
ctr=0
|
||||
while ! docker exec tmp_postgres psql -U postgres || [ $ctr -lt 5 ]; do
|
||||
ctr=$[ctr + 1]
|
||||
sleep 1;
|
||||
done
|
||||
|
||||
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres)
|
||||
|
||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres cargo sqlx migrate run
|
||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres cargo sqlx prepare -- --lib --profile=test
|
||||
)
|
||||
|
||||
docker stop tmp_postgres
|
||||
sudo rm -rf $TMP_DIR
|
||||
@@ -13,6 +13,12 @@ fi
|
||||
|
||||
passwd -l start9
|
||||
|
||||
while ! ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null; do
|
||||
>&2 echo "Waiting for internet connection..."
|
||||
sleep 1
|
||||
done
|
||||
echo "Connected to network"
|
||||
|
||||
# change timezone
|
||||
timedatectl set-timezone Etc/UTC
|
||||
|
||||
@@ -45,7 +51,9 @@ apt-get install -y \
|
||||
network-manager \
|
||||
vim \
|
||||
jq \
|
||||
ncdu
|
||||
ncdu \
|
||||
postgresql \
|
||||
pgloader
|
||||
|
||||
# Setup repository from The Guardian Project and install latest stable Tor daemon
|
||||
touch /etc/apt/sources.list.d/tor.list
|
||||
@@ -64,6 +72,10 @@ systemctl start systemd-resolved
|
||||
apt-get remove --purge openresolv dhcpcd5 -y
|
||||
systemctl disable wpa_supplicant.service
|
||||
|
||||
sudo -u postgres createuser root
|
||||
sudo -u postgres createdb secrets -O root
|
||||
systemctl disable postgresql.service
|
||||
|
||||
ln -rsf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||
|
||||
systemctl disable bluetooth.service
|
||||
|
||||
Reference in New Issue
Block a user