From e6cb327c3dc4fe23cfe6c6cfbd32d600bd2bc608 Mon Sep 17 00:00:00 2001 From: oluceps Date: Sun, 15 Sep 2024 16:13:27 +0800 Subject: [PATCH] + + --- TODO.md | 1 + src/cmd/deploy.rs | 91 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..bdf449e --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +- [ ] should get entire Secret when deploy diff --git a/src/cmd/deploy.rs b/src/cmd/deploy.rs index 8901004..3e1c303 100644 --- a/src/cmd/deploy.rs +++ b/src/cmd/deploy.rs @@ -1,15 +1,19 @@ use std::{ collections::HashMap, - fs::{self, DirEntry, ReadDir}, - io::ErrorKind, + fs::{self, DirEntry, File, ReadDir}, + io::{ErrorKind, Read, Write}, + iter, path::{Path, PathBuf}, + str::FromStr, }; use crate::profile::Profile; +use age::x25519; use eyre::{eyre, Context, Result}; -use spdlog::{debug, error, info}; +use spdlog::{debug, error, info, trace}; +const KEY_TYPE: &str = "ed25519"; impl Profile { pub fn get_decrypted_mount_point_path(&self) -> String { self.settings.decrypted_mount_point.to_string() @@ -20,13 +24,35 @@ impl Profile { pub fn read_decrypted_mount_point(&self) -> std::io::Result { fs::read_dir(self.get_decrypted_mount_point_path()) } + + pub fn get_host_key_identity(&self) -> Result { + if let Some(k) = self + .settings + .host_keys + .iter() + .find(|i| i.r#type == KEY_TYPE) + { + fs::read_to_string(&k.path) + .wrap_err_with(|| eyre!("reading ssh host key error: {}", k.path)) + .and_then(|i| { + age::ssh::Identity::from_buffer(i.as_bytes(), Some(String::from("thekey"))) + .map_err(|e| eyre!("convert age identity from ssh key error: {}", e)) + }) + } else { + Err(eyre!("key with type {} not found", KEY_TYPE)) + } + } /// init decrypted mount point and return the generation count pub fn init_decrypted_mount_point(&self) -> Result { let mut max = 0; - let b = match self.read_decrypted_mount_point() { + let res = match self.read_decrypted_mount_point() { Err(e) if e.kind() == ErrorKind::NotFound => { - fs::create_dir_all(self.get_decrypted_mount_point_path()) - .wrap_err("create decrypted mountpoint error") + fs::create_dir_all(self.get_decrypted_mount_point_path()).wrap_err_with(|| { + format!( + "creating decrypted mountpoint: {:?}", + self.get_decrypted_mount_point_path() + ) + }) } Err(e) => { error!("{}", e); @@ -56,13 +82,14 @@ impl Profile { } }; - Ok(max) + res.map(|_| max) } /** extract secrets to `/run/vaultix.d/$num` and link to `/run/vaultix` */ pub fn deploy(self) -> Result<()> { - let storage_name_ctt_map: HashMap> = { + // hash-name.age => vec + let name_ciphertext_map: HashMap> = { let mut map = HashMap::new(); // dir with host pub key encrypted material, prefix hash let storage = PathBuf::from(&self.settings.storage_dir_store); @@ -80,12 +107,50 @@ impl Profile { map }; - let secs_map = self.get_renced_store_paths().into_map(); + trace!("{:?}", name_ciphertext_map); - for s in secs_map.values().into_iter() { - debug!("found cipher file {:?}", s.canonicalize()?); - } + let generation_count = self.init_decrypted_mount_point()?; + + let target_extract_dir_with_gen = { + let mut p = PathBuf::from(self.get_decrypted_mount_point_path()); + p.push(generation_count.to_string()); + + debug!("target extract dir with generation number: {:?}", p); + + fs::create_dir_all(&p).map(|_| p).wrap_err(eyre!( + "cannot create target extract dir with generation number" + ))? + }; + + let decrypt_host_ident = &self.get_host_key_identity()?; + + name_ciphertext_map.into_iter().for_each(|(n, c)| { + let decryptor = match age::Decryptor::new(&c[..]).expect("parse cipher text error") { + age::Decryptor::Recipients(d) => d, + _ => unreachable!(), + }; + + let mut decrypted = vec![]; + + let mut reader = decryptor + .decrypt(iter::once(decrypt_host_ident as &dyn age::Identity)) + .unwrap(); + + let _ = reader.read_to_end(&mut decrypted); + + let mut the_file_fd = { + let mut p = target_extract_dir_with_gen.clone(); + p.push(n); + File::create(p) + } + .expect("create file error"); + the_file_fd + .write_all(&decrypted) + .expect("write decrypted file error") + }); - Ok(()) + // link back to /run/vaultix + std::os::unix::fs::symlink(target_extract_dir_with_gen, self.get_decrypt_dir_path()) + .wrap_err("create symlink error") } }