A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
2.8 KiB

use crate::command_runner::CommandRunner;
use crate::symbols::Symbol;
use async_trait::async_trait;
use std::borrow::Borrow;
use std::error::Error;
use std::fs::File as FsFile;
use std::io::Write;
use std::marker::PhantomData;
use std::path::Path;
#[derive(Debug)]
pub struct Cert<_C, C, D, P> {
domain: D,
command_runner: C,
root_cert_path: P,
account_key_path: P,
challenges_path: P,
csr_path: P,
cert_path: P,
phantom: PhantomData<_C>,
}
impl<_C, C, D, P> Cert<_C, C, D, P> {
pub fn new(
domain: D,
command_runner: C,
root_cert_path: P,
account_key_path: P,
challenges_path: P,
csr_path: P,
cert_path: P,
) -> Self {
Self {
domain,
command_runner,
root_cert_path,
account_key_path,
challenges_path,
csr_path,
cert_path,
phantom: PhantomData::default(),
}
}
}
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
#[async_trait(?Send)]
impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for Cert<_C, C, D, P> {
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
if !self.cert_path.as_ref().exists() {
return Ok(false);
}
let output = self
.command_runner
.borrow()
.run_with_args(
"openssl",
args![
"x509",
"-in",
self.cert_path.as_ref(),
"-noout",
"-subject",
"-checkend",
(30 * DAYS_IN_SECONDS).to_string(),
],
)
.await?;
if output.status.success()
&& output.stdout
== format!(
"subject=CN = {}\nCertificate will not expire\n",
self.domain.as_ref()
)
.as_bytes()
{
Ok(
self
.command_runner
.borrow()
.run_successfully(
"openssl",
args![
"verify",
"--untrusted",
self.root_cert_path.as_ref(),
self.cert_path.as_ref(),
],
)
.await
.is_ok(),
)
} else if output.status.code() == Some(1)
&& output.stdout
== format!(
"subject=CN = {}\nCertificate will expire\n",
self.domain.as_ref()
)
.as_bytes()
{
Ok(false)
} else {
Err(String::from_utf8(output.stderr)?.into())
}
}
async fn execute(&self) -> Result<(), Box<dyn Error>> {
let output = self
.command_runner
.borrow()
.get_output(
"acme-tiny",
args![
"--account-key",
self.account_key_path.as_ref(),
"--csr",
self.csr_path.as_ref(),
"--acme-dir",
self.challenges_path.as_ref(),
],
)
.await?;
let mut file = FsFile::create(self.cert_path.as_ref())?;
file.write_all(&output)?;
Ok(())
}
}
#[cfg(test)]
mod test {}