Queue autoconfig post install
This commit is contained in:
@@ -34,6 +34,7 @@ use utils::config::{Config, utils::ParseValue};
|
||||
rkyv::Archive,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
#[rkyv(derive(Debug, Clone, Copy, PartialEq), compare(PartialEq))]
|
||||
#[repr(transparent)]
|
||||
pub struct QueueName([u8; 8]);
|
||||
|
||||
@@ -433,13 +434,13 @@ fn parse_route(config: &mut Config, id: &str) -> Option<RoutingStrategy> {
|
||||
"local" => RoutingStrategy::Local.into(),
|
||||
"mx" => RoutingStrategy::Mx(MxConfig {
|
||||
max_mx: config
|
||||
.property_require(("queue.route", id, "limits.mx"))
|
||||
.property(("queue.route", id, "limits.mx"))
|
||||
.unwrap_or(5),
|
||||
max_multi_homed: config
|
||||
.property_require(("queue.route", id, "limits.multihomed"))
|
||||
.property(("queue.route", id, "limits.multihomed"))
|
||||
.unwrap_or(2),
|
||||
ip_lookup_strategy: config
|
||||
.property_require(("queue.route", id, "ip-lookup"))
|
||||
.property(("queue.route", id, "ip-lookup"))
|
||||
.unwrap_or(IpLookupStrategy::Ipv4thenIpv6),
|
||||
})
|
||||
.into(),
|
||||
@@ -475,22 +476,22 @@ fn parse_tls_strategies(config: &mut Config) -> AHashMap<String, TlsStrategy> {
|
||||
fn parse_tls(config: &mut Config, id: &str) -> Option<TlsStrategy> {
|
||||
Some(TlsStrategy {
|
||||
dane: config
|
||||
.property_require::<RequireOptional>(("queue.tls", id, "dane"))
|
||||
.property::<RequireOptional>(("queue.tls", id, "dane"))
|
||||
.unwrap_or(RequireOptional::Optional),
|
||||
mta_sts: config
|
||||
.property_require::<RequireOptional>(("queue.tls", id, "mta-sts"))
|
||||
.property::<RequireOptional>(("queue.tls", id, "mta-sts"))
|
||||
.unwrap_or(RequireOptional::Optional),
|
||||
tls: config
|
||||
.property_require::<RequireOptional>(("queue.tls", id, "starttls"))
|
||||
.property::<RequireOptional>(("queue.tls", id, "starttls"))
|
||||
.unwrap_or(RequireOptional::Optional),
|
||||
allow_invalid_certs: config
|
||||
.property_require::<bool>(("queue.tls", id, "allow-invalid-certs"))
|
||||
.property::<bool>(("queue.tls", id, "allow-invalid-certs"))
|
||||
.unwrap_or(false),
|
||||
timeout_tls: config
|
||||
.property_require::<Duration>(("queue.tls", id, "timeout.tls"))
|
||||
.property::<Duration>(("queue.tls", id, "timeout.tls"))
|
||||
.unwrap_or(Duration::from_secs(3 * 60)),
|
||||
timeout_mta_sts: config
|
||||
.property_require::<Duration>(("queue.tls", id, "timeout.mta-sts"))
|
||||
.property::<Duration>(("queue.tls", id, "timeout.mta-sts"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
})
|
||||
}
|
||||
@@ -538,22 +539,22 @@ fn parse_connection(config: &mut Config, id: &str) -> Option<ConnectionStrategy>
|
||||
source_ipv6,
|
||||
ehlo_hostname: config.property::<String>(("queue.connection", id, "ehlo-hostname")),
|
||||
timeout_connect: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.connect"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.connect"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
timeout_greeting: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.greeting"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.greeting"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
timeout_ehlo: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.ehlo"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.ehlo"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
timeout_mail: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.mail-from"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.mail-from"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
timeout_rcpt: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.rcpt-to"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.rcpt-to"))
|
||||
.unwrap_or(Duration::from_secs(5 * 60)),
|
||||
timeout_data: config
|
||||
.property_require::<Duration>(("queue.connection", id, "timeout.data"))
|
||||
.property::<Duration>(("queue.connection", id, "timeout.data"))
|
||||
.unwrap_or(Duration::from_secs(10 * 60)),
|
||||
})
|
||||
}
|
||||
@@ -935,6 +936,14 @@ impl QueueName {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedQueueName {
|
||||
pub fn as_str(&self) -> &str {
|
||||
std::str::from_utf8(self.0.as_ref())
|
||||
.unwrap_or_default()
|
||||
.trim_end_matches('\0')
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QueueName {
|
||||
fn default() -> Self {
|
||||
DEFAULT_QUEUE_NAME
|
||||
@@ -955,7 +964,13 @@ impl ParseValue for QueueName {
|
||||
|
||||
impl Display for QueueName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArchivedQueueName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -367,8 +367,14 @@ impl TokenMap {
|
||||
V_QUEUE_EXPIRES_IN,
|
||||
V_QUEUE_LAST_STATUS,
|
||||
V_QUEUE_LAST_ERROR,
|
||||
V_QUEUE_NAME,
|
||||
V_QUEUE_AGE,
|
||||
V_ASN,
|
||||
V_COUNTRY,
|
||||
V_RECEIVED_FROM_IP,
|
||||
V_RECEIVED_VIA_PORT,
|
||||
V_SOURCE,
|
||||
V_SIZE,
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ use store::{
|
||||
};
|
||||
use tokio::sync::{Notify, mpsc};
|
||||
use utils::{
|
||||
Semver, UnwrapFailure,
|
||||
UnwrapFailure,
|
||||
config::{Config, ConfigKey},
|
||||
failed,
|
||||
};
|
||||
@@ -75,6 +75,106 @@ enum StoreOp {
|
||||
None,
|
||||
}
|
||||
|
||||
pub const DEFAULT_SETTINGS: &[(&str, &str)] = &[
|
||||
("queue.quota.size.messages", "100000"),
|
||||
("queue.quota.size.size", "10737418240"),
|
||||
("queue.quota.size.enable", "true"),
|
||||
("queue.limiter.inbound.ip.key", "remote_ip"),
|
||||
("queue.limiter.inbound.ip.rate", "5/1s"),
|
||||
("queue.limiter.inbound.ip.enable", "true"),
|
||||
("queue.limiter.inbound.sender.key.0", "sender_domain"),
|
||||
("queue.limiter.inbound.sender.key.1", "rcpt"),
|
||||
("queue.limiter.inbound.sender.rate", "25/1h"),
|
||||
("queue.limiter.inbound.sender.enable", "true"),
|
||||
("report.analysis.addresses", "postmaster@*"),
|
||||
("queue.virtual.local.threads-per-node", "25"),
|
||||
("queue.virtual.local.description", "Local delivery queue"),
|
||||
("queue.virtual.remote.threads-per-node", "50"),
|
||||
("queue.virtual.remote.description", "Remote delivery queue"),
|
||||
("queue.virtual.dsn.threads-per-node", "5"),
|
||||
(
|
||||
"queue.virtual.dsn.description",
|
||||
"Delivery Status Notification delivery queue",
|
||||
),
|
||||
("queue.virtual.report.threads-per-node", "5"),
|
||||
(
|
||||
"queue.virtual.report.description",
|
||||
"DMARC and TLS report delivery queue",
|
||||
),
|
||||
("queue.schedule.local.queue-name", "local"),
|
||||
("queue.schedule.local.retry.0", "2m"),
|
||||
("queue.schedule.local.retry.1", "5m"),
|
||||
("queue.schedule.local.retry.2", "10m"),
|
||||
("queue.schedule.local.retry.3", "15m"),
|
||||
("queue.schedule.local.retry.4", "30m"),
|
||||
("queue.schedule.local.retry.5", "1h"),
|
||||
("queue.schedule.local.retry.6", "2h"),
|
||||
("queue.schedule.local.notify.0", "1d"),
|
||||
("queue.schedule.local.notify.1", "3d"),
|
||||
("queue.schedule.local.expire-type", "ttl"),
|
||||
("queue.schedule.local.expire", "3d"),
|
||||
(
|
||||
"queue.schedule.local.description",
|
||||
"Local delivery schedule",
|
||||
),
|
||||
("queue.schedule.remote.queue-name", "remote"),
|
||||
("queue.schedule.remote.retry.0", "2m"),
|
||||
("queue.schedule.remote.retry.1", "5m"),
|
||||
("queue.schedule.remote.retry.2", "10m"),
|
||||
("queue.schedule.remote.retry.3", "15m"),
|
||||
("queue.schedule.remote.retry.4", "30m"),
|
||||
("queue.schedule.remote.retry.5", "1h"),
|
||||
("queue.schedule.remote.retry.6", "2h"),
|
||||
("queue.schedule.remote.notify.0", "1d"),
|
||||
("queue.schedule.remote.notify.1", "3d"),
|
||||
("queue.schedule.remote.expire-type", "ttl"),
|
||||
("queue.schedule.remote.expire", "3d"),
|
||||
(
|
||||
"queue.schedule.remote.description",
|
||||
"Remote delivery schedule",
|
||||
),
|
||||
("queue.schedule.dsn.queue-name", "dsn"),
|
||||
("queue.schedule.dsn.retry.0", "15m"),
|
||||
("queue.schedule.dsn.retry.1", "30m"),
|
||||
("queue.schedule.dsn.retry.2", "1h"),
|
||||
("queue.schedule.dsn.retry.3", "2h"),
|
||||
("queue.schedule.dsn.expire-type", "attempts"),
|
||||
("queue.schedule.dsn.max-attempts", "10"),
|
||||
(
|
||||
"queue.schedule.dsn.description",
|
||||
"Delivery Status Notification delivery schedule",
|
||||
),
|
||||
("queue.schedule.report.queue-name", "report"),
|
||||
("queue.schedule.report.retry.0", "30m"),
|
||||
("queue.schedule.report.retry.1", "1h"),
|
||||
("queue.schedule.report.retry.2", "2h"),
|
||||
("queue.schedule.report.expire-type", "attempts"),
|
||||
("queue.schedule.report.max-attempts", "8"),
|
||||
(
|
||||
"queue.schedule.report.description",
|
||||
"DMARC and TLS report delivery schedule",
|
||||
),
|
||||
("queue.tls.invalid-tls.allow-invalid-certs", "true"),
|
||||
(
|
||||
"queue.tls.invalid-tls.description",
|
||||
"Allow invalid TLS certificates",
|
||||
),
|
||||
("queue.tls.default.allow-invalid-certs", "false"),
|
||||
("queue.tls.default.description", "Default TLS settings"),
|
||||
("queue.route.local.type", "local"),
|
||||
("queue.route.local.description", "Local delivery route"),
|
||||
("queue.route.mx.type", "mx"),
|
||||
("queue.route.mx.limits.multihomed", "2"),
|
||||
("queue.route.mx.limits.mx", "5"),
|
||||
("queue.route.mx.ip-lookup", "ipv4_then_ipv6"),
|
||||
("queue.route.mx.description", "MX delivery route"),
|
||||
("queue.connection.default.timeout.connect", "5m"),
|
||||
(
|
||||
"queue.connection.default.description",
|
||||
"Default connection settings",
|
||||
),
|
||||
];
|
||||
|
||||
impl BootManager {
|
||||
pub async fn init() -> Self {
|
||||
let mut config_path = std::env::var("CONFIG_PATH").ok();
|
||||
@@ -244,84 +344,29 @@ impl BootManager {
|
||||
}
|
||||
|
||||
// Download Spam filter rules if missing
|
||||
// TODO remove this check in 1.0
|
||||
let update_webadmin = match config.value("version.spam-filter").and_then(|v| {
|
||||
if !v.is_empty() {
|
||||
Some(Semver::try_from(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(Err(_)) => {
|
||||
let _ = manager.clear_prefix("lookup.spam-").await;
|
||||
let _ = manager
|
||||
.clear_prefix("sieve.trusted.scripts.spam-filter")
|
||||
.await;
|
||||
let _ = manager
|
||||
.clear_prefix("sieve.trusted.scripts.track-replies")
|
||||
.await;
|
||||
let _ = manager.clear_prefix("sieve.trusted.scripts.greylist").await;
|
||||
let _ = manager.clear_prefix("sieve.trusted.scripts.train").await;
|
||||
let _ = manager.clear("version.spam-filter").await;
|
||||
|
||||
match manager.fetch_spam_rules().await {
|
||||
Ok(external_config) => {
|
||||
trc::event!(
|
||||
Config(trc::ConfigEvent::ImportExternal),
|
||||
Version = external_config.version.to_string(),
|
||||
Id = "spam-filter"
|
||||
);
|
||||
insert_keys.extend(external_config.keys);
|
||||
}
|
||||
Err(err) => {
|
||||
config.new_build_error(
|
||||
"*",
|
||||
format!("Failed to fetch spam filter: {err}"),
|
||||
);
|
||||
}
|
||||
if config.value("version.spam-filter").is_none() {
|
||||
match manager.fetch_spam_rules().await {
|
||||
Ok(external_config) => {
|
||||
trc::event!(
|
||||
Config(trc::ConfigEvent::ImportExternal),
|
||||
Version = external_config.version.to_string(),
|
||||
Id = "spam-filter"
|
||||
);
|
||||
insert_keys.extend(external_config.keys);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
Some(Ok(_)) => false,
|
||||
None => {
|
||||
match manager.fetch_spam_rules().await {
|
||||
Ok(external_config) => {
|
||||
trc::event!(
|
||||
Config(trc::ConfigEvent::ImportExternal),
|
||||
Version = external_config.version.to_string(),
|
||||
Id = "spam-filter"
|
||||
);
|
||||
insert_keys.extend(external_config.keys);
|
||||
}
|
||||
Err(err) => {
|
||||
config.new_build_error(
|
||||
"*",
|
||||
format!("Failed to fetch spam filter: {err}"),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
config.new_build_error(
|
||||
"*",
|
||||
format!("Failed to fetch spam filter: {err}"),
|
||||
);
|
||||
}
|
||||
|
||||
// Add default settings
|
||||
for key in [
|
||||
("queue.quota.size.messages", "100000"),
|
||||
("queue.quota.size.size", "10737418240"),
|
||||
("queue.quota.size.enable", "true"),
|
||||
("queue.limiter.inbound.ip.key", "remote_ip"),
|
||||
("queue.limiter.inbound.ip.rate", "5/1s"),
|
||||
("queue.limiter.inbound.ip.enable", "true"),
|
||||
("queue.limiter.inbound.sender.key.0", "sender_domain"),
|
||||
("queue.limiter.inbound.sender.key.1", "rcpt"),
|
||||
("queue.limiter.inbound.sender.rate", "25/1h"),
|
||||
("queue.limiter.inbound.sender.enable", "true"),
|
||||
("report.analysis.addresses", "postmaster@*"),
|
||||
] {
|
||||
insert_keys.push(ConfigKey::from(key));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
// Add default settings
|
||||
for key in DEFAULT_SETTINGS {
|
||||
insert_keys.push(ConfigKey::from(*key));
|
||||
}
|
||||
}
|
||||
|
||||
// Download webadmin if missing
|
||||
if let Some(blob_store) = config
|
||||
@@ -393,10 +438,9 @@ impl BootManager {
|
||||
);
|
||||
|
||||
// Webadmin auto-update
|
||||
if update_webadmin
|
||||
|| config
|
||||
.property_or_default::<bool>("webadmin.auto-update", "false")
|
||||
.unwrap_or_default()
|
||||
if config
|
||||
.property_or_default::<bool>("webadmin.auto-update", "false")
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if let Err(err) = data.webadmin.update(&core).await {
|
||||
trc::event!(
|
||||
|
||||
Reference in New Issue
Block a user