mirror of https://github.com/breard-r/acmed.git
Rodolphe Breard
4 years ago
19 changed files with 449 additions and 139 deletions
-
7CHANGELOG.md
-
2README.md
-
29acme_common/src/crypto/openssl_certificate.rs
-
6acme_common/src/error.rs
-
1acme_common/src/tests.rs
-
84acme_common/src/tests/certificate.rs
-
26acmed/config/default_hooks.toml
-
36acmed/src/acme_proto.rs
-
2acmed/src/acme_proto/structs.rs
-
2acmed/src/acme_proto/structs/authorization.rs
-
29acmed/src/acme_proto/structs/order.rs
-
66acmed/src/certificate.rs
-
54acmed/src/config.rs
-
5acmed/src/hooks.rs
-
149acmed/src/identifier.rs
-
1acmed/src/main.rs
-
2acmed/src/main_event_loop.rs
-
8man/en/acmed.8
-
79man/en/acmed.toml.5
@ -1,2 +1,3 @@ |
|||
mod certificate;
|
|||
mod crypto_keys;
|
|||
mod idna;
|
@ -0,0 +1,84 @@ |
|||
use crate::crypto::X509Certificate;
|
|||
use std::collections::HashSet;
|
|||
use std::iter::FromIterator;
|
|||
|
|||
const CERTIFICATE_P256_DOMAINS_PEM: &str = r#"-----BEGIN CERTIFICATE-----
|
|||
MIICtDCCAZygAwIBAgIIf5BEPlNrrYkwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
|
|||
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAyZDE2ODgwHhcNMjAwODI1MTMwMzE3
|
|||
WhcNMjUwODI1MTMwMzE3WjAYMRYwFAYDVQQDEw1sb2NhbC53aGF0LnRmMFkwEwYH
|
|||
KoZIzj0CAQYIKoZIzj0DAQcDQgAE0c/unUqpoOMxxc8e1pkpPQTSsh2irQruOJgd
|
|||
ITN9WLC4mzFSJ/ad64TFi4HsCFNd7mv/QRH6rW1s3LbocEvBuqOBvDCBuTAOBgNV
|
|||
HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud
|
|||
EwEB/wQCMAAwHQYDVR0OBBYEFGjf1TWIZyE+QP9SGkBN6dfviIsaMB8GA1UdIwQY
|
|||
MBaAFLgD5DMU2ijpIxlxaAv82sQvb5ofMDoGA1UdEQQzMDGCDWxvY2FsLndoYXQu
|
|||
dGaCDzEubG9jYWwud2hhdC50ZoIPMi5sb2NhbC53aGF0LnRmMA0GCSqGSIb3DQEB
|
|||
CwUAA4IBAQDREOAU2JwHfSPGt4SYlQ3OmFl4HHI2f+XyNE/09uZVteM0aChkntgX
|
|||
rAZltuAAX+coSlgv3a04hJBqioDG1R9MFtf4LZBhfkgZwbzucMt8Ga3QL3XFXOkn
|
|||
FlOwb/ZEIjFsBFQWt1ZSA85WxIVkGsgMfQeGpu/p8gEmJAE5l0qHEVFP9cYNsIqg
|
|||
wsUGwZzPZFLsBXurM2cEA7cTt2HryVXlQWl8QI5YFpIpa43itYaerfMldfIfNdJ9
|
|||
8GLZPEfJb6t/UYYexXEkpQY9wGZkaTWvYeItuC0YlPY9RUCAl48Q85Yjf37Wbm5z
|
|||
f810HGl+/c6ttyoHKmLfY/GcX07AUcLc
|
|||
-----END CERTIFICATE-----"#;
|
|||
const CERTIFICATE_P256_IP_PEM: &str = r#"-----BEGIN CERTIFICATE-----
|
|||
MIICkTCCAXmgAwIBAgIIMW1X7DjQOFgwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
|
|||
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxYWM3MzcwHhcNMjAwODI1MTQzMjQw
|
|||
WhcNMjUwODI1MTQzMjQwWjAOMQwwCgYDVQQDEwM6OjEwWTATBgcqhkjOPQIBBggq
|
|||
hkjOPQMBBwNCAASF+MvxX7GBAVe3McuAc+0emdFpBfAQG4mt9j8417qT76qHHyJ6
|
|||
oIHRNXAUxh4J78ihrvyph8TvqND73Nxk8Jj9o4GjMIGgMA4GA1UdDwEB/wQEAwIF
|
|||
oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
|
|||
BgNVHQ4EFgQU5R7EGzjpZqrs2o/ZwuBqNHlMB2AwHwYDVR0jBBgwFoAUhEUnWREW
|
|||
GoAScr1wv/aXHTGOVoswIQYDVR0RBBowGIcQAAAAAAAAAAAAAAAAAAAAAYcEfwAA
|
|||
ATANBgkqhkiG9w0BAQsFAAOCAQEAS8oRpjGakUU+KRtXCGoVlXgKYFe3u/G2aFMF
|
|||
soApjvwd3L1W9b3bsT4FquF7F5qB6TGBwiXoNBoDAeVhRcUsHbmN8GZRUaq2TEsm
|
|||
MwpPr8L4rqeRIuxY85AqmbGfMuFUie6r4FbwelnBniO0eMQkTW/XY41rbhGZ+lmj
|
|||
DTQy08oj0892py2U/YbkL3JnCBwBba//f/Ji7nnSKdJl4Yd1iguA0nbdElcWaKk3
|
|||
ij3t17FSNeI5uMOI3TRBr4k4bu3ZMnuN2DYFPnL6GiSEhyNrxaiac8xKuOXBICmJ
|
|||
oyO7pZVvc5cDcP/USPcWJYcnR9gvuL8snQdFpWND8H19eZ+i0g==
|
|||
-----END CERTIFICATE-----"#;
|
|||
const CERTIFICATE_P256_DOMAINS_IP_PEM: &str = r#"-----BEGIN CERTIFICATE-----
|
|||
MIICzDCCAbSgAwIBAgIIff0SyxJBhtMwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
|
|||
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxYWM3MzcwHhcNMjAwODI1MTQzNjE1
|
|||
WhcNMjUwODI1MTQzNjE1WjAYMRYwFAYDVQQDEw1sb2NhbC53aGF0LnRmMFkwEwYH
|
|||
KoZIzj0CAQYIKoZIzj0DAQcDQgAE7Jp4AmF0TTcYfUy4TtZhN4bXn4DXWnqF0I6i
|
|||
Yvz4kc0r2L01nrUrICg2bmCFM7BU9pr9fcCDodH3ZuhlRqBAf6OB1DCB0TAOBgNV
|
|||
HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud
|
|||
EwEB/wQCMAAwHQYDVR0OBBYEFHV0lnh55aQGfljcsjNkzZa4lTG6MB8GA1UdIwQY
|
|||
MBaAFIRFJ1kRFhqAEnK9cL/2lx0xjlaLMFIGA1UdEQRLMEmCDWxvY2FsLndoYXQu
|
|||
dGaCDzEubG9jYWwud2hhdC50ZoIPMi5sb2NhbC53aGF0LnRmhwR/AAABhxAAAAAA
|
|||
AAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQC3VmoTlrrTCWCd4eUB4RSB
|
|||
+080uco6Jl7VMqcY5F+eG1S7p4Kqz6kc1wiiKB8ILA94hdP1qTbfphdGllYiEvbs
|
|||
urj0x62cm5URahEDx4xn+dQkmh4XiiZgZVw2ccphjqJqJa28GsuR2zAxSkKMDnB7
|
|||
eX1G4/Av0XE7RqJ3Frq8qa5EjjLJTw0iEaWS5NGtZxMqWEIetCgb0IDZNxNvbeAv
|
|||
mmH6qnF3xQPx5FkwP/Yw4d9T4KhSHNf2/tImIlbuk3SEsOglGbKNY1juor8uw+J2
|
|||
5XsUZxD5QiDbCFd3dGmH58XmkiQHXs8hhIbhu9ZLgp+fNv0enVMHTTI1gGpZ5MPm
|
|||
-----END CERTIFICATE-----"#;
|
|||
|
|||
#[test]
|
|||
fn test_san_domains() {
|
|||
let san = vec!["local.what.tf", "1.local.what.tf", "2.local.what.tf"];
|
|||
let san = HashSet::from_iter(san.iter().map(|v| v.to_string()));
|
|||
let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_PEM.as_bytes()).unwrap();
|
|||
assert_eq!(crt.subject_alt_names(), san);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_san_ip() {
|
|||
let san = vec!["127.0.0.1", "::1"];
|
|||
let san = HashSet::from_iter(san.iter().map(|v| v.to_string()));
|
|||
let crt = X509Certificate::from_pem(CERTIFICATE_P256_IP_PEM.as_bytes()).unwrap();
|
|||
assert_eq!(crt.subject_alt_names(), san);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_san_domains_and_ip() {
|
|||
let san = vec![
|
|||
"127.0.0.1",
|
|||
"::1",
|
|||
"local.what.tf",
|
|||
"1.local.what.tf",
|
|||
"2.local.what.tf",
|
|||
];
|
|||
let san = HashSet::from_iter(san.iter().map(|v| v.to_string()));
|
|||
let crt = X509Certificate::from_pem(CERTIFICATE_P256_DOMAINS_IP_PEM.as_bytes()).unwrap();
|
|||
assert_eq!(crt.subject_alt_names(), san);
|
|||
}
|
@ -0,0 +1,149 @@ |
|||
use crate::acme_proto::Challenge;
|
|||
use acme_common::error::Error;
|
|||
use acme_common::to_idna;
|
|||
use serde::{Deserialize, Serialize};
|
|||
use std::collections::HashMap;
|
|||
use std::fmt;
|
|||
use std::net::IpAddr;
|
|||
use std::str::FromStr;
|
|||
|
|||
// RFC 3596, section 2.5
|
|||
fn u8_to_nibbles_string(value: &u8) -> String {
|
|||
let bytes = value.to_ne_bytes();
|
|||
let first = bytes[0] & 0x0f;
|
|||
let second = (bytes[0] >> 4) & 0x0f;
|
|||
format!("{:x}.{:x}", first, second)
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
|
|||
pub enum IdentifierType {
|
|||
#[serde(rename = "dns")]
|
|||
Dns,
|
|||
#[serde(rename = "ip")]
|
|||
Ip,
|
|||
}
|
|||
|
|||
impl IdentifierType {
|
|||
pub fn supported_challenges(&self) -> Vec<Challenge> {
|
|||
match self {
|
|||
IdentifierType::Dns => vec![Challenge::Http01, Challenge::Dns01, Challenge::TlsAlpn01],
|
|||
IdentifierType::Ip => vec![Challenge::Http01, Challenge::TlsAlpn01],
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Display for IdentifierType {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
let name = match self {
|
|||
IdentifierType::Dns => "dns",
|
|||
IdentifierType::Ip => "ip",
|
|||
};
|
|||
write!(f, "{}", name)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug)]
|
|||
pub struct Identifier {
|
|||
pub id_type: IdentifierType,
|
|||
pub value: String,
|
|||
pub challenge: Challenge,
|
|||
pub env: HashMap<String, String>,
|
|||
}
|
|||
|
|||
impl Identifier {
|
|||
pub fn new(
|
|||
id_type: IdentifierType,
|
|||
value: &str,
|
|||
challenge: &str,
|
|||
env: &HashMap<String, String>,
|
|||
) -> Result<Self, Error> {
|
|||
let value = match id_type {
|
|||
IdentifierType::Dns => to_idna(value)?,
|
|||
IdentifierType::Ip => IpAddr::from_str(value)?.to_string(),
|
|||
};
|
|||
let challenge = Challenge::from_str(challenge)?;
|
|||
if !id_type.supported_challenges().contains(&challenge) {
|
|||
let msg = format!(
|
|||
"Challenge {} cannot be used with identifier of type {}",
|
|||
challenge, id_type
|
|||
);
|
|||
return Err(msg.into());
|
|||
}
|
|||
Ok(Identifier {
|
|||
id_type,
|
|||
value,
|
|||
challenge,
|
|||
env: env.clone(),
|
|||
})
|
|||
}
|
|||
|
|||
pub fn get_tls_alpn_name(&self) -> Result<String, Error> {
|
|||
match &self.id_type {
|
|||
IdentifierType::Dns => Ok(self.value.to_owned()),
|
|||
IdentifierType::Ip => match IpAddr::from_str(&self.value)? {
|
|||
IpAddr::V4(ip) => {
|
|||
let dn = ip
|
|||
.octets()
|
|||
.iter()
|
|||
.rev()
|
|||
.map(|v| v.to_string())
|
|||
.collect::<Vec<String>>()
|
|||
.join(".");
|
|||
let dn = format!("{}.in-addr.arpa", dn);
|
|||
Ok(dn)
|
|||
}
|
|||
IpAddr::V6(ip) => {
|
|||
let dn = ip
|
|||
.octets()
|
|||
.iter()
|
|||
.rev()
|
|||
.map(u8_to_nibbles_string)
|
|||
.collect::<Vec<String>>()
|
|||
.join(".");
|
|||
let dn = format!("{}.ip6.arpa", dn);
|
|||
Ok(dn)
|
|||
}
|
|||
},
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Display for Identifier {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
write!(f, "{}: {} ({})", self.id_type, self.value, self.challenge)
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod tests {
|
|||
use super::*;
|
|||
use std::collections::HashMap;
|
|||
|
|||
#[test]
|
|||
fn test_ipv4_tls_alpn_name() {
|
|||
let env = HashMap::new();
|
|||
let id = Identifier::new(IdentifierType::Ip, "203.0.113.1", "http-01", &env).unwrap();
|
|||
assert_eq!(&id.get_tls_alpn_name().unwrap(), "1.113.0.203.in-addr.arpa");
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_ipv6_tls_alpn_name() {
|
|||
let env = HashMap::new();
|
|||
let id = Identifier::new(IdentifierType::Ip, "2001:db8::1", "http-01", &env).unwrap();
|
|||
assert_eq!(
|
|||
&id.get_tls_alpn_name().unwrap(),
|
|||
"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
|
|||
);
|
|||
let id = Identifier::new(
|
|||
IdentifierType::Ip,
|
|||
"4321:0:1:2:3:4:567:89ab",
|
|||
"http-01",
|
|||
&env,
|
|||
)
|
|||
.unwrap();
|
|||
assert_eq!(
|
|||
&id.get_tls_alpn_name().unwrap(),
|
|||
"b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa"
|
|||
);
|
|||
}
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue