ACME freshness check improvements

This commit is contained in:
Maurus Decimus
2026-06-16 11:17:13 +02:00
parent 3a871742ef
commit 8f627fbd69
5 changed files with 48 additions and 36 deletions

View File

@@ -150,9 +150,22 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v6.0.2 uses: actions/checkout@v6.0.2
- name: Free disk space (heavy ARM targets)
if: contains(matrix.target, 'arm')
run: |
df -h /mnt /
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/.ghcup /usr/local/share/powershell /usr/share/swift /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force || true
df -h /mnt /
- name: Add swap (heavy ARM targets) - name: Add swap (heavy ARM targets)
if: contains(matrix.target, 'arm') if: contains(matrix.target, 'arm')
run: | run: |
mnt_avail=$(df --output=avail -k /mnt | tail -1)
if [ "$mnt_avail" -lt 18874368 ]; then
echo "Insufficient space on /mnt (${mnt_avail}K available), aborting swap setup"
exit 1
fi
sudo fallocate -l 16G /mnt/swapfile sudo fallocate -l 16G /mnt/swapfile
sudo chmod 600 /mnt/swapfile sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile sudo mkswap /mnt/swapfile

View File

@@ -238,8 +238,8 @@ impl Server {
.addresses .addresses
.iter() .iter()
.any(|address| { .any(|address| {
address.local_part.as_ref() == local_part address.domain_id == domain_id
&& address.domain_id == domain_id && address.local_part.as_ref() == local_part
}) })
{ {
EmailCache::Account(item_id) EmailCache::Account(item_id)
@@ -250,8 +250,8 @@ impl Server {
ObjectType::MailingList => { ObjectType::MailingList => {
if let Some(list) = self.try_list(item_id).await? if let Some(list) = self.try_list(item_id).await?
&& !list.addresses.iter().any(|address| { && !list.addresses.iter().any(|address| {
address.local_part.as_ref() == local_part address.domain_id == domain_id
&& address.domain_id == domain_id && address.local_part.as_ref() == local_part
}) })
{ {
EmailCache::DisabledListAddress(item_id) EmailCache::DisabledListAddress(item_id)

View File

@@ -32,6 +32,7 @@ pub enum AcmeError {
Json(serde_json::Error), Json(serde_json::Error),
Crypto(String), Crypto(String),
Invalid(String), Invalid(String),
NotDue(String),
Dns(String), Dns(String),
AuthInvalid(String), AuthInvalid(String),
OrderInvalid(String), OrderInvalid(String),
@@ -257,6 +258,7 @@ impl Display for AcmeError {
AcmeError::Dns(err) => write!(f, "DNS error: {}", err), AcmeError::Dns(err) => write!(f, "DNS error: {}", err),
AcmeError::Crypto(err) => write!(f, "Cryptographic error: {}", err), AcmeError::Crypto(err) => write!(f, "Cryptographic error: {}", err),
AcmeError::Invalid(err) => write!(f, "Invalid request: {}", err), AcmeError::Invalid(err) => write!(f, "Invalid request: {}", err),
AcmeError::NotDue(err) => write!(f, "{}", err),
AcmeError::AuthInvalid(status) => write!(f, "Authentication failed: {:?}", status), AcmeError::AuthInvalid(status) => write!(f, "Authentication failed: {:?}", status),
AcmeError::OrderTimeout { .. } => write!(f, "Order processing timed out"), AcmeError::OrderTimeout { .. } => write!(f, "Order processing timed out"),
AcmeError::OrderInvalid(reason) => write!(f, "Order is invalid: {}", reason), AcmeError::OrderInvalid(reason) => write!(f, "Order is invalid: {}", reason),

View File

@@ -65,10 +65,11 @@ impl Server {
); );
if let Some(renew_at) = self.acme_certificate_renewal_due(&domains, renew_before, now()) { if let Some(renew_at) = self.acme_certificate_renewal_due(&domains, renew_before, now()) {
return Ok(vec![Task::AcmeRenewal(TaskDomainManagement { return Err(AcmeError::NotDue(format!(
domain_id, "Certificate for domain {} is still valid; renewal is not due until {}",
status: TaskStatus::at(renew_at as i64), domain.name,
})]); UTCDateTime::from_timestamp(renew_at as i64)
)));
} }
let dns_parameters = match &domain.dns_management { let dns_parameters = match &domain.dns_management {
@@ -168,29 +169,15 @@ impl Server {
self.cluster_broadcast(BroadcastEvent::RegistryChange(change)) self.cluster_broadcast(BroadcastEvent::RegistryChange(change))
.await; .await;
// Schedule next renewal
let mut tasks = Vec::new(); let mut tasks = Vec::new();
let renew_in = match renew_before { let renew_at = Self::acme_renewal_due_at(
AcmeRenewBefore::R12 => { parsed_cert.valid_not_before.timestamp(),
// 1/2 of the remaining time until expiration parsed_cert.valid_not_after.timestamp(),
expires_in / 2 renew_before,
} );
AcmeRenewBefore::R23 => {
// 2/3 of the remaining time until expiration
expires_in * 2 / 3
}
AcmeRenewBefore::R34 => {
// 3/4 of the remaining time until expiration
expires_in * 3 / 4
}
AcmeRenewBefore::R45 => {
// 4/5 of the remaining time until expiration
expires_in * 4 / 5
}
};
tasks.push(Task::AcmeRenewal(TaskDomainManagement { tasks.push(Task::AcmeRenewal(TaskDomainManagement {
domain_id, domain_id,
status: TaskStatus::at((now + renew_in) as i64), status: TaskStatus::at(renew_at),
})); }));
// Update TLSA records // Update TLSA records
@@ -245,14 +232,8 @@ impl Server {
return None; return None;
} }
let not_valid_before = parsed.valid_not_before.timestamp(); let not_valid_before = parsed.valid_not_before.timestamp();
let total = not_valid_after.saturating_sub(not_valid_before); let renew_at =
let (numerator, denominator) = match renew_before { Self::acme_renewal_due_at(not_valid_before, not_valid_after, renew_before);
AcmeRenewBefore::R12 => (1, 2),
AcmeRenewBefore::R23 => (2, 3),
AcmeRenewBefore::R34 => (3, 4),
AcmeRenewBefore::R45 => (4, 5),
};
let renew_at = not_valid_before + total * numerator / denominator;
return if now < renew_at { return if now < renew_at {
Some(renew_at as u64) Some(renew_at as u64)
} else { } else {
@@ -262,4 +243,19 @@ impl Server {
None None
} }
fn acme_renewal_due_at(
not_valid_before: i64,
not_valid_after: i64,
renew_before: AcmeRenewBefore,
) -> i64 {
let total = not_valid_after.saturating_sub(not_valid_before);
let (numerator, denominator) = match renew_before {
AcmeRenewBefore::R12 => (1, 2),
AcmeRenewBefore::R23 => (2, 3),
AcmeRenewBefore::R34 => (3, 4),
AcmeRenewBefore::R45 => (4, 5),
};
not_valid_before + total * numerator / denominator
}
} }

View File

@@ -48,6 +48,7 @@ async fn acme_management(server: &Server, task: &TaskDomainManagement) -> trc::R
Err(err) => match err { Err(err) => match err {
AcmeError::Crypto(_) AcmeError::Crypto(_)
| AcmeError::Invalid(_) | AcmeError::Invalid(_)
| AcmeError::NotDue(_)
| AcmeError::ChallengeNotSupported { .. } | AcmeError::ChallengeNotSupported { .. }
| AcmeError::OrderInvalid(_) | AcmeError::OrderInvalid(_)
| AcmeError::Json(_) | AcmeError::Json(_)