|
|
||
|---|---|---|
| .vscode | ||
| imapd | ||
| smoketest | ||
| smtpd | ||
| vendor | ||
| webcal | ||
| .drone.yml | ||
| .gitignore | ||
| backend.go | ||
| config.go | ||
| docker-compose.example | ||
| Dockerfile | ||
| example-run.sh | ||
| go.mod | ||
| go.sum | ||
| graylist.go | ||
| handler.go | ||
| LICENSE | ||
| logger.go | ||
| panic_recover.go | ||
| README.md | ||
| REFACTORING.md | ||
| run.sh | ||
| session.go | ||
| SINGLE_DOMAIN_GUIDE.md | ||
| smoketest.py | ||
| tls.go | ||
| version.go | ||
| zangtumb.go | ||
ZangTumb
Futuristic SMTP INBOUND-only server for home usage.
Futuristic SMTP/IMAP Catch-All server for home usage.
Inspired by Marinetti's RFC (also known as the Manifesto of Futurism).
It only serves a specific list of email addresses. No aliases.
It serves a single domain in catch-all mode: one mailbox for everything.
Everything else will apparently be accepted and then silently discarded, so that spammers waste their time (and money).
Every message sent to your domain will be accepted and stored in a single Maildir.
Spammers waste resources; you receive every message.
REQUIREMENTS
- Golang version >= 1.13
- Golang version >= 1.20
- git
(1 and 2 implies arithmetic, which then implies Gödel. So yes, we need Gödel too. Just kidding, it was a mistake, so I made it fun)
INSTALLATION
First, download the code into the folder you want to use with Golang:
git clone https://git.keinpfusch.net/loweel/zangtumb.git
go build -mod=vendor
./zangtumb
Set the environment variables (see below) and run ./zangtumb to start the daemon.
CONFIGURATION
ZangTumb is configured exclusively via environment variables.
It is designed to be easily containerized.
A reference pseudo-Dockerfile could be:
FROM debian:stable-slim
## MAIN
ENV DOMAINNAME "yourdomain.tld"
ENV MAILUSER "admin"
ENV MAILPASSWORD "a-secure-password"
## SESSION
ENV MAILFOLDER "/zangmail"
ENV SMTP_LISTEN ":25"
ENV IMAP_LISTEN ":143"
## CERTIFICATES (Optional, auto-generated if missing)
ENV KEYFILE "/certs/mydomain.key"
ENV CERTFILE "/certs/mydomain.crt"
RUN useradd -ms /bin/bash zangtumb
RUN mkdir -p /zangmail
COPY . /opt/zangtumb/
RUN chown -R zangtumb:zangtumb /opt/zangtumb
RUN chown -R zangtumb:zangtumb /zangmail
EXPOSE 25 143
USER zangtumb
WORKDIR /opt/zangtumb
ENTRYPOINT ["/opt/zangtumb/zangtumb"]
Everything is configured using environment variables, as follows:
| ENV STRING | Example Value | Meaning |
|---|---|---|
| DOMAINNAME | "mydomain.tld" | The domain for which mail will be received. |
| MAILUSER | "admin" | The single catch-all user. All mail for *@domain will end up here. |
| MAILPASSWORD | "secret" | Password used to access the Maildir over IMAP. |
| SMTP_LISTEN | ":25" | SMTP listening port. TLS is mandatory. |
| IMAP_LISTEN | ":143" | IMAP listening port used to read mail. |
| MAILFOLDER | "/zangmail" | Root storage path. Maildir will be created in /zangmail/admin/Maildir/. |
| KEYFILE | "/certs/key.pem" | TLS private key path. Automatically generated if missing. |
| CERTFILE | "/certs/cert.pem" | TLS certificate path. Automatically generated if missing. |
| GRAYLISTING_ENABLED | "true" | Enable graylisting protection. |
| GRAYLISTING_DB_PATH | "/tmp/gray.db" | Path for the SQLite database. Must be on local storage, not NFS. |
| GRAYLISTING_DELAY | "30s" | Time the sender must wait before the mail is accepted. |
| GRAYLISTING_EXPIRY | "36h" | Lifetime of a graylisting record. |
| WEBCAL_ENABLED | "true" | Enable the optional mail-driven read-only iCalendar feed. |
| WEBCAL_INGEST_USER | "secretary" | Local part of the calendar ingest address, e.g. secretary@domain. |
| WEBCAL_LISTEN | ":8088" | HTTP listening address for the authenticated read-only calendar feed. |
| DEBUG | "true" | Enable verbose logging for SMTP and IMAP protocols. |
| MAX_MESSAGE_SIZE | "10451212" | Size, in BYTES, of a single email body |
Of course, if you place your certificates into /certs (as in the example), that folder must already exist.
ZangTumb will automatically create the Maildir structure on first startup.
That's it.
WEBCAL: MAIL-DRIVEN READ-ONLY CALENDAR
ZangTumb can optionally expose a read-only iCalendar feed generated from calendar invitations received by email.
This feature is deliberately small.
It is not CalDAV.
It is not WebDAV.
It does not allow clients to edit the calendar through HTTP.
It only does this:
calendar invitation email
↓
special calendar ingest address
↓
ZangTumb extracts the iCalendar payload
↓
ZangTumb merges the event into one calendar file
↓
clients download that file over authenticated HTTP
The generated file lives here:
MAILFOLDER/webcal/calendar.ics
and is served over HTTP at:
/calendar.ics
Access to the HTTP endpoint uses the same credentials as IMAP:
username: MAILUSER
password: MAILPASSWORD
Conceptual model
WebCal works by using a special mailbox as a passive calendar attendee.
For example, with:
DOMAINNAME=keinpfusch.net
WEBCAL_INGEST_USER=secretary
the WebCal ingest address becomes:
secretary@keinpfusch.net
When that address receives an iCalendar message, ZangTumb imports the event into:
MAILFOLDER/webcal/calendar.ics
Then any calendar client subscribed to the HTTP feed will see the event at its next refresh.
So the mental model is:
secretary@domain receives invitations
ZangTumb publishes the secretary's agenda
clients subscribe to the published .ics file
Configuration
Enable WebCal with:
WEBCAL_ENABLED=true
WEBCAL_INGEST_USER=secretary
WEBCAL_LISTEN=:8088
Example:
DOMAINNAME=keinpfusch.net
MAILUSER=admin
MAILPASSWORD=a-secure-password
MAILFOLDER=/zangmail
WEBCAL_ENABLED=true
WEBCAL_INGEST_USER=secretary
WEBCAL_LISTEN=:8088
This creates the ingest address:
secretary@keinpfusch.net
and serves the generated calendar from:
http://domain/calendar.ics
The URL is protected with HTTP Basic Authentication using MAILUSER and MAILPASSWORD.
How to use it from a calendar client
In your calendar client, add a new remote calendar subscription using the HTTP URL:
http://domain/calendar.ics
Use the same credentials you use for IMAP:
username: MAILUSER
password: MAILPASSWORD
The exact wording depends on the client. It may be called:
Subscribe to calendar
Add calendar by URL
Internet calendar
Remote iCalendar
Web calendar
Do not add it as CalDAV.
This is only a read-only iCalendar feed.
Clients can read the calendar, but they cannot modify it through this URL.
How events enter the calendar
The normal workflow is to invite the WebCal ingest address to the event.
For example:
secretary@keinpfusch.net
If a calendar client creates, updates, or cancels an event and sends email notifications to the attendees, ZangTumb receives those iCalendar messages and updates the feed.
ZangTumb understands the usual iCalendar identity fields:
UID
SEQUENCE
RECURRENCE-ID
METHOD:REQUEST
METHOD:CANCEL
STATUS:CANCELLED
The UID identifies the event.
A new UID adds an event.
The same UID with a newer SEQUENCE updates the event.
METHOD:CANCEL or STATUS:CANCELLED removes the event.
RECURRENCE-ID, when present, identifies a single occurrence of a recurring event.
Events from external services
Some services, such as a barber booking website, a hotel booking system, a train company, or a medical appointment platform, may send an .ics file to the email address you provide.
In that case you may give the service the WebCal ingest address:
secretary@keinpfusch.net
If the service sends a valid .ics attachment or inline text/calendar part, ZangTumb imports it.
However, external services often create events where the original attendee is not the WebCal address. For this reason, WebCal treats the ingest mailbox as the source of truth and may normalize imported events so that the event is represented as an appointment involving:
MAILUSER@DOMAINNAME
WEBCAL_INGEST_USER@DOMAINNAME
In other words, even if the barber website does not explicitly invite the secretary in the semantic iCalendar data, the fact that the message arrived at the WebCal ingest address is enough for ZangTumb to publish it in the secretary calendar.
The practical rule is simple:
If an event reaches the WebCal ingest address,
it belongs in the WebCal feed.
Supported mail formats
ZangTumb WebCal is designed to accept the common ways calendar invitations are sent by email:
inline text/calendar
attached .ics files
Outlook / Exchange winmail.dat TNEF attachments, when TNEF support is built
This means both of these are valid:
Content-Type: text/calendar; method=REQUEST
and:
Content-Disposition: attachment; filename="invite.ics"
Outlook/Exchange may also wrap calendar data inside:
winmail.dat
When TNEF support is available, ZangTumb tries to extract calendar payloads from that file too.
Limitations
WebCal is intentionally limited.
It does not implement:
CalDAV
WebDAV
server-side calendar editing
free/busy
RSVP handling
calendar sharing permissions
push notifications
It is only a mail-driven, read-only iCalendar feed.
The client refresh interval is controlled by the client. ZangTumb does not push changes to clients.
Security notes
The calendar may contain private information.
The HTTP feed is authenticated using the same credentials as IMAP. If you expose WEBCAL_LISTEN through a reverse proxy, use HTTPS.
The ingest address should also be considered sensitive. Anyone who can send a valid calendar invitation to that address may be able to add an event to the published calendar.
For a private deployment, this is usually acceptable. For a public or hostile environment, future policy controls may be useful, such as:
accept only authenticated local senders
accept only specific sender addresses
quarantine suspicious calendar messages
limit attachment size
limit number of events per message
FAQ
In v2.0, TLS is mandatory and certificates are automatically generated if missing.
-
Why is it now a Catch-All server?
-
Because managing multiple mailboxes is for people with too much free time. You own your domain.
One domain, one user, maximum speed. -
Why don't you use OpenSMTPD?
-
Because making this server took less effort than containerizing OpenSMTPD properly.
-
Why don't you use Postfix/Sendmail/Qmail/Courier?
-
I host four mailboxes in total. Why should I deploy all that complexity?
Complexity != security. -
!ZangTumb forces StartTLS. This is against the RFC!!!!
-
Unfortunately, English cannot properly translate the correct Italian answer, which would roughly be: "Esticazzi?"
-
This Golang code is not idiomatic. And there is no graphene, no quantum computing, no UI/UX, and no horizontally scalable Internet of Things powered by Artificial Intelligence and Big Data.
-
Please, bring me a Frappuccino.
-
Why you expect the sender server has a client certificate!!! You evil!!!!
-
Is perfectly reasonable. In real life, the equivalent would be that only real couriers and national post service can put paper in your mailbox. Reasonable and legit, IMHO_