Skip to main content

· 2 min read
Mauro D.

Today we are announcing the latest release of Stalwart Mail Server: version 0.3.6. This update includes multiple enhancements to the Sieve filtering language, including the ability to evaluate arithmetical and logical expressions, and fetch data from SQL or LDAP databases to Sieve variables.

Arithmetical and Logical Expressions

Stalwart Mail Server now incorporates the ability to evaluate arithmetical and logical operations within Sieve scripts. For instance, the following Sieve script rejects a mail if it satisfies a particular condition:

if test eval "score + ((awl_score / awl_count) - score) * awl_factor > 2.25" {
reject "Your message is SPAM.";
stop;
}

Whether you're aiming to refine your filtering mechanisms or just add some mathematical magic to your scripts, this feature is sure to come in handy.

To learn more about expressions in Sieve scripts, check out the Arithmetical and Logical Expressions section in the documentation.

Fetching Data from Databases

Using Sieve scripts, you can now query SQL or LDAP databases and store the results as Sieve variables. This is done using the query command with the optional :set argument.

Consider this example:

query :use "sql" :set ["awl_score", "awl_count"] "SELECT score, count FROM awl WHERE sender = ? AND ip = ?" ["${env.from}", "%{env.remote_ip}"];

The above Sieve script fetches the score and count columns from the awl table in an SQL database and stores them as the Sieve variables awl_score and awl_count respectively.

To learn more about fetching data from SQL or LDAP queries, check out the query extension documentation.

Conclusion

These features allow for more advanced filtering mechanisms and more powerful Sieve scripts. We hope you enjoy them!

· 3 min read
Mauro D.

In the digital age where privacy and data protection are paramount, we continually strive to enhance the security features offered by Stalwart Mail Server. Today, we're thrilled to announce our latest upgrade – Encryption at Rest!

Understanding Encryption at Rest

Encryption at Rest is designed to protect your data when it's stored, or 'at rest,' on your server. This new feature introduces the ability to automatically encrypt plain-text email messages with OpenPGP (Pretty Good Privacy) or S/MIME (Secure/Multipurpose Internet Mail Extensions) before being written to disk. It provides the option to use either AES256 or AES128 encryption for PGP and AES256-CBC or AES128-CBC for S/MIME.

Why It Matters

With Encryption at Rest, your data remains secure even in the event of a physical storage breach. The encrypted data stored on your mail server is inaccessible without the unique decryption keys. Even system administrators don't have the capacity to decrypt these messages, reinforcing the privacy of your communications.

How it Works

Encryption at rest in Stalwart Mail Server is easy to enable and use. All it requires is for users to upload their S/MIME certificate or PGP public key using a user-friendly web interface. These keys are utilized to automatically encrypt plain-text messages before they are written to disk.

Comparative Look

What sets Stalwart Mail Server's implementation apart is its unique approach to key management. Unlike some other mail servers, Stalwart Mail Server does not store the private key on the server or in the database. This means that even the system administrators or anyone with access to the database won't be able to decrypt your messages.

Take for instance, Dovecot's mail-crypt plugin. While it's a powerful tool for ensuring the security of email storage, its design requires the private key to be stored in the database. This effectively means that your emails can still be decrypted by someone with the right access. In contrast, Stalwart Mail Server provides an extra layer of security by allowing the user to retain sole possession of their private keys.

Looking Ahead

At Stalwart Labs, we're committed to your data protection and privacy. Encryption at Rest is a significant addition to our email security arsenal, and we're excited for you to start using it. For detailed information on Encryption at Rest and instructions on its use, please visit our updated documentation and FAQ.

Stay tuned for more updates, and happy mailing!

· One min read
Mauro D.

Stalwart Mail Server continues its tradition of constant innovation and advancement with the release of version 0.3.2. Address rewriting has always been a highly requested feature, and we've delivered in a big way. The 0.3.2 update introduces sender and recipient address rewriting using both regular expressions and Sieve scripts. This means you now have the ability to manipulate and manage email addresses like never before, providing unparalleled flexibility in routing your emails.

But that's not all. We've also included support for subaddressing and catch-all addresses using regular expressions. This feature allows you to handle email addressing in more unique and sophisticated ways, aiding in spam management, and simplifying email routing to non-standard addresses.

Lastly, we've introduced dynamic variables in configuration rules. This allows you to use variables within settings that are resolved at runtime, further enhancing the flexibility and control you have over your mail server configuration.

This release is all about providing you with more control, adaptability, and management options for your email. We're incredibly excited to see how you will leverage these features to optimize your mail server. Upgrade to Stalwart Mail Server 0.3.2 and revolutionize the way you manage email addresses.

· 2 min read
Mauro D.

E-mail filtering is a crucial part of any modern mail server and, for this reason, we're excited to announce the addition of Milter support to Stalwart Mail Server. This update, driven by user feedback, allows the integration of both new and old Milter filters, supporting versions 2 and 6, which expands the server's capabilities in inspecting, filtering, or modifying emails during processing.

A milter, or "mail filter", is an extension to mail servers based on the Sendmail protocol. Milters allow third-party software to access mail messages as they are being processed in order to filter, modify, or annotate them. By using Milters, a mail server can utilize a variety of functionalities such as spam filtering, virus scanning, and other types of mail processing, beyond what is built into the mail server itself. Milters operate at the SMTP protocol level, which means they have access to both the SMTP envelope and the message contents.

This new feature not only responds to our users' needs but also ensures that Stalwart can work seamlessly with any existing setup. Whether you are seeking better spam protection, antivirus measures, or implementing specific processing rules, milter filtering has got you covered.

Learn more about milter filters and how to set them up in our documentation.

· 2 min read
Mauro D.

We're thrilled to announce the release of Stalwart Mail Server, our biggest leap forward yet. This version combines the powerful capabilities of Stalwart JMAP, Stalwart IMAP, and Stalwart SMTP servers into one easy-to-install binary, offering you a unified, highly efficient mail server solution.

Here are some of the exciting new features:

  • LDAP and SQL authentication support was added, giving you more flexibility and options to integrate Stalwart with your existing infrastructure.
  • We've incorporated support for disk quotas to provide better control over your storage resources.
  • Subaddressing and catch-all addresses are now supported. These features make the email handling process more flexible and efficient.
  • Storage options have been extended with the inclusion of S3-compatible storage. Now you can store your emails and blobs using reliable and scalable solutions such as MinIO, Amazon S3, or Google Cloud Storage.
  • In response to user feedback, we've replaced RocksDB with SQLite. Our community told us they wanted an open, trusted database technology with easier access to their data, and we listened!
  • For those operating in distributed environments, you can now opt for the FoundationDB backend, supporting millions of users without sacrificing performance.
  • Stalwart IMAP is no longer an IMAP-to-JMAP proxy, instead, it now provides direct access to the message store. This significant change has brought a tremendous improvement in performance, reducing latency, and making your mail operations faster than ever.
  • We've also made significant strides in enhancing performance by rewriting the JMAP protocol parser and the storage API.
  • Lastly, we've made the decision to switch from Actix Web Server to Hyper. This change has allowed us to reduce memory footprint and increase performance, resulting in a more optimized and efficient mail server.

With Stalwart Mail Server, we're delivering a more unified, powerful, and efficient solution that meets your growing email infrastructure needs. We're excited to see how you'll leverage these new capabilities, and as always, we're here to support you every step of the way!

· 2 min read
Mauro D.

We’re excited to announce that Stalwart Labs has received a grant from the NGI0 Entrust Fund. This fund, established by NLnet with financial support from the European Commission’s Next Generation Internet programme, aims to support the development of innovative and secure technologies for the internet of the future.

The purpose of the grant is to support the completion of the development of Stalwart Mail server, an open-source mail server written in Rust that is designed to be simple to run, but at the same time, extremely secure, robust, and focused on privacy. The funds from this grant will cover the development of several new features that will make Stalwart Mail server even more useful and user-friendly.

The features that Stalwart Labs team will develop with the support of the NGI0 Entrust Fund include Stalwart SMTP, HTTP Listener / JSON Parsing refactoring, Store Refactoring, MinIO/S3 blob storage, Quota support, External authentication support, Catch-all addresses and aliases, JMAP Quota, JMAP Blob, JMAP Contacts, JMAP Calendars, JMAP Tasks, JMAP MDN, and the integration of SMTP, JMAP, and IMAP. Additionally, the grant will also cover security auditing, ensuring that Stalwart Mail Server is as secure and reliable as possible.

With the completion of these features, Stalwart Mail server will become an even more powerful tool for individuals and organizations looking for a secure and private email solution. We look forward to seeing the impact that this innovative technology will have on the future of email and online communication.

We are grateful to NLnet, and the European Commission’s Next Generation Internet programme for their support in the development of Stalwart Mail server. We believe that this grant will enable us to make significant progress in the development of this important open-source project, and we look forward to sharing our progress with the community in the coming months.

· One min read
Mauro D.

It’s official! We are proud to announce the release of Stalwart SMTP, the next-generation email server solution written in Rust for businesses, organizations, and individuals alike.

Stalwart SMTP is a robust and secure email server that offers a comprehensive set of features to meet the needs of today’s demanding email communications. Whether you’re running a large enterprise or a small business, Stalwart SMTP is designed to handle the most complex email environments with ease.

One of the key features of Stalwart SMTP is its support for advanced email security protocols, including DMARC, DKIM, SPF, ARC, DANE, MTA-STS, and SMTP TLS reporting. This means that you can be sure that your emails are protected from spoofing and phishing attempts, and that your email messages are delivered securely to their intended recipients.

We are confident that Stalwart SMTP will meet the needs of businesses and organizations of all sizes, and we look forward to hearing your feedback and suggestions. To learn more about Stalwart SMTP, visit our website and start exploring the many features and benefits of this powerful email server solution.

· 3 min read
Mauro D.

Today the mail-auth library was released, which is an e-mail authentication and reporting library written in Rust that supports the DKIM, ARC, SPF and DMARC protocols. It is the Rust equivalent of OpenDKIM, OpenSPF, OpenARC and OpenDMARC combined in one library (as well as some extras such ARF support). mail-auth includes the following features:

  • DomainKeys Identified Mail (DKIM):
  • ED25519-SHA256 (Edwards-Curve Digital Signature Algorithm), RSA-SHA256 and RSA-SHA1 signing and verification.
  • DKIM Authorized Third-Party Signatures.
  • DKIM failure reporting using the Abuse Reporting Format.
  • Authenticated Received Chain (ARC):
  • ED25519-SHA256 (Edwards-Curve Digital Signature Algorithm), RSA-SHA256 and RSA-SHA1 chain verification.
  • ARC sealing.
  • Sender Policy Framework (SPF):
  • Policy evaluation.
  • SPF failure reporting using the Abuse Reporting Format.
  • Domain-based Message Authentication, Reporting, and Conformance (DMARC):
  • Policy evaluation.
  • DMARC aggregate report parsing and generation.
  • Abuse Reporting Format (ARF):
  • Abuse and Authentication failure reporting.
  • Feedback report parsing and generation.

DKIM Signature Verification

        // Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();

// Parse message
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();

// Validate signature
let result = resolver.verify_dkim(&authenticated_message).await;

// Make sure all signatures passed verification
assert!(result.iter().all(|s| s.result() == &DKIMResult::Pass));

DKIM Signing

        // Sign an e-mail message using RSA-SHA256
let pk_rsa = PrivateKey::from_rsa_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
let signature_rsa = Signature::new()
.headers(["From", "To", "Subject"])
.domain("example.com")
.selector("default")
.sign(RFC5322_MESSAGE.as_bytes(), &pk_rsa)
.unwrap();

// Sign an e-mail message using ED25519-SHA256
let pk_ed = PrivateKey::from_ed25519(
&base64_decode(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
&base64_decode(ED25519_PRIVATE_KEY.as_bytes()).unwrap(),
)
.unwrap();
let signature_ed = Signature::new()
.headers(["From", "To", "Subject"])
.domain("example.com")
.selector("default-ed")
.sign(RFC5322_MESSAGE.as_bytes(), &pk_ed)
.unwrap();

// Print the message including both signatures to stdout
println!(
"{}{}{}",
signature_rsa.to_header(),
signature_ed.to_header(),
RFC5322_MESSAGE
);

ARC Chain Verification

        // Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();

// Parse message
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();

// Validate ARC chain
let result = resolver.verify_arc(&authenticated_message).await;

// Make sure ARC passed verification
assert_eq!(result.result(), &DKIMResult::Pass);

ARC Chain Sealing

        // Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();

// Parse message to be sealed
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();

// Verify ARC and DKIM signatures
let arc_result = resolver.verify_arc(&authenticated_message).await;
let dkim_result = resolver.verify_dkim(&authenticated_message).await;

// Build Authenticated-Results header
let auth_results = AuthenticationResults::new("mx.mydomain.org")
.with_dkim_result(&dkim_result, "[email protected]")
.with_arc_result(&arc_result, "127.0.0.1".parse().unwrap());

// Seal message
if arc_result.can_be_sealed() {
// Seal the e-mail message using RSA-SHA256
let pk_rsa = PrivateKey::from_rsa_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
let arc_set = ARC::new(&auth_results)
.domain("example.org")
.selector("default")
.headers(["From", "To", "Subject", "DKIM-Signature"])
.seal(&authenticated_message, &arc_result, &pk_rsa)
.unwrap();

// Print the sealed message to stdout
println!("{}{}", arc_set.to_header(), RFC5322_MESSAGE)
} else {
eprintln!("The message could not be sealed, probably an ARC chain with cv=fail was found.")
}

SPF Policy Evaluation

        // Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();

// Verify HELO identity
let result = resolver
.verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com")
.await;
assert_eq!(result.result(), SPFResult::Fail);

// Verify MAIL-FROM identity
let result = resolver
.verify_spf_sender("::1".parse().unwrap(), "gmail.com", "[email protected]")
.await;
assert_eq!(result.result(), SPFResult::Fail);

DMARC Policy Evaluation

        // Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();

// Verify DKIM signatures
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
let dkim_result = resolver.verify_dkim(&authenticated_message).await;

// Verify SPF MAIL-FROM identity
let spf_result = resolver
.verify_spf_sender("::1".parse().unwrap(), "example.org", "[email protected]")
.await;

// Verify DMARC
let dmarc_result = resolver
.verify_dmarc(
&authenticated_message,
&dkim_result,
"example.org",
&spf_result,
)
.await;
assert_eq!(dmarc_result.dkim_result(), &DMARCResult::Pass);
assert_eq!(dmarc_result.spf_result(), &DMARCResult::Pass);

More examples available on Github under the examples directory.

· 2 min read
Mauro D.

Sieve (RFC5228) is a scripting language for filtering email messages at or around the time of final delivery. It is suitable for running on a mail server where users may not be allowed to execute arbitrary programs as it has no user-controlled loops or the ability to run external programs. Sieve is a data-driven programming language, similar to earlier email filtering languages such as procmail and maildrop, and earlier line-oriented languages such as sed and AWK: it specifies conditions to match and actions to take on matching.

Today Stalwart JMAP v0.2 was released including support for the for JMAP for Sieve Scripts draft. Additionally, ManageSieve support was added to Stalwart IMAP v0.2.

Stalwart JMAP safely runs Sieve scripts in a controlled sandbox that ensures that programs do not exceed or abuse their allocated system resources.

Unlike other mail servers that offer limited support for Sieve extensions, Stalwart JMAP supports all existing Sieve extensions including:

· 4 min read
Mauro D.

Sieve is a language that can be used to create filters for electronic mail. It is not tied to any particular operating system or mail architecture. It requires the use of RFC 822-compliant messages, but otherwise should generalize to other systems that meet these criteria.

Today, the sieve-rs crate was released which is an interpreter for Sieve scripts written in Rust. The interpreter includes support for all existing Sieve extensions.

Currently the interpreter is available as a standalone library but it will be soon added to Stalwart JMAP (including JMAP Sieve support) and Stalwart IMAP (including ManageSieve support).

Compiling and running a Sieve script is straightforward:

    use sieve::{runtime::RuntimeError, Action, Compiler, Event, Input, Runtime};

let text_script = br#"
require ["fileinto", "body", "imap4flags"];

if body :contains "tps" {
setflag "$tps_reports";
}

if header :matches "List-ID" "*<*@*" {
fileinto "INBOX.lists.${2}"; stop;
}
"#;

// Compile
let compiler = Compiler::new();
let script = compiler.compile(text_script).unwrap();

// Build runtime
let runtime = Runtime::new();

// Create filter instance
let mut instance = runtime.filter(
br#"From: Sales Mailing List <[email protected]>
To: John Doe <[email protected]>
List-ID: <[email protected]>
Subject: TPS Reports

We're putting new coversheets on all the TPS reports before they go out now.
So if you could go ahead and try to remember to do that from now on, that'd be great. All right!
"#,
);
let mut input = Input::script("my-script", script);

// Start event loop
while let Some(result) = instance.run(input) {
match result {
Ok(event) => match event {
Event::IncludeScript { name, optional } => {
// NOTE: Just for demonstration purposes, script name needs to be validated first.
if let Ok(bytes) = std::fs::read(name.as_str()) {
let script = compiler.compile(&bytes).unwrap();
input = Input::script(name, script);
} else if optional {
input = Input::False;
} else {
panic!("Script {} not found.", name);
}
}
Event::MailboxExists { .. } => {
// Return true if the mailbox exists
input = false.into();
}
Event::ListContains { .. } => {
// Return true if the list(s) contains an entry
input = false.into();
}
Event::DuplicateId { .. } => {
// Return true if the ID is duplicate
input = false.into();
}
Event::Execute { command, arguments } => {
println!(
"Script executed command {:?} with parameters {:?}",
command, arguments
);
input = false.into(); // Report whether the script succeeded
}
#[cfg(test)]
_ => unreachable!(),
},
Err(error) => {
match error {
RuntimeError::IllegalAction => {
eprintln!("Script tried allocating more variables than allowed.");
}
RuntimeError::TooManyIncludes => {
eprintln!("Too many included scripts.");
}
RuntimeError::InvalidInstruction(instruction) => {
eprintln!(
"Invalid instruction {:?} found at {}:{}.",
instruction.name(),
instruction.line_num(),
instruction.line_pos()
);
}
RuntimeError::ScriptErrorMessage(message) => {
eprintln!("Script called the 'error' function with {:?}", message);
}
RuntimeError::CapabilityNotAllowed(capability) => {
eprintln!(
"Capability {:?} has been disabled by the administrator.",
capability
);
}
RuntimeError::CapabilityNotSupported(capability) => {
eprintln!("Capability {:?} not supported.", capability);
}
RuntimeError::OutOfMemory => {
eprintln!("Script exceeded the configured memory limit.");
}
RuntimeError::CPULimitReached => {
eprintln!("Script exceeded the configured CPU limit.");
}
}
break;
}
}
}

// Process actions
for action in instance.get_actions() {
match action {
Action::Keep { flags, message_id } => {
println!(
"Keep message '{}' with flags {:?}.",
std::str::from_utf8(instance.get_message(*message_id).unwrap()).unwrap(),
flags
);
}
Action::Discard => {
println!("Discard message.")
}
Action::Reject { reason } => {
println!("Reject message with reason {:?}.", reason);
}
Action::Ereject { reason } => {
println!("Ereject message with reason {:?}.", reason);
}
Action::FileInto {
folder,
flags,
message_id,
..
} => {
println!(
"File message '{}' in folder {:?} with flags {:?}.",
std::str::from_utf8(instance.get_message(*message_id).unwrap()).unwrap(),
folder,
flags
);
}
Action::SendMessage {
recipient,
message_id,
..
} => {
println!(
"Send message '{}' to {:?}.",
std::str::from_utf8(instance.get_message(*message_id).unwrap()).unwrap(),
recipient
);
}
Action::Notify {
message, method, ..
} => {
println!("Notify URI {:?} with message {:?}", method, message);
}
}
}

Additional examples are available on the repository.