Stalwart: Difference between revisions
No edit summary |
typo 127.0.01 |
||
(15 intermediate revisions by 2 users not shown) | |||
Line 2: | Line 2: | ||
== Setup == | == Setup == | ||
The following example enables the Stalwart mail server for the domain ''example.org'', listening on mail delivery SMTP/Submission | The following example enables the Stalwart mail server for the domain ''example.org'', listening on mail delivery SMTP/Submission (<code>25, 465</code>), IMAPS (<code>993</code>) and JMAP ports (8080/443) for mail clients to connect to. Mailboxes for the accounts <code>postmaster@example.org</code> and <code>user1@example.org</code> get created if they don't exist yet. | ||
{{file|/etc/nixos/configuration.nix|nix|3=environment.etc = { | |||
"stalwart/mail-pw1".text = "foobar"; | |||
"stalwart/mail-pw2".text = "foobar"; | |||
"stalwart/admin-pw".text = "foobar"; | |||
"stalwart/acme-secret".text = "secret123"; | |||
}; | |||
services.stalwart-mail = { | |||
enable = true; | enable = true; | ||
package = pkgs.stalwart-mail; | package = pkgs.stalwart-mail; | ||
Line 8: | Line 17: | ||
settings = { | settings = { | ||
server = { | server = { | ||
hostname = "example.org"; | hostname = "mx1.example.org"; | ||
tls = { | tls = { | ||
enable = true; | enable = true; | ||
Line 28: | Line 37: | ||
jmap = { | jmap = { | ||
bind = "[::]:8080"; | bind = "[::]:8080"; | ||
url = "https://mail. | url = "https://mail.example.org"; | ||
protocol = "jmap"; | protocol = "jmap"; | ||
}; | }; | ||
Line 45: | Line 54: | ||
challenge = "dns-01"; | challenge = "dns-01"; | ||
contact = "user1@example.org"; | contact = "user1@example.org"; | ||
domains = [ "example.org" ]; | domains = [ "example.org" "mx1.example.org" ]; | ||
provider = "cloudflare"; | provider = "cloudflare"; | ||
secret = " | secret = "%{file:/etc/stalwart/acme-secret}%"; | ||
}; | }; | ||
session.auth = { | session.auth = { | ||
Line 61: | Line 70: | ||
principals = [ | principals = [ | ||
{ | { | ||
class = " | class = "individual"; | ||
name = "User 1"; | name = "User 1"; | ||
secret = " | secret = "%{file:/etc/stalwart/mail-pw1}%"; | ||
email = [ "user1@example.org" ]; | email = [ "user1@example.org" ]; | ||
} | } | ||
Line 69: | Line 78: | ||
class = "individual"; | class = "individual"; | ||
name = "postmaster"; | name = "postmaster"; | ||
secret = " | secret = "%{file:/etc/stalwart/mail-pw1}%"; | ||
email = [ "postmaster@example.org" ]; | email = [ "postmaster@example.org" ]; | ||
} | } | ||
]; | ]; | ||
}; | |||
authentication.fallback-admin = { | |||
user = "admin"; | |||
secret = "%{file:/etc/stalwart/admin-pw}%"; | |||
}; | }; | ||
}; | }; | ||
Line 82: | Line 95: | ||
"webadmin.example.org" = { | "webadmin.example.org" = { | ||
extraConfig = '' | extraConfig = '' | ||
reverse_proxy http://127.0. | reverse_proxy http://127.0.0.1:8080 | ||
''; | ''; | ||
serverAliases = [ | serverAliases = [ | ||
Line 88: | Line 101: | ||
"autoconfig.example.org" | "autoconfig.example.org" | ||
"autodiscover.example.org" | "autodiscover.example.org" | ||
"mail.example.org" | |||
]; | ]; | ||
}; | }; | ||
}; | }; | ||
};}}TLS key generation is done using DNS-01 challenge through Cloudflare domain provider, see dns-update library for [https://github.com/stalwartlabs/dns-update further providers] or configure [https://stalw.art/docs/server/tls/certificates manual certificates]. | };}} | ||
TLS key generation is done using DNS-01 challenge through Cloudflare domain provider, see dns-update library for [https://github.com/stalwartlabs/dns-update further providers] or configure [https://stalw.art/docs/server/tls/certificates manual certificates]. | |||
== Configuration == | == Configuration == | ||
=== DNS records === | |||
Before adding required records to the example domain <code>example.org</code>, we need to register the domain on the Stalwart server.<syntaxhighlight lang="shell"> | |||
stalwart-cli --url https://webadmin.example.org domain create example.org | |||
</syntaxhighlight>Authenticate using the fallback-admin password. | |||
Review the list of which DNS records are required including their values for the mail server to work at https://webadmin.example.org/manage/directory/domains/tuxtux.com.co/view. Especially following records are essential: | |||
* Record type: A, Name: example.org | |||
* Record type: AAAA, Name: example.org | |||
* Record type: CNAME, Name: autoconfig Value: example.org | |||
* Record type: CNAME, Name: autodiscover, Value: example.org | |||
* Record type: CNAME, Name: mail, Value: example.org | |||
* Record type: CNAME, Name: mta-sts, Value: example.org | |||
* Record type: CNAME, Name: mail, Value: example.org | |||
* Record type: CNAME, Name: webadmin, Value: example.org | |||
* Record type: MX, Name: example.org, Value: mx1.example.org | |||
* Record type: SRV, Name: _imaps._tcp | |||
* Record type: SRV, Name: _submissions._tcp | |||
* Record type: TLSA, Name: _25._tcp.example.org., Value: Only the one starting with "3 1 1" required | |||
* Record type: TLSA, Name: _25._tcp.mx1.example.org., Value: Only the one starting with "3 1 1" required | |||
* Record type: TXT, Name: 202409e._domainkey | |||
* Record type: TXT, Name: 202409r._domainkey | |||
* Record type: TXT, Name: _dmarc | |||
* Record type: TXT, Name: mx1 | |||
* Record type: TXT, Name: _smtp._tls | |||
* Record type: TXT, Name: example.org | |||
=== DNSSEC === | === DNSSEC === | ||
Line 105: | Line 148: | ||
== Tips and tricks == | == Tips and tricks == | ||
=== Test mail server === | |||
You can use several online tools to test your mail server configuration: | |||
* [https://en.internet.nl/test-mail en.internet.nl/test-mail]: Test your mail server configuration for validity and security. | |||
* [https://www.mail-tester.com mail-tester.com]: Send a mail to this service and get a rating about the "spaminess" of your mail server. | |||
* Send a mail to the echo server <code>echo@univie.ac.at</code>. You should receive a response containing your message in several seconds. | |||
=== Unsecure setup for testing environments === | === Unsecure setup for testing environments === | ||
The following minimal configuration example is unsecure and for testing purpose only. It will run the Stalwart mail server on <code>localhost</code>, listening on port <code>143</code> (IMAP) and <code>587</code> (Submission). Users <code>alice</code> and <code>bob</code> are configured with the password <code>foobar</code>.{{file|/etc/nixos/configuration.nix|nix|3=services.stalwart-mail = { | The following minimal configuration example is unsecure and for testing purpose only. It will run the Stalwart mail server on <code>localhost</code>, listening on port <code>143</code> (IMAP) and <code>587</code> (Submission). Users <code>alice</code> and <code>bob</code> are configured with the password <code>foobar</code>.{{file|/etc/nixos/configuration.nix|nix|3=services.stalwart-mail = { | ||
enable = true; | enable = true; | ||
settings = { | settings = { | ||
server = { | server = { |
Latest revision as of 21:50, 11 February 2025
Stalwart is an open-source, all-in-one mail server solution that supports JMAP, IMAP4, and SMTP protocols. It's designed to be secure, fast, robust, and scalable, with features like built-in DMARC, DKIM, SPF, and ARC support for message authentication. It also provides strong transport security through DANE, MTA-STS, and SMTP TLS reporting. Stalwart is written in Rust, ensuring high performance and memory safety.
Setup
The following example enables the Stalwart mail server for the domain example.org, listening on mail delivery SMTP/Submission (25, 465
), IMAPS (993
) and JMAP ports (8080/443) for mail clients to connect to. Mailboxes for the accounts postmaster@example.org
and user1@example.org
get created if they don't exist yet.
environment.etc = {
"stalwart/mail-pw1".text = "foobar";
"stalwart/mail-pw2".text = "foobar";
"stalwart/admin-pw".text = "foobar";
"stalwart/acme-secret".text = "secret123";
};
services.stalwart-mail = {
enable = true;
package = pkgs.stalwart-mail;
openFirewall = true;
settings = {
server = {
hostname = "mx1.example.org";
tls = {
enable = true;
implicit = true;
};
listener = {
smtp = {
protocol = "smtp";
bind = "[::]:25";
};
submissions = {
bind = "[::]:465";
protocol = "smtp";
};
imaps = {
bind = "[::]:993";
protocol = "imap";
};
jmap = {
bind = "[::]:8080";
url = "https://mail.example.org";
protocol = "jmap";
};
management = {
bind = [ "127.0.0.1:8080" ];
protocol = "http";
};
};
};
lookup.default = {
hostname = "mx1.example.org";
domain = "example.org";
};
acme."letsencrypt" = {
directory = "https://acme-v02.api.letsencrypt.org/directory";
challenge = "dns-01";
contact = "user1@example.org";
domains = [ "example.org" "mx1.example.org" ];
provider = "cloudflare";
secret = "%{file:/etc/stalwart/acme-secret}%";
};
session.auth = {
mechanisms = "[plain]";
directory = "'in-memory'";
};
storage.directory = "in-memory";
session.rcpt.directory = "'in-memory'";
queue.outbound.next-hop = "'local'";
directory."imap".lookup.domains = [ "example.org" ];
directory."in-memory" = {
type = "memory";
principals = [
{
class = "individual";
name = "User 1";
secret = "%{file:/etc/stalwart/mail-pw1}%";
email = [ "user1@example.org" ];
}
{
class = "individual";
name = "postmaster";
secret = "%{file:/etc/stalwart/mail-pw1}%";
email = [ "postmaster@example.org" ];
}
];
};
authentication.fallback-admin = {
user = "admin";
secret = "%{file:/etc/stalwart/admin-pw}%";
};
};
};
services.caddy = {
enable = true;
virtualHosts = {
"webadmin.example.org" = {
extraConfig = ''
reverse_proxy http://127.0.0.1:8080
'';
serverAliases = [
"mta-sts.example.org"
"autoconfig.example.org"
"autodiscover.example.org"
"mail.example.org"
];
};
};
};
TLS key generation is done using DNS-01 challenge through Cloudflare domain provider, see dns-update library for further providers or configure manual certificates.
Configuration
DNS records
Before adding required records to the example domain example.org
, we need to register the domain on the Stalwart server.
stalwart-cli --url https://webadmin.example.org domain create example.org
Authenticate using the fallback-admin password.
Review the list of which DNS records are required including their values for the mail server to work at https://webadmin.example.org/manage/directory/domains/tuxtux.com.co/view. Especially following records are essential:
- Record type: A, Name: example.org
- Record type: AAAA, Name: example.org
- Record type: CNAME, Name: autoconfig Value: example.org
- Record type: CNAME, Name: autodiscover, Value: example.org
- Record type: CNAME, Name: mail, Value: example.org
- Record type: CNAME, Name: mta-sts, Value: example.org
- Record type: CNAME, Name: mail, Value: example.org
- Record type: CNAME, Name: webadmin, Value: example.org
- Record type: MX, Name: example.org, Value: mx1.example.org
- Record type: SRV, Name: _imaps._tcp
- Record type: SRV, Name: _submissions._tcp
- Record type: TLSA, Name: _25._tcp.example.org., Value: Only the one starting with "3 1 1" required
- Record type: TLSA, Name: _25._tcp.mx1.example.org., Value: Only the one starting with "3 1 1" required
- Record type: TXT, Name: 202409e._domainkey
- Record type: TXT, Name: 202409r._domainkey
- Record type: TXT, Name: _dmarc
- Record type: TXT, Name: mx1
- Record type: TXT, Name: _smtp._tls
- Record type: TXT, Name: example.org
DNSSEC
Ensure that DNSSEC is enabled for your primary and mail server domain. It can be enabled by your domain provider.
For example, check if DNSSEC is working correctly for your new TLSA record
# nix shell nixpkgs#dnsutils --command delv _25._tcp.mx1.example.org TLSA @1.1.1.1 ; fully validated _25._tcp.mx1.example.org. 10800 IN TLSA 3 1 1 7f59d873a70e224b184c95a4eb54caa9621e47d48b4a25d312d83d96 e3498238 _25._tcp.mx1.example.org. 10800 IN RRSIG TLSA 13 5 10800 20230601000000 20230511000000 39688 example.org. He9VYZ35xTC3fNo8GJa6swPrZodSnjjIWPG6Th2YbsOEKTV1E8eGtJ2A +eyBd9jgG+B3cA/jw8EJHmpvy/buCw==
Tips and tricks
Test mail server
You can use several online tools to test your mail server configuration:
- en.internet.nl/test-mail: Test your mail server configuration for validity and security.
- mail-tester.com: Send a mail to this service and get a rating about the "spaminess" of your mail server.
- Send a mail to the echo server
echo@univie.ac.at
. You should receive a response containing your message in several seconds.
Unsecure setup for testing environments
The following minimal configuration example is unsecure and for testing purpose only. It will run the Stalwart mail server on localhost
, listening on port 143
(IMAP) and 587
(Submission). Users alice
and bob
are configured with the password foobar
.
services.stalwart-mail = {
enable = true;
settings = {
server = {
hostname = "localhost";
tls.enable = false;
listener = {
"smtp-submission" = {
bind = [ "[::]:587" ];
protocol = "smtp";
};
"imap" = {
bind = [ "[::]:143" ];
protocol = "imap";
};
};
};
imap.auth.allow-plain-text = true;
session.auth = {
mechanisms = "[plain, auth]";
directory = "'in-memory'";
};
storage.directory = "in-memory";
session.rcpt.directory = "'in-memory'";
queue.outbound.next-hop = "'local'";
directory."in-memory" = {
type = "memory";
principals = [
{
class = "individual";
name = "alice";
secret = "foobar";
email = [ "alice@localhost" ];
}
{
class = "individual";
name = "bob";
secret = "foobar";
email = [ "bob@$localhost" ];
}
];
};
};
};
See also
- Maddy, a composable, modern mail server written in Go.
- Simple NixOS Mailserver