switch to postgresql (#1763)

switch sqlx to postgresql
This commit is contained in:
Aiden McClelland
2022-09-01 10:32:01 -06:00
committed by GitHub
parent 705653465a
commit 76682ebef0
30 changed files with 800 additions and 537 deletions

View File

@@ -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) 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) COMPAT_SRC := $(shell find system-images/compat/src)
UTILS_SRC := $(shell find system-images/utils/Dockerfile) 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_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_UI_SRC := $(shell find frontend/projects/ui)
FRONTEND_SETUP_WIZARD_SRC := $(shell find frontend/projects/setup-wizard) FRONTEND_SETUP_WIZARD_SRC := $(shell find frontend/projects/setup-wizard)

98
backend/Cargo.lock generated
View File

@@ -982,6 +982,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]] [[package]]
name = "dirs-next" name = "dirs-next"
version = "2.0.0" version = "2.0.0"
@@ -992,6 +1001,17 @@ dependencies = [
"dirs-sys-next", "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]] [[package]]
name = "dirs-sys-next" name = "dirs-sys-next"
version = "0.1.2" version = "0.1.2"
@@ -1302,18 +1322,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 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]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@@ -1641,6 +1649,15 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 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]] [[package]]
name = "hmac" name = "hmac"
version = "0.11.0" version = "0.11.0"
@@ -2151,17 +2168,6 @@ dependencies = [
"winapi", "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]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@@ -2213,6 +2219,15 @@ dependencies = [
"opaque-debug", "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]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.5.0"
@@ -2509,7 +2524,7 @@ checksum = "e7249a699cdeea261ac73f1bf9350777cb867324f44373aafb5a287365bf1771"
dependencies = [ dependencies = [
"base64 0.13.0", "base64 0.13.0",
"byteorder", "byteorder",
"md-5", "md-5 0.9.1",
"sha2 0.9.9", "sha2 0.9.9",
"thiserror", "thiserror",
] ]
@@ -3219,7 +3234,7 @@ dependencies = [
"cc", "cc",
"libc", "libc",
"once_cell", "once_cell",
"spin 0.5.2", "spin",
"untrusted", "untrusted",
"web-sys", "web-sys",
"winapi", "winapi",
@@ -3725,15 +3740,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 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]] [[package]]
name = "spki" name = "spki"
version = "0.6.0" version = "0.6.0"
@@ -3773,34 +3779,39 @@ checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093"
dependencies = [ dependencies = [
"ahash", "ahash",
"atoi", "atoi",
"base64 0.13.0",
"bitflags", "bitflags",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono", "chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"dirs 4.0.0",
"either", "either",
"event-listener", "event-listener",
"flume",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor",
"futures-intrusive", "futures-intrusive",
"futures-util", "futures-util",
"hashlink", "hashlink",
"hex", "hex",
"hkdf",
"hmac 0.12.1",
"indexmap", "indexmap",
"itoa 1.0.2", "itoa 1.0.2",
"libc", "libc",
"libsqlite3-sys",
"log", "log",
"md-5 0.10.1",
"memchr", "memchr",
"once_cell", "once_cell",
"paste", "paste",
"percent-encoding", "percent-encoding",
"rand 0.8.5",
"rustls", "rustls",
"rustls-pemfile", "rustls-pemfile",
"serde", "serde",
"serde_json",
"sha-1",
"sha2 0.10.2", "sha2 0.10.2",
"smallvec", "smallvec",
"sqlformat", "sqlformat",
@@ -3810,6 +3821,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"url", "url",
"webpki-roots", "webpki-roots",
"whoami",
] ]
[[package]] [[package]]
@@ -4356,7 +4368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"dirs", "dirs 1.0.5",
"winapi", "winapi",
] ]
@@ -5167,6 +5179,16 @@ dependencies = [
"libc", "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]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View File

@@ -123,7 +123,7 @@ sqlx = { version = "0.6.0", features = [
"chrono", "chrono",
"offline", "offline",
"runtime-tokio-rustls", "runtime-tokio-rustls",
"sqlite", "postgres",
] } ] }
stderrlog = "0.5.3" stderrlog = "0.5.3"
tar = "0.4.38" tar = "0.4.38"
@@ -142,8 +142,11 @@ trust-dns-server = "0.21.2"
typed-builder = "0.10.0" typed-builder = "0.10.0"
url = { version = "2.2.2", features = ["serde"] } url = { version = "2.2.2", features = ["serde"] }
[profile.test]
opt-level = 3
[profile.dev.package.backtrace] [profile.dev.package.backtrace]
opt-level = 3 opt-level = 3
[profile.test] [profile.dev.package.sqlx-macros]
opt-level = 3 opt-level = 3

View File

@@ -1,45 +1,47 @@
-- Add migration script here -- Add migration script here
CREATE TABLE IF NOT EXISTS tor CREATE TABLE IF NOT EXISTS tor (
( package TEXT NOT NULL,
package TEXT NOT NULL, interface TEXT NOT NULL,
interface TEXT NOT NULL, key BYTEA NOT NULL CHECK (length(key) = 64),
key BLOB NOT NULL CHECK (length(key) = 64),
PRIMARY KEY (package, interface) PRIMARY KEY (package, interface)
); );
CREATE TABLE IF NOT EXISTS session
( CREATE TABLE IF NOT EXISTS session (
id TEXT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
logged_in TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, logged_in TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
logged_out TIMESTAMP, logged_out TIMESTAMP,
last_active TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, last_active TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
user_agent TEXT, user_agent TEXT,
metadata TEXT NOT NULL DEFAULT 'null' metadata TEXT NOT NULL DEFAULT 'null'
); );
CREATE TABLE IF NOT EXISTS account
( CREATE TABLE IF NOT EXISTS account (
id INTEGER PRIMARY KEY CHECK (id = 0), id SERIAL PRIMARY KEY CHECK (id = 0),
password TEXT NOT NULL, 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, fingerprint TEXT NOT NULL,
openssh_pubkey TEXT NOT NULL, openssh_pubkey TEXT NOT NULL,
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
PRIMARY KEY (fingerprint) PRIMARY KEY (fingerprint)
); );
CREATE TABLE IF NOT EXISTS certificates
( CREATE TABLE IF NOT EXISTS certificates (
id INTEGER PRIMARY KEY, -- Root = 0, Int = 1, Other = 2.. id SERIAL PRIMARY KEY,
-- Root = 0, Int = 1, Other = 2..
priv_key_pem TEXT NOT NULL, priv_key_pem TEXT NOT NULL,
certificate_pem TEXT NOT NULL, certificate_pem TEXT NOT NULL,
lookup_string TEXT UNIQUE, lookup_string TEXT UNIQUE,
created_at TEXT, created_at TEXT,
updated_at TEXT updated_at TEXT
); );
CREATE TABLE IF NOT EXISTS notifications
( ALTER SEQUENCE certificates_id_seq START 2 RESTART 2;
id INTEGER PRIMARY KEY,
CREATE TABLE IF NOT EXISTS notifications (
id SERIAL PRIMARY KEY,
package_id TEXT, package_id TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
code INTEGER NOT NULL, code INTEGER NOT NULL,
@@ -48,9 +50,9 @@ CREATE TABLE IF NOT EXISTS notifications
message TEXT NOT NULL, message TEXT NOT NULL,
data TEXT data TEXT
); );
CREATE TABLE IF NOT EXISTS cifs_shares
( CREATE TABLE IF NOT EXISTS cifs_shares (
id INTEGER PRIMARY KEY, id SERIAL PRIMARY KEY,
hostname TEXT NOT NULL, hostname TEXT NOT NULL,
path TEXT NOT NULL, path TEXT NOT NULL,
username TEXT NOT NULL, username TEXT NOT NULL,

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ use rpc_toolkit::command_helpers::prelude::{RequestParts, ResponseParts};
use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::yajrc::RpcError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tracing::instrument; use tracing::instrument;
use crate::context::{CliContext, RpcContext}; 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> pub async fn check_password_against_db<Ex>(secrets: &mut Ex, password: &str) -> Result<(), Error>
where 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") let pw_hash = sqlx::query!("SELECT password FROM account")
.fetch_one(secrets) .fetch_one(secrets)
@@ -124,7 +124,7 @@ pub async fn login(
let metadata = serde_json::to_string(&metadata).with_kind(crate::ErrorKind::Database)?; let metadata = serde_json::to_string(&metadata).with_kind(crate::ErrorKind::Database)?;
let hash_token_hashed = hash_token.hashed(); let hash_token_hashed = hash_token.hashed();
sqlx::query!( sqlx::query!(
"INSERT INTO session (id, user_agent, metadata) VALUES (?, ?, ?)", "INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)",
hash_token_hashed, hash_token_hashed,
user_agent, user_agent,
metadata, metadata,
@@ -328,7 +328,7 @@ pub async fn set_password<Db: DbHandle, Ex>(
password: &str, password: &str,
) -> Result<(), Error> ) -> Result<(), Error>
where where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
let password = argon2::hash_encoded( let password = argon2::hash_encoded(
password.as_bytes(), password.as_bytes(),
@@ -337,7 +337,7 @@ where
) )
.with_kind(crate::ErrorKind::PasswordHashGeneration)?; .with_kind(crate::ErrorKind::PasswordHashGeneration)?;
sqlx::query!("UPDATE account SET password = ?", password,) sqlx::query!("UPDATE account SET password = $1", password,)
.execute(secrets) .execute(secrets)
.await?; .await?;

View File

@@ -8,7 +8,7 @@ use patch_db::{DbHandle, HasModel, LockType};
use reqwest::Url; use reqwest::Url;
use rpc_toolkit::command; use rpc_toolkit::command;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tokio::fs::File; use tokio::fs::File;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tracing::instrument; use tracing::instrument;
@@ -195,7 +195,7 @@ impl BackupActions {
volumes: &Volumes, volumes: &Volumes,
) -> Result<(), Error> ) -> Result<(), Error>
where where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
let mut volumes = volumes.clone(); let mut volumes = volumes.clone();
volumes.insert(VolumeId::Backup, Volume::Backup { readonly: true }); volumes.insert(VolumeId::Backup, Volume::Backup { readonly: true });
@@ -231,7 +231,7 @@ impl BackupActions {
) )
})?; })?;
sqlx::query!( 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, **pkg_id,
*iface, *iface,
key_vec, key_vec,

View File

@@ -221,7 +221,7 @@ pub async fn recover_full_embassy(
let key_vec = os_backup.tor_key.as_bytes().to_vec(); let key_vec = os_backup.tor_key.as_bytes().to_vec();
let secret_store = ctx.secret_store().await?; let secret_store = ctx.secret_store().await?;
sqlx::query!( 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, 0,
password, password,
key_vec, key_vec,

View File

@@ -4,7 +4,7 @@ use color_eyre::eyre::eyre;
use futures::TryStreamExt; use futures::TryStreamExt;
use rpc_toolkit::command; use rpc_toolkit::command;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use super::{BackupTarget, BackupTargetId}; use super::{BackupTarget, BackupTargetId};
use crate::context::RpcContext; use crate::context::RpcContext;
@@ -49,8 +49,8 @@ pub async fn add(
let embassy_os = recovery_info(&guard).await?; let embassy_os = recovery_info(&guard).await?;
guard.unmount().await?; guard.unmount().await?;
let path_string = Path::new("/").join(&cifs.path).display().to_string(); let path_string = Path::new("/").join(&cifs.path).display().to_string();
let id: u32 = sqlx::query!( let id: i32 = sqlx::query!(
"INSERT INTO cifs_shares (hostname, path, username, password) VALUES (?, ?, ?, ?) RETURNING id AS \"id: u32\"", "INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id",
cifs.hostname, cifs.hostname,
path_string, path_string,
cifs.username, cifs.username,
@@ -98,7 +98,7 @@ pub async fn update(
guard.unmount().await?; guard.unmount().await?;
let path_string = Path::new("/").join(&cifs.path).display().to_string(); let path_string = Path::new("/").join(&cifs.path).display().to_string();
if sqlx::query!( 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, cifs.hostname,
path_string, path_string,
cifs.username, cifs.username,
@@ -137,7 +137,7 @@ pub async fn remove(#[context] ctx: RpcContext, #[arg] id: BackupTargetId) -> Re
crate::ErrorKind::NotFound, 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) .execute(&ctx.secret_store)
.await? .await?
.rows_affected() .rows_affected()
@@ -151,12 +151,12 @@ pub async fn remove(#[context] ctx: RpcContext, #[arg] id: BackupTargetId) -> Re
Ok(()) 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 where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
let record = sqlx::query!( 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 id
) )
.fetch_one(secrets) .fetch_one(secrets)
@@ -170,14 +170,13 @@ 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 where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
let mut records = sqlx::query!( let mut records =
"SELECT id AS \"id: u32\", hostname, path, username, password FROM cifs_shares" sqlx::query!("SELECT id, hostname, path, username, password FROM cifs_shares")
) .fetch_many(secrets);
.fetch_many(secrets);
let mut cifs = Vec::new(); let mut cifs = Vec::new();
while let Some(query_result) = records.try_next().await? { while let Some(query_result) = records.try_next().await? {

View File

@@ -10,7 +10,7 @@ use digest::OutputSizeUser;
use rpc_toolkit::command; use rpc_toolkit::command;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha256; use sha2::Sha256;
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tracing::instrument; use tracing::instrument;
use self::cifs::CifsBackupTarget; use self::cifs::CifsBackupTarget;
@@ -45,12 +45,12 @@ pub enum BackupTarget {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum BackupTargetId { pub enum BackupTargetId {
Disk { logicalname: PathBuf }, Disk { logicalname: PathBuf },
Cifs { id: u32 }, Cifs { id: i32 },
} }
impl BackupTargetId { impl BackupTargetId {
pub async fn load<Ex>(self, secrets: &mut Ex) -> Result<BackupTargetFS, Error> pub async fn load<Ex>(self, secrets: &mut Ex) -> Result<BackupTargetFS, Error>
where where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
Ok(match self { Ok(match self {
BackupTargetId::Disk { logicalname } => { BackupTargetId::Disk { logicalname } => {

View File

@@ -18,7 +18,7 @@ use regex::Regex;
use serde::de::{MapAccess, Visitor}; use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{Number, Value}; use serde_json::{Number, Value};
use sqlx::SqlitePool; use sqlx::PgPool;
use super::util::{self, CharSet, NumRange, UniqueBy, STATIC_NULL}; use super::util::{self, CharSet, NumRange, UniqueBy, STATIC_NULL};
use super::{Config, MatchError, NoMatchWithPath, TimeoutError, TypeOf}; use super::{Config, MatchError, NoMatchWithPath, TimeoutError, TypeOf};
@@ -2050,7 +2050,7 @@ impl TorKeyPointer {
async fn deref( async fn deref(
&self, &self,
source_package: &PackageId, source_package: &PackageId,
secrets: &SqlitePool, secrets: &PgPool,
) -> Result<Value, ConfigurationError> { ) -> Result<Value, ConfigurationError> {
if &self.package_id != source_package { if &self.package_id != source_package {
return Err(ConfigurationError::PermissionDenied( return Err(ConfigurationError::PermissionDenied(
@@ -2058,7 +2058,7 @@ impl TorKeyPointer {
)); ));
} }
let x = sqlx::query!( 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.package_id,
*self.interface *self.interface
) )

View File

@@ -4,7 +4,6 @@ use std::ops::Deref;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use bollard::Docker; use bollard::Docker;
use helpers::to_tmp_path; use helpers::to_tmp_path;
@@ -14,8 +13,8 @@ use reqwest::Url;
use rpc_toolkit::url::Host; use rpc_toolkit::url::Host;
use rpc_toolkit::Context; use rpc_toolkit::Context;
use serde::Deserialize; use serde::Deserialize;
use sqlx::sqlite::SqliteConnectOptions; use sqlx::postgres::PgConnectOptions;
use sqlx::SqlitePool; use sqlx::PgPool;
use tokio::fs::File; use tokio::fs::File;
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::{broadcast, oneshot, Mutex, RwLock}; use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
@@ -24,6 +23,7 @@ use tracing::instrument;
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation}; use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation};
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry}; use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
use crate::hostname::{derive_hostname, derive_id, get_product_key}; 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::install::cleanup::{cleanup_failed, uninstall, CleanupFailedReceipts};
use crate::manager::ManagerMap; use crate::manager::ManagerMap;
use crate::middleware::auth::HashSessionToken; use crate::middleware::auth::HashSessionToken;
@@ -71,7 +71,7 @@ impl RpcContextConfig {
.as_deref() .as_deref()
.unwrap_or_else(|| Path::new("/embassy-data")) .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 sid = derive_id(product_key);
let hostname = derive_hostname(&sid); let hostname = derive_hostname(&sid);
let db_path = self.datadir().join("main").join("embassy.db"); let db_path = self.datadir().join("main").join("embassy.db");
@@ -94,18 +94,19 @@ impl RpcContextConfig {
Ok(db) Ok(db)
} }
#[instrument] #[instrument]
pub async fn secret_store(&self) -> Result<SqlitePool, Error> { pub async fn secret_store(&self) -> Result<PgPool, Error> {
let secret_store = SqlitePool::connect_with( init_postgres(self.datadir()).await?;
SqliteConnectOptions::new() let secret_store =
.filename(self.datadir().join("main").join("secrets.db")) PgPool::connect_with(PgConnectOptions::new().database("secrets").username("root"))
.create_if_missing(true) .await?;
.busy_timeout(Duration::from_secs(30)),
)
.await?;
sqlx::migrate!() sqlx::migrate!()
.run(&secret_store) .run(&secret_store)
.await .await
.with_kind(crate::ErrorKind::Database)?; .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) Ok(secret_store)
} }
} }
@@ -118,7 +119,7 @@ pub struct RpcContextSeed {
pub datadir: PathBuf, pub datadir: PathBuf,
pub disk_guid: Arc<String>, pub disk_guid: Arc<String>,
pub db: PatchDb, pub db: PatchDb,
pub secret_store: SqlitePool, pub secret_store: PgPool,
pub docker: Docker, pub docker: Docker,
pub net_controller: NetController, pub net_controller: NetController,
pub managers: ManagerMap, pub managers: ManagerMap,
@@ -214,7 +215,7 @@ impl RpcContext {
))); )));
let (shutdown, _) = tokio::sync::broadcast::channel(1); let (shutdown, _) = tokio::sync::broadcast::channel(1);
let secret_store = base.secret_store().await?; 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?; let db = base.db(&secret_store, &get_product_key().await?).await?;
tracing::info!("Opened PatchDB"); tracing::info!("Opened PatchDB");
let docker = Docker::connect_with_unix_defaults()?; let docker = Docker::connect_with_unix_defaults()?;

View File

@@ -9,9 +9,10 @@ use patch_db::PatchDb;
use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::Context; use rpc_toolkit::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::sqlite::SqliteConnectOptions; use sqlx::postgres::PgConnectOptions;
use sqlx::SqlitePool; use sqlx::PgPool;
use tokio::fs::File; use tokio::fs::File;
use tokio::process::Command;
use tokio::sync::broadcast::Sender; use tokio::sync::broadcast::Sender;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::instrument; use tracing::instrument;
@@ -19,10 +20,11 @@ use url::Host;
use crate::db::model::Database; use crate::db::model::Database;
use crate::hostname::{derive_hostname, derive_id, get_product_key}; use crate::hostname::{derive_hostname, derive_id, get_product_key};
use crate::init::{init_postgres, pgloader};
use crate::net::tor::os_key; use crate::net::tor::os_key;
use crate::setup::{password_hash, RecoveryStatus}; use crate::setup::{password_hash, RecoveryStatus};
use crate::util::io::from_yaml_async_reader; use crate::util::io::from_yaml_async_reader;
use crate::util::AsyncFileExt; use crate::util::{AsyncFileExt, Invoke};
use crate::{Error, ResultExt}; use crate::{Error, ResultExt};
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
@@ -93,7 +95,7 @@ impl SetupContext {
}))) })))
} }
#[instrument(skip(self))] #[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_path = self.datadir.join("main").join("embassy.db");
let db = PatchDb::open(&db_path) let db = PatchDb::open(&db_path)
.await .await
@@ -117,18 +119,19 @@ impl SetupContext {
Ok(db) Ok(db)
} }
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn secret_store(&self) -> Result<SqlitePool, Error> { pub async fn secret_store(&self) -> Result<PgPool, Error> {
let secret_store = SqlitePool::connect_with( init_postgres(&self.datadir).await?;
SqliteConnectOptions::new() let secret_store =
.filename(self.datadir.join("main").join("secrets.db")) PgPool::connect_with(PgConnectOptions::new().database("secrets").username("root"))
.create_if_missing(true) .await?;
.busy_timeout(Duration::from_secs(30)),
)
.await?;
sqlx::migrate!() sqlx::migrate!()
.run(&secret_store) .run(&secret_store)
.await .await
.with_kind(crate::ErrorKind::Database)?; .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) Ok(secret_store)
} }
#[instrument(skip(self))] #[instrument(skip(self))]

View File

@@ -48,13 +48,15 @@ pub async fn unmount<P: AsRef<Path>>(mountpoint: P) -> Result<(), Error> {
.arg(mountpoint.as_ref()) .arg(mountpoint.as_ref())
.invoke(crate::ErrorKind::Filesystem) .invoke(crate::ErrorKind::Filesystem)
.await?; .await?;
tokio::fs::remove_dir_all(mountpoint.as_ref()) match tokio::fs::remove_dir(mountpoint.as_ref()).await {
.await Err(e) if e.raw_os_error() == Some(39) => Ok(()), // directory not empty
.with_ctx(|_| { a => a,
( }
crate::ErrorKind::Filesystem, .with_ctx(|_| {
format!("rm {}", mountpoint.as_ref().display()), (
) crate::ErrorKind::Filesystem,
})?; format!("rm {}", mountpoint.as_ref().display()),
)
})?;
Ok(()) Ok(())
} }

View File

@@ -1,10 +1,14 @@
use std::path::Path;
use std::process::Stdio;
use std::time::Duration; use std::time::Duration;
use color_eyre::eyre::eyre;
use patch_db::{DbHandle, LockReceipt, LockType}; use patch_db::{DbHandle, LockReceipt, LockType};
use tokio::process::Command; use tokio::process::Command;
use crate::context::rpc::RpcContextConfig; use crate::context::rpc::RpcContextConfig;
use crate::db::model::ServerStatus; use crate::db::model::ServerStatus;
use crate::disk::mount::util::unmount;
use crate::install::PKG_DOCKER_DIR; use crate::install::PKG_DOCKER_DIR;
use crate::util::Invoke; use crate::util::Invoke;
use crate::Error; 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> { pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error> {
let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok(); let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok();
let secret_store = cfg.secret_store().await?; 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() { if tokio::fs::metadata(&log_dir).await.is_err() {
tokio::fs::create_dir_all(&log_dir).await?; 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?; tokio::fs::remove_dir_all(&tmp_docker).await?;
} }
Command::new("cp") Command::new("cp")
.arg("-r") .arg("-ra")
.arg("/var/lib/docker") .arg("/var/lib/docker")
.arg(&tmp_docker) .arg(&tmp_docker)
.invoke(crate::ErrorKind::Filesystem) .invoke(crate::ErrorKind::Filesystem)

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use bollard::image::ListImagesOptions; use bollard::image::ListImagesOptions;
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier}; use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tracing::instrument; use tracing::instrument;
use super::{PKG_ARCHIVE_DIR, PKG_DOCKER_DIR}; use super::{PKG_ARCHIVE_DIR, PKG_DOCKER_DIR};
@@ -337,7 +337,7 @@ pub async fn uninstall<Ex>(
id: &PackageId, id: &PackageId,
) -> Result<(), Error> ) -> Result<(), Error>
where 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 mut tx = db.begin().await?;
let receipts = UninstallReceipts::new(&mut tx, id).await?; let receipts = UninstallReceipts::new(&mut tx, id).await?;
@@ -383,10 +383,10 @@ where
#[instrument(skip(secrets))] #[instrument(skip(secrets))]
pub async fn remove_tor_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error> pub async fn remove_tor_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
where 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(); 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) .execute(secrets)
.await?; .await?;
Ok(()) Ok(())

View File

@@ -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 BUFFER_SIZE: usize = 1024;
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1]; pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
pub const TARGET: &str = current_platform::CURRENT_PLATFORM; pub const TARGET: &str = current_platform::CURRENT_PLATFORM;

View File

@@ -12,7 +12,7 @@ use color_eyre::eyre::eyre;
use nix::sys::signal::Signal; use nix::sys::signal::Signal;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use patch_db::DbHandle; use patch_db::DbHandle;
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tokio::sync::watch::error::RecvError; use tokio::sync::watch::error::RecvError;
use tokio::sync::watch::{channel, Receiver, Sender}; use tokio::sync::watch::{channel, Receiver, Sender};
use tokio::sync::{Notify, RwLock}; use tokio::sync::{Notify, RwLock};
@@ -47,7 +47,7 @@ impl ManagerMap {
secrets: &mut Ex, secrets: &mut Ex,
) -> Result<(), Error> ) -> Result<(), Error>
where where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
let mut res = BTreeMap::new(); let mut res = BTreeMap::new();
for package in crate::db::DatabaseModel::new() for package in crate::db::DatabaseModel::new()

View File

@@ -41,7 +41,7 @@ impl HasLoggedOutSessions {
for session in logged_out_sessions { for session in logged_out_sessions {
let session = session.as_logout_session_id(); let session = session.as_logout_session_id();
sqlx::query!( sqlx::query!(
"UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = ?", "UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1",
session session
) )
.execute(&mut sqlx_conn) .execute(&mut sqlx_conn)
@@ -68,7 +68,7 @@ impl HasValidSession {
pub async fn from_session(session: &HashSessionToken, ctx: &RpcContext) -> Result<Self, Error> { pub async fn from_session(session: &HashSessionToken, ctx: &RpcContext) -> Result<Self, Error> {
let session_hash = session.hashed(); 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?) .execute(&mut ctx.secret_store.acquire().await?)
.await?; .await?;
if session.rows_affected() == 0 { if session.rows_affected() == 0 {

7
backend/src/migrate.load Normal file
View 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';

View File

@@ -6,7 +6,7 @@ use indexmap::IndexSet;
use itertools::Either; use itertools::Either;
pub use models::InterfaceId; pub use models::InterfaceId;
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use torut::onion::TorSecretKeyV3; use torut::onion::TorSecretKeyV3;
use tracing::instrument; use tracing::instrument;
@@ -39,7 +39,7 @@ impl Interfaces {
package_id: &PackageId, package_id: &PackageId,
) -> Result<InterfaceAddressMap, Error> ) -> Result<InterfaceAddressMap, Error>
where 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()); let mut interface_addresses = InterfaceAddressMap(BTreeMap::new());
for (id, iface) in &self.0 { for (id, iface) in &self.0 {
@@ -51,7 +51,7 @@ impl Interfaces {
let key = TorSecretKeyV3::generate(); let key = TorSecretKeyV3::generate();
let key_vec = key.as_bytes().to_vec(); let key_vec = key.as_bytes().to_vec();
sqlx::query!( 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, **package_id,
**id, **id,
key_vec, key_vec,
@@ -59,7 +59,7 @@ impl Interfaces {
.execute(&mut *secrets) .execute(&mut *secrets)
.await?; .await?;
let key_row = sqlx::query!( 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, **package_id,
**id, **id,
) )
@@ -89,10 +89,10 @@ impl Interfaces {
package_id: &PackageId, package_id: &PackageId,
) -> Result<BTreeMap<InterfaceId, TorSecretKeyV3>, Error> ) -> Result<BTreeMap<InterfaceId, TorSecretKeyV3>, Error>
where where
for<'a> &'a mut Ex: Executor<'a, Database = Sqlite>, for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
{ {
Ok(sqlx::query!( Ok(sqlx::query!(
"SELECT interface, key FROM tor WHERE package = ?", "SELECT interface, key FROM tor WHERE package = $1",
**package_id **package_id
) )
.fetch_many(secrets) .fetch_many(secrets)

View File

@@ -4,7 +4,7 @@ use std::path::PathBuf;
use openssl::pkey::{PKey, Private}; use openssl::pkey::{PKey, Private};
use openssl::x509::X509; use openssl::x509::X509;
use rpc_toolkit::command; use rpc_toolkit::command;
use sqlx::SqlitePool; use sqlx::PgPool;
use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use torut::onion::{OnionAddressV3, TorSecretKeyV3};
use tracing::instrument; use tracing::instrument;
@@ -56,7 +56,7 @@ impl NetController {
embassyd_tor_key: TorSecretKeyV3, embassyd_tor_key: TorSecretKeyV3,
tor_control: SocketAddr, tor_control: SocketAddr,
dns_bind: &[SocketAddr], dns_bind: &[SocketAddr],
db: SqlitePool, db: PgPool,
import_root_ca: Option<(PKey<Private>, X509)>, import_root_ca: Option<(PKey<Private>, X509)>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let ssl = match import_root_ca { let ssl = match import_root_ca {

View File

@@ -11,7 +11,7 @@ use openssl::nid::Nid;
use openssl::pkey::{PKey, Private}; use openssl::pkey::{PKey, Private};
use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509}; use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509};
use openssl::*; use openssl::*;
use sqlx::SqlitePool; use sqlx::PgPool;
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::instrument; use tracing::instrument;
@@ -33,17 +33,17 @@ pub struct SslManager {
#[derive(Debug)] #[derive(Debug)]
struct SslStore { struct SslStore {
secret_store: SqlitePool, secret_store: PgPool,
} }
impl SslStore { impl SslStore {
fn new(db: SqlitePool) -> Result<Self, Error> { fn new(db: PgPool) -> Result<Self, Error> {
Ok(SslStore { secret_store: db }) Ok(SslStore { secret_store: db })
} }
#[instrument(skip(self))] #[instrument(skip(self))]
async fn save_root_certificate(&self, key: &PKey<Private>, cert: &X509) -> Result<(), Error> { 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 key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
let cert_str = String::from_utf8(cert.to_pem()?)?; 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(()) Ok(())
} }
#[instrument(skip(self))] #[instrument(skip(self))]
@@ -69,7 +69,7 @@ impl SslStore {
) -> Result<(), Error> { ) -> Result<(), Error> {
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
let cert_str = String::from_utf8(cert.to_pem()?)?; 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(()) Ok(())
} }
async fn load_intermediate_certificate(&self) -> Result<Option<(PKey<Private>, X509)>, Error> { async fn load_intermediate_certificate(&self) -> Result<Option<(PKey<Private>, X509)>, Error> {
@@ -108,7 +108,7 @@ impl SslStore {
) -> Result<(), Error> { ) -> Result<(), Error> {
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
let cert_str = String::from_utf8(cert.to_pem()?)?; 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(()) Ok(())
} }
async fn load_certificate( async fn load_certificate(
@@ -116,7 +116,7 @@ impl SslStore {
lookup_string: &str, lookup_string: &str,
) -> Result<Option<(PKey<Private>, X509)>, Error> { ) -> Result<Option<(PKey<Private>, X509)>, Error> {
let m_row = sqlx::query!( 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 lookup_string
) )
.fetch_optional(&self.secret_store) .fetch_optional(&self.secret_store)
@@ -139,7 +139,8 @@ impl SslStore {
) -> Result<(), Error> { ) -> Result<(), Error> {
let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?;
let cert_str = String::from_utf8(cert.to_pem()?)?; 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 { if n.rows_affected() == 0 {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
@@ -161,7 +162,7 @@ lazy_static::lazy_static! {
impl SslManager { impl SslManager {
#[instrument(skip(db))] #[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 store = SslStore::new(db)?;
let (root_key, root_cert) = match store.load_root_certificate().await? { let (root_key, root_cert) = match store.load_root_certificate().await? {
None => { None => {
@@ -204,6 +205,10 @@ impl SslManager {
} }
Some((key, cert)) => Ok((key, cert)), 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 { Ok(SslManager {
store, store,
root_cert, root_cert,
@@ -221,7 +226,7 @@ impl SslManager {
// the intermediate certificate // the intermediate certificate
#[instrument(skip(db))] #[instrument(skip(db))]
pub async fn import_root_ca( pub async fn import_root_ca(
db: SqlitePool, db: PgPool,
root_key: PKey<Private>, root_key: PKey<Private>,
root_cert: X509, root_cert: X509,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
@@ -508,10 +513,11 @@ fn make_leaf_cert(
#[tokio::test] #[tokio::test]
async fn ca_details_persist() -> Result<(), Error> { async fn ca_details_persist() -> Result<(), Error> {
let pool = sqlx::Pool::<sqlx::Sqlite>::connect("sqlite::memory:").await?; let pool = sqlx::Pool::<sqlx::Postgres>::connect("postgres::memory:").await?;
sqlx::query_file!("migrations/20210629193146_Init.sql") sqlx::migrate!()
.execute(&pool) .run(&pool)
.await?; .await
.with_kind(crate::ErrorKind::Database)?;
let mgr = SslManager::init(pool.clone()).await?; let mgr = SslManager::init(pool.clone()).await?;
let root_cert0 = mgr.root_cert; let root_cert0 = mgr.root_cert;
let int_key0 = mgr.int_key; let int_key0 = mgr.int_key;
@@ -532,10 +538,11 @@ async fn ca_details_persist() -> Result<(), Error> {
#[tokio::test] #[tokio::test]
async fn certificate_details_persist() -> Result<(), Error> { async fn certificate_details_persist() -> Result<(), Error> {
let pool = sqlx::Pool::<sqlx::Sqlite>::connect("sqlite::memory:").await?; let pool = sqlx::Pool::<sqlx::Postgres>::connect("postgres::memory:").await?;
sqlx::query_file!("migrations/20210629193146_Init.sql") sqlx::migrate!()
.execute(&pool) .run(&pool)
.await?; .await
.with_kind(crate::ErrorKind::Database)?;
let mgr = SslManager::init(pool.clone()).await?; let mgr = SslManager::init(pool.clone()).await?;
let package_id = "bitcoind".parse().unwrap(); let package_id = "bitcoind".parse().unwrap();
let (key0, cert_chain0) = mgr.certificate_for("start9", &package_id).await?; let (key0, cert_chain0) = mgr.certificate_for("start9", &package_id).await?;

View File

@@ -9,7 +9,7 @@ use futures::FutureExt;
use reqwest::Client; use reqwest::Client;
use rpc_toolkit::command; use rpc_toolkit::command;
use serde_json::json; use serde_json::json;
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Postgres};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use torut::control::{AsyncEvent, AuthenticatedConn, ConnError}; use torut::control::{AsyncEvent, AuthenticatedConn, ConnError};
@@ -60,7 +60,7 @@ pub async fn list_services(
#[instrument(skip(secrets))] #[instrument(skip(secrets))]
pub async fn os_key<Ex>(secrets: &mut Ex) -> Result<TorSecretKeyV3, Error> pub async fn os_key<Ex>(secrets: &mut Ex) -> Result<TorSecretKeyV3, Error>
where 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") let key = sqlx::query!("SELECT tor_key FROM account")
.fetch_one(secrets) .fetch_one(secrets)

View File

@@ -6,7 +6,7 @@ use chrono::{DateTime, Utc};
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use patch_db::{DbHandle, LockType}; use patch_db::{DbHandle, LockType};
use rpc_toolkit::command; use rpc_toolkit::command;
use sqlx::SqlitePool; use sqlx::PgPool;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::instrument; use tracing::instrument;
@@ -27,7 +27,7 @@ pub async fn notification() -> Result<(), Error> {
#[instrument(skip(ctx))] #[instrument(skip(ctx))]
pub async fn list( pub async fn list(
#[context] ctx: RpcContext, #[context] ctx: RpcContext,
#[arg] before: Option<u32>, #[arg] before: Option<i32>,
#[arg] limit: Option<u32>, #[arg] limit: Option<u32>,
) -> Result<WithRevision<Vec<Notification>>, Error> { ) -> Result<WithRevision<Vec<Notification>>, Error> {
let limit = limit.unwrap_or(40); let limit = limit.unwrap_or(40);
@@ -39,8 +39,8 @@ pub async fn list(
.unread_notification_count(); .unread_notification_count();
model.lock(&mut handle, LockType::Write).await?; model.lock(&mut handle, LockType::Write).await?;
let records = sqlx::query!( let records = sqlx::query!(
"SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT ?", "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT $1",
limit limit as i64
).fetch_all(&ctx.secret_store).await?; ).fetch_all(&ctx.secret_store).await?;
let notifs = records let notifs = records
.into_iter() .into_iter()
@@ -80,9 +80,9 @@ pub async fn list(
} }
Some(before) => { Some(before) => {
let records = sqlx::query!( 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, before,
limit limit as i64
).fetch_all(&ctx.secret_store).await?; ).fetch_all(&ctx.secret_store).await?;
let res = records let res = records
.into_iter() .into_iter()
@@ -122,16 +122,16 @@ pub async fn list(
} }
#[command(display(display_none))] #[command(display(display_none))]
pub async fn delete(#[context] ctx: RpcContext, #[arg] id: u32) -> Result<(), Error> { pub async fn delete(#[context] ctx: RpcContext, #[arg] id: i32) -> Result<(), Error> {
sqlx::query!("DELETE FROM notifications WHERE id = ?", id) sqlx::query!("DELETE FROM notifications WHERE id = $1", id)
.execute(&ctx.secret_store) .execute(&ctx.secret_store)
.await?; .await?;
Ok(()) Ok(())
} }
#[command(rename = "delete-before", display(display_none))] #[command(rename = "delete-before", display(display_none))]
pub async fn delete_before(#[context] ctx: RpcContext, #[arg] before: u32) -> Result<(), Error> { pub async fn delete_before(#[context] ctx: RpcContext, #[arg] before: i32) -> Result<(), Error> {
sqlx::query!("DELETE FROM notifications WHERE id < ?", before) sqlx::query!("DELETE FROM notifications WHERE id < $1", before)
.execute(&ctx.secret_store) .execute(&ctx.secret_store)
.await?; .await?;
Ok(()) Ok(())
@@ -218,22 +218,22 @@ pub struct Notification {
pub trait NotificationType: pub trait NotificationType:
serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug
{ {
const CODE: u32; const CODE: i32;
} }
impl NotificationType for () { impl NotificationType for () {
const CODE: u32 = 0; const CODE: i32 = 0;
} }
impl NotificationType for BackupReport { impl NotificationType for BackupReport {
const CODE: u32 = 1; const CODE: i32 = 1;
} }
pub struct NotificationManager { pub struct NotificationManager {
sqlite: SqlitePool, sqlite: PgPool,
cache: Mutex<HashMap<(Option<PackageId>, NotificationLevel, String), i64>>, cache: Mutex<HashMap<(Option<PackageId>, NotificationLevel, String), i64>>,
} }
impl NotificationManager { impl NotificationManager {
pub fn new(sqlite: SqlitePool) -> Self { pub fn new(sqlite: PgPool) -> Self {
NotificationManager { NotificationManager {
sqlite, sqlite,
cache: Mutex::new(HashMap::new()), cache: Mutex::new(HashMap::new()),
@@ -267,9 +267,9 @@ impl NotificationManager {
let sql_data = let sql_data =
serde_json::to_string(&subtype).with_kind(crate::ErrorKind::Serialization)?; serde_json::to_string(&subtype).with_kind(crate::ErrorKind::Serialization)?;
sqlx::query!( 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_package_id,
sql_code, sql_code as i32,
sql_level, sql_level,
title, title,
message, message,

View File

@@ -17,7 +17,7 @@ use rpc_toolkit::command;
use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::yajrc::RpcError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use sqlx::{Connection, Executor, Sqlite}; use sqlx::{Connection, Executor, Postgres};
use tokio::fs::File; use tokio::fs::File;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use torut::onion::{OnionAddressV3, TorSecretKeyV3};
@@ -52,7 +52,7 @@ use crate::{ensure_code, Error, ErrorKind, ResultExt};
#[instrument(skip(secrets))] #[instrument(skip(secrets))]
pub async fn password_hash<Ex>(secrets: &mut Ex) -> Result<String, Error> pub async fn password_hash<Ex>(secrets: &mut Ex) -> Result<String, Error>
where 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") let password = sqlx::query!("SELECT password FROM account")
.fetch_one(secrets) .fetch_one(secrets)
@@ -448,7 +448,7 @@ async fn fresh_setup(
let key_vec = tor_key.as_bytes().to_vec(); let key_vec = tor_key.as_bytes().to_vec();
let sqlite_pool = ctx.secret_store().await?; let sqlite_pool = ctx.secret_store().await?;
sqlx::query!( 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, 0,
password, password,
key_vec, key_vec,
@@ -696,7 +696,7 @@ async fn recover_v2(
.with_kind(crate::ErrorKind::PasswordHashGeneration)?; .with_kind(crate::ErrorKind::PasswordHashGeneration)?;
let sqlite_pool = ctx.secret_store().await?; let sqlite_pool = ctx.secret_store().await?;
sqlx::query!( 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, 0,
password, password,
key_vec key_vec
@@ -790,7 +790,7 @@ async fn recover_v2(
); );
let key_vec = key_vec[32..].to_vec(); let key_vec = key_vec[32..].to_vec();
sqlx::query!( 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, *dst_id,
key_vec, key_vec,
) )

View File

@@ -4,7 +4,7 @@ use chrono::Utc;
use clap::ArgMatches; use clap::ArgMatches;
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use rpc_toolkit::command; use rpc_toolkit::command;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Postgres};
use tracing::instrument; use tracing::instrument;
use crate::context::RpcContext; 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; let pool = &ctx.secret_store;
// check fingerprint for duplicates // check fingerprint for duplicates
let fp = key.0.fingerprint_md5(); 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) .fetch_optional(pool)
.await? .await?
{ {
@@ -70,7 +70,7 @@ pub async fn add(#[context] ctx: RpcContext, #[arg] key: PubKey) -> Result<SshKe
let raw_key = format!("{}", key.0); let raw_key = format!("{}", key.0);
let created_at = Utc::now().to_rfc3339(); let created_at = Utc::now().to_rfc3339();
sqlx::query!( 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, fp,
raw_key, raw_key,
created_at created_at
@@ -96,7 +96,7 @@ pub async fn delete(#[context] ctx: RpcContext, #[arg] fingerprint: String) -> R
let pool = &ctx.secret_store; let pool = &ctx.secret_store;
// check if fingerprint is in DB // check if fingerprint is in DB
// if in DB, remove it from 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) .execute(pool)
.await? .await?
.rows_affected(); .rows_affected();
@@ -172,7 +172,10 @@ pub async fn list(
} }
#[instrument(skip(pool, dest))] #[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 dest = dest.as_ref();
let keys = sqlx::query!("SELECT openssh_pubkey FROM ssh_keys") let keys = sqlx::query!("SELECT openssh_pubkey FROM ssh_keys")
.fetch_all(pool) .fetch_all(pool)

View File

@@ -112,7 +112,7 @@ pub async fn kernel_logs_nofollow(
_ctx: (), _ctx: (),
(limit, cursor, before, _): (Option<usize>, Option<String>, bool, bool), (limit, cursor, before, _): (Option<usize>, Option<String>, bool, bool),
) -> Result<LogResponse, Error> { ) -> 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))] #[command(rpc_only, rename = "follow", display(display_none))]
@@ -120,7 +120,7 @@ pub async fn kernel_logs_follow(
#[context] ctx: RpcContext, #[context] ctx: RpcContext,
#[parent_data] (limit, _, _, _): (Option<usize>, Option<String>, bool, bool), #[parent_data] (limit, _, _, _): (Option<usize>, Option<String>, bool, bool),
) -> Result<LogFollowResponse, Error> { ) -> Result<LogFollowResponse, Error> {
follow_logs(ctx, LogSource::Service(SYSTEMD_UNIT), limit).await follow_logs(ctx, LogSource::Kernel, limit).await
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

22
backend/update-sqlx-data.sh Executable file
View 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

View File

@@ -13,6 +13,12 @@ fi
passwd -l start9 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 # change timezone
timedatectl set-timezone Etc/UTC timedatectl set-timezone Etc/UTC
@@ -45,7 +51,9 @@ apt-get install -y \
network-manager \ network-manager \
vim \ vim \
jq \ jq \
ncdu ncdu \
postgresql \
pgloader
# Setup repository from The Guardian Project and install latest stable Tor daemon # Setup repository from The Guardian Project and install latest stable Tor daemon
touch /etc/apt/sources.list.d/tor.list touch /etc/apt/sources.list.d/tor.list
@@ -64,6 +72,10 @@ systemctl start systemd-resolved
apt-get remove --purge openresolv dhcpcd5 -y apt-get remove --purge openresolv dhcpcd5 -y
systemctl disable wpa_supplicant.service 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 ln -rsf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
systemctl disable bluetooth.service systemctl disable bluetooth.service