diff --git a/CHANGELOG.md b/CHANGELOG.md index 90eaad5..267256e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The minimum supported Rust version (MSRV) is now 1.64. - Manual (and badly designed) threads have been replaced by async. - Randomized early delay, for spacing out renewals when dealing with a lot of certificates. +- Replaced the template engine TinyTemplate with MiniJinja. ## [0.21.0] - 2022-12-19 diff --git a/Cargo.lock b/Cargo.lock index 80c49be..e5b0218 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "env_logger", "glob", "log", + "minijinja", "native-tls", "nix", "openssl", @@ -19,7 +20,6 @@ dependencies = [ "reqwest", "serde_json", "syslog", - "tinytemplate", "toml", ] @@ -35,13 +35,13 @@ dependencies = [ "futures", "glob", "log", + "minijinja", "nix", "nom", "rand", "reqwest", "serde", "serde_json", - "tinytemplate", "tokio", "toml", ] @@ -797,6 +797,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minijinja" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40870a194358132836de5c67e5038c279de3bff7a05f5da201ed13f6064b979" +dependencies = [ + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1387,16 +1396,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.6.0" diff --git a/acme_common/Cargo.toml b/acme_common/Cargo.toml index 2ad86c3..a94e2c7 100644 --- a/acme_common/Cargo.toml +++ b/acme_common/Cargo.toml @@ -24,6 +24,7 @@ daemonize = "0.5" env_logger = "0.10" glob = "0.3" log = "0.4" +minijinja = "1.0.3" native-tls = "0.2" openssl = { version = "0.10", optional = true } openssl-sys = { version = "0.9", optional = true } @@ -31,7 +32,6 @@ punycode = "0.4" reqwest = { version = "0.11.16", default-features = false } serde_json = "1.0" syslog = "6.0" -tinytemplate = "1.2" toml = "0.7" [target.'cfg(unix)'.dependencies] diff --git a/acme_common/src/error.rs b/acme_common/src/error.rs index 18ca131..7f25c7b 100644 --- a/acme_common/src/error.rs +++ b/acme_common/src/error.rs @@ -105,8 +105,8 @@ impl From for Error { } } -impl From for Error { - fn from(error: tinytemplate::error::Error) -> Self { +impl From for Error { + fn from(error: minijinja::Error) -> Self { format!("template error: {error}").into() } } diff --git a/acmed/Cargo.toml b/acmed/Cargo.toml index 22de4af..a78b416 100644 --- a/acmed/Cargo.toml +++ b/acmed/Cargo.toml @@ -31,11 +31,11 @@ log = "0.4" nom = { version = "7.0", default-features = false, features = [] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tinytemplate = "1.2" toml = "0.7" tokio = { version = "1", features = ["full"] } rand = "0.8.5" reqwest = "0.11.16" +minijinja = "1.0.3" [target.'cfg(unix)'.dependencies] nix = "0.26" diff --git a/acmed/build.rs b/acmed/build.rs index 631c03d..7a60ac4 100644 --- a/acmed/build.rs +++ b/acmed/build.rs @@ -117,7 +117,7 @@ fn set_default_values() { set_data_path_if_absent!("ACMED_DEFAULT_CERT_DIR", "certs"); set_env_var_if_absent!( "ACMED_DEFAULT_CERT_FORMAT", - "{ name }_{ key_type }.{ file_type }.{ ext }" + "{{ name }}_{{ key_type }}.{{ file_type }}.{{ ext }}" ); set_cfg_path_if_absent!("ACMED_DEFAULT_CONFIG_FILE", "acmed.toml"); set_runstate_path_if_absent!("ACMED_DEFAULT_PID_FILE", "acmed.pid"); diff --git a/acmed/config/default_hooks.toml b/acmed/config/default_hooks.toml index 37e4ffb..1300855 100644 --- a/acmed/config/default_hooks.toml +++ b/acmed/config/default_hooks.toml @@ -12,7 +12,7 @@ # -# http-01 challenge in "/var/www/{ identifier }/" +# http-01 challenge in "/var/www/{{ identifier }}/" # [[hook]] @@ -21,7 +21,7 @@ type = ["challenge-http-01"] cmd = "mkdir" args = [ "-m", "0755", - "-p", "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge" + "-p", "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge" ] allow_failure = true @@ -30,7 +30,7 @@ name = "http-01-echo-echo" type = ["challenge-http-01"] cmd = "echo" args = ["{ proof }"] -stdout = "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" +stdout = "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" [[hook]] name = "http-01-echo-chmod" @@ -38,7 +38,7 @@ type = ["challenge-http-01"] cmd = "chmod" args = [ "a+r", - "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" + "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" ] allow_failure = true @@ -48,7 +48,7 @@ type = ["challenge-http-01-clean"] cmd = "rm" args = [ "-f", - "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" + "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" ] allow_failure = true @@ -71,10 +71,10 @@ name = "tls-alpn-01-tacd-start-tcp" type = ["challenge-tls-alpn-01"] cmd = "tacd" args = [ - "--pid-file", "{{ if env.TACD_PID_ROOT }}{ env.TACD_PID_ROOT }{{ else }}/run{{ endif }}/tacd_{ identifier }.pid", + "--pid-file", "{{ env.TACD_PID_ROOT | default('/run') }}/tacd_{{ identifier }}.pid", "--domain", "{ identifier_tls_alpn }", "--acme-ext", "{ proof }", - "--listen", "{{ if env.TACD_HOST }}{ env.TACD_HOST }{{ else }}{ identifier }{{ endif }}:{{ if env.TACD_PORT }}{ env.TACD_PORT }{{ else }}5001{{ endif }}" + "--listen", "{{ env.TACD_PORT | default('5001') }}" ] [[hook]] @@ -82,10 +82,10 @@ name = "tls-alpn-01-tacd-start-unix" type = ["challenge-tls-alpn-01"] cmd = "tacd" args = [ - "--pid-file", "{{ if env.TACD_PID_ROOT }}{ env.TACD_PID_ROOT }{{ else }}/run{{ endif }}/tacd_{ identifier }.pid", + "--pid-file", "{{ env.TACD_PID_ROOT | default('/run') }}/tacd_{{ identifier }}.pid", "--domain", "{ identifier_tls_alpn }", "--acme-ext", "{ proof }", - "--listen", "unix:{{ if env.TACD_SOCK_ROOT }}{ env.TACD_SOCK_ROOT }{{ else }}/run{{ endif }}/tacd_{ identifier }.sock" + "--listen", "unix:{{ env.TACD_SOCK_ROOT | default('/run') }}/tacd_{{ identifier }}.sock" ] [[hook]] @@ -93,7 +93,7 @@ name = "tls-alpn-01-tacd-kill" type = ["challenge-tls-alpn-01-clean"] cmd = "pkill" args = [ - "-F", "{{ if env.TACD_PID_ROOT }}{ env.TACD_PID_ROOT }{{ else }}/run{{ endif }}/tacd_{ identifier }.pid", + "-F", "{{ env.TACD_PID_ROOT | default('/run') }}/tacd_{{ identifier }}.pid", ] allow_failure = true @@ -102,7 +102,7 @@ name = "tls-alpn-01-tacd-rm" type = ["challenge-tls-alpn-01-clean"] cmd = "rm" args = [ - "-f", "{{ if env.TACD_PID_ROOT }}{ env.TACD_PID_ROOT }{{ else }}/run{{ endif }}/tacd_{ identifier }.pid", + "-f", "{{ env.TACD_PID_ROOT | default('/run') }}/tacd_{{ identifier }}.pid", ] allow_failure = true @@ -125,7 +125,7 @@ type = ["file-pre-create", "file-pre-edit"] cmd = "git" args = [ "init", - "{ file_directory }" + "{{ file_directory }}" ] [[hook]] @@ -133,8 +133,8 @@ name = "git-add" type = ["file-post-create", "file-post-edit"] cmd = "git" args = [ - "-C", "{ file_directory }", - "add", "{ file_name }" + "-C", "{{ file_directory }}", + "add", "{{ file_name }}" ] allow_failure = true @@ -143,12 +143,12 @@ name = "git-commit" type = ["file-post-create", "file-post-edit"] cmd = "git" args = [ - "-C", "{ file_directory }", - "-c", "user.name='{{ if env.GIT_USERNAME }}{ env.GIT_USERNAME }{{ else }}ACMEd{{ endif }}'", - "-c", "user.email='{{ if env.GIT_EMAIL }}{ env.GIT_EMAIL }{{ else }}acmed@localhost{{ endif }}'", + "-C", "{{ file_directory }}", + "-c", "user.name='{{ env.GIT_USERNAME | default('ACMEd') }}'", + "-c", "user.email='{{ env.GIT_EMAIL | default('acmed@localhost') }}'", "commit", - "-m", "{ file_name }", - "--only", "{ file_name }" + "-m", "{{ file_name }}", + "--only", "{{ file_name }}" ] allow_failure = true diff --git a/acmed/src/template.rs b/acmed/src/template.rs index 976f79e..2b46975 100644 --- a/acmed/src/template.rs +++ b/acmed/src/template.rs @@ -1,37 +1,23 @@ -use acme_common::error::Error; +use minijinja::{value::Value, Environment}; use serde::Serialize; -use serde_json::Value; -use tinytemplate::TinyTemplate; -macro_rules! default_format { - ($value: ident, $output: ident) => {{ - $output.push_str(&$value.to_string()); - Ok(()) - }}; -} - -fn formatter_rev_labels(value: &Value, output: &mut String) -> tinytemplate::error::Result<()> { - match value { - Value::Null => Ok(()), - Value::Bool(v) => default_format!(v, output), - Value::Number(v) => default_format!(v, output), - Value::String(v) => { - let s = v.rsplit('.').collect::>().join("."); - output.push_str(&s); - Ok(()) - } - _ => Ok(()), +fn formatter_rev_labels(value: Value) -> Result { + if let Some(value) = value.as_str() { + Ok(value.rsplit('.').collect::>().join(".").into()) + } else { + Ok(value) } } -pub fn render_template(template: &str, data: &T) -> Result +pub fn render_template(template: &str, data: &T) -> Result where T: Serialize, { - let mut reg = TinyTemplate::new(); - reg.add_formatter("rev_labels", formatter_rev_labels); - reg.add_template("reg", template)?; - Ok(reg.render("reg", data)?) + let mut environment = Environment::new(); + environment.add_filter("rev_labels", formatter_rev_labels); + environment.add_template("template", template)?; + let template = environment.get_template("template")?; + Ok(template.render(data)?) } #[cfg(test)] @@ -51,7 +37,7 @@ mod tests { foo: String::from("test"), bar: 42, }; - let tpl = "This is { foo } { bar -} !"; + let tpl = "This is {{ foo }} {{ bar -}} !"; let rendered = render_template(tpl, &c); assert!(rendered.is_ok()); let rendered = rendered.unwrap(); @@ -64,7 +50,7 @@ mod tests { foo: String::from("mx1.example.org"), bar: 42, }; - let tpl = "{ foo } - { foo | rev_labels }"; + let tpl = "{{ foo }} - {{ foo | rev_labels }}"; let rendered = render_template(tpl, &c); assert!(rendered.is_ok()); let rendered = rendered.unwrap(); diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index bb7286f..0d3be4e 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -122,17 +122,17 @@ Name of the endpoint to use. Table of environment variables that will be accessible from hooks. .It Ic file_name_format Ar string Template used to build the file's name. The template syntax is -.Em TinyTemplate . +.Em MiniJinja . See the .Sx STANDARDS section for a link to the -.Em TinyTemplate +.Em MiniJinja specifications. If not specified, the value defined in the .Em endpoint element, and then the .Em global element, is used. Default is -.Dq { name }_{ key_type }.{ file_type }.{ ext } . +.Dq {{ name }}_{{ key_type }}.{{ file_type }}.{{ ext }} . Possible variables are: .Bl -tag .It Ic ext Ar string @@ -424,11 +424,11 @@ and are considered as template strings whereas .Em cmd is not. The template syntax is -.Em TinyTemplate . +.Em MiniJinja . See the .Sx STANDARDS section for a link to the -.Em TinyTemplate +.Em MiniJinja specifications. .Pp The available types and the associated template variable are described below. @@ -604,10 +604,10 @@ and environment variables. .It Pa http-01-echo This hook is designed to solve the http-01 challenge. For this purpose, it will write the proof into -.Pa { env.HTTP_ROOT }/{ identifier }/.well-known/acme-challenge/{ file_name } . +.Pa {{ env.HTTP_ROOT }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }} . .Pp The web server must be configured so the file -.Pa http://{ identifier }/.well-known/acme-challenge/{ file_name } +.Pa http://{{ identifier }}/.well-known/acme-challenge/{{ file_name }} can be accessed from the CA. .Pp If @@ -632,7 +632,7 @@ environment variable (default is 5001). .Pp .Xr tacd 8 will store its pid into -.Pa { TACD_PID_ROOT }/tacd_{ identifier }.pid . +.Pa {{ TACD_PID_ROOT }}/tacd_{{ identifier }}.pid . If .Ev TACD_PID_ROOT is not specified, it will be set to @@ -648,7 +648,7 @@ option. .Pp .Xr tacd 8 will listen on the unix socket -.Pa { env.TACD_SOCK_ROOT }/tacd_{ identifier }.sock . +.Pa {{ env.TACD_SOCK_ROOT }}/tacd_{{ identifier }}.sock . If .Ev TACD_SOCK_ROOT is not specified, it will be set to @@ -656,7 +656,7 @@ is not specified, it will be set to .Pp .Xr tacd 8 will store its pid into -.Pa { TACD_PID_ROOT }/tacd_{ identifier }.pid . +.Pa {{ TACD_PID_ROOT }}/tacd_{{ identifier }}.pid . If .Ev TACD_PID_ROOT is not specified, it will be set to @@ -696,8 +696,8 @@ For example, and .Dq 40s20h4h2s both represents a period of one day and forty-two seconds. -.Sh TEMPLATE FORMATTERS -In addition the the formatters provided by default by TinyTemplate, ACMEd provides the following formatters: +.Sh TEMPLATE FILTERS +In addition the the filters provided by default by MiniJinja, ACMEd provides the following filters: .Bl -tag .It Pa rev_labels Reverts the labels of a domain name (eg: @@ -763,7 +763,7 @@ type = ["challenge-http-01"] cmd = "mkdir" args = [ "-m", "0755", - "-p", "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge" + "-p", "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge" ] [[hook]] @@ -771,7 +771,7 @@ name = "http-01-echo-echo" type = ["challenge-http-01"] cmd = "echo" args = ["{ proof }"] -stdout = "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" +stdout = "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" [[hook]] name = "http-01-echo-chmod" @@ -779,7 +779,7 @@ type = ["challenge-http-01-clean"] cmd = "chmod" args = [ "a+r", - "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" + "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" ] [[hook]] @@ -788,7 +788,7 @@ type = ["challenge-http-01-clean"] cmd = "rm" args = [ "-f", - "{{ if env.HTTP_ROOT }}{ env.HTTP_ROOT }{{ else }}/var/www{{ endif }}/{ identifier }/.well-known/acme-challenge/{ file_name }" + "{{ env.HTTP_ROOT | default('/var/www') }}/{{ identifier }}/.well-known/acme-challenge/{{ file_name }}" ] .Ed .Pp @@ -821,12 +821,12 @@ args = [ "-f", "noreply.certs@example.net", "contact@example.net" ] -stdin_str = """Subject: Certificate renewal {{ if is_success }}succeeded{{ else }}failed{{ endif }} for { identifiers.0 } +stdin_str = """Subject: Certificate renewal {{ 'succeeded' if is_success else 'failed' }} for {{ identifiers.0 }} -The following certificate has {{ if not is_success }}*not* {{ endif }}been renewed. -identifiers: {{ for ident in identifiers }}{{ if not @first }}, {{ endif }}{ ident }{{ endfor }} -key type: { key_type } -status: { status }""" +The following certificate has {{ '' if is_success else '*not* ' }}been renewed. +identifiers: {% for ident in identifiers %}{% if not loop.first %}, {% endif %}{{ ident }}{% endfor %} +key type: {{ key_type }} +status: {{ status }}""" .Ed .Sh SEE ALSO .Xr acmed 8 , @@ -842,9 +842,9 @@ status: { status }""" .Re .It .Rs -.%A Brook Heisler -.%T TinyTemplate -.%U https://docs.rs/tinytemplate/latest/tinytemplate/syntax/index.html +.%A Armin Ronacher +.%T MiniJinja +.%U https://docs.rs/minijinja/latest/minijinja/syntax/index.html .Re .It .Rs