git.delta.rocks / jrsonnet / refs/commits / b0625df8861d

difftreelog

style fix clippy warnings

Yaroslav Bolyukin2022-09-02parent: #7a2e42e.patch.diff
in: trunk

1 file changed

modifiedcmds/install-secrets/src/main.rsdiffbeforeafterboth
before · cmds/install-secrets/src/main.rs
1use age::Decryptor;2use anyhow::{anyhow, bail, Context, Result};3use clap::Parser;4use log::{error, warn};5use nix::fcntl::{renameat2, RenameFlags};6use nix::sys::stat::Mode;7use nix::unistd::{chown, Group, User};8use serde::{Deserialize, Deserializer};9use std::fs::{self, DirBuilder, File};10use std::io::{self, Cursor, Read, Write};11use std::iter;12use std::os::unix::prelude::PermissionsExt;13use std::str::from_utf8;14use std::{15	collections::HashMap,16	os::unix::fs::DirBuilderExt,17	path::{Path, PathBuf},18};1920#[derive(Parser)]21#[clap(author)]22struct Opts {23	data: PathBuf,24}2526#[derive(Deserialize)]27#[serde(rename_all = "camelCase")]28struct DataItem {29	group: String,30	mode: String,31	owner: String,3233	#[serde(deserialize_with = "from_z85")]34	secret: Option<Vec<u8>>,35	public: Option<String>,3637	public_path: PathBuf,38	stable_public_path: PathBuf,3940	secret_path: PathBuf,41	stable_secret_path: PathBuf,42}4344fn from_z85<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>45where46	D: Deserializer<'de>,47{48	use serde::de::Error;49	if let Some(v) = <Option<String>>::deserialize(deserializer)? {50		Ok(Some(51			z85::decode(&v).map_err(|err| Error::custom(err.to_string()))?,52		))53	} else {54		Ok(None)55	}56}5758type Data = HashMap<String, DataItem>;5960fn init_secret(identity: &age::ssh::Identity, value: DataItem) -> Result<()> {61	if let Some(public) = &value.public {62		let mut hashed = File::create(&value.public_path)?;63		let mut stable_dir = value.stable_public_path.parent().expect("not root");64		let mut stable_temp =65			tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?;66		hashed.write_all(public.as_bytes())?;67		stable_temp.write_all(public.as_bytes())?;68		stable_temp.flush()?;69		fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(0o444))70			.context("perm")?;71		fs::set_permissions(&value.public_path, fs::Permissions::from_mode(0o444))72			.context("perm")?;7374		stable_temp75			.persist(value.stable_public_path)76			.context("failed to persist")?;77	}78	if value.secret.is_none() {79		return Ok(());80	}81	let secret = value.secret.as_ref().unwrap();8283	let mode = Mode::from_bits(84		u32::from_str_radix(&value.mode, 8).context("failed to parse mode as octal")?,85	)86	.context("failed to parse mode")?;87	let user = User::from_name(&value.owner)88		.context("failed to get user")?89		.ok_or_else(|| anyhow!("user not found"))?;90	let group = Group::from_name(&value.group)91		.context("failed to get group")?92		.ok_or_else(|| anyhow!("group not found"))?;9394	let mut stable_dir = value.stable_secret_path.parent().expect("not root");95	let mut stable_temp =96		tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?;97	let mut hashed = File::create(&value.secret_path)?;9899	// File is owned by root, and only root can modify it100	let decrypted = {101		let mut input = Cursor::new(&secret);102		let decryptor = Decryptor::new(&mut input).context("failed to init decryptor")?;103		let decryptor = match decryptor {104			Decryptor::Recipients(r) => r,105			Decryptor::Passphrase(_) => bail!("should be recipients"),106		};107		let mut decryptor = decryptor108			.decrypt(iter::once(identity as &dyn age::Identity))109			.context("failed to decrypt, wrong key?")?;110111		let mut decrypted = Vec::new();112		decryptor113			.read_to_end(&mut decrypted)114			.context("failed to decrypt")?;115		decrypted116	};117118	io::copy(&mut Cursor::new(&decrypted), &mut stable_temp)119		.context("failed to write decrypted file")?;120	io::copy(&mut Cursor::new(decrypted), &mut hashed).context("failed to write decrypted file")?;121122	// Make file owned by specified user and group, then change mode123	chown(stable_temp.path(), Some(user.uid), Some(group.gid))124		.context("failed to apply user/group")?;125	chown(&value.secret_path, Some(user.uid), Some(group.gid))126		.context("failed to apply user/group")?;127	fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(mode.bits())).unwrap();128	fs::set_permissions(&value.secret_path, fs::Permissions::from_mode(mode.bits())).unwrap();129	stable_temp130		.persist(value.stable_secret_path)131		.context("failed to persist")?;132133	Ok(())134}135136fn main() -> anyhow::Result<()> {137	env_logger::Builder::new()138		.filter_level(log::LevelFilter::Info)139		.init();140141	let opts = Opts::parse();142	let data = fs::read(&opts.data).context("failed to read secrets data")?;143	let data_str = from_utf8(&data).context("failed to read data to string")?;144	let data: Data = serde_json::from_str(data_str).context("failed to parse data")?;145146	if !fs::metadata("/run/secrets")147		.map(|m| m.is_dir())148		.unwrap_or(false)149	{150		fs::create_dir("/run/secrets").context("failed to create secrets directory")?;151	}152153	let identity = age::ssh::Identity::from_buffer(154		&mut Cursor::new(155			fs::read("/etc/ssh/ssh_host_ed25519_key").context("failed to read host private key")?,156		),157		None,158	)159	.context("failed to parse identity")?;160161	let mut failed = false;162	for (name, value) in data {163		if let Err(e) = init_secret(&identity, value) {164			error!(165				"{:?}",166				e.context(format!("failed to initialize secret {}", name))167			);168			failed = true;169		}170	}171	if failed {172		bail!("one or more secrets failed");173	}174175	Ok(())176}
after · cmds/install-secrets/src/main.rs
1use age::Decryptor;2use anyhow::{anyhow, bail, Context, Result};3use clap::Parser;4use log::error;5use nix::sys::stat::Mode;6use nix::unistd::{chown, Group, User};7use serde::{Deserialize, Deserializer};8use std::fs::{self, File};9use std::io::{self, Cursor, Read, Write};10use std::iter;11use std::os::unix::prelude::PermissionsExt;12use std::str::from_utf8;13use std::{collections::HashMap, path::PathBuf};1415#[derive(Parser)]16#[clap(author)]17struct Opts {18	data: PathBuf,19}2021#[derive(Deserialize)]22#[serde(rename_all = "camelCase")]23struct DataItem {24	group: String,25	mode: String,26	owner: String,2728	#[serde(deserialize_with = "from_z85")]29	secret: Option<Vec<u8>>,30	public: Option<String>,3132	public_path: PathBuf,33	stable_public_path: PathBuf,3435	secret_path: PathBuf,36	stable_secret_path: PathBuf,37}3839fn from_z85<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>40where41	D: Deserializer<'de>,42{43	use serde::de::Error;44	if let Some(v) = <Option<String>>::deserialize(deserializer)? {45		Ok(Some(46			z85::decode(&v).map_err(|err| Error::custom(err.to_string()))?,47		))48	} else {49		Ok(None)50	}51}5253type Data = HashMap<String, DataItem>;5455fn init_secret(identity: &age::ssh::Identity, value: DataItem) -> Result<()> {56	if let Some(public) = &value.public {57		let mut hashed = File::create(&value.public_path)?;58		let stable_dir = value.stable_public_path.parent().expect("not root");59		let mut stable_temp =60			tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?;61		hashed.write_all(public.as_bytes())?;62		stable_temp.write_all(public.as_bytes())?;63		stable_temp.flush()?;64		fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(0o444))65			.context("perm")?;66		fs::set_permissions(&value.public_path, fs::Permissions::from_mode(0o444))67			.context("perm")?;6869		stable_temp70			.persist(value.stable_public_path)71			.context("failed to persist")?;72	}73	if value.secret.is_none() {74		return Ok(());75	}76	let secret = value.secret.as_ref().unwrap();7778	let mode = Mode::from_bits(79		u32::from_str_radix(&value.mode, 8).context("failed to parse mode as octal")?,80	)81	.context("failed to parse mode")?;82	let user = User::from_name(&value.owner)83		.context("failed to get user")?84		.ok_or_else(|| anyhow!("user not found"))?;85	let group = Group::from_name(&value.group)86		.context("failed to get group")?87		.ok_or_else(|| anyhow!("group not found"))?;8889	let stable_dir = value.stable_secret_path.parent().expect("not root");90	let mut stable_temp =91		tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?;92	let mut hashed = File::create(&value.secret_path)?;9394	// File is owned by root, and only root can modify it95	let decrypted = {96		let mut input = Cursor::new(&secret);97		let decryptor = Decryptor::new(&mut input).context("failed to init decryptor")?;98		let decryptor = match decryptor {99			Decryptor::Recipients(r) => r,100			Decryptor::Passphrase(_) => bail!("should be recipients"),101		};102		let mut decryptor = decryptor103			.decrypt(iter::once(identity as &dyn age::Identity))104			.context("failed to decrypt, wrong key?")?;105106		let mut decrypted = Vec::new();107		decryptor108			.read_to_end(&mut decrypted)109			.context("failed to decrypt")?;110		decrypted111	};112113	io::copy(&mut Cursor::new(&decrypted), &mut stable_temp)114		.context("failed to write decrypted file")?;115	io::copy(&mut Cursor::new(decrypted), &mut hashed).context("failed to write decrypted file")?;116117	// Make file owned by specified user and group, then change mode118	chown(stable_temp.path(), Some(user.uid), Some(group.gid))119		.context("failed to apply user/group")?;120	chown(&value.secret_path, Some(user.uid), Some(group.gid))121		.context("failed to apply user/group")?;122	fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(mode.bits())).unwrap();123	fs::set_permissions(&value.secret_path, fs::Permissions::from_mode(mode.bits())).unwrap();124	stable_temp125		.persist(value.stable_secret_path)126		.context("failed to persist")?;127128	Ok(())129}130131fn main() -> anyhow::Result<()> {132	env_logger::Builder::new()133		.filter_level(log::LevelFilter::Info)134		.init();135136	let opts = Opts::parse();137	let data = fs::read(&opts.data).context("failed to read secrets data")?;138	let data_str = from_utf8(&data).context("failed to read data to string")?;139	let data: Data = serde_json::from_str(data_str).context("failed to parse data")?;140141	if !fs::metadata("/run/secrets")142		.map(|m| m.is_dir())143		.unwrap_or(false)144	{145		fs::create_dir("/run/secrets").context("failed to create secrets directory")?;146	}147148	let identity = age::ssh::Identity::from_buffer(149		&mut Cursor::new(150			fs::read("/etc/ssh/ssh_host_ed25519_key").context("failed to read host private key")?,151		),152		None,153	)154	.context("failed to parse identity")?;155156	let mut failed = false;157	for (name, value) in data {158		if let Err(e) = init_secret(&identity, value) {159			error!(160				"{:?}",161				e.context(format!("failed to initialize secret {}", name))162			);163			failed = true;164		}165	}166	if failed {167		bail!("one or more secrets failed");168	}169170	Ok(())171}