mirror of
https://github.com/Kruhlmann/punlock.git
synced 2026-02-05 02:29:25 +00:00
Add domain support
This commit is contained in:
172
src/store.rs
172
src/store.rs
@@ -1,21 +1,21 @@
|
||||
use std::{path::PathBuf, process::Stdio};
|
||||
use std::{os::unix::fs::PermissionsExt, path::PathBuf, sync::Arc};
|
||||
|
||||
use futures::{StreamExt, stream::FuturesUnordered};
|
||||
use tokio::{fs::File, io::AsyncWriteExt, process::Command};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::{bitwarden::Bitwarden, config::PunlockConfigurationEntry, statics::PROJECT_DIRS};
|
||||
use crate::{
|
||||
bitwarden::Bitwarden,
|
||||
config::PunlockConfigurationEntry,
|
||||
statics::{self, HOME_DIRECTORY},
|
||||
};
|
||||
|
||||
pub struct UnmountedSecretStore {
|
||||
bitwarden: Bitwarden<String>,
|
||||
root_path: PathBuf,
|
||||
}
|
||||
|
||||
impl UnmountedSecretStore {
|
||||
pub fn new(bitwarden: Bitwarden<String>) -> Self {
|
||||
Self {
|
||||
bitwarden,
|
||||
root_path: PROJECT_DIRS.cache_dir().to_owned(),
|
||||
}
|
||||
Self { bitwarden }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ impl UnmountedSecretStore {
|
||||
pub async fn into_platform_store(self) -> anyhow::Result<UnixSecretStore> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
let store = UnixSecretStore::new(self.bitwarden, self.root_path).unmount().await?.mount().await?;
|
||||
let root_path = statics::RUNTIME_DIRECTORY.join("punlock");
|
||||
let store = UnixSecretStore::new(self.bitwarden, root_path).teardown().await?.setup().await?;
|
||||
Ok(store)
|
||||
} else if #[cfg(target_os = "macos")] {
|
||||
// mount_ramdisk_macos(mount_point)?;
|
||||
@@ -37,85 +38,132 @@ impl UnmountedSecretStore {
|
||||
}
|
||||
|
||||
pub struct UnixSecretStore {
|
||||
bitwarden: Bitwarden<String>,
|
||||
root_path: PathBuf,
|
||||
bitwarden: Arc<Bitwarden<String>>,
|
||||
root_path: Arc<PathBuf>,
|
||||
}
|
||||
|
||||
impl UnixSecretStore {
|
||||
pub fn new(bitwarden: Bitwarden<String>, root_path: PathBuf) -> Self {
|
||||
Self {
|
||||
bitwarden,
|
||||
root_path,
|
||||
bitwarden: Arc::new(bitwarden),
|
||||
root_path: Arc::new(root_path),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn write_secrets(
|
||||
&self,
|
||||
entries: &Vec<PunlockConfigurationEntry>,
|
||||
) -> anyhow::Result<()> {
|
||||
pub async fn write_secrets(&self, entries: &[PunlockConfigurationEntry]) -> anyhow::Result<()> {
|
||||
let mut tasks = FuturesUnordered::new();
|
||||
|
||||
for entry in entries.iter() {
|
||||
tasks.push(async move {
|
||||
let secret = self.bitwarden.fetch(&entry).await?;
|
||||
let path = self.root_path.join(&entry.path);
|
||||
tokio::fs::create_dir_all(path.parent().unwrap_or(&path)).await.inspect_err(|error| tracing::error!(?error, "create secret directory"))?;
|
||||
let mut file = File::create(path).await.inspect_err(
|
||||
|error| tracing::error!(?error, id = ?entry.id, path = ?entry.path, "create secret file"),
|
||||
)?;
|
||||
file.write_all(secret.as_bytes()).await.inspect_err(
|
||||
|error| tracing::error!(?error, id = ?entry.id, path = ?entry.path, "write secret"),
|
||||
)?;
|
||||
Ok::<(String, String), anyhow::Error>((entry.id.clone(), entry.path.clone()))
|
||||
})
|
||||
let root = self.root_path.clone();
|
||||
let bw = self.bitwarden.clone();
|
||||
|
||||
tasks.push(
|
||||
async move {
|
||||
let secret = bw.fetch(entry).await.inspect_err(|error| tracing::error!(?error, ?entry, "item not found"))?;
|
||||
let path = root.join(&entry.path);
|
||||
|
||||
tokio::fs::create_dir_all(path.parent().unwrap_or(&path)).await?;
|
||||
{
|
||||
let mut file = tokio::fs::File::create(&path).await?;
|
||||
file.write_all(secret.as_bytes()).await?;
|
||||
if !secret.ends_with('\n') {
|
||||
file.write_all(b"\n").await?;
|
||||
}
|
||||
file.flush().await?;
|
||||
}
|
||||
|
||||
let mut perms = tokio::fs::metadata(&path).await?.permissions();
|
||||
perms.set_readonly(true);
|
||||
|
||||
if !entry.public {
|
||||
#[cfg(unix)]
|
||||
perms.set_mode(0o400);
|
||||
}
|
||||
tokio::fs::set_permissions(&path, perms)
|
||||
.await
|
||||
.inspect(|_| tracing::debug!(?path, "set readonly"))
|
||||
.inspect_err(|error| tracing::error!(?error, ?path, "remove runtime dir"))?;
|
||||
|
||||
if let Some(ref links) = entry.links {
|
||||
for link in links {
|
||||
let link_path: PathBuf = if PathBuf::from(link).is_absolute() {
|
||||
PathBuf::from(link)
|
||||
} else {
|
||||
HOME_DIRECTORY.join(link)
|
||||
};
|
||||
tokio::fs::create_dir_all(link_path.parent().unwrap_or(&link_path))
|
||||
.await?;
|
||||
match tokio::fs::symlink_metadata(&link_path).await {
|
||||
Ok(meta) if meta.file_type().is_symlink() => {
|
||||
let current = tokio::fs::read_link(&link_path).await?;
|
||||
if current == path {
|
||||
tracing::debug!(?link_path, "skipping existing symlink");
|
||||
continue;
|
||||
}
|
||||
tokio::fs::remove_file(&link_path).await?;
|
||||
}
|
||||
Ok(_) => tokio::fs::remove_file(&link_path).await?,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
|
||||
let src = path.clone();
|
||||
let dst = link_path.clone();
|
||||
tokio::task::spawn_blocking(move || -> std::io::Result<()> {
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(&src, &dst)?;
|
||||
#[cfg(windows)]
|
||||
std::os::windows::fs::symlink_file(&src, &dst)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))??;
|
||||
|
||||
tracing::info!(src =? path, destination = ?link_path, "created/updated symlink");
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, anyhow::Error>((entry.id.clone(), entry.path.clone()))
|
||||
}
|
||||
);
|
||||
}
|
||||
while let Some(result) = tasks.next().await {
|
||||
match result {
|
||||
Ok((id, path)) => tracing::info!(?id, ?path, "load secret"),
|
||||
Err(error) => tracing::error!(?error, "load secret"),
|
||||
|
||||
let mut count = 0;
|
||||
let mut success = 0;
|
||||
while let Some(res) = tasks.next().await {
|
||||
count += 1;
|
||||
match res {
|
||||
Ok((id, path)) => {
|
||||
success += 1;
|
||||
tracing::info!(?id, ?path, "secret written")
|
||||
}
|
||||
Err(error) => tracing::error!(?error, "failed to write secret"),
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("wrote {success}/{count} secrets");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unmount(self) -> anyhow::Result<Self> {
|
||||
async fn teardown(self) -> anyhow::Result<Self> {
|
||||
if self.root_path.exists() {
|
||||
Command::new("sudo")
|
||||
.args(&["umount", self.root_path.to_str().unwrap()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
tokio::fs::remove_dir_all(&*self.root_path)
|
||||
.await
|
||||
.inspect_err(
|
||||
|error| tracing::error!(?error, path = ?self.root_path, "remove runtime dir"),
|
||||
)
|
||||
.ok();
|
||||
tokio::fs::remove_dir_all(&self.root_path).await.ok();
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
async fn mount(self) -> anyhow::Result<Self> {
|
||||
tokio::fs::create_dir_all(&self.root_path)
|
||||
async fn setup(self) -> anyhow::Result<Self> {
|
||||
tokio::fs::create_dir_all(&*self.root_path)
|
||||
.await
|
||||
.inspect_err(
|
||||
|error| tracing::error!(?error, path = ?self.root_path, "unable to create secret path"),
|
||||
|error| tracing::error!(?error, path = ?self.root_path, "create runtime dir"),
|
||||
)?;
|
||||
let status = Command::new("sudo")
|
||||
.args(&["mount", "-t", "tmpfs", "-o", "size=50M", "tmpfs"])
|
||||
.arg(&self.root_path)
|
||||
.status()
|
||||
.await
|
||||
.inspect_err(|error| tracing::error!(?error, "mount failed"))?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("mount command failed with {}", status);
|
||||
}
|
||||
tracing::debug!(path = ?self.root_path, "tmpfs mounted");
|
||||
|
||||
let uid = users::get_current_uid();
|
||||
let gid = users::get_current_gid();
|
||||
Command::new("sudo")
|
||||
.args(&["chown", &format!("{}:{}", uid, gid)])
|
||||
.arg(&self.root_path)
|
||||
.status()
|
||||
.await
|
||||
.inspect_err(|error| tracing::error!(?error, "chown failed"))?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user