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
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 {}
|