phase 4
This commit is contained in:
@@ -106,6 +106,13 @@ I'm going to add some test data to the project under `tests/` but it's just rand
|
|||||||
|
|
||||||
As a minor improvement, make the `perform_close` method also gzip the contents of the tarball.
|
As a minor improvement, make the `perform_close` method also gzip the contents of the tarball.
|
||||||
|
|
||||||
|
### Phase 4
|
||||||
|
|
||||||
|
For phase 4, add encryption. We'll have to add a new error type for the given key not being a valid key for `age`.
|
||||||
|
|
||||||
|
Now, after we create the tar but before we write it to disk, we need to encrypt the array, so that plaintext is never
|
||||||
|
written to disk.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ pub enum DrawerError {
|
|||||||
DrawerInsideTarget,
|
DrawerInsideTarget,
|
||||||
TarFailed,
|
TarFailed,
|
||||||
WriteFailed(PathBuf),
|
WriteFailed(PathBuf),
|
||||||
|
KeyNotValidSsh,
|
||||||
|
EncryptFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DrawerError {
|
impl fmt::Display for DrawerError {
|
||||||
@@ -28,6 +30,8 @@ impl fmt::Display for DrawerError {
|
|||||||
DrawerError::DrawerInsideTarget => write!(f, "drawer file is inside the target directory"),
|
DrawerError::DrawerInsideTarget => write!(f, "drawer file is inside the target directory"),
|
||||||
DrawerError::TarFailed => write!(f, "failed to create tarball"),
|
DrawerError::TarFailed => write!(f, "failed to create tarball"),
|
||||||
DrawerError::WriteFailed(p) => write!(f, "failed to write drawer file: {}", p.display()),
|
DrawerError::WriteFailed(p) => write!(f, "failed to write drawer file: {}", p.display()),
|
||||||
|
DrawerError::KeyNotValidSsh => write!(f, "key is not a valid SSH key"),
|
||||||
|
DrawerError::EncryptFailed => write!(f, "encryption failed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-9
@@ -1,4 +1,5 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
@@ -119,7 +120,26 @@ impl DrawerOperation {
|
|||||||
gz.finish()
|
gz.finish()
|
||||||
.map_err(|_| DrawerError::TarFailed)?;
|
.map_err(|_| DrawerError::TarFailed)?;
|
||||||
}
|
}
|
||||||
std::fs::write(&self.drawer_file, &buf)
|
|
||||||
|
let key_path = self.key_path()?;
|
||||||
|
let file = std::fs::File::open(&key_path)
|
||||||
|
.map_err(|_| DrawerError::KeyInvalid(key_path.clone()))?;
|
||||||
|
let reader = std::io::BufReader::new(file);
|
||||||
|
let identity = age::ssh::Identity::from_buffer(reader, None)
|
||||||
|
.map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
let recipient = age::ssh::Recipient::try_from(identity)
|
||||||
|
.map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
|
||||||
|
let mut encrypted: Vec<u8> = Vec::new();
|
||||||
|
let encryptor = age::Encryptor::with_recipients(
|
||||||
|
std::iter::once(&recipient as &dyn age::Recipient)
|
||||||
|
).map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
let mut writer = encryptor.wrap_output(&mut encrypted)
|
||||||
|
.map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
writer.write_all(&buf).map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
writer.finish().map_err(|_| DrawerError::EncryptFailed)?;
|
||||||
|
|
||||||
|
std::fs::write(&self.drawer_file, &encrypted)
|
||||||
.map_err(|_| DrawerError::WriteFailed(self.drawer_file.clone()))
|
.map_err(|_| DrawerError::WriteFailed(self.drawer_file.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,20 +154,28 @@ impl DrawerOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_key(&self) -> Result<(), DrawerError> {
|
fn key_path(&self) -> Result<PathBuf, DrawerError> {
|
||||||
let key_path = match &self.key {
|
match &self.key {
|
||||||
EnvVar => {
|
EnvVar => {
|
||||||
let val = std::env::var("DRAWER_KEY").map_err(|_| DrawerError::NoKeyPath)?;
|
let val = std::env::var("DRAWER_KEY").map_err(|_| DrawerError::NoKeyPath)?;
|
||||||
PathBuf::from(val)
|
Ok(PathBuf::from(val))
|
||||||
}
|
}
|
||||||
Path(p) => p.clone(),
|
Path(p) => Ok(p.clone()),
|
||||||
};
|
|
||||||
match std::fs::File::open(&key_path) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(_) => Err(DrawerError::KeyInvalid(key_path)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_key(&self) -> Result<(), DrawerError> {
|
||||||
|
let key_path = self.key_path()?;
|
||||||
|
let file = std::fs::File::open(&key_path)
|
||||||
|
.map_err(|_| DrawerError::KeyInvalid(key_path.clone()))?;
|
||||||
|
let reader = std::io::BufReader::new(file);
|
||||||
|
let identity = age::ssh::Identity::from_buffer(reader, None)
|
||||||
|
.map_err(|_| DrawerError::KeyNotValidSsh)?;
|
||||||
|
age::ssh::Recipient::try_from(identity)
|
||||||
|
.map_err(|_| DrawerError::KeyNotValidSsh)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_open(&self) -> Result<(), DrawerError> {
|
fn validate_open(&self) -> Result<(), DrawerError> {
|
||||||
if !self.drawer_file.exists() {
|
if !self.drawer_file.exists() {
|
||||||
return Err(DrawerError::DrawerFileNotFound(self.drawer_file.clone()));
|
return Err(DrawerError::DrawerFileNotFound(self.drawer_file.clone()));
|
||||||
|
|||||||
Reference in New Issue
Block a user