Email over XNS
Email can run on top of XNS when the name owner publishes mail records through owner DNS and exposes the usual mail ports on the same I2P destination.
The route is ordinary at the application layer:
user@example.xns
|
MX lookup for example.xns
|
mail.example.xns
|
XNS Resolver synthetic address
|
I2P destination derived from the owner key
|
mail server
The mail server does not need to understand XNS. It needs to listen on the ports that i2pd forwards, and clients need to resolve .xns names through XNS Resolver.
Requirements
This guide assumes DNS over XNS is already working.
You will need:
- an owner DNS server on port
53 - i2pd server tunnels for the mail ports
- a mail server
- XNS Resolver on the client side
The examples use example.xns, mail.example.xns and maddy.
I2P tunnels
Add mail ports to the same i2pd destination used by the XNS name:
[example_smtp]
type = server
host = 127.0.0.1
inport = 25
port = 9025
keys = xns/private.dat
signaturetype = 11
i2cp.leaseSetType = 5
[example_submission]
type = server
host = 127.0.0.1
inport = 587
port = 9587
keys = xns/private.dat
signaturetype = 11
i2cp.leaseSetType = 5
[example_submission_tls]
type = server
host = 127.0.0.1
inport = 465
port = 9465
keys = xns/private.dat
signaturetype = 11
i2cp.leaseSetType = 5
[example_imap]
type = server
host = 127.0.0.1
inport = 993
port = 9993
keys = xns/private.dat
signaturetype = 11
i2cp.leaseSetType = 5
Port 25 is for server-to-server SMTP. Port 587 is for authenticated submission with STARTTLS. Port 465 is implicit TLS submission. Port 993 is IMAP over TLS.
Restart i2pd after changing the tunnel configuration:
sudo systemctl restart i2pd
DNS records
Add mail records to the owner DNS zone:
@ IN MX 10 mail.example.xns.
@ IN TXT "v=spf1 -all"
_dmarc IN TXT "v=DMARC1; p=reject; adkim=s; aspf=s"
The MX record tells mail software to deliver mail for example.xns to mail.example.xns.
The SPF record is deliberately strict. IP-based authorization is not the trust boundary here. The useful proof is DKIM: outgoing mail is signed by a key published under the XNS name, and receivers verify that signature through owner DNS. DMARC then tells receivers to reject mail that does not align with that domain policy.
maddy
maddy is a small all-in-one mail server. It can provide SMTP, submission, IMAP, local accounts and DKIM signing from one config.
Create a local state directory and a TLS certificate:
mkdir -p /srv/xns-mail/data /srv/xns-mail/run /srv/xns-mail/tls
openssl req -x509 -newkey rsa:4096 -nodes -days 3650 \
-subj "/CN=mail.example.xns" \
-keyout /srv/xns-mail/tls/privkey.pem \
-out /srv/xns-mail/tls/fullchain.pem
The certificate may be self-signed. I2P already provides encrypted transport to the destination, but mail clients still expect TLS on submission and IMAP. A self-signed certificate will have to be accepted by the client.
A local XNS setup should bind only to loopback. The public side is the I2P tunnel:
$(hostname) = mail.example.xns
$(primary_domain) = example.xns
$(local_domains) = $(primary_domain)
$(base_dir) = /srv/xns-mail
state_dir $(base_dir)/data
runtime_dir $(base_dir)/run
tls file $(base_dir)/tls/fullchain.pem $(base_dir)/tls/privkey.pem
auth.pass_table local_authdb {
table sql_table {
driver sqlite3
dsn $(base_dir)/data/credentials.db
table_name passwords
}
}
storage.imapsql local_mailboxes {
driver sqlite3
dsn $(base_dir)/data/imapsql.db
}
hostname $(hostname)
msgpipeline local_routing {
destination postmaster $(local_domains) {
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
smtp tcp://127.0.0.1:9025 {
dmarc yes
check {
dkim
spf
}
source $(local_domains) {
reject 501 5.1.8 "Use Submission for outgoing SMTP"
}
default_source {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
}
submission tls://127.0.0.1:9465 tcp://127.0.0.1:9587 {
auth &local_authdb
source $(local_domains) {
check {
authorize_sender {
user_to_email identity
}
}
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
modify {
dkim $(primary_domain) $(local_domains) default
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Non-local sender domain"
}
}
target.remote outbound_delivery {
mx_auth {
local_policy {
min_tls_level none
min_mx_level none
}
}
}
target.queue remote_queue {
target &outbound_delivery
autogenerated_msg_domain $(primary_domain)
}
imap tls://127.0.0.1:9993 {
auth &local_authdb
storage &local_mailboxes
}
Verify the config:
maddy --config maddy.conf verify-config
This also creates the DKIM key used by the dkim $(primary_domain) $(local_domains) default modifier. With the state directory above, maddy writes:
/srv/xns-mail/data/dkim_keys/example.xns_default.key
/srv/xns-mail/data/dkim_keys/example.xns_default.dns
The .key file is the private signing key. The .dns file contains the public TXT value that must be published in owner DNS:
cat /srv/xns-mail/data/dkim_keys/example.xns_default.dns
Add that value to the zone under default._domainkey:
default._domainkey IN TXT "v=DKIM1; k=rsa; p=<public-key>"
Long DKIM keys may need to be split into multiple quoted strings:
default._domainkey IN TXT (
"v=DKIM1; k=rsa; p=first-part"
"second-part"
)
DNS joins those strings into one TXT value. Restart or reload the DNS server after changing the zone.
Create an account:
maddy --config maddy.conf creds create postmaster@example.xns
maddy --config maddy.conf imap-acct create postmaster@example.xns
Start the server:
maddy --config maddy.conf run
Testing
Check the DNS records through XNS Resolver:
dig example.xns MX +short
dig default._domainkey.example.xns TXT +short
dig _dmarc.example.xns TXT +short
Check that the mail host resolves through the virtual route:
getent hosts mail.example.xns
Then check the mail ports:
openssl s_client \
-connect mail.example.xns:587 \
-starttls smtp \
-servername mail.example.xns
openssl s_client \
-connect mail.example.xns:993 \
-servername mail.example.xns
The certificate may be self-signed. That warning is expected unless the operator uses a certificate trusted by the client.
Mail clients
Automatic account discovery may not understand .xns. Configure the account manually:
Email address: user@example.xns
Username: user@example.xns
Incoming IMAP:
Host: mail.example.xns
Port: 993
Security: SSL/TLS
Outgoing SMTP:
Host: mail.example.xns
Port: 587
Security: STARTTLS
The client must use the system resolver path where XNS Resolver is active. If it bypasses the system resolver or runs inside a sandbox that cannot see the XNS route, it will not find mail.example.xns.
What is proven
XNS establishes which owner key controls example. I2P reaches the destination derived from that key. Owner DNS publishes the MX, DKIM and DMARC records. DKIM signs the outgoing mail for the domain.
This is not ordinary global clearnet mail discovery. It is mail inside an XNS-aware environment, using ordinary mail protocols after resolution has taken the XNS route.