mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-10-03 17:24:42 +08:00
ACME External Account Binding support (closes #379 closes ##650)
This commit is contained in:
parent
a1ca7fa849
commit
8ff2438f04
6 changed files with 193 additions and 111 deletions
|
@ -21,8 +21,6 @@
|
|||
<a href="https://mastodon.social/@stalwartlabs"><img src="https://img.shields.io/mastodon/follow/109929667531941122?style=flat-square&logo=mastodon&color=%236364ff&label=Follow%20on%20Mastodon" alt="Mastodon"></a>
|
||||
|
||||
<a href="https://twitter.com/stalwartlabs"><img src="https://img.shields.io/twitter/follow/stalwartlabs?style=flat-square&logo=x&label=Follow%20on%20Twitter" alt="Twitter"></a>
|
||||
|
||||
<a href="nostr:npub167hk2ermhky3pmudc3q0d2vnnhcesdgsrcqgywv447ls4xs5u89q5d6395"><img src="https://img.shields.io/nostr-band/followers/npub167hk2ermhky3pmudc3q0d2vnnhcesdgsrcqgywv447ls4xs5u89q5d6395?style=flat-square&logo=chatbot&label=Follow%20on%20Nostr" alt="Nostr"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/jtgtCNj66U"><img src="https://img.shields.io/discord/923615863037390889?label=Join%20Discord&logo=discord&style=flat-square" alt="Discord"></a>
|
||||
|
|
|
@ -12,7 +12,10 @@ use std::{
|
|||
};
|
||||
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use base64::{
|
||||
engine::general_purpose::{self, STANDARD},
|
||||
Engine,
|
||||
};
|
||||
use dns_update::{providers::rfc2136::DnsAddress, DnsUpdater, TsigAlgorithm};
|
||||
use rcgen::generate_simple_self_signed;
|
||||
use rustls::{
|
||||
|
@ -31,7 +34,9 @@ use x509_parser::{
|
|||
};
|
||||
|
||||
use crate::listener::{
|
||||
acme::{directory::LETS_ENCRYPT_PRODUCTION_DIRECTORY, AcmeProvider, ChallengeSettings},
|
||||
acme::{
|
||||
directory::LETS_ENCRYPT_PRODUCTION_DIRECTORY, AcmeProvider, ChallengeSettings, EabSettings,
|
||||
},
|
||||
tls::AcmeProviders,
|
||||
};
|
||||
|
||||
|
@ -129,6 +134,34 @@ impl AcmeProviders {
|
|||
continue 'outer;
|
||||
}
|
||||
|
||||
// Obtain EAB settings
|
||||
let eab = if let (Some(eab_kid), Some(eab_hmac_key)) = (
|
||||
config
|
||||
.value(("acme", acme_id, "eab.kid"))
|
||||
.filter(|s| !s.is_empty()),
|
||||
config
|
||||
.value(("acme", acme_id, "eab.hmac-key"))
|
||||
.filter(|s| !s.is_empty()),
|
||||
) {
|
||||
if let Ok(hmac_key) =
|
||||
general_purpose::URL_SAFE_NO_PAD.decode(eab_hmac_key.trim().as_bytes())
|
||||
{
|
||||
EabSettings {
|
||||
kid: eab_kid.to_string(),
|
||||
hmac_key,
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
config.new_build_error(
|
||||
format!("acme.{acme_id}.eab.hmac-key"),
|
||||
"Failed to base64 decode HMAC key",
|
||||
);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// This ACME manager is the default when SNI is not available
|
||||
let default = config
|
||||
.property::<bool>(("acme", acme_id, "default"))
|
||||
|
@ -141,6 +174,7 @@ impl AcmeProviders {
|
|||
domains,
|
||||
contact,
|
||||
challenge,
|
||||
eab,
|
||||
renew_before,
|
||||
default,
|
||||
) {
|
||||
|
|
|
@ -10,14 +10,16 @@ use reqwest::{Method, Response};
|
|||
use ring::rand::SystemRandom;
|
||||
use ring::signature::{EcdsaKeyPair, EcdsaSigningAlgorithm, ECDSA_P256_SHA256_FIXED_SIGNING};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use store::write::Bincode;
|
||||
use store::Serialize;
|
||||
use trc::event::conv::AssertSuccess;
|
||||
use trc::AddContext;
|
||||
|
||||
use super::jose::{
|
||||
key_authorization, key_authorization_sha256, key_authorization_sha256_base64, sign,
|
||||
eab_sign, key_authorization, key_authorization_sha256, key_authorization_sha256_base64, sign,
|
||||
Body,
|
||||
};
|
||||
use super::AcmeProvider;
|
||||
|
||||
pub const LETS_ENCRYPT_STAGING_DIRECTORY: &str =
|
||||
"https://acme-staging-v02.api.letsencrypt.org/directory";
|
||||
|
@ -32,6 +34,16 @@ pub struct Account {
|
|||
pub kid: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct NewAccountPayload<'x> {
|
||||
#[serde(rename = "termsOfServiceAgreed")]
|
||||
tos_agreed: bool,
|
||||
contact: &'x [String],
|
||||
#[serde(rename = "externalAccountBinding")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
eab: Option<Body>,
|
||||
}
|
||||
|
||||
static ALG: &EcdsaSigningAlgorithm = &ECDSA_P256_SHA256_FIXED_SIGNING;
|
||||
|
||||
impl Account {
|
||||
|
@ -42,35 +54,39 @@ impl Account {
|
|||
.to_vec()
|
||||
}
|
||||
|
||||
pub async fn create<'a, S, I>(directory: Directory, contact: I) -> trc::Result<Self>
|
||||
where
|
||||
S: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a S>,
|
||||
{
|
||||
Self::create_with_keypair(directory, contact, &Self::generate_key_pair()).await
|
||||
pub async fn create(directory: Directory, provider: &AcmeProvider) -> trc::Result<Self> {
|
||||
Self::create_with_keypair(directory, provider).await
|
||||
}
|
||||
|
||||
pub async fn create_with_keypair<'a, S, I>(
|
||||
pub async fn create_with_keypair(
|
||||
directory: Directory,
|
||||
contact: I,
|
||||
key_pair: &[u8],
|
||||
) -> trc::Result<Self>
|
||||
where
|
||||
S: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a S>,
|
||||
{
|
||||
let key_pair =
|
||||
EcdsaKeyPair::from_pkcs8(ALG, key_pair, &SystemRandom::new()).map_err(|err| {
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
.reason(err)
|
||||
.caused_by(trc::location!())
|
||||
})?;
|
||||
let contact: Vec<&'a str> = contact.into_iter().map(AsRef::<str>::as_ref).collect();
|
||||
let payload = json!({
|
||||
"termsOfServiceAgreed": true,
|
||||
"contact": contact,
|
||||
provider: &AcmeProvider,
|
||||
) -> trc::Result<Self> {
|
||||
let key_pair = EcdsaKeyPair::from_pkcs8(
|
||||
ALG,
|
||||
provider.account_key.load().as_slice(),
|
||||
&SystemRandom::new(),
|
||||
)
|
||||
.map_err(|err| {
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
.reason(err)
|
||||
.caused_by(trc::location!())
|
||||
})?;
|
||||
let eab = if let Some(eab) = &provider.eab {
|
||||
eab_sign(&key_pair, &eab.kid, &eab.hmac_key, &directory.new_account)
|
||||
.caused_by(trc::location!())?
|
||||
.into()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload = serde_json::to_string(&NewAccountPayload {
|
||||
tos_agreed: true,
|
||||
contact: &provider.contact,
|
||||
eab,
|
||||
})
|
||||
.to_string();
|
||||
.unwrap_or_default();
|
||||
|
||||
let body = sign(
|
||||
&key_pair,
|
||||
None,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
use ring::digest::{digest, Digest, SHA256};
|
||||
use ring::hmac;
|
||||
use ring::rand::SystemRandom;
|
||||
use ring::signature::{EcdsaKeyPair, KeyPair};
|
||||
use serde::Serialize;
|
||||
|
@ -18,7 +19,7 @@ pub(crate) fn sign(
|
|||
None => Some(Jwk::new(key)),
|
||||
Some(_) => None,
|
||||
};
|
||||
let protected = Protected::base64(jwk, kid, nonce, url)?;
|
||||
let protected = Protected::encode("ES256", jwk, kid, nonce.into(), url)?;
|
||||
let payload = URL_SAFE_NO_PAD.encode(payload);
|
||||
let combined = format!("{}.{}", &protected, &payload);
|
||||
let signature = key
|
||||
|
@ -28,15 +29,34 @@ pub(crate) fn sign(
|
|||
.caused_by(trc::location!())
|
||||
.reason(err)
|
||||
})?;
|
||||
let signature = URL_SAFE_NO_PAD.encode(signature.as_ref());
|
||||
let body = Body {
|
||||
|
||||
serde_json::to_string(&Body {
|
||||
protected,
|
||||
payload,
|
||||
signature: URL_SAFE_NO_PAD.encode(signature.as_ref()),
|
||||
})
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
}
|
||||
|
||||
pub(crate) fn eab_sign(
|
||||
key: &EcdsaKeyPair,
|
||||
kid: &str,
|
||||
hmac_key: &[u8],
|
||||
url: &str,
|
||||
) -> trc::Result<Body> {
|
||||
let protected = Protected::encode("HS256", None, kid.into(), None, url)?;
|
||||
let payload = Jwk::new(key).base64()?;
|
||||
let combined = format!("{}.{}", &protected, &payload);
|
||||
|
||||
let key = hmac::Key::new(hmac::HMAC_SHA256, hmac_key);
|
||||
let tag = hmac::sign(&key, combined.as_bytes());
|
||||
let signature = URL_SAFE_NO_PAD.encode(tag.as_ref());
|
||||
|
||||
Ok(Body {
|
||||
protected,
|
||||
payload,
|
||||
signature,
|
||||
};
|
||||
|
||||
serde_json::to_string(&body)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn key_authorization(key: &EcdsaKeyPair, token: &str) -> trc::Result<String> {
|
||||
|
@ -58,8 +78,8 @@ pub(crate) fn key_authorization_sha256_base64(
|
|||
key_authorization_sha256(key, token).map(|s| URL_SAFE_NO_PAD.encode(s.as_ref()))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Body {
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct Body {
|
||||
protected: String,
|
||||
payload: String,
|
||||
signature: String,
|
||||
|
@ -72,27 +92,28 @@ struct Protected<'a> {
|
|||
jwk: Option<Jwk>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
kid: Option<&'a str>,
|
||||
nonce: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
nonce: Option<String>,
|
||||
url: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Protected<'a> {
|
||||
fn base64(
|
||||
fn encode(
|
||||
alg: &'static str,
|
||||
jwk: Option<Jwk>,
|
||||
kid: Option<&'a str>,
|
||||
nonce: String,
|
||||
nonce: Option<String>,
|
||||
url: &'a str,
|
||||
) -> trc::Result<String> {
|
||||
let protected = Self {
|
||||
alg: "ES256",
|
||||
serde_json::to_vec(&Protected {
|
||||
alg,
|
||||
jwk,
|
||||
kid,
|
||||
nonce,
|
||||
url,
|
||||
};
|
||||
let protected = serde_json::to_vec(&protected)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))?;
|
||||
Ok(URL_SAFE_NO_PAD.encode(protected))
|
||||
})
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
.map(|v| URL_SAFE_NO_PAD.encode(v.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,17 +140,24 @@ impl Jwk {
|
|||
y: URL_SAFE_NO_PAD.encode(y),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn base64(&self) -> trc::Result<String> {
|
||||
serde_json::to_vec(self)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
.map(|v| URL_SAFE_NO_PAD.encode(v.as_slice()))
|
||||
}
|
||||
|
||||
pub(crate) fn thumb_sha256_base64(&self) -> trc::Result<String> {
|
||||
let jwk_thumb = JwkThumb {
|
||||
crv: self.crv,
|
||||
kty: self.kty,
|
||||
x: &self.x,
|
||||
y: &self.y,
|
||||
};
|
||||
let json = serde_json::to_vec(&jwk_thumb)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))?;
|
||||
let hash = digest(&SHA256, &json);
|
||||
Ok(URL_SAFE_NO_PAD.encode(hash))
|
||||
Ok(URL_SAFE_NO_PAD.encode(digest(
|
||||
&SHA256,
|
||||
&serde_json::to_vec(&JwkThumb {
|
||||
crv: self.crv,
|
||||
kty: self.kty,
|
||||
x: &self.x,
|
||||
y: &self.y,
|
||||
})
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,18 @@ pub struct AcmeProvider {
|
|||
pub domains: Vec<String>,
|
||||
pub contact: Vec<String>,
|
||||
pub challenge: ChallengeSettings,
|
||||
pub eab: Option<EabSettings>,
|
||||
renew_before: chrono::Duration,
|
||||
account_key: ArcSwap<Vec<u8>>,
|
||||
default: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EabSettings {
|
||||
pub kid: String,
|
||||
pub hmac_key: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ChallengeSettings {
|
||||
Http01,
|
||||
|
@ -49,12 +56,14 @@ pub struct StaticResolver {
|
|||
}
|
||||
|
||||
impl AcmeProvider {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: String,
|
||||
directory_url: String,
|
||||
domains: Vec<String>,
|
||||
contact: Vec<String>,
|
||||
challenge: ChallengeSettings,
|
||||
eab: Option<EabSettings>,
|
||||
renew_before: Duration,
|
||||
default: bool,
|
||||
) -> trc::Result<Self> {
|
||||
|
@ -75,6 +84,7 @@ impl AcmeProvider {
|
|||
domains,
|
||||
account_key: Default::default(),
|
||||
challenge,
|
||||
eab,
|
||||
default,
|
||||
})
|
||||
}
|
||||
|
@ -142,6 +152,7 @@ impl Clone for AcmeProvider {
|
|||
challenge: self.challenge.clone(),
|
||||
renew_before: self.renew_before,
|
||||
account_key: ArcSwap::from_pointee(self.account_key.load().as_ref().clone()),
|
||||
eab: self.eab.clone(),
|
||||
default: self.default,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use rustls::sign::CertifiedKey;
|
|||
use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use trc::{AcmeEvent, EventType};
|
||||
use x509_parser::parse_x509_certificate;
|
||||
|
||||
use crate::listener::acme::directory::Identifier;
|
||||
|
@ -36,7 +37,7 @@ impl Server {
|
|||
let renewal_date = validity[1] - provider.renew_before;
|
||||
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::ProcessCert),
|
||||
Acme(AcmeEvent::ProcessCert),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
ValidFrom = trc::Value::Timestamp(validity[0].timestamp() as u64),
|
||||
|
@ -56,16 +57,18 @@ impl Server {
|
|||
loop {
|
||||
match self.order(provider).await {
|
||||
Ok(pem) => return self.process_cert(provider, pem, false).await,
|
||||
Err(err) if backoff < 16 => {
|
||||
Err(err)
|
||||
if !err.matches(EventType::Acme(AcmeEvent::OrderInvalid)) && backoff < 16 =>
|
||||
{
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::RenewBackoff),
|
||||
Acme(AcmeEvent::RenewBackoff),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
Total = backoff,
|
||||
NextRetry = 1 << backoff,
|
||||
CausedBy = err,
|
||||
);
|
||||
backoff = (backoff + 1).min(16);
|
||||
backoff += 1;
|
||||
tokio::time::sleep(Duration::from_secs(1 << backoff)).await;
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -80,18 +83,13 @@ impl Server {
|
|||
|
||||
async fn order(&self, provider: &AcmeProvider) -> trc::Result<Vec<u8>> {
|
||||
let directory = Directory::discover(&provider.directory_url).await?;
|
||||
let account = Account::create_with_keypair(
|
||||
directory,
|
||||
&provider.contact,
|
||||
provider.account_key.load().as_slice(),
|
||||
)
|
||||
.await?;
|
||||
let account = Account::create_with_keypair(directory, provider).await?;
|
||||
|
||||
let mut params = CertificateParams::new(provider.domains.clone());
|
||||
params.distinguished_name = DistinguishedName::new();
|
||||
params.alg = &PKCS_ECDSA_P256_SHA256;
|
||||
let cert = rcgen::Certificate::from_params(params).map_err(|err| {
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
EventType::Acme(AcmeEvent::Error)
|
||||
.caused_by(trc::location!())
|
||||
.reason(err)
|
||||
})?;
|
||||
|
@ -106,7 +104,7 @@ impl Server {
|
|||
.map(|url| self.authorize(provider, &account, url));
|
||||
try_join_all(auth_futures).await?;
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::AuthCompleted),
|
||||
Acme(AcmeEvent::AuthCompleted),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
);
|
||||
|
@ -115,7 +113,7 @@ impl Server {
|
|||
OrderStatus::Processing => {
|
||||
for i in 0u64..10 {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::OrderProcessing),
|
||||
Acme(AcmeEvent::OrderProcessing),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
Total = i,
|
||||
|
@ -128,20 +126,20 @@ impl Server {
|
|||
}
|
||||
}
|
||||
if order.status == OrderStatus::Processing {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
return Err(EventType::Acme(AcmeEvent::Error)
|
||||
.caused_by(trc::location!())
|
||||
.details("Order processing timed out"));
|
||||
}
|
||||
}
|
||||
OrderStatus::Ready => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::OrderReady),
|
||||
Acme(AcmeEvent::OrderReady),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
);
|
||||
|
||||
let csr = cert.serialize_request_der().map_err(|err| {
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
EventType::Acme(AcmeEvent::Error)
|
||||
.caused_by(trc::location!())
|
||||
.reason(err)
|
||||
})?;
|
||||
|
@ -149,7 +147,7 @@ impl Server {
|
|||
}
|
||||
OrderStatus::Valid { certificate } => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::OrderValid),
|
||||
Acme(AcmeEvent::OrderValid),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
);
|
||||
|
@ -163,15 +161,7 @@ impl Server {
|
|||
return Ok(pem.into_bytes());
|
||||
}
|
||||
OrderStatus::Invalid => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::OrderInvalid),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = provider.domains.as_slice(),
|
||||
);
|
||||
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
.into_err()
|
||||
.details("Invalid ACME order"));
|
||||
return Err(EventType::Acme(AcmeEvent::OrderInvalid).into_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +180,7 @@ impl Server {
|
|||
let challenge_type = provider.challenge.challenge_type();
|
||||
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::AuthStart),
|
||||
Acme(AcmeEvent::AuthStart),
|
||||
Hostname = domain.to_string(),
|
||||
Type = challenge_type.as_str(),
|
||||
Id = provider.id.to_string(),
|
||||
|
@ -201,11 +191,18 @@ impl Server {
|
|||
.iter()
|
||||
.find(|c| c.typ == challenge_type)
|
||||
.ok_or(
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
EventType::Acme(AcmeEvent::OrderInvalid)
|
||||
.into_err()
|
||||
.details("Missing Parameter")
|
||||
.details("Challenge not supported by ACME provider")
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Type, challenge_type.as_str()),
|
||||
.ctx(trc::Key::Type, challenge_type.as_str())
|
||||
.ctx(
|
||||
trc::Key::Contents,
|
||||
auth.challenges
|
||||
.iter()
|
||||
.map(|c| trc::Value::Static(c.typ.as_str()))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
)?;
|
||||
|
||||
match &provider.challenge {
|
||||
|
@ -247,7 +244,7 @@ impl Server {
|
|||
if let Err(err) = updater.delete(&name, &origin).await {
|
||||
// Errors are expected if the record does not exist
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordDeletionFailed),
|
||||
Acme(AcmeEvent::DnsRecordDeletionFailed),
|
||||
Hostname = name.to_string(),
|
||||
Reason = err.to_string(),
|
||||
Details = origin.to_string(),
|
||||
|
@ -267,17 +264,15 @@ impl Server {
|
|||
)
|
||||
.await
|
||||
{
|
||||
return Err(trc::EventType::Acme(
|
||||
trc::AcmeEvent::DnsRecordCreationFailed,
|
||||
)
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Hostname, name)
|
||||
.ctx(trc::Key::Details, origin)
|
||||
.reason(err));
|
||||
return Err(EventType::Acme(AcmeEvent::DnsRecordCreationFailed)
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Hostname, name)
|
||||
.ctx(trc::Key::Details, origin)
|
||||
.reason(err));
|
||||
}
|
||||
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordCreated),
|
||||
Acme(AcmeEvent::DnsRecordCreated),
|
||||
Hostname = name.to_string(),
|
||||
Details = origin.to_string(),
|
||||
Id = provider.id.to_string(),
|
||||
|
@ -295,7 +290,7 @@ impl Server {
|
|||
break;
|
||||
} else {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordNotPropagated),
|
||||
Acme(AcmeEvent::DnsRecordNotPropagated),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = name.to_string(),
|
||||
Details = origin.to_string(),
|
||||
|
@ -306,7 +301,7 @@ impl Server {
|
|||
}
|
||||
Err(err) => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordLookupFailed),
|
||||
Acme(AcmeEvent::DnsRecordLookupFailed),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = name.to_string(),
|
||||
Details = origin.to_string(),
|
||||
|
@ -320,14 +315,14 @@ impl Server {
|
|||
|
||||
if did_propagate {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordPropagated),
|
||||
Acme(AcmeEvent::DnsRecordPropagated),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = name.to_string(),
|
||||
Details = origin.to_string(),
|
||||
);
|
||||
} else {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::DnsRecordPropagationTimeout),
|
||||
Acme(AcmeEvent::DnsRecordPropagationTimeout),
|
||||
Id = provider.id.to_string(),
|
||||
Hostname = name.to_string(),
|
||||
Details = origin.to_string(),
|
||||
|
@ -341,7 +336,7 @@ impl Server {
|
|||
}
|
||||
AuthStatus::Valid => return Ok(()),
|
||||
_ => {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::AuthError)
|
||||
return Err(EventType::Acme(AcmeEvent::AuthError)
|
||||
.into_err()
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Details, auth.status.as_str()))
|
||||
|
@ -354,7 +349,7 @@ impl Server {
|
|||
match auth.status {
|
||||
AuthStatus::Pending => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::AuthPending),
|
||||
Acme(AcmeEvent::AuthPending),
|
||||
Hostname = domain.to_string(),
|
||||
Id = provider.id.to_string(),
|
||||
Total = i,
|
||||
|
@ -364,7 +359,7 @@ impl Server {
|
|||
}
|
||||
AuthStatus::Valid => {
|
||||
trc::event!(
|
||||
Acme(trc::AcmeEvent::AuthValid),
|
||||
Acme(AcmeEvent::AuthValid),
|
||||
Hostname = domain.to_string(),
|
||||
Id = provider.id.to_string(),
|
||||
);
|
||||
|
@ -372,14 +367,14 @@ impl Server {
|
|||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::AuthError)
|
||||
return Err(EventType::Acme(AcmeEvent::AuthError)
|
||||
.into_err()
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Details, auth.status.as_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(trc::EventType::Acme(trc::AcmeEvent::AuthTooManyAttempts)
|
||||
Err(EventType::Acme(AcmeEvent::AuthTooManyAttempts)
|
||||
.into_err()
|
||||
.ctx(trc::Key::Id, provider.id.to_string())
|
||||
.ctx(trc::Key::Hostname, domain))
|
||||
|
@ -388,12 +383,12 @@ impl Server {
|
|||
|
||||
fn parse_cert(pem: &[u8]) -> trc::Result<(CertifiedKey, [DateTime<Utc>; 2])> {
|
||||
let mut pems = pem::parse_many(pem).map_err(|err| {
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
EventType::Acme(AcmeEvent::Error)
|
||||
.reason(err)
|
||||
.caused_by(trc::location!())
|
||||
})?;
|
||||
if pems.len() < 2 {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
return Err(EventType::Acme(AcmeEvent::Error)
|
||||
.caused_by(trc::location!())
|
||||
.ctx(trc::Key::Size, pems.len())
|
||||
.details("Too few PEMs"));
|
||||
|
@ -403,7 +398,7 @@ fn parse_cert(pem: &[u8]) -> trc::Result<(CertifiedKey, [DateTime<Utc>; 2])> {
|
|||
))) {
|
||||
Ok(pk) => pk,
|
||||
Err(err) => {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
return Err(EventType::Acme(AcmeEvent::Error)
|
||||
.reason(err)
|
||||
.caused_by(trc::location!()))
|
||||
}
|
||||
|
@ -422,7 +417,7 @@ fn parse_cert(pem: &[u8]) -> trc::Result<(CertifiedKey, [DateTime<Utc>; 2])> {
|
|||
})
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
return Err(EventType::Acme(AcmeEvent::Error)
|
||||
.reason(err)
|
||||
.caused_by(trc::location!()))
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue