mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-09-04 02:54:11 +08:00
OVH DNS update support
This commit is contained in:
parent
285d1cc90e
commit
51a0a1445d
5 changed files with 82 additions and 64 deletions
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## [0.13.2] - 2025-07-27
|
## [0.13.2] - 2025-07-28
|
||||||
|
|
||||||
If you are upgrading from v0.11.x or v0.12.x, this version includes **breaking changes** to the message queue and MTA configuration. Please read the [UPGRADING.md](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING.md) file for more information on how to upgrade from previous versions.
|
If you are upgrading from v0.11.x or v0.12.x, this version includes **breaking changes** to the message queue and MTA configuration. Please read the [UPGRADING.md](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING.md) file for more information on how to upgrade from previous versions.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
- ACME: DeSEC cloud DNS provider support (contributed by @Tyr3al).
|
- ACME: DeSEC cloud DNS provider support (contributed by @Tyr3al).
|
||||||
|
- ACME: OVH cloud DNS provider support (contributed by @srachner).
|
||||||
- CalDAV Scheduling: Catalan language support (contributed by @jolupa) (#1873).
|
- CalDAV Scheduling: Catalan language support (contributed by @jolupa) (#1873).
|
||||||
- MTA: Allow to send e-mails as group, while member of that group (#485).
|
- MTA: Allow to send e-mails as group, while member of that group (#485).
|
||||||
- OIDC: Allow local access tokens to be used with third-party OIDC backends (#1311 stalwartlabs/webadmin#52).
|
- OIDC: Allow local access tokens to be used with third-party OIDC backends (#1311 stalwartlabs/webadmin#52).
|
||||||
|
|
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -2051,15 +2051,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dns-update"
|
name = "dns-update"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41dbb7b41e755c6d9f9a62d3861e84fd33860b796d93aea6cfc8ef1068f4bff6"
|
checksum = "42788b21a1231c646c46508c406db9bf628342a781c24888bf60e1a29396deb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hickory-client",
|
"hickory-client",
|
||||||
"reqwest 0.12.15",
|
"reqwest 0.12.15",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
|
"sha1",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ mail-builder = { version = "0.4" }
|
||||||
mail-auth = { version = "0.7.1" }
|
mail-auth = { version = "0.7.1" }
|
||||||
mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||||
smtp-proto = { version = "0.1", features = ["rkyv"] }
|
smtp-proto = { version = "0.1", features = ["rkyv"] }
|
||||||
dns-update = { version = "0.1.4" }
|
dns-update = { version = "0.1.5" }
|
||||||
calcard = { version = "0.1.3", features = ["rkyv"] }
|
calcard = { version = "0.1.3", features = ["rkyv"] }
|
||||||
ahash = { version = "0.8.2", features = ["serde"] }
|
ahash = { version = "0.8.2", features = ["serde"] }
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
|
|
|
@ -186,6 +186,10 @@ impl AcmeProviders {
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_to_owned)]
|
#[allow(clippy::unnecessary_to_owned)]
|
||||||
fn build_dns_updater(config: &mut Config, acme_id: &str) -> Option<DnsUpdater> {
|
fn build_dns_updater(config: &mut Config, acme_id: &str) -> Option<DnsUpdater> {
|
||||||
|
let timeout = config
|
||||||
|
.property_or_default(("acme", acme_id, "timeout"), "30s")
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(30));
|
||||||
|
|
||||||
match config.value_require(("acme", acme_id, "provider"))? {
|
match config.value_require(("acme", acme_id, "provider"))? {
|
||||||
"rfc2136-tsig" => {
|
"rfc2136-tsig" => {
|
||||||
let algorithm: TsigAlgorithm = config
|
let algorithm: TsigAlgorithm = config
|
||||||
|
@ -231,67 +235,79 @@ fn build_dns_updater(config: &mut Config, acme_id: &str) -> Option<DnsUpdater> {
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
"cloudflare" => {
|
"cloudflare" => DnsUpdater::new_cloudflare(
|
||||||
let timeout = config
|
config
|
||||||
.property_or_default(("acme", acme_id, "timeout"), "30s")
|
.value_require(("acme", acme_id, "secret"))?
|
||||||
.unwrap_or_else(|| Duration::from_secs(30));
|
.trim()
|
||||||
|
.to_string(),
|
||||||
DnsUpdater::new_cloudflare(
|
config.value(("acme", acme_id, "user")).map(|s| s.trim()),
|
||||||
config
|
timeout.into(),
|
||||||
.value_require(("acme", acme_id, "secret"))?
|
)
|
||||||
.trim()
|
.map_err(|err| {
|
||||||
.to_string(),
|
config.new_build_error(
|
||||||
config.value(("acme", acme_id, "user")).map(|s| s.trim()),
|
("acme", acme_id, "provider"),
|
||||||
timeout.into(),
|
format!("Failed to create Cloudflare DNS updater: {err}"),
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
})
|
||||||
config.new_build_error(
|
.ok(),
|
||||||
("acme", acme_id, "provider"),
|
"digitalocean" => DnsUpdater::new_digitalocean(
|
||||||
format!("Failed to create Cloudflare DNS updater: {err}"),
|
config
|
||||||
)
|
.value_require(("acme", acme_id, "secret"))?
|
||||||
})
|
.trim()
|
||||||
.ok()
|
.to_string(),
|
||||||
}
|
timeout.into(),
|
||||||
"digitalocean" => {
|
)
|
||||||
let timeout = config
|
.map_err(|err| {
|
||||||
.property_or_default(("acme", acme_id, "timeout"), "30s")
|
config.new_build_error(
|
||||||
.unwrap_or_else(|| Duration::from_secs(30));
|
("acme", acme_id, "provider"),
|
||||||
|
format!("Failed to create DigitalOcean DNS updater: {err}"),
|
||||||
DnsUpdater::new_digitalocean(
|
|
||||||
config
|
|
||||||
.value_require(("acme", acme_id, "secret"))?
|
|
||||||
.trim()
|
|
||||||
.to_string(),
|
|
||||||
timeout.into(),
|
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
})
|
||||||
config.new_build_error(
|
.ok(),
|
||||||
("acme", acme_id, "provider"),
|
"desec" => DnsUpdater::new_desec(
|
||||||
format!("Failed to create DigitalOcean DNS updater: {err}"),
|
config
|
||||||
)
|
.value_require(("acme", acme_id, "secret"))?
|
||||||
})
|
.trim()
|
||||||
.ok()
|
.to_string(),
|
||||||
}
|
timeout.into(),
|
||||||
"desec" => {
|
)
|
||||||
let timeout = config
|
.map_err(|err| {
|
||||||
.property_or_default(("acme", acme_id, "timeout"), "30s")
|
config.new_build_error(
|
||||||
.unwrap_or_else(|| Duration::from_secs(30));
|
("acme", acme_id, "provider"),
|
||||||
|
format!("Failed to create Desec DNS updater: {err}"),
|
||||||
DnsUpdater::new_desec(
|
|
||||||
config
|
|
||||||
.value_require(("acme", acme_id, "secret"))?
|
|
||||||
.trim()
|
|
||||||
.to_string(),
|
|
||||||
timeout.into(),
|
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
})
|
||||||
config.new_build_error(
|
.ok(),
|
||||||
("acme", acme_id, "provider"),
|
"ovh" => DnsUpdater::new_ovh(
|
||||||
format!("Failed to create Desec DNS updater: {err}"),
|
config
|
||||||
)
|
.value_require(("acme", acme_id, "key"))
|
||||||
})
|
.map(|s| s.trim())?
|
||||||
.ok()
|
.to_string(),
|
||||||
}
|
config
|
||||||
|
.value_require(("acme", acme_id, "secret"))?
|
||||||
|
.trim()
|
||||||
|
.to_string(),
|
||||||
|
config
|
||||||
|
.value_require(("acme", acme_id, "consumer-key"))?
|
||||||
|
.trim()
|
||||||
|
.to_string(),
|
||||||
|
config
|
||||||
|
.value_require(("acme", acme_id, "ovh-endpoint"))?
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| {
|
||||||
|
config
|
||||||
|
.new_parse_error(("acme", acme_id, "ovh-endpoint"), "Invalid OVH endpoint")
|
||||||
|
})
|
||||||
|
.ok()?,
|
||||||
|
timeout.into(),
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
config.new_build_error(
|
||||||
|
("acme", acme_id, "provider"),
|
||||||
|
format!("Failed to create Desec DNS updater: {err}"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok(),
|
||||||
_ => {
|
_ => {
|
||||||
config.new_parse_error(("acme", acme_id, "provider"), "Unsupported provider");
|
config.new_parse_error(("acme", acme_id, "provider"), "Unsupported provider");
|
||||||
None
|
None
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use dns_update::DnsRecord;
|
use dns_update::{DnsRecord, DnsRecordType};
|
||||||
use futures::future::try_join_all;
|
use futures::future::try_join_all;
|
||||||
use rcgen::{CertificateParams, DistinguishedName, PKCS_ECDSA_P256_SHA256};
|
use rcgen::{CertificateParams, DistinguishedName, PKCS_ECDSA_P256_SHA256};
|
||||||
use rustls::crypto::ring::sign::any_ecdsa_type;
|
use rustls::crypto::ring::sign::any_ecdsa_type;
|
||||||
|
@ -252,7 +252,7 @@ impl Server {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
// First try deleting the record
|
// First try deleting the record
|
||||||
if let Err(err) = updater.delete(&name, &origin).await {
|
if let Err(err) = updater.delete(&name, &origin, DnsRecordType::TXT).await {
|
||||||
// Errors are expected if the record does not exist
|
// Errors are expected if the record does not exist
|
||||||
trc::event!(
|
trc::event!(
|
||||||
Acme(AcmeEvent::DnsRecordDeletionFailed),
|
Acme(AcmeEvent::DnsRecordDeletionFailed),
|
||||||
|
|
Loading…
Add table
Reference in a new issue