Gossip service implementation for cluster node autodiscovery with failure detection

This commit is contained in:
mdecimus
2024-05-10 20:34:08 +02:00
parent cf6765b70d
commit f4e5a0baf5
24 changed files with 1257 additions and 56 deletions

View File

@@ -1,3 +1,26 @@
/*
* Copyright (c) 2023 Stalwart Labs Ltd.
*
* This file is part of Stalwart Mail Server.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* in the LICENSE file at the top-level directory of this distribution.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can be released from the requirements of the AGPLv3 license by
* purchasing a commercial license. Please contact licensing@stalw.art
* for more details.
*/
use std::borrow::Cow;
use directory::Directory;

View File

@@ -576,24 +576,6 @@ impl Display for Variable<'_> {
}
}
trait IntoBool {
fn into_bool(self) -> bool;
}
impl IntoBool for f64 {
#[inline(always)]
fn into_bool(self) -> bool {
self != 0.0
}
}
impl IntoBool for i64 {
#[inline(always)]
fn into_bool(self) -> bool {
self != 0
}
}
impl<'x> From<&'x Constant> for Variable<'x> {
fn from(value: &'x Constant) -> Self {
match value {

View File

@@ -1,3 +1,26 @@
/*
* Copyright (c) 2023 Stalwart Labs Ltd.
*
* This file is part of Stalwart Mail Server.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* in the LICENSE file at the top-level directory of this distribution.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can be released from the requirements of the AGPLv3 license by
* purchasing a commercial license. Please contact licensing@stalw.art
* for more details.
*/
use std::{borrow::Cow, net::IpAddr, sync::Arc};
use arc_swap::ArcSwap;

View File

@@ -21,7 +21,7 @@
* for more details.
*/
use std::{fmt::Debug, net::IpAddr};
use std::{fmt::Debug, net::IpAddr, sync::atomic::AtomicU8};
use ahash::AHashSet;
use parking_lot::RwLock;
@@ -35,6 +35,7 @@ use crate::Core;
pub struct BlockedIps {
pub ip_addresses: RwLock<AHashSet<IpAddr>>,
pub version: AtomicU8,
ip_networks: Vec<IpAddrMask>,
has_networks: bool,
limiter_rate: Option<Rate>,
@@ -71,6 +72,7 @@ impl BlockedIps {
has_networks: !ip_networks.is_empty(),
ip_networks,
limiter_rate: config.property_or_default::<Rate>("authentication.fail2ban", "100/1d"),
version: 0.into(),
}
}
}
@@ -103,6 +105,9 @@ impl Core {
}])
.await?;
// Increment version
self.network.blocked_ips.increment_version();
return Ok(true);
}
}
@@ -126,6 +131,13 @@ impl Core {
}
}
impl BlockedIps {
pub fn increment_version(&self) {
self.version
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
}
impl Default for BlockedIps {
fn default() -> Self {
Self {
@@ -133,6 +145,7 @@ impl Default for BlockedIps {
ip_networks: Default::default(),
has_networks: Default::default(),
limiter_rate: Default::default(),
version: Default::default(),
}
}
}
@@ -144,6 +157,10 @@ impl Clone for BlockedIps {
ip_networks: self.ip_networks.clone(),
has_networks: self.has_networks,
limiter_rate: self.limiter_rate.clone(),
version: self
.version
.load(std::sync::atomic::Ordering::Relaxed)
.into(),
}
}
}

View File

@@ -325,7 +325,7 @@ impl Servers {
pub fn spawn(
mut self,
spawn: impl Fn(Server, TcpAcceptor, watch::Receiver<bool>),
) -> watch::Sender<bool> {
) -> (watch::Sender<bool>, watch::Receiver<bool>) {
// Spawn listeners
let (shutdown_tx, shutdown_rx) = watch::channel(false);
for server in self.servers {
@@ -336,7 +336,7 @@ impl Servers {
spawn(server, acceptor, shutdown_rx.clone());
}
shutdown_tx
(shutdown_tx, shutdown_rx)
}
}

View File

@@ -221,6 +221,22 @@ impl BootManager {
)));
}
// Generate a Cluster encryption key if missing
if config
.value("cluster.key")
.filter(|v| !v.is_empty())
.is_none()
{
insert_keys.push(ConfigKey::from((
"cluster.key",
thread_rng()
.sample_iter(Alphanumeric)
.take(64)
.map(char::from)
.collect::<String>(),
)));
}
// Download SPAM filters if missing
if config
.value("version.spam-filter")