Browse Source

Add environment variables to hook templates

pull/5/head
Rodolphe Breard 6 years ago
parent
commit
03850d20e6
  1. 1
      CHANGELOG.md
  2. 4
      acmed/src/certificate.rs
  3. 54
      acmed/src/hooks.rs
  4. 2
      acmed/src/storage.rs
  5. 48
      man/en/acmed.toml.5

1
CHANGELOG.md

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- ACMEd now displays a warning when the server indicates an error in an order or an authorization.
- A configuration file can now include several other files.
- Hooks have access to environment variables.
- tacd is now able to listen on a unix socket.

4
acmed/src/certificate.rs

@ -5,7 +5,7 @@ use crate::storage::{certificate_files_exists, get_certificate};
use acme_common::error::Error;
use log::{debug, trace};
use openssl::x509::X509;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::fmt;
use time::{strptime, Duration};
@ -203,6 +203,7 @@ impl Certificate {
file_name: file_name.to_string(),
proof: proof.to_string(),
is_clean_hook: false,
env: HashMap::new(),
};
let hook_type = match challenge {
Challenge::Http01 => (HookType::ChallengeHttp01, HookType::ChallengeHttp01Clean),
@ -235,6 +236,7 @@ impl Certificate {
algorithm: self.algo.to_string(),
status: status.to_string(),
is_success,
env: HashMap::new(),
};
hooks::call(&hook_data, &self.hooks, HookType::PostOperation)?;
Ok(())

54
acmed/src/hooks.rs

@ -3,37 +3,63 @@ use acme_common::error::Error;
use handlebars::Handlebars;
use log::debug;
use serde::Serialize;
use std::fmt;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::{env, fmt};
#[derive(Serialize)]
pub trait HookEnvData {
fn set_env(&mut self);
}
macro_rules! imple_hook_data_env {
($t: ty) => {
impl HookEnvData for $t {
fn set_env(&mut self) {
for (key, value) in env::vars() {
self.env.insert(key, value);
}
}
}
};
}
#[derive(Clone, Serialize)]
pub struct PostOperationHookData {
pub domains: Vec<String>,
pub algorithm: String,
pub status: String,
pub is_success: bool,
pub env: HashMap<String, String>,
}
#[derive(Serialize)]
imple_hook_data_env!(PostOperationHookData);
#[derive(Clone, Serialize)]
pub struct ChallengeHookData {
pub domain: String,
pub challenge: String,
pub file_name: String,
pub proof: String,
pub is_clean_hook: bool,
pub env: HashMap<String, String>,
}
#[derive(Serialize)]
imple_hook_data_env!(ChallengeHookData);
#[derive(Clone, Serialize)]
pub struct FileStorageHookData {
// TODO: add the current operation (create/edit)
pub file_name: String,
pub file_directory: String,
pub file_path: PathBuf,
pub env: HashMap<String, String>,
}
imple_hook_data_env!(FileStorageHookData);
#[derive(Clone, Debug)]
pub struct Hook {
pub name: String,
@ -64,14 +90,19 @@ macro_rules! get_hook_output {
}};
}
fn call_single<T: Serialize>(data: &T, hook: &Hook) -> Result<(), Error> {
fn call_single<T>(data: &T, hook: &Hook) -> Result<(), Error>
where
T: Clone + HookEnvData + Serialize,
{
debug!("Calling hook: {}", hook.name);
let mut data = (*data).clone();
data.set_env();
let reg = Handlebars::new();
let mut v = vec![];
let args = match &hook.args {
Some(lst) => {
for fmt in lst.iter() {
let s = reg.render_template(fmt, data)?;
let s = reg.render_template(fmt, &data)?;
v.push(s);
}
v.as_slice()
@ -82,15 +113,15 @@ fn call_single<T: Serialize>(data: &T, hook: &Hook) -> Result<(), Error> {
debug!("Hook {}: args: {:?}", hook.name, args);
let mut cmd = Command::new(&hook.cmd)
.args(args)
.stdout(get_hook_output!(&hook.stdout, reg, data))
.stderr(get_hook_output!(&hook.stderr, reg, data))
.stdout(get_hook_output!(&hook.stdout, reg, &data))
.stderr(get_hook_output!(&hook.stderr, reg, &data))
.stdin(match &hook.stdin {
Some(_) => Stdio::piped(),
None => Stdio::null(),
})
.spawn()?;
if hook.stdin.is_some() {
let data_in = reg.render_template(&hook.stdin.to_owned().unwrap(), data)?;
let data_in = reg.render_template(&hook.stdin.to_owned().unwrap(), &data)?;
debug!("Hook {}: stdin: {}", hook.name, data_in);
let stdin = cmd.stdin.as_mut().ok_or("stdin not found")?;
stdin.write_all(data_in.as_bytes())?;
@ -104,7 +135,10 @@ fn call_single<T: Serialize>(data: &T, hook: &Hook) -> Result<(), Error> {
Ok(())
}
pub fn call<T: Serialize>(data: &T, hooks: &[Hook], hook_type: HookType) -> Result<(), Error> {
pub fn call<T>(data: &T, hooks: &[Hook], hook_type: HookType) -> Result<(), Error>
where
T: Clone + HookEnvData + Serialize,
{
for hook in hooks.iter().filter(|h| h.hook_type.contains(&hook_type)) {
call_single(data, &hook)?;
}

2
acmed/src/storage.rs

@ -6,6 +6,7 @@ use acme_common::error::Error;
use log::trace;
use openssl::pkey::{PKey, Private, Public};
use openssl::x509::X509;
use std::collections::HashMap;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
@ -138,6 +139,7 @@ fn write_file(cert: &Certificate, file_type: FileType, data: &[u8]) -> Result<()
file_name,
file_directory,
file_path: path.to_path_buf(),
env: HashMap::new(),
};
let is_new = !path.is_file();

48
man/en/acmed.toml.5

@ -211,21 +211,23 @@ Invoked when the ownership of a domain must be proved using the
.Em http-01
challenge. The available template variables are:
.Bl -tag -compact
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm challenge Ar string
The name of the challenge type
.Aq http-01 .
Mostly used in hooks with multiple types.
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm env Ar array
Array containing all the environment variables.
.It Cm file_name Ar string
Name of the file containing the proof. This is not a full path and does not include the
.Ql .well-known/acme-challenge/
prefix.
.It Cm is_clean_hook Ar bool
False
.It Cm proof Ar string
The content of the proof that must be written to
.Em file_name .
.It Cm is_clean_hook Ar bool
False
.El
.It Ic challenge-http-01-clean
Invoked once a domain ownership has been proven using the
@ -241,20 +243,22 @@ Invoked when the ownership of a domain must be proved using the
.Em dns-01
challenge. The available template variables are:
.Bl -tag -compact
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm challenge Ar string
The name of the challenge type
.Aq dns-01 .
Mostly used in hooks with multiple types.
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm env Ar array
Array containing all the environment variables.
.It Cm is_clean_hook Ar bool
False
.It Cm proof Ar string
The content of the proof that must be written to a
.Ql TXT
entry of the DNS zone for the
.Ql _acme-challenge
subdomain.
.It Cm is_clean_hook Ar bool
False
.El
.It Ic challenge-dns-01-clean
Invoked once a domain ownership has been proven using the
@ -270,12 +274,16 @@ Invoked when the ownership of a domain must be proved using the
.Em tls-alpn-01
challenge. The available template variables are:
.Bl -tag -compact
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm challenge Ar string
The name of the challenge type
.Aq tls-alpn-01 .
Mostly used in hooks with multiple types.
.It Cm domain Ar string
The domain name whom ownership is currently being validated.
.It Cm env Ar array
Array containing all the environment variables.
.It Cm is_clean_hook Ar bool
False
.It Cm proof Ar string
Plain-text representation of the
.Em acmeIdentifier
@ -285,8 +293,6 @@ ALPN extension value.
.Xr acmed 8
will not generate the certificate itself since it can be done using
.Xr tacd 8 .
.It Cm is_clean_hook Ar bool
False
.El
.It Ic challenge-tls-alpn-01-clean
Invoked once a domain ownership has been proven using the
@ -304,10 +310,12 @@ a non-existent file
.Em created .
The available template variables are:
.Bl -tag -compact
.It Cm file_name Ar string
Name of the impacted file.
.It Cm env Ar array
Array containing all the environment variables.
.It Cm file_directory Ar string
Name of the directory where the impacted file is located.
.It Cm file_name Ar string
Name of the impacted file.
.It Cm file_path Ar string
Full path to the impacted file.
.El
@ -338,14 +346,16 @@ type.
.It Ic post-operation
Invoked at the end of the certificate request process. The available template variables are:
.Bl -tag -compact
.It Cm domains Ar string
Array containing the domain names included in the requested certificate.
.It Cm algorithm Ar string
Name of the algorithm used in the certificate.
.It Cm status Ar string
Human-readable status. If the certificate request failed, it contains the error description.
.It Cm domains Ar string
Array containing the domain names included in the requested certificate.
.It Cm env Ar array
Array containing all the environment variables.
.It Cm is_success Ar boolean
True if the certificate request is successful.
.It Cm status Ar string
Human-readable status. If the certificate request failed, it contains the error description.
.El
.El
.Sh FILES
@ -460,7 +470,7 @@ args = [
]
stdin = """Subject: Certificate renewal {{#if is_success}}succeeded{{else}}failed{{/if}} for {{domains.[0]}}
The following certificate has {{#unless is_success}}*not* {{/if}}been renewed.
The following certificate has {{#unless is_success}}*not* {{/unless}}been renewed.
domains: {{#each domains}}{{#if @index}}, {{/if}}{{this}}{{/each}}
algorithm: {{algorithm}}
status: {{status}}"""

Loading…
Cancel
Save