Skip to content

Commit 60f27e4

Browse files
committed
feat: fixed some repetitive code, added server map on client, planned next init key-exchange & auth
1 parent 7e23c18 commit 60f27e4

8 files changed

Lines changed: 149 additions & 82 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ dashmap = "7.0.0-rc2"
4343
sha2 = "0.11.0"
4444
uuid = { version = "1.23.1", features = ["v4"] }
4545
indicatif = "0.18.4"
46-
rand = "0.10.1"
46+
rand = { version = "0.10.1" }
4747
postcard = { version = "1.1.3", features = ["alloc", "use-std"] }
4848
aes = "0.9.0"
4949
ed25519-dalek = { version = "3.0.0-pre.7", features = ["rand_core", "pem", "pkcs8"] }
5050
x25519-dalek = "3.0.0-pre.6"
51+
serde_json = "1.0.149"
5152
# zstd = "0.13.3"
5253
# tar = "0.4.45"
5354
# ahash = "0.8.12"
54-
# serde_json = "1.0.149"
5555
# bytes = "1.11.1"
5656
# blake3 = "1.8.5"
5757
# threadpool = "1.8.1"

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
Tweaking with Network Protocol & Security while building Amazon s3 ***COMMUNIST VERSION***
22

3-
`Ohh no I'm getting side-tracked by crypto shit`
3+
`Goal: Protect servers from 'bad' users. Protect users from 'bad' servers`
44

55
**Features**
66

77
- file-system native database
8-
- super secure & decentralized e2e `(uses ed22519 key for identify & x25519 for protocol encryption)`
8+
- super secure & decentralized e2e `(uses ed22519 key for identify & x25519 for encryption)`
99
- layering, delta transfer push/pull
1010
- built-in TLS'ish, server secure even on raw ip port
1111
- backups, versioning, secure key rotation
1212
- zero-trust architecture `(ssh like handshake)`
1313
- fearless concurrency `(very little locks freeze)`
1414

15+
**Limitations**
16+
17+
- vulnerable to path injection `(maybe have some edge cases)`
18+
- first connection not secure `(user must trust the server first)`
19+
- no recovery keys
20+
1521
**How to set up?**
1622

1723
Use docker [image](https://hub.docker.com/repository/docker/ronakgh97/rdrive/general) then you can use the CLI to

src/bin/client.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,9 @@ async fn main() -> Result<()> {
143143
.to_string_lossy()
144144
.to_string();
145145

146+
// read existing or create new
146147
let catalog_path = get_catalog_path()?;
147-
let catalog_dir = catalog_path
148-
.parent()
149-
.ok_or_else(|| anyhow::anyhow!("Invalid catalog path"))?;
150-
tokio::fs::create_dir_all(catalog_dir).await?;
151-
152-
// Read existing or new
153-
let mut catalog = if catalog_path.exists() {
154-
Catalog::read(&catalog_path).await?
155-
} else {
156-
Catalog::default()
157-
};
148+
let mut catalog = Catalog::read_or_create(&catalog_path).await?;
158149

159150
let file_id = if let Some(tracked) = catalog.file_index.get(&file_name) {
160151
for (i, uuid) in tracked.iter().enumerate() {
@@ -249,7 +240,7 @@ async fn main() -> Result<()> {
249240
let catalog_path = get_catalog_path()?;
250241

251242
if catalog_path.exists() {
252-
let mut catalog = Catalog::read(&catalog_path).await?;
243+
let mut catalog = Catalog::read_or_create(&catalog_path).await?;
253244
catalog.update_on_pull(&catalog_path, &file_id).await?;
254245
}
255246
}
@@ -272,7 +263,7 @@ async fn main() -> Result<()> {
272263
todo!("Non-trivial to implement this feature")
273264
}
274265
Some(ClientCommands::Ls { .. }) => {
275-
let file_map = Catalog::read(&get_catalog_path()?).await.map_err(|e| {
266+
let file_map = Catalog::read_or_create(&get_catalog_path()?).await.map_err(|e| {
276267
anyhow::anyhow!(
277268
"Failed to read catalog, make sure to push at least one file before listing: {}",
278269
e

src/bin/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async fn main() -> Result<()> {
1818
match args.command {
1919
Some(ServerCommands::Serve { port, protocol }) => match protocol.as_str() {
2020
"v1" => {
21-
let key_path = get_server_key_dir().await?;
21+
let key_path = get_server_key_dir()?;
2222

2323
let (pri_key, pub_key) =
2424
(key_path.join("private.pem"), key_path.join("public.pem"));

src/lib.rs

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ pub async fn get_public_storage_dir() -> Result<PathBuf> {
4242
}
4343

4444
#[inline(always)]
45-
pub async fn get_allowed_client_dir() -> Result<PathBuf> {
45+
pub async fn get_authorized_client_dir() -> Result<PathBuf> {
4646
let home_dir =
4747
dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Failed to get home directory"))?;
4848
let allowed_clients_path = home_dir.join(".rdrive").join("authorized_keys");
4949
Ok(allowed_clients_path)
5050
}
5151

5252
#[inline]
53-
pub async fn get_server_key_dir() -> Result<PathBuf> {
53+
pub fn get_server_key_dir() -> Result<PathBuf> {
5454
let home_dir =
5555
dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Failed to get home directory"))?;
5656
let server_keys_path = home_dir.join(".rdrive").join("server");
@@ -72,6 +72,11 @@ pub fn get_catalog_path() -> Result<PathBuf> {
7272
Ok(path)
7373
}
7474

75+
pub fn get_authorized_server_map_path() -> Result<PathBuf> {
76+
let path = get_user_key_dir()?.join("server.map");
77+
Ok(path)
78+
}
79+
7580
/// Hash a whole file and return the hex string of the hash
7681
pub fn file_hasher(path: &Path) -> Result<String> {
7782
let file = std::fs::File::open(path)?;
@@ -145,28 +150,43 @@ impl MetadataFile {
145150
}
146151

147152
#[derive(Deserialize, Serialize, Default)]
148-
pub struct FileInfo {
153+
pub struct FileHistory {
149154
pub name: String,
150155
pub last_push: String,
151156
pub last_pull: String,
152157
}
153158

154159
#[derive(Deserialize, Serialize, Default)]
155160
pub struct Catalog {
156-
pub file_map: HashMap<String, FileInfo>,
161+
pub file_map: HashMap<String, FileHistory>,
157162
pub file_index: HashMap<String, Vec<String>>,
158163
}
159164

160165
impl Catalog {
161-
pub async fn read(path: &PathBuf) -> Result<Self> {
166+
async fn read(path: &PathBuf) -> Result<Self> {
162167
use postcard::from_bytes;
163168

164169
let file = tokio::fs::read(path).await?;
165170
let catalog = from_bytes(&file)?;
166171
Ok(catalog)
167172
}
168173

169-
pub async fn write(&mut self, path: &PathBuf) -> Result<()> {
174+
pub async fn read_or_create(path: &PathBuf) -> Result<Self> {
175+
let catalog_dir = path
176+
.parent()
177+
.ok_or_else(|| anyhow::anyhow!("Invalid catalog path"))?;
178+
tokio::fs::create_dir_all(catalog_dir).await?;
179+
180+
// Read existing or new
181+
let catalog = match path.exists() {
182+
true => Self::read(path).await?,
183+
false => Self::default(),
184+
};
185+
186+
Ok(catalog)
187+
}
188+
189+
async fn write(&mut self, path: &PathBuf) -> Result<()> {
170190
use postcard::to_allocvec;
171191

172192
let bytes = to_allocvec(self)?;
@@ -188,7 +208,7 @@ impl Catalog {
188208
.and_modify(|meta| {
189209
meta.last_push = timestamp.clone();
190210
})
191-
.or_insert_with(|| FileInfo {
211+
.or_insert_with(|| FileHistory {
192212
name: file_name.to_string(),
193213
last_push: timestamp.clone(),
194214
last_pull: "never".to_string(),
@@ -217,6 +237,39 @@ impl Catalog {
217237
}
218238
}
219239

240+
#[derive(Deserialize, Serialize, Default)]
241+
pub struct AuthServerMap {
242+
/// Map -> (Host/IP, pubkey_hex)
243+
pub server_map: HashMap<String, String>,
244+
}
245+
246+
impl AuthServerMap {
247+
pub async fn read_or_create(path: &PathBuf) -> Result<Self> {
248+
let server_map = path
249+
.parent()
250+
.ok_or_else(|| anyhow::anyhow!("Invalid server map path"))?;
251+
tokio::fs::create_dir_all(server_map).await?;
252+
253+
let map = match path.exists() {
254+
true => {
255+
let str = tokio::fs::read_to_string(&path).await?;
256+
serde_json::from_str(str.as_str())
257+
.map_err(|e| anyhow::anyhow!("Failed to parse server map JSON: {}", e))?
258+
}
259+
false => Self::default(),
260+
};
261+
262+
Ok(map)
263+
}
264+
265+
pub async fn write(&mut self, path: &PathBuf) -> Result<()> {
266+
let json = serde_json::to_string_pretty(self)
267+
.map_err(|e| anyhow::anyhow!("Failed to serialize server map to JSON: {}", e))?;
268+
tokio::fs::write(path, json).await?;
269+
Ok(())
270+
}
271+
}
272+
220273
pub static START_TIME: OnceLock<chrono::DateTime<Local>> = OnceLock::new();
221274
pub static ACTIVE_CONNECTIONS: LazyLock<Arc<AtomicUsize>> =
222275
LazyLock::new(|| Arc::new(AtomicUsize::new(0)));
@@ -226,6 +279,53 @@ pub static ENABLE_CLIENT_WHITELIST: LazyLock<bool> = LazyLock::new(|| {
226279
.and_then(|s| s.parse().ok())
227280
.unwrap_or(true) // default to true
228281
});
282+
283+
pub static SERVER_PUB_KEY_PEM: LazyLock<String> = LazyLock::new(|| {
284+
let pubkey = get_server_key_dir()
285+
.unwrap_or_else(|e| {
286+
eprintln!(
287+
"{}",
288+
format!("Failed to get server key directory: {}", e)
289+
.red()
290+
.bold()
291+
);
292+
std::process::exit(1);
293+
})
294+
.join("public.pem");
295+
std::fs::read_to_string(pubkey).unwrap_or_else(|e| {
296+
eprintln!(
297+
"{}",
298+
format!("Failed to read server public key: {}", e)
299+
.red()
300+
.bold()
301+
);
302+
std::process::exit(1);
303+
})
304+
});
305+
306+
pub static SERVER_PRI_KEY_PEM: LazyLock<String> = LazyLock::new(|| {
307+
let prikey = get_server_key_dir()
308+
.unwrap_or_else(|e| {
309+
eprintln!(
310+
"{}",
311+
format!("Failed to get server key directory: {}", e)
312+
.red()
313+
.bold()
314+
);
315+
std::process::exit(1);
316+
})
317+
.join("private.pem");
318+
std::fs::read_to_string(prikey).unwrap_or_else(|e| {
319+
eprintln!(
320+
"{}",
321+
format!("Failed to read server private key: {}", e)
322+
.red()
323+
.bold()
324+
);
325+
std::process::exit(1);
326+
})
327+
});
328+
229329
pub static MAX_CONNECTIONS: LazyLock<usize> = LazyLock::new(|| {
230330
std::env::var("MAX_CONNECTIONS")
231331
.ok()
@@ -243,7 +343,7 @@ pub static SHARED_FILE_LOCK: LazyLock<Arc<DashMap<String, Arc<RwLock<()>>>>> =
243343
LazyLock::new(|| Arc::new(DashMap::new()));
244344

245345
#[inline(always)]
246-
pub fn get_file_lock(file_id: &str) -> Arc<RwLock<()>> {
346+
pub fn hold_file_lock(file_id: &str) -> Arc<RwLock<()>> {
247347
let map = &*SHARED_FILE_LOCK;
248348
map.entry(file_id.to_string())
249349
.or_insert_with(|| Arc::new(RwLock::new(())))

0 commit comments

Comments
 (0)