Sample config and doc

This commit is contained in:
Balakrishnan Balasubramanian 2023-06-28 15:15:31 -04:00
parent 5f56a1256b
commit 9bb870ac99
7 changed files with 223 additions and 49 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ __pycache__
dummy.py dummy.py
build build
mail4one.pyz mail4one.pyz
deploy_configs/config.json

View File

@ -1,20 +1,5 @@
shell: # Needs python3 >= 3.9, sed, git for build
MYPYPATH=`pipenv --venv`/lib/python3.11/site-packages pipenv shell build: clean
test:
pipenv run python -m unittest discover
docker-tests:
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.11-alpine sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.10-alpine sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.11 sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.10 sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.9 sh runtests.sh
requirements.txt: Pipfile.lock
pipenv requirements > requirements.txt
build: clean requirements.txt
python3 -m pip install -r requirements.txt --no-compile --target build python3 -m pip install -r requirements.txt --no-compile --target build
cp -r mail4one/ build/ cp -r mail4one/ build/
sed -i "s/DEVELOMENT/$(shell scripts/get_version.sh)/" build/mail4one/version.py sed -i "s/DEVELOMENT/$(shell scripts/get_version.sh)/" build/mail4one/version.py
@ -30,5 +15,31 @@ clean:
rm -rf build rm -rf build
rm -rf mail4one.pyz rm -rf mail4one.pyz
docker-tests:
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.11-alpine sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.10-alpine sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.11 sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.10 sh runtests.sh
docker run --pull=always -v `pwd`:/app -w /app --rm -it python:3.9 sh runtests.sh
# ============================================================================
requirements.txt: Pipfile.lock
pipenv requirements > requirements.txt
format: format:
black mail4one/*py black mail4one/*py
build-dev: requirements.txt build
setup:
pipenv install
cleanup:
pipenv --rm
shell:
MYPYPATH=`pipenv --venv`/lib/python3.11/site-packages pipenv shell
test:
pipenv run python -m unittest discover

View File

@ -1,44 +1,50 @@
# mail4one # mail4one
Mail server for single user #asyncio #python Personal mail server for a single user or a small family. Written in pure python with minimal dependencies.
Designed for dynamic alias based workflow where a different alias is used for each purpose.
## Features # Getting started
1. Get a domain name
1. Get a vps (or a home server). Setup firewall rules for receive on port 25, 995, 465
1. Setup MX record
1. Build/Download latest release - `mail4one.pyz`
1. Generate `config.json` from `deploy_configs/config.sample`
1. Run `./mail4one.pyz -c config.json`
1. [Optional] Setup systemd service and tls certificates. See `deploy_configs/` for examples
* smtp server with STARTTLS # Sending email
* pop3 server with TLS
* Both running on single thread using asyncio
* Saves mails in simple Maildir format (i.e one file per email message)
* After opening port, drops root privileges. So the process will not running as `nobody`
## How to use mail4one only takes care of receiving and serving email. For sending email, use an external service like below
echo -n "balki is awesome+<YOUR PASSWORD>" | sha256sum * https://www.smtp2go.com/pricing/
pipenv install * https://www.mailgun.com/pricing/
sudo $(pipenv --venv)/bin/python ./run.py --certfile /etc/letsencrypt/live/your.domain.com/fullchain.pem --keyfile /etc/letsencrypt/live/your.domain.com/privkey.pem /var/mails --password_hash <PASSWORD_HASH_FROM_ABOVE> * https://sendgrid.com/free/
## Just pop server for debugging Most of them have generous free tier which is more than enough for personal use. Sending email is tricky. Even if everything is correctly setup (DMARC, DKIM, SPF), popular email vendors like google, microsoft may mark emails sent from your ip as spam
pipenv run python -m mail4one.pop3 /path/to/mails 9995 your_password # Community
## Nextups Original source is at https://gitea.balki.me/mail4one
For issues, pull requests, discussions, please use github mirror: https://github.com/mail4one/mail4one
* Support sending emails - Also support for popular services like mailgun/sendgrid # Documentation
* Smart assistant like functionality. For e.g.
* You don't need all emails of package deliver status. Just the latest one would be enough.
* Some type of emails can auto expire. Old newsletters are not very helpful
* Aggregate emails for weekend reading.
* Small webserver
* SPAM filtering - not that important as you can use unique addresses for each service. e.g. facebook@mydomian.com, bankac@mydomain.com, reddit@mydomain.com etc. You can easily figure out who sold your address to spammers and block it.
## Goals See files under `deploy_configs` for configuring and deploying to a standard systemd based linux system (e.g debian, ubuntu, fedora, archlinux etc). `deploy_configs/config.sample` has inline comments for more details. Feel free create github issue/discussions for help.
* Intended to be used for one person. So won't have features that don't make sense in this context. e.g. LDAP AUTH, Mail quota, etc,
* Supports only python3.7. No plans to support older versions
## Known to work # Building from source
* Server: Google Cloud f1-micro with Ubuntu 18.04 - Always Free instance
* Clients: thunderbird, evolution, k9mail
* smtp: Received email from all. Didn't see any drops. Tested from gmail, protonmail, reddit and few others
## Contribution Make sure to have make, git, python >= 3.9, and pip installed in your system and run below
Pull requests and issues welcome make build
# Roadmap (Planned features for future)
* Write dedicated documentation
* Test with more email clients (Thunderbird and k9mail is tested)
* IMAP support
* Web UI for editing config
* Support email submission from client to forward to other senders or direct delivery
* Optional SPAM filtering
* Optional DKIM verification
* Webmail client
* Web UI to view graphs and smart reports

View File

@ -0,0 +1,144 @@
# NOTE: Sample config is provided in yaml format for easy editing
# mail4one needs a json config, Please convert the config to json before passing to app
# This is to avoid yaml depependency in the app
#
# Some tools to convert to json:
# If you have go in your system (https://go.dev/)
# go run github.com/mikefarah/yq/v4@latest -oj -P . config.sample > config.json
#
# If you have pipx in your system (https://pypa.github.io/pipx/)
# pipx run yq . config.sample > config.json
#
# or a browser:
# https://onlineyamltools.com/convert-yaml-to-json
#
default_tls: # Will be used by both pop and smtp servers
# If using certbot(https://certbot.eff.org/),
# the following files will be here /etc/letsencrypt/live/<domain name>
# Use mail4one_cert_copy.sh to automaticallly copy on renewal
certfile: /var/lib/mail4one/certs/fullchain.pem
keyfile: /var/lib/mail4one/certs/privkey.pem
# default_host: '0.0.0.0'
logging:
# Setup logrotate(https://github.com/logrotate/logrotate) if needed
logfile: /var/log/mail4one/mail4one.log
mails_path: /var/lib/mail4one/mails
matches:
# only <to> address is matched. (sent by smtp RCPT command)
# address is converted to lowercase before matching
- name: example.com
addr_rexs:
- .*@example.com
- name: promotion-spammers
addrs:
- twitter@example.com
- random-app-not-used-anymore@example.com
- flyer-walmart@example.com
- name: john
addrs:
- john.doe@example.com # Tip: Dont use this. Always use a different alias, this way there is no address for spammers
- secret.name@example.com
- john.facebook@example.com
- name: jane
addrs:
- jane.doe@example.com
- jane.instagram@example.com
- name: jane-all
addr_rexs:
- jane.*@example.com
- name: shared
addrs:
- kidschool@example.com
- mortgage@example.com
- water@example.com
- electricity@example.com
- airbnb@example.com
boxes:
# Mails saved in maildir format under <mails_path>/<name>/new
- name: default_null_mbox # Means, the mails are dropped
rules:
- match_name: example.com
negate: true # Anything mail that does not match '.*@example.com'
stop_check: true # No further rules will be checked, mail is dropped
# Mailbox to store non-interesting emails but occasionally have a useful mail
# Create a second account in your email client and disable notification
- name: promotion-box
rules:
- match_name: promotion-spammers
stop_check: true
- name: johnsmbox
rules:
- match_name: john
- match_name: shared
## To receive all emails excluding jane's personal emails
# - match_name: jane
# negate: true
- name: janesmbox
rules:
- match_name: jane
- match_name: shared
- match_name: jane-all
- name: all
rules:
# matches all emails except those are not for 'example.com', which are dropped before
- match_name: default_match_all
users: # Used only by the pop server, smtp is for receiving mails only. No auth is used
- username: johnmobile
# Generated using below command. Will produce different hash each time (salt is random)
# ./mail4one.pyz -g johnsecretpassword
password_hash: AEH6JG3IZR3ASA2ORJHQ62YTR6PHFRP6PAXQ6RI2VZFXAT5M6VAATE373PGCUHBJTLIDOQV6UJKICP2JTKDE3QXP7ROJ227QYFQDAXPP4LY4TLPTEHUZG7D7X6VKWZ4BVCASYCD3SSNQ555AZPIFMDAV
mbox: johnsmbox
# **NOTE** Use different username for each email client.
# Otherwise emails may appear in only one client
- username: johnlaptop
password_hash: AEH6JG3IZR3ASA2ORJHQ62YTR6PHFRP6PAXQ6RI2VZFXAT5M6VAATE373PGCUHBJTLIDOQV6UJKICP2JTKDE3QXP7ROJ227QYFQDAXPP4LY4TLPTEHUZG7D7X6VKWZ4BVCASYCD3SSNQ555AZPIFMDAV
mbox: johnsmbox
# Second account to not clutter main mailbox. Disable notification for this account
- username: john-mobile-promotion
password_hash: AGBD47ZYBA7BMUQY25YDTYQWVPJFDBTLIICKFP2IL2GI4M7AO2LIVIZXTY6N25KBRLOEC7TLXGAFW7SSQEBKCG7U3FJNKW6RZWZBS3ABSP2U53BBIOCXZNWPXJGWAQ6WFXIF7T4YQJZD5QLF2OO4JZ45
mbox: promotion-box
- username: janemobile
password_hash: AGQNPATXU7PP7LDD6DZ4HFLUUHRJDHFQKKKRLVLGOIIEHC7TPOZF7NTXGDAIGDNHF62RAH4N44DB46O3VC4TBOLE5XHY6S77YPLTWCNAHGONEOZYO6YWJ3NHLKOHFJLF6BOHNMCI3RCPWXWXQPHSFDQR
mbox: janesmbox
- username: family_desktop # Catch all for backup
password_hash: AGBD47ZYBA7BMUQY25YDTYQWVPJFDBTLIICKFP2IL2GI4M7AO2LIVIZXTY6N25KBRLOEC7TLXGAFW7SSQEBKCG7U3FJNKW6RZWZBS3ABSP2U53BBIOCXZNWPXJGWAQ6WFXIF7T4YQJZD5QLF2OO4JZ45
mbox: all
servers:
- server_type: pop
## default values
# port: 995
# host: '0.0.0.0'
# tls: default # Uses default_tls config
- server_type: smtp
## default values
# port: 465
# host: '0.0.0.0'
# tls: default # Uses default_tls config
# tls: disable # disable tls and receive emails in plain text only
- server_type: smtp_starttls
## default values
# port: 25
# host: '0.0.0.0'
# tls: default # Uses default_tls config
# vim: ft=yaml

View File

@ -1,3 +1,7 @@
# This file should be copied to /etc/sysusers.d/mail4one.conf
# Then either restart the system or run `systemctl restart systemd-sysusers`
# That should create a system user 'mail4one'
#
# See sysusers.d(5) for details. # See sysusers.d(5) for details.
u mail4one - "Personal Mail server" u mail4one - "Personal Mail server"

View File

@ -1,4 +1,7 @@
# mail4one.service # This file should be copied to /etc/systemd/system/mail4one.service
# Quickstart
# systemctl daemon-reload
# systemctl enable --now mail4one.service
[Unit] [Unit]
Description=Personal Mail server Description=Personal Mail server
@ -7,12 +10,14 @@ After=network.target network-online.target
Requires=network-online.target Requires=network-online.target
[Service] [Service]
# This user should already exist. See mail4one.conf for creating user with sysusers
User=mail4one User=mail4one
ExecStart=/usr/local/bin/mail4one --config /etc/mail4one/config.json ExecStart=/usr/local/bin/mail4one --config /etc/mail4one/config.json
# Below allows to bind to port < 1024. Standard ports are 25, 465, 995
AmbientCapabilities=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
StateDirectory=mail4one/certs mail4one/mails StateDirectory=mail4one/certs mail4one/mails
StateDirectoryMode=0750 StateDirectoryMode=0750
@ -23,6 +28,7 @@ ProtectSystem=strict
PrivateTmp=true PrivateTmp=true
ProtectHome=yes ProtectHome=yes
ProtectProc=invisible ProtectProc=invisible
NoNewPrivileges=yes
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
# certbot deploy hook to copy certificates to mail4one when renewed. # certbot deploy hook to copy certificates to mail4one when renewed.
# Initial setup, Install certbot(https://certbot.eff.org/) and run `certbot certonly` as root
#
# This file is supposed to be copied to /etc/letsencrypt/renewal-hooks/deploy/ # This file is supposed to be copied to /etc/letsencrypt/renewal-hooks/deploy/
# Change the mail domain to the one on MX record # Change the mail domain to the one on MX record