diff --git a/src/symbols/acme/mod.rs b/src/symbols/acme/mod.rs index 556ec82..c6390ef 100644 --- a/src/symbols/acme/mod.rs +++ b/src/symbols/acme/mod.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use crate::command_runner::CommandRunner; use crate::command_runner::SetuidCommandRunner; +use crate::symbols::concat::Concat; use crate::symbols::dir::Dir; use crate::symbols::file::File; use crate::symbols::list::List; @@ -94,4 +95,19 @@ impl<'a, U: Clone + AsRef, H: AsRef, C: AsRef, R: CommandRunner> AcmeCertChain::new(host, &self.acme_command_runner, root_cert_path), )) } + pub fn get_key_and_cert_bundle>( + &'a self, + host: HOST, + ) -> impl Symbol + 'a { + List::from(( + self.get_cert(host.clone()), + Concat::new( + [ + format!("/etc/ssl/private/{}.key", host.as_ref()), + format!("/etc/ssl/local_certs/{}.chained.crt", host.as_ref()), + ], + format!("/etc/ssl/private/{}.with_key.crt", host.as_ref()), + ), + )) + } } diff --git a/src/symbols/concat.rs b/src/symbols/concat.rs new file mode 100644 index 0000000..186dbcc --- /dev/null +++ b/src/symbols/concat.rs @@ -0,0 +1,79 @@ +use std::error::Error; +use std::fmt; +use std::fs::{metadata, File}; +use std::io::copy; +use std::marker::PhantomData; +use std::path::Path; + +use crate::resources::Resource; +use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; + +pub struct Concat { + target: D, + sources: S, + source_item: PhantomData, +} + +impl Concat { + pub fn new(sources: S, target: D) -> Self { + Self { + target, + sources, + source_item: PhantomData::default(), + } + } +} + +impl, D: AsRef, I: AsRef> Symbol for Concat { + fn target_reached(&self) -> Result> { + let target = self.target.as_ref(); + if !target.exists() { + return Ok(false); + } + let target_date = metadata(target)?.modified()?; + for source in self.sources.as_ref() { + if metadata(source)?.modified()? > target_date { + return Ok(false); + } + } + Ok(true) + } + + fn execute(&self) -> Result<(), Box> { + let mut file = File::create(self.target.as_ref())?; + for source in self.sources.as_ref() { + copy(&mut File::open(source)?, &mut file)?; + } + Ok(()) + } + + fn get_prerequisites(&self) -> Vec { + let mut r: Vec = self + .sources + .as_ref() + .iter() + .map(|s| Resource::new("file", s.as_ref().to_str().unwrap())) + .collect(); + if let Some(parent) = self.target.as_ref().parent() { + r.push(Resource::new("dir", parent.to_str().unwrap())) + } + r + } + + fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box { + Box::new(SymbolAction::new(runner, self)) + } + + fn into_action<'a>(self: Box, runner: &'a dyn SymbolRunner) -> Box + where + Self: 'a, + { + Box::new(OwnedSymbolAction::new(runner, *self)) + } +} + +impl, I> fmt::Display for Concat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "Concat {}", self.target.as_ref().display()) + } +} diff --git a/src/symbols/factory.rs b/src/symbols/factory.rs index 7dfea68..697cf16 100644 --- a/src/symbols/factory.rs +++ b/src/symbols/factory.rs @@ -63,6 +63,13 @@ impl<'b, C: 'b + CommandRunner, P: 'b + Policy> SymbolFactory<'b, C, P> { self.acme_factory.get_cert(host) } + pub fn get_key_and_cert_bundle<'a, H: 'a + AsRef + Clone>( + &'a self, + host: H, + ) -> impl Symbol + 'a { + self.acme_factory.get_key_and_cert_bundle(host) + } + pub fn get_nginx_acme_server<'a, S: 'a + Symbol>( &'a self, host: &'a str, diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs index 8c3150f..779d186 100644 --- a/src/symbols/mod.rs +++ b/src/symbols/mod.rs @@ -68,6 +68,7 @@ impl<'a, S: Symbol + 'a> Action for OwnedSymbolAction<'a, S> { } pub mod acme; +pub mod concat; pub mod cron; pub mod dir; pub mod factory;