{"id":23803564,"url":"https://github.com/d3cod3/endtoendencryptedmailserver","last_synced_at":"2026-02-17T18:36:44.255Z","repository":{"id":149329731,"uuid":"103747390","full_name":"d3cod3/EndtoEndEncryptedMailServer","owner":"d3cod3","description":"Securely host your own e-mail accounts, as e-mail was originally designed to work!","archived":false,"fork":false,"pushed_at":"2025-01-19T19:13:55.000Z","size":246,"stargazers_count":74,"open_issues_count":0,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-10T00:37:30.099Z","etag":null,"topics":["dovecot","mail-server","message-encryption","postfix","privacy-protection"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/d3cod3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"d3cod3","patreon":null,"open_collective":null,"ko_fi":"d3cod3","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2017-09-16T11:33:24.000Z","updated_at":"2025-09-28T18:23:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"92f9d202-5b63-4397-93a5-1785ba7bb8d6","html_url":"https://github.com/d3cod3/EndtoEndEncryptedMailServer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/d3cod3/EndtoEndEncryptedMailServer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d3cod3%2FEndtoEndEncryptedMailServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d3cod3%2FEndtoEndEncryptedMailServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d3cod3%2FEndtoEndEncryptedMailServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d3cod3%2FEndtoEndEncryptedMailServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/d3cod3","download_url":"https://codeload.github.com/d3cod3/EndtoEndEncryptedMailServer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d3cod3%2FEndtoEndEncryptedMailServer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29552799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T18:16:07.221Z","status":"ssl_error","status_checked_at":"2026-02-17T18:16:04.782Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dovecot","mail-server","message-encryption","postfix","privacy-protection"],"created_at":"2025-01-01T22:32:38.820Z","updated_at":"2026-02-17T18:36:39.236Z","avatar_url":"https://github.com/d3cod3.png","language":null,"readme":"\n# End-to-end (E2EE) Encrypted Email Server\n\n\nTable of Contents\n=================\n\n   * [Description](#description)\n   * [Sources](#sources)\n   * [DNS](#dns)\n   * [Encryption](#encription)\n      * [ENCRYPT the Mail Store](#encrypt-the-mail-store)\n   * [Postfix](#postfix)\n      * [SSL / Let's Encrypt](#ssl-lets-encrypt)\n   * [Dovecot](#dovecot)\n   * [GPGIT](#gpgit)\n   * [Anti-Spam](#anti-spam)\n      * [SPF](#spf)\n      * [Amavis](#amavis)\n      * [Postgray](#postgray)\n      * [OpenDKIM](#opendkim)\n      * [Spamassassin](#spamassassin)\n   * [Anonymize headers](#anonymize-headers)\n   * [FAIL2BAN](#fail2ban)\n   * [IPTABLES](#iptables)\n   * [Router Settings](#router-settings)\n   * [Testing](#testing)\n   * [Conclusions](#conclusions)\n\n\n\n# Description\n\nSecure (reasonably) host your own e-mail accounts, as e-mail was originally designed to work!\n\nLet's make ourselves more independent from corporations, from others minding our own business, and most important, re-gain control of our personal communication over the web, in this case specifically over e-mail.\n\nYes, those electronic mail boxes that others maintain for us, the technical infrastructure that give us this ability to communicate instantly with everyone all over the planet, this last revision of what long ago was smoke messages, or carrier pigeons, through the centuries of postal systems, till the actual technology, where we do have, now, nor knowledge or control about every part of the entire mechanism, but hey, they give it to us for free!!!\n\nSo\n\nCorporations and governments read and/or store all our emails, plus, we can't even complain about it anymore (from august 2013), and that doesn't mean necessary \"spying\" until it eventually became that.\n\nAnd if you're ok with it, then you don't need this tutorial\n\nBut in case you are not, good news, i'm going to explain here how to set up an End-to-end encrypted ([E2EE](https://ssd.eff.org/en/glossary/end-end-encryption)) email server, and hosting it in your personal server at home.\nI'm assuming here you know how to configure a reasonably secure server at home, but if you don't, you can check my [Raspbian Secure Server Config Tutorial](https://github.com/d3cod3/raspbian-server) first.\n\n# Sources\n\nThis is a list of sources and other articles i've used to learn, prototype and realize what i'll try to explain in detail in this tutorial.\n\nThanks to all this people for the help and knowledge:\n\n[http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/](http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/)\n\n[https://scaron.info/blog/debian-mail-postfix-dovecot.html](https://scaron.info/blog/debian-mail-postfix-dovecot.html)\n\n[https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-1.html](https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-1.html)\n\n[https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-2.html](https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-2.html)\n\n[https://www.digitalocean.com/community/tutorials/how-to-set-up-a-postfix-email-server-with-dovecot-dynamic-maildirs-and-lmtp](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-postfix-email-server-with-dovecot-dynamic-maildirs-and-lmtp)\n\n[https://security.stackexchange.com/questions/81944/perfectly-secure-postfix-mta-smtp-configuration](https://security.stackexchange.com/questions/81944/perfectly-secure-postfix-mta-smtp-configuration)\n\n[https://scaron.info/blog/debian-mail-spf-dkim.html](https://scaron.info/blog/debian-mail-spf-dkim.html)\n\n[https://www.upcloud.com/support/secure-postfix-using-lets-encrypt/](https://www.upcloud.com/support/secure-postfix-using-lets-encrypt/)\n\n[https://gist.github.com/jkullick/bbb36828a1f413abd6b9d6431bafa54b](https://gist.github.com/jkullick/bbb36828a1f413abd6b9d6431bafa54b)\n\n[https://www.void.gr/kargig/blog/2013/11/24/anonymize-headers-in-postfix/](https://www.void.gr/kargig/blog/2013/11/24/anonymize-headers-in-postfix/)\n\n[http://kacangbawang.com/encrypting-stored-email-with-postfix/](http://kacangbawang.com/encrypting-stored-email-with-postfix/)\n\n[https://www.grepular.com/Automatically_Encrypting_all_Incoming_Email](https://www.grepular.com/Automatically_Encrypting_all_Incoming_Email)\n\n[https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8)\n\n[https://www.void.gr/kargig/blog/2013/11/24/anonymize-headers-in-postfix/](https://www.void.gr/kargig/blog/2013/11/24/anonymize-headers-in-postfix/)\n\n\n\n# DNS\n\nSo, we'll start with the DNS records, and obviously we need a domain, we can buy one through some hosting platform, or we can obtain one for free (with some limitations, on the name for example). Take a look at [FreeDNS](https://freedns.afraid.org/) if you want to learn more about free DNS hosting and domain hosting.\nSo we'll use here an example domain called _supersecure.mydomain.net_, and just to avoid confusion, a possible mail address could be:\n_astronaut57@supersecure.mydomain.net_.\nWe are using here a different subdomain to avoid the disruption of the standard mail service usually packed with standard hosting services, for example _mymail@mydomain.net_, so we will maintain the standard mail service on our domain and add a new super-secure encrypted one on a subdomain.\nWhen we have our domain, we'll just need to configure it to point to our public IP address:\n\n- **DNS A** record, that maps your domain name to the IP address\n```bash\nsupersecure  A   YOUR_IPv4_ADDRESS\n```\n\n- **MX** record, which tells to the others mail servers where deliver mails\n```bash\nsupersecure           MX      supersecure.mydomain.net\n```\n\n- **TXT/SPF** record, a Sender Policy Framework (SPF) record in order to not be considered a spammer\n```bash\ndefault._domainkey.supersecure    TXT     v=DKIM1; h=sha256; p=v=DKIM1;h=sha256;k=rsa;p=YOUR_DKIM_KEY; s=email; t=s\nsupersecure                       txt     v=DKIM1;h=sha256;k=rsa;p=YOUR_DKIM_KEY\nsupersecure                       TXT     v=spf1 a mx ip4:YOUR_IPv4_ADDRESS ip6:YOUR_IPv6_ADDRESS ~all\n```\n\n- **TXT/SPF** record, DMARC record in order to not be considered a spammer by big e-mail providers (google, yahoo, etc...)\n```bash\nsupersecure           TXT     v=DMARC1; p=none\n_dmarc.supersecure    TXT     v=DMARC1; p=none\n```\n\nRegarding the **YOUR_DKIM_KEY** field, we'll work on that later, when configuring [OpenDKIM](#opendkim) (Domain Keys Identified Mail sender authentication system), so you can wait for that, or jump to the section and close the DNS records config right now, your choice!\n\nThat's a standard scenario, feel free to customize yours!\n\nUsually DNS propagation takes a while, so it could be useful to set it at the beginning.\n\nOne easy step more, edit the file _/etc/mailname_\n\n```bash\nsudo nano /etc/mailname\n```\n\nand enter your selected mail domain name, in our case _supersecure.mydomain.net_\n\nLet's start talking about encryption!\n\n# ENCRYPTION\n\nOk, this step is delicate, so i'll start describing what we're trying to achieve here:\nfirst i want an encrypted mail store for my mail server, but most important, i want every mail account associated with a gpg public key to asymmetrically encrypt messages, so only the users, with his/her private gpg key, will be able to decrypt and read their messages, plus, in case of server compromised, the attacker will own only a bunch of gpg encrypted texts!\n\nSo, before we start implementing this, a little reminder:\n\n\u003e Even if you're using the best encrypted super secure mail account of the entire universe, when you write to someone that don't, well, your conversation is potentially already compromised. A reasonably secure conversation over the web must be encrypted on both sides.\n\nIf you ended up here not randomly, probably you already know that, but just in case, check this tutorial from Free Software Foundation about [email self-defense](https://emailselfdefense.fsf.org/en/)\n\nPerfect, let's analyze in depth how and why we'll implement the required conditions to obtain such server encryption level.\n\n## ENCRYPT the Mail Store\n\nI'll start with the easy one, encrypt the mail store, and for that we'll use [gocryptfs](https://nuetzlich.net/gocryptfs/), a project inspired by [EncFS](https://vgough.github.io/encfs/)\n\n### WHY\n\nSomeone could be asking, why encrypt the mail store if we are going to asymmetrically encrypt every single message with users public gpg keys?\nWell, is actually a redundancy, but let's consider this, on one side we'll learn how to properly configure generic encrypted filesystem in user-space, and on another side, we can consider it as planting some kind of honeypot for possible attackers; imagine someone taking control of our server, with read/write access to our mail store, he/she will rapidly detect the presence of gocryptfs, that, like every filesystem encryption mechanism, is potentially vulnerable to some kind of advanced attacks, so he/she will probably start to try to exploit the gocryptfs vulnerabilities (if your are interested, take a look at this audit [here](https://defuse.ca/audits/gocryptfs.htm)), and after some time, maybe, with some luck, he/she will decrypt the mail store, finding a bunch of asymmetric gpg encrypted messages!!! Wouldn't you like, just in that moment, to see his/her face?\nAnd to go further, we could plant some other kind of hidden side-channel remote logging system over our encrypted mail store, in order to try to extract information about our potential attacker working on our compromised server, but this is just a little far away from the purposes and skill level of this tutorial, so i'll let this particular point in the hands of the interested enthusiastic contributor that will teach us how to implement this properly (thanks in advance!).\n\n### HOW\n\nAs always, let's install it:\n\n```bash\nsudo apt-get install gocryptfs\n```\n\nThen create our encrypted filesystem and the mount point, basically we'll create two folders (where you prefer):\n\n```bash\nmkdir cipher plain\n```\n\nInit our encrypted filesystem (our folder for the mailstore):\n\n```bash\ngocryptfs -init cipher\n```\n\nand mount it:\n\n```bash\ngocryptfs cipher plain\n```\n\nWe can now try a little test, just listing the content of the folder where we created the two folders, _cypher_ and _plain_ (or whatever names you used)\n\n```bash\nls -la\n```\n\nAnd we will obtain\n\n```bash\nls: cannot access plain: Permission denied\n```\n\nBut if we list it as sudo\n\n```bash\nsudo ls -la\n```\n\nobviously no problem!\n\nThat's all, we now have a password protected encrypted folder for our mail store. If you want to try it, just create some text file inside the plain folder, and you 'll find a new encrypted file inside cipher folder. Any time we want to mount our plain folder, we just use the second command again.\n\n# POSTFIX\n\nPostfix is a Mail Transfer Agent (MTA), that is, software that sends and receives emails to and from other computers on the network using the Simple Mail Transfer Protocol (SMTP). And as a reminder:\n\n* POP/IMAP are used by a client to read messages from an email server\n* SMTP is used to exchange emails between computers\n\nSo, let's install it:\n\n```bash\nsudo apt-get install postfix\n```\n\nA command line GUI will pop up, configure it as follow:\n\n* Internet Site\n* your mail server domain: _supersecure.mydomain.net_\n\nPostfix configuration file is located at _/etc/postfix/main.cf_, in case you'll need to change more settings\n\nHere follows a basic configuration:\n\n```bash\nmyhostname = supersecure.mydomain.net\nmyorigin = /etc/mailname\n\nappend_dot_mydomain = no\nreadme_directory = no\nconfig_directory = /etc/postfix\ninet_interfaces = all\ninet_protocols = ipv4\nmydestination = supersecure.mydomain.net, supersecure.mydomain.net, localhost.localdomain, localhost\nrelayhost =\nmynetworks = 127.0.0.0/8\nmailbox_size_limit = 0\nrecipient_delimiter = +\n```\nNow, the default for postfix is to use the _mbox_ format (one single file to store all messages) or the _Maildir_ format (each email stored in a individual file), but we are going to take it a step further and use Dovecot instead, so be patient, we are now going to set up our SSL/TLS certificates, and come back here for the Dovecot part in a bit.\n\n\n## SSL / Let's Encrypt\n\nWe are using here the amazing free, automated and open Certificate Authority called [Let's Encrypt](https://letsencrypt.org/), a project by the [Linux Foundation](https://www.linuxfoundation.org/)\n\nAs always, install the module:\n\n```bash\nsudo apt-get install letsencrypt\n```\n\nAnd run the certificate creation process:\n\n```bash\nsudo letsencrypt certonly --standalone -d \u003csupersecure.mydomain.net\u003e\n```\n\nReplace here \u003csupersecure.mydomain.net\u003e with your domain name, obviusly! (and without the \u003c \u003e ...)\n\nThen follow the process:\n\n  1.  Use the default vhost filesystem\n  2.  Enter your email server domain name: ex. _supersecure.mydomain.net_\n  3.  Enter a contact email\n  4.  Read and agree to Let's Encrypt Terms of Service (only if you agree)\n  5.  Select the _Secure_ option (HTTPS ONLY)\n\nThat's it, if everything went ok, you will have your certificates stored under _/etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e_\n\nNow we need to enable SMTP-AUTH on Postfix, which will allow a client to identify itself through the authentication mechanism SASL. TLS (Transport Layer Security) will be used to encrypt the authentication process, and once authenticated, our server will allow the client to relay mail.\n\nHere follow the config to add to _/etc/postfix/main.cf_\n\n```bash\nsmtp_use_tls = yes\nsmtp_tls_CApath = /etc/ssl/certs\nsmtp_tls_cert_file = /etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/fullchain.pem\nsmtp_tls_key_file = /etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/privkey.pem\nsmtp_tls_security_level = may\nsmtp_tls_session_cache_database = btree:${data_directory}/smtp_scache\n\nsmtpd_use_tls=yes\nsmtpd_tls_CApath = /etc/ssl/certs\nsmtpd_tls_cert_file = /etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/fullchain.pem\nsmtpd_tls_key_file = /etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/privkey.pem\n\nsmtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_path = private/auth\nsmtpd_sasl_security_options = noanonymous\n\nsmtpd_tls_eecdh_grade = strong\nsmtpd_tls_mandatory_ciphers = medium\nsmtpd_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1\nsmtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1\nsmtpd_tls_security_level = may\n\nsmtpd_banner = $myhostname ESMTP\n\nsmtpd_recipient_restrictions = permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_rbl_client sbl.spamhaus.org, reject_unknown_sender_domain, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination\nsmtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination\n\ntls_medium_cipherlist = AES128+EECDH:AES128+EDH\ntls_preempt_cipherlist = yes\n\npolicy-spf_time_limit = 3600s\n```\n\nNow, one trick more to have an even better security, generate new [_Diffie Hellman Keys_](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange)\n\n```bash\nopenssl gendh -out /etc/postfix/dh_512.pem -2 512\nopenssl gendh -out /etc/postfix/dh_2048.pem -2 2048\n```\n\nAnd add it to _/etc/postfix/main.cf_\n\n```bash\nsmtpd_tls_dh1024_param_file = /etc/postfix/dh_2048.pem\nsmtpd_tls_dh512_param_file = /etc/postfix/dh_512.pem\n```\n\nThen we can edit the other Postfix important config file, _/etc/postfix/master.cf_\n\n```bash\nsudo nano /etc/postfix/master.cf\n```\n\nAnd make sure you have this:\n\n```bash\nsmtp      inet  n       -       -       -       -       smtpd\n\nsubmission       inet    n       -       n       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject\n  -o smtpd_client_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n  -o smtpd_sasl_type=dovecot\n  -o smtpd_sasl_path=private/auth\n\nsmtps     inet  n       -       -       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_client_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n\ndovecot   unix  -       n       n       -       -       pipe\n  flags=DRhu user=email:email argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}\n```\n\nWe'll come back later at this file, so study it a little bit and get comfortable with it.\n\nRight, so we have our MTA (Mail Transfer Agent) half configured, already secured with Let's Encrypt certificates and a strong encryption settings; but why only half configured, well, for example our MTA didn't know anything, yet, about possible recipients, who are they? and how we want email to be stored?\n\nSetting up a mail server is not an easy task, especially when we are trying to build up a reasonably secure one (and amazingly encrypted :P), so one step at the time, no rush here, the goal is to learn more as possible, because at the time we'll have this tutorial finished and our mail server up and running, it wouldn't be unusual to have to change or add something in order to avoid the possibility of some brand new attack vector.\n\nBut i'm changing course, so let's get back to work, next story, [Dovecot](https://www.dovecot.org/), the open source IMAP and POP3 email server written with security primarily in mind.\n\n# DOVECOT\n\nThe usual first step, install it:\n\n```bash\nsudo apt-get install dovecot-core dovecot-imapd dovecot-lmtpd dovecot-pgsql postgresql postfix-pgsql\n```\n\nAs you can see, i've decided to install the dovecot PostgreSQL module, and that's because we will use a PostgreSQL database to securely encrypt and store the mail users data.\n\nSomeone could be asking right now, why PostgreSQL and not MySQL or MongoDB or whatever, and the answer is: there is not just one answer!\nSo to get to the point fast, let's use a line from [_The Database Hacker's Handbook_](https://www.wiley.com/en-gb/The+Database+Hacker%27s+Handbook%3A+Defending+Database+Servers-p-9780764578014), _\"By default, PostgreSQL is probably the most security-aware database available ...\"_.\n\nSo here we are, program and modules installed, now the tricky part, the configuration, and i think now it's time, like Agent Cooper use to say, for a damn fine cup of coffee!\n\nWe'll start with the _/etc/dovecot/dovecot.conf_ file\n\n```bash\nsudo nano /etc/dovecot/dovecot.conf\n```\n\nAnd set this:\n\n```bash\n# Enable installed protocols\n!include_try /usr/share/dovecot/protocols.d/*.protocol\n\nlisten = *\n\ndisable_plaintext_auth = yes\nmail_privileged_group = mail\n\npassdb {\n  args = /etc/dovecot/dovecot-sql.conf\n  driver = sql\n}\nprotocols = imap lmtp\n\nnamespace inbox {\n  inbox = yes\n\n  mailbox Trash {\n    auto = subscribe # autocreate and autosubscribe the Trash mailbox\n    special_use = \\Trash\n  }\n  mailbox Sent {\n    auto = subscribe # autocreate and autosubscribe the Sent mailbox\n    special_use = \\Sent\n  }\n}\n\nservice auth {\n  unix_listener /var/spool/postfix/private/auth {\n    group = postfix\n    mode = 0660\n    user = postfix\n  }\n}\nservice imap-login {\n  inet_listener imap {\n    port = 0\n  }\n  inet_listener imaps {\n    port = 993\n  }\n}\n\nservice lmtp {\n    unix_listener /var/spool/postfix/private/dovecot-lmtp {\n      group = postfix\n      mode = 0600\n      user = postfix\n    }\n}\nprotocol lmtp {\n    postmaster_address=postmaster@\u003csupersecure.mydomain.net\u003e\n    hostname=\u003csupersecure.mydomain.net\u003e\n}\n\nssl = required\nssl_cert = \u003c/etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/fullchain.pem\nssl_cipher_list = AES128+EECDH:AES128+EDH\nssl_dh_parameters_length = 4096\nssl_key = \u003c/etc/letsencrypt/live/\u003csupersecure.mydomain.net\u003e/privkey.pem\nssl_prefer_server_ciphers = yes\nssl_protocols = !SSLv3\n\nuserdb {\n  driver = prefetch\n}\n\nuserdb {\n  driver = sql\n  args = /etc/dovecot/dovecot-sql.conf\n}\n\n```\n\nSo we have connected Dovecot with Postfix, configured a standard inbox for mails, configured the SSL/TLS certificates and instructed Dovecot to search for user data in a SQL database with access specified in _/etc/dovecot/dovecot-sql.conf_ file, so let's edit that one:\n\n```bash\nsudo nano /etc/dovecot/dovecot-sql.conf\n```\n\nAnd we edit it as follows:\n\n```bash\ndriver = pgsql\nuserdb_warning_disable = yes\nconnect = host=/var/run/postgresql/ dbname=\u003cyour_db_name\u003e user=\u003cyour_db_user\u003e\ndefault_pass_scheme = SHA512\npassword_query = SELECT email as user, password FROM users WHERE email = '%u'\nuser_query = SELECT email as user, 'maildir:/your_mailstore_path/plain/maildir/'||maildir as mail, '/your_mailstore_path/plain/home/'||maildir as home, 500 as uid, 500 as gid FROM users WHERE email = '%u'\n```\n\nWe'll leave _dbname_ and _user_ alone just for now, because we haven't created our database to store users data yet, so let's do it, and we'll come back here later.\n\nThe idea here is to have the less identifiable information stored in plain on our server, so we will design the simplest of user table structure in our database, with the following fields:\n\n```bash\n  Column  |           Type           |   Modifiers   \n----------+--------------------------+---------------\n email    | text                     | not null\n pgpkey   | text                     | not null\n password | text                     | not null\n maildir  | text                     | not null\n created  | timestamp with time zone | default now()\n```\n\nWhere _pgpkey_ field will store the public key related with the email in order to automatically encrypt every receiving message, each user with his public key. The other fields are the common ones, the complete email, the SHA512 hashed password, the timestamp of the mail creation and the name of the directory (_maildir_) in our mailstore were Dovecot will be store (and automatically encrypt thanks to more stuff we'll see later) every user message.\n\nSo, first things first, we need now to create the database, and a database user, and that is a really straightforward process, but if you're not familiar with PostgreSQL, let's have a quick recap:\n\n0. Just before we start, let's setup the Postgres Database, first we edit _/etc/postgresql/9.4/main/pg_ident.conf_ file (my version is 9.4, so check yours before for the correct file path)\n```bash\nsudo nano /etc/postgresql/9.4/main/pg_ident.conf\n```\n\nand make it look like this\n\n```bash\n# MAPNAME       SYSTEM-USERNAME         PG-USERNAME\n\nmailmap\t\t       dovecot\t\t\t          testuser\nmailmap\t\t       postfix\t\t\t          testuser\nmailmap\t\t       root\t\t\t              testuser\n```\n\nWhere _testuser_ will be the name of the postgres user we will use for accessing the mailstore database\n\nthen edit _/etc/postgresql/9.4/main/pg_hba.conf_ file\n\n```bash\nsudo nano /etc/postgresql/9.4/main/pg_hba.conf\n```\n\nand add this line (Warning: Make sure to add it right after the **Put your actual configuration here** comment block! Otherwise one of the default entries might catch first and the database authentication will fail)\n\n```bash\nlocal       mail    all     peer map=mailmap\n```\n\nsave it and reload postgresql service\n\n```bash\nsudo service postgresql reload\n```\n\n1.  On Debian, PostgreSQL is installed with a default user and default database both called _postgres_, so\n\n2.  we'll connect to the PostgreSQL console with this default user and\n```bash\npsql -U postgres -h localhost\n```\n\n3.  create our database for the mailstore,\n```bash\nCREATE DATABASE mailstore_db;\n```\n\n4.  create a db user to access this new database (choose your preferred username and password) and\n```bash\nCREATE USER testuser WITH PASSWORD 'test_password';\n```\n\n5.  grant all the necessary privileges to this new db user.\n```bash\nGRANT ALL PRIVILEGES ON DATABASE \"mailstore_db\" to testuser;\n```\n\n6.  Now exit the PostgreSQL console\n```bash\n\\q\n```\n\n7.  And re-log into the PostgreSQL console as the new created _testuser_, to edit our new created db _mailstore_db_\n```bash\npsql -U testuser -d mailstore_db -h localhost\n```\n\n8. And finally, we create our tables, the one for the users, and another one for aliases\n```bash\nCREATE TABLE users (\n  email text NOT NULL,\n  pgpkey text NOT NULL,\n  password text NOT NULL,\n  maildir text NOT NULL,\n  created TIMESTAMPTZ DEFAULT NOW()\n);\n\nCREATE TABLE aliases (\n  alias text NOT NULL,\n  email text NOT NULL\n);\n```\n\n9.  now we check that everything is in it's right place, we list the databases\n```bash\n\\l\n```\n\nmy output:\n\n```bash\nList of databases\n      Name      |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges   \n----------------+------------+----------+-------------+-------------+-----------------------\n mailstore_db   | testuser   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |\n postgres       | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |\n```\n\n10. we list the _mailstore_db_ tables\n```bash\n\\dt\n```\n\n```bash\nList of relations\nSchema |  Name   | Type  |   Owner    \n-------+---------+-------+------------\npublic | aliases | table | testuser\npublic | users   | table | testuser\n```\n\n11. and the tables structure\n```bash\n\\d users\n```\n\n```bash\n  Column  |           Type           |   Modifiers   \n----------+--------------------------+---------------\n email    | text                     | not null\n pgpkey   | text                     | not null\n password | text                     | not null\n maildir  | text                     | not null\n created  | timestamp with time zone | default now()\n```\n\n```bash\n\\d aliases\n```\n\n```bash\n Column | Type | Modifiers\n--------+------+-----------\n alias  | text | not null\n email  | text | not null\n```\n\n12. And here we are! We have now our database ready, we can come back to the _/etc/dovecot/dovecot-sql.conf_ file, and finally configure the db name and db username:\n\n```bash\nsudo nano /etc/dovecot/dovecot-sql.conf\n```\n\nand we edit the specific line:\n\n```bash\nconnect = host=/var/run/postgresql/ dbname=mailstore_db user=testuser\n```\n\nRight after that, we can now tell postfix to deliver mail directly to dovecot, so we open again _/etc/postfix/main.cf_ and added\n\n```bash\nmailbox_transport = lmtp:unix:private/dovecot-lmtp\nalias_maps = hash:/etc/aliases proxy:pgsql:/etc/postfix/pgsql-aliases.cf\nlocal_recipient_maps = proxy:pgsql:/etc/postfix/pgsql-boxes.cf $alias_maps\n```\n\nthen we need to create two files, first _/etc/postfix/pgsql-aliases.cf_\n\n```bash\nuser=testuser\ndbname=mailstore_db\ntable=aliases\nselect_field=alias\nwhere_field=email\nhosts=unix:/var/run/postgresql\n```\n\nand then _/etc/postfix/pgsql-boxes.cf_\n\n```bash\nuser=testuser\ndbname=mailstore_db\ntable=users\nselect_field=email\nwhere_field=email\nhosts=unix:/var/run/postgresql/\n```\n\nThis is done!\n\nNow restart Dovecot and Postfix to apply the new settings\n\n```bash\nsudo service postfix restart\nsudo service dovecot restart\n```\n\nAmazing! Now, theoretically, we are already able to send and receive emails using secure authentication , **BUT**, in order to be able to do that, we have to create our first user mail account! And how we do that? Well, we just need to add a correct entry in our _users_ table inside our postgres database.\n\nA little digression about a possible scenario here, we could imagine some sort of form in a webpage, for everyone or maybe just for a few (your choice), to sign up and obtain an email account in our server.\n\nOk, So i'm _astronaut57_, i use this aka almost everywhere on the internet (is it true?, or is it just for the story? you decide!), and, while surfing the web, i happen to enter into this amazing and little mysterious webpage where i can sign up and obtain a really interesting reasonably secure gpg encrypted email account, and i start to think: why not? So the webpage seem legit, i like the Terms of Use, is free and [everything is in it's right place](https://www.youtube.com/watch?v=bD2j0fH6-UQ), let's have one, i'm thinking _astronaut57@supersecure.mydomain.net_\n\nThe form moment then, i see that is a really simple one, really straightforward, it don't ask for my telephone number, or my street address in case i loose my devices and for some reason the mail provider decide to send me, printed in paper, the last 7 GB of received emails, or for the name of my dog, among other tipical questions you have to answer when registering for a free (free?) service. No, this form is neat, it ask me for those simple things:\n\n1.  the email name, _astronaut57@supersecure.mydomain.net_, check\n2.  the pgp public key you want to associate with this mail account, i create a pgp keypair in my machine at home (or my laptop) associated with this new email, and paste here the public key, check\n3.  the password for the account, _*****************************_, check\n\nAnd that's it, push register button and a message appear that in, let's say, 1 minute, my new email account will be available, with a link to the mail config guide for thunderbird \u0026 enigmail. Yes, no checking your email in a browser, this kind of reasonably secure gpg encrypted email account cannot work with the actual browsers security standards. Sounds pro!\n\nThat's it, the user is happy, but let's analyze the server side now, we obtain from the online form all the necessary info to add a new entry in our mailstore database _users_ table, so when the user push the register button, we check all the fields (front-end side) and if all is correct, we call a script to add a new entry in our database. I'm not going to cover here how to do that from the front-end, but i'm going to show how to do it the old fashioned way, from the terminal\n\nFirst of all, we need to SHA512 the password entered by the user, yes, because we have configured dovecot to work with SHA512 password scheme, but most important because we do not want to know anything about the passwords used by our users, so we hash it:\n\n```bash\ndoveadm pw -s sha512 -r 100\n```\n\nEnter the password twice and we will obtain something like this:\n\n```bash\n{SHA512}NieQminDE4Ggcewn98nKl3Jhgq7Smn3dLlQ1MyLPswq7njpt8qwsIP4jQ2MR1nhWTQyNMFkwV19g4tPQSBhNeQ==\n```\n\nLet's save that somewhere (a variable maybe?) and continue\n\nWe log into the PostgreSQL console\n\n```bash\npsql -U testuser -d mailstore_db -h localhost\n```\n\nthen we add the new entry\n\n```bash\nINSERT INTO aliases ( alias,email ) VALUES (\n  'astronaut57',\n  'astronaut57@supersecure.mydomain.net'\n);\n```\n\nand\n\n```bash\nINSERT INTO users ( email,pgpkey,password,maildir ) VALUES (\n'astronaut57@supersecure.mydomain.net',\n'\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n................................................................\n................................................................\n-----END PGP PUBLIC KEY BLOCK-----\n',\n'{SHA512}NieQminDE4Ggcewn98nKl3Jhgq7Smn3dLlQ1MyLPswq7njpt8qwsIP4jQ2MR1nhWTQyNMFkwV19g4tPQSBhNeQ==',\n'astronaut57/'\n);\n```\n\nAnd exit PostgreSQL console.\n```bash\n\\q\n```\n\nDone, user created and added to the database.\n\nIt's time to test the system, sending an email to and from _astronaut57@supersecure.mydomain.net_, and to properly do that, we'll need to configure our thunderbird client imagining we are the real _astronaut57_.\n\nSo Thunderbird, new account, with this settings:\n\n1.  Your name:          \u003cTHE_USER_NAME_OR_WHATEVER\u003e\n2.  Email address:      astronaut57@supersecure.mydomain.net\n3.  Password:           \u003cTHE_USER_PASSWORD\u003e\n4.  Incoming:           IMAP    |    supersecure.mydomain.net    |    993   |   SSL/TLS   |  Normal password\n5.  Outgoing:           SMTP    |    supersecure.mydomain.net    |    587   |   STARTTLS  |  Normal password\n6.  Username:           astronaut57@supersecure.mydomain.net     |    astronaut57@supersecure.mydomain.net\n\nDONE!\n\nIf everything went ok, all the config files are correct and the DNS of your server are working, then you will be able now to send and receive emails from/to your new email address _astronaut57@supersecure.mydomain.net_\n\nBut where is the encryption? Well, we have some more work to do, so next story, [GPGIT](https://gitlab.com/mikecardwell/gpgit)!\n\n# GPGIT\n\nI want to remember something here, maybe i'm repeating but i believe it's important to make it clear, WE ARE NOT building a NSA-proof mail server, the skill level and the infrastructure to try that is far beyond the knowledge available in this tutorial, but, we can say without doubts that our system, if working properly, will maintain at least confidentiality and authenticity of our email service; and if some evil hacker attack us with some 0-day, gaining temporary control over the server, this scheme with automated encryption we are building will keep the contents of all our users mails secret.\n\nSo, how we do it?\n\nThis is the idea, we'll set up a trigger on every message arriving (at Postfix level) to the server that will call an encryption function, as we have every account related with a pgp public key (nothing dangerous to store public keys on the server), we'll use this key to encrypt the messages, so the message will land automatically encrypted. Only the owner of the account, or the one who control the associated private key, will be able to open and read the content of the messages (remember that this mechanism make impossible to send messages with multiple recipients, so one message at the time).\n\nAnd how we treat the message sending or the _sent_ folder? Well, we are focusing on security and privacy here, so this mail server will not have the _sent_ folder, for the following reasons:\n\n1.  An obvious and easy one is that we don't know if the recipients, on the other side, use encryption or not, so even if on our side the sent message is safe, on the other side could be stored in plain, so we just lost confidentiality.\n\n2.  Postfix do not differentiate incoming mail from outgoing mail, so everything must be handled in the same hook. Due to that apply the trigger to outgoing email will need saving the _stdin_ content to the filesystem temporarily, and this is vulnerable to forensic recover. This could be partially resolved with encrypted folder/volumes, but there is a bigger problem:\n\n3.  If we want to make changes to the _IMAP_ mailbox, we'll need that mailbox username/password, and there is no way of doing that without store this credentials somewhere on plaintext (remember, we store in our mail user database the email in plain, but we hash with SHA512 the user password). Or else use a \"master\" account with read/write permission over all the accounts, but this is really counter-productive to what we are trying to do here.\n\nIn conclusion, we are not going to have here the typical _sent_ folder with a copy of our sent messages, if we want a copy of something we are sending, we just re-send the message to ourselves. Plus, we can add a filter in Thunderbird, [thunderbird filters](http://write.flossmanuals.net/thunderbird/filters/), to automatically move a message into the _sent_ folder if comes directly from ourselves.\n\nInstallation time! We need to install the [Enigmail](https://www.enigmail.net/index.php/en/) plugin on our local machine (and gpg obviously, but if we already created our keypair before, we already have it!), while on the server (if debian) comes preinstalled, so we check the version:\n\n```bash\ngpg --version\n```\n\n```bash\ngpg (GnuPG) 2.1.18\nlibgcrypt 1.7.6-beta\nCopyright (C) 2017 Free Software Foundation, Inc.\nLicense GPLv3+: GNU GPL version 3 or later \u003chttps://gnu.org/licenses/gpl.html\u003e\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n\nHome: /home/user/.gnupg\nSupported algorithms:\nPubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA\nCipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,\n        CAMELLIA128, CAMELLIA192, CAMELLIA256\nHash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224\nCompression: Uncompressed, ZIP, ZLIB, BZIP2\n```\n\nIn case your linux distro doesn't come with gpg preinstalled, just install it.\n\nNext, we need to create an unprivileged user for running the encryption scripts:\n\n```bash\nsudo adduser --shell /bin/false --home /var/opt/gpgit --disabled-password --disabled-login --gecos \"\" gpgit\n```\n\nthen set up _gpg_ for this user (if you're not familiar with gpg, you can start [here](https://theprivacyguide.org/tutorials/gpg.html)):\n\n```bash\nsudo mkdir /var/opt/gpgit/.gnupg\nsudo chown gpgit:gpgit /var/opt/gpgit/.gnupg\nsudo chmod 700 /var/opt/gpgit/.gnupg\n# Import the mail users public keys (we import this one, but this must be repeated for each new registered user)\nsudo -u gpgit /usr/bin/gpg --homedir=/var/opt/gpgit/.gnupg --import astronaut57@supersecure.mydomain.net.gpg\n# List all imported keys (you should see the just imported key)\nsudo -u gpgit /usr/bin/gpg --homedir=/var/opt/gpgit/.gnupg/ --list-keys\n# give ultimate trust (5) to the imported key\nsudo -u gpgit /usr/bin/gpg --homedir=/var/opt/gpgit/.gnupg --edit-key astronaut57@supersecure.mydomain.net trust quit\n```\n\nWe then install (clone) gpgit script by Mark Cardwell from [here](https://gitlab.com/mikecardwell/gpgit), inside our newly created _gpgit_ user home folder:\n\n```bash\ncd /var/opt/gpgit/\nsudo -u gpgit /usr/bin/git clone https://gitlab.com/mikecardwell/gpgit\n```\n\nIf necessary, install the required Perl modules, we are using here the [cpan](https://www.cpan.org/) console:\n\n```bash\n# install cpanminus\ncpan App::cpanminus\n# Now install module.\ncpanm Mail::GnuPG\n# or any others you may be missing\n# cpanm XXX::YYY\n```\n\nAnd finally a little test:\n\n```bash\n# this should produce a text file with some b64 data, call the command, wait two seconds, then Ctrl-D\nsudo -u gpgit /var/opt/gpgit/gpgit/gpgit.pl astronaut57@supersecure.mydomain.net \u003e /var/opt/gpgit/success\ncat /var/opt/gpgit/success\n```\n\nMy output:\n\n```bash\nContent-Type: multipart/encrypted; boundary=\"----------=_1524436225-25788-0\"; protocol=\"application/pgp-encrypted\"\n\nThis is a multi-part message in MIME format...\n\n------------=_1524436225-25788-0\nContent-Type: application/pgp-encrypted; name=\"msg.asc\"\nContent-Disposition: inline; filename=\"msg.asc\"\nContent-Transfer-Encoding: 7bit\n\nVersion: 1\n------------=_1524436225-25788-0\nContent-Type: application/octet-stream\nContent-Disposition: inline\nContent-Transfer-Encoding: 7bit\n\n-----BEGIN PGP MESSAGE-----\n\nhQIMA6SuSblhzUCkAQ/+M520zEDPEOwmsPmjOS8Sv1teygcLbId5wUEwHgPz3o3r\nteu7UIUXHvVLGVW9zg7ggIaUzMj+XPPhZvmKWLyK2pBNYOjwaBAYTjlB6y6ANs3b\nM2t3/OUzoFRchdY6AVZQGwD+RNvb0pUTrvf4tC8TQBHbdOYojP2qodNVTJ408kxx\nq0qcoYjHz+1L7Qng8uMXSX1LI4PNWcVqG0sBvtiaTUgDDVJb7MA5Ig/EW2V8FB1i\nsTAR13sDJ7VhbCasDUUBhs0x1Y0ssj+LiDd1qkQNgqvePqJ0RikwtQ9OzaZ11o5H\nN/FikujIYUTzoGvR7KBLmGBpUyAwagkbUlJKsGhLNDaSo32dgnz34UBaTxa41GDF\nM3JGuhiIQe7nH9AMavTt4sHc28cmYdqnOOKDA+1dwE6lLOoGQg3IkFlz0cunN8ee\nJVyuC7lUnEKgJJVjRaWQOlzWltwIuQXEitoj33/bMktYtEbRydRSwvM2ajmpgEyF\nU0lTr/yz/PdfBk/kPrezPIAJFMWDun58Vi+VJemvCsOxgi7e0MwrEpe4u9ap1SKf\ng2zEXovVlPFldapqFztSjLKIlskompXQbSs4IaF4ciMXatRUNpzIFICUFMw/Cd4T\nG2N9Kq5f4hGdskbb5PVAqWXvy8m7zH3pIrQaPQHJAFbAh99Ull4w4LCaEGUjI9bS\nPAGEAqNYGUUuvN+srtVtj/ciMMET2VvPoA0BuiVzbD7XZqdF2jlf6fL8JAQ3zls2\nCHUR7ahLHVffgqHqPA==\n=h9sJ\n-----END PGP MESSAGE-----\n\n------------=_1524436225-25788-0--\n```\n\nIt's working, **gpgit** Perl script from Mike Cardwell automatically use the email address provided, to look up the public key that the message will be encrypted with.\n\nAlmost finished, we just need now to trigger this script on arriving mail in Postfix, and basically this is just a filter over the incoming messages, not so different to the spam filters that we'll implement later, in this case encrypting the content of the message before delivery.\n\nPostfix then, but we need here to clarify something about how we achieve this; the Postfix mechanics doesn't let us apply some content filter to incoming messages only, if we setup some filter, it will be applied to all messages, incoming and outgoing. So far so good, the gpgit script search for local installed gpg public keys, so the only keys available will be the ones related with users accounts, so the only messages encrypted will be the ones with a local mail user as recipients, meaning incoming messages only! We let on the client side the possible encryption of outgoing messages, using the common (and user friendly) mechanism through the enigmail plugin.\nAnd a last detail, while the message will be encrypted, headers (from:, time:, and more metadata) will not, so later we'll add some sort of anonymization of message headers, but let just finish the encryption part first.\n\nWe open again the _/etc/postfix/master.cf_ Postfix config file\n\n```bash\nsudo nano /etc/postfix/master.cf\n```\n\nand add _-o content_filter=gpgit-pipe_ to the specified blocks (smtp, smtps, submission)\n\n```bash\nsmtp      inet  n       -       -       -       -       smtpd\n  -o content_filter=gpgit-pipe\n\nsubmission       inet    n       -       n       -       -       smtpd\n  -o content_filter=gpgit-pipe\n\nsmtps     inet  n       -       -       -       -       smtpd\n  -o content_filter=gpgit-pipe\n```\n\nand at the end we add our hook\n\n```bash\ngpgit-pipe unix -     n       n       -       -       pipe\n  flags=Rq user=gpgit argv=/var/opt/gpgit/gpgit_postfix.sh -oi -f ${sender} ${recipient}\n```\n\nSave it and close it. Now we need to create the _gpgit_postfix.sh_ script to finally get the job done!\n\nCreate the new file\n\n```bash\nsudo -u gpgit nano /var/opt/gpgit/gpgit_postfix.sh\n```\n\nwith the following contents\n\n```bash\n#!/bin/bash\n\nSENDMAIL=/usr/sbin/sendmail\nGPGIT=/var/opt/gpgit/gpgit/gpgit.pl\n\n#encrypt and resend directly from stdin\nset -o pipefail\n\n${GPGIT} \"$4\" |  ${SENDMAIL} \"$@\"\n\nexit $?\n```\n\nsave \u0026 close it, and change the file permission to 755\n\n```bash\nsudo chmod 755 /var/opt/gpgit/gpgit_postfix.sh\n```\n\nAt this point, we should have everything tuned, restart Postfix service\n\n```bash\nsudo service postfix restart\n```\n\nAnd try to send an email to your brand new email account _astronaut57@supersecure.mydomain.net_, if everything is correct, thunderbird will ask for your private key password to decrypt your incoming message (client side).\n\nThe beauty of this piped construct (the script in _/var/opt/gpgit/gpgit_postfix.sh_) is in that the message body is never saved as a file on the mail server disk. If it was, it could potentially be recovered via forensic disk analysis, which is undesirable.\n\nThis chapter is not a walk in the park, as they say, so if you want some more technical details, or just more info about that, here you'll find the original article from which i've extracted this part of the tutorial: [Encrypting Stored Email with Postfix](http://kacangbawang.com/encrypting-stored-email-with-postfix/)\n\nNext story, anti-spam chicanery!\n\n# Anti-Spam\n\nWell, yes, email use to go hand by hand with spam, their relationship has grown exponentially over the years, and all the ecosystems around email are filled with various anti-spam hacks and workarounds.\nThe combination of tools we are going to use here for our mail server is, at the moment of writing (April 2018), pretty solid and well tested, we will try at the end a quality test over our mail server using [MailTester](https://www.mail-tester.com/) spam test, and i've used this combination in my personal mail server for almost a year with 0 spam issues, yes, ZERO!\nThis coupled with properly configured jails in [Fail2ban](#fail2ban) will give us a solid system protected from spammers.\nBut first of all, we'll need to not be considered spammers ourselves, we'll start then with SPF!\n\n## SPF\n\nSender Policy Framework (SPF) is one of the two services you should configure in order not to be considered as a spammer by major e-mail service providers.\n\nSo install it:\n\n```bash\nsudo apt-get install postfix-policyd-spf-python\n```\n\nedit the postfix _/etc/postfix/master.cf_ config file\n\n```bash\nsudo nano /etc/postfix/master.cf\n```\n\nand add this\n\n```bash\npolicy-spf  unix  -       n       n       -       -       spawn\n     user=nobody argv=/usr/bin/policyd-spf\n```\n\nThen, modify this line in _/etc/postfix/main.cf_\n\n```bash\nsmtpd_recipient_restrictions = permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_rbl_client sbl.spamhaus.org, reject_unknown_sender_domain, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policy-spf\n```\n\nas you can see, we added at the end _check_policy_service unix:private/policy-spf_, to activate the SPF policy in Postfix.\n\nAnd that's it, we already setup a SPF record in our DNS at the beginning of this tutorial, so we are good to go to the next story, Amavis!\n\n## AMAVIS\n\n[Amavis](https://amavis.org/) is a high-performance and reliable interface between mailer (MTA) and one or more content checkers: virus scanners, and/or Mail::SpamAssassin Perl module.\n\nAs always\n\n```bash\nsudo apt-get install amavisd-new\n```\n\nThen the config, first we need to tell Postfix about our content filter\n\n```bash\nsudo nano /etc/postfix/main.cf\n```\n\nadd this\n\n```bash\ncontent_filter = amavis:[127.0.0.1]:10024\n```\n\nsave it and open _/etc/postfix/master.cf_\n\n```bash\nsudo nano /etc/postfix/master.cf\n```\n\nand add this blocks\n\n```bash\namavis           unix    -       -       -       -       2       smtp\n  -o smtp_send_xforward_command=yes\n  -o smtp_tls_security_level=none\n\n127.0.0.1:10025  inet    n       -       -       -       -       smtpd\n  -o content_filter=\n  -o receive_override_options=no_milters\n```\n\nSave it and restart Postfix\n\n```bash\nsudo service postfix restart\n```\n\nThen the last one, open _/etc/amavis/conf.d/20-debian_defaults_ file\n\n```bash\nsudo nano /etc/amavis/conf.d/20-debian_defaults\n```\n\nand make this line looks like this\n\n```bash\n$inet_socket_bind = '127.0.0.1';\n```\n\nThat's was easy! So next one, Postgray!\n\n## POSTGRAY\n\n[Postgrey](http://postgrey.schweikert.ch/) is a Postfix policy server implementing greylisting developed by [David Schweikert](http://david.schweikert.ch/).\n\nInstall it\n\n```bash\nsudo apt-get install postgrey\n```\n\nThen edit his config file _/etc/default/postgrey_\n\n```bash\nsudo nano /etc/default/postgrey\n```\n\nand edit this line as follows\n\n```bash\nPOSTGREY_OPTS=\"--inet=10023 --delay=30\"\n```\n\nOk, now we need to tell Postfix to use Postgray, edit the usual _/etc/postfix/main.cf_ file and modify this line\n\n```bash\nsmtpd_recipient_restrictions = permit_mynetworks, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_rbl_client sbl.spamhaus.org, reject_unknown_sender_domain, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination, check_policy_service inet:[127.0.0.1]:10023, check_policy_service unix:private/policy-spf\n```\n\nadding _check_policy_service inet:[127.0.0.1]:10023_ just before the SPF policy check\n\nrestart Postfix as usual\n\n```bash\nsudo service postfix restart\n```\n\nand jump to the next one, OpenDKIM!\n\n## OPENDKIM\n\nDomainKeys Identified Mail (DKIM) is the other service you need to configure in order not to be considered as a spammer by big e-mail providers. It ties your e-mail server to your domain name, so that receivers can check that e-mails originating from your domain indeed correspond to your computer. So we'll use [opendkim](http://www.opendkim.org/) for this; as always, install it:\n\n```bash\nsudo apt-get install opendkim opendkim-tools\n```\n\nDKIM is based on asymmetric cryptography. Basically, we will generate a pair of public/private keys on your server, and publish the public key on your DNS records (remember the beginning of the tutorial?).\n\nSo, first we edit _/etc/opendkim.conf_\n\n```bash\nsudo nano /etc/opendkim.conf\n```\n\nand we make sure it contains what follows\n\n```bash\nSyslog                  yes\n\nUMask                   002\n\nAutoRestart             Yes\nAutoRestartRate         10/1h\nSyslogSuccess           Yes\nLogWhy                  Yes\n\nCanonicalization        relaxed/simple\n\nExternalIgnoreList      refile:/etc/opendkim/TrustedHosts\nInternalHosts           refile:/etc/opendkim/TrustedHosts\nKeyTable                refile:/etc/opendkim/KeyTable\nSigningTable            refile:/etc/opendkim/SigningTable\n\nMode                    sv\nPidFile                 /var/run/opendkim/opendkim.pid\nSignatureAlgorithm      rsa-sha256\n\nUserID                  opendkim:opendkim\n\nSocket                  inet:12345@localhost\n```\n\nThen we edit _/etc/opendkim/TrustedHosts_ and we make sure it contains all our domains, hostnames or IP addresses\n\n```bash\n127.0.0.1\nlocalhost\n*.supersecure.mydomain.net\n```\n\nnext, we tell Postfix to connect with OpenDKIM, we open _/etc/postfix/main.cf_ and add this:\n\n```bash\n# DKIM\nmilter_default_action = accept\nmilter_protocol = 6\nsmtpd_milters = inet:localhost:12345\nnon_smtpd_milters = inet:localhost:12345\n```\n\ngood, now we need to generate a keypair for our server:\n\n```bash\nsudo mkdir /etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e\nsudo chown opendkim:opendkim -R /etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e\ncd /etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e\nsudo opendkim-genkey -s default -d \u003csupersecure.mydomain.net\u003e\nsudo chown opendkim:opendkim /etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e/default.private\n```\n\nKeypair created, now we add the key to _/etc/opendkim/KeyTable_\n\n```bash\ndefault._domainkey.\u003csupersecure.mydomain.net\u003e \u003csupersecure.mydomain.net\u003e:default:/etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e/default.private\n```\n\nand don't forget to change _\u003csupersecure.mydomain.net\u003e_ with your real domain (and cut the \u003c \u003e !!!)\n\nok, last one, in _/etc/opendkim/SigningTable_ file, we add this:\n\n```bash\n*@\u003csupersecure.mydomain.net\u003e default._domainkey.\u003csupersecure.mydomain.net\u003e\n```\n\nWith everything saved, restart Postfix and opendkim and we have it!\n\n```bash\nsudo service postfix restart\nsudo service opendkim restart\n```\n\nAnd to finish this chapter, the last thing will be to display our openDKIM DNS generated key, to add it to our **TXT/SPF** record and finally have it properly configured\n\n```bash\nsudo cat /etc/opendkim/keys/\u003csupersecure.mydomain.net\u003e/default.txt\n```\n\nmy output (some)\n\n```bash\ndefault._domainkey\tIN\tTXT\t( \"v=DKIM1; h=sha256; k=rsa; \"\n\t  \"p=WT...long hash....niuohefopUgIPUGUVWYF\" )  ; ----- DKIM key default for \u003csupersecure.mydomain.net\u003e\n```\n\nAnd that's it! Wait some time for the DNS to propagate and test it\n\n```bash\ndig \u003csupersecure.mydomain.net\u003e TXT\n```\n\nOur mail server is starting to look really fine-tuned! It's time for our last tool of anti-spam magic, next story [SpamAssassin](https://spamassassin.apache.org/)\n\n## SPAMASSASSIN\n\nSpamAssassin is a server level filter to avoid junk mails (spam), it's a renowned one and easy to configure.\n\nInstall it\n\n```bash\nsudo apt-get install spamassassin spamc\n```\n\nand add it to Postfix as _content_filter_, but we have a little problem here, we already have a _content_filter_ configured in Postfix, we add it with the setup of [GPGIT](#gpgit), and we can't add two _content_filter_.\n\nAnyway, every problem have a solution, and this one is pretty easy to solve, we just need to modify our _gpgit_postfix.sh_ script file, in order to filter the message with SpamAssassin BEFORE encrypting it.\n\nSo we open the file and make it look like that:\n\n```bash\nsudo nano /var/opt/gpgit/gpgit_postfix.sh\n```\n\n```bash\n#!/bin/bash\n\nSENDMAIL=/usr/sbin/sendmail\nSPAMASSASSIN=/usr/bin/spamc\nGPGIT=/var/opt/gpgit/gpgit/gpgit.pl\n\n#encrypt and resend directly from stdin\nset -o pipefail\n\n${SPAMASSASSIN} | ${GPGIT} \"$4\" |  ${SENDMAIL} \"$@\"\n\nexit $?\n```\n\nsave it and the last step, we open _/etc/spamassassin/local.cf_ file and uncomment this line\n\n```bash\nrewrite_header Subject *****SPAM*****\n```\n\nto label spam mails.\n\nWe have it!\n\nForm time to time, if you want to update the SpamAssassin database, you can run this command\n\n```bash\nsa-update\n```\n\nand restart SpamAssassin\n\n```bash\n/etc/init.d/spamassassin restart\n```\n\nWe are almost finished, just one step more to increase anonymity on our mail server, next story: Anonymize Headers!\n\n# ANONYMIZE HEADERS\n\nIn Postfix, we can manage to hide the sender originating IP (plus some other stuff), in order to reduce the presence of user information on the server, and this can be achieved using Postfix's _cleanup_service_name_ directive; let's do it!\n\nInstall _postfix-pcre_ module\n\n```bash\nsudo apt-get install postfix-pcre\n```\n\nthen create a file _/etc/postfix/smtp_header_checks.pcre_ with this content:\n\n```bash\n/^\\s*(Received: from)[^\\n]*(.*)/ REPLACE $1 [127.0.0.1] (localhost [127.0.0.1])$2\n/^\\s*User-Agent/        IGNORE\n/^\\s*X-Enigmail/        IGNORE\n/^\\s*X-Mailer/          IGNORE\n/^\\s*X-Originating-IP/  IGNORE\n```\n\nsave\u0026close\n\nNow we edit again the _/etc/postfix/master.cf_ Postfix config file, and add this:\n\n```bash\n-o cleanup_service_name=subcleanup\n```\n\nat the end of _smtp_, _submission_, _smtps_ and _amavis_ blocks, just like this:\n\n```bash\nsmtp      inet  n       -       -       -       -       smtpd\n  ........\n  -o cleanup_service_name=subcleanup\n\nsubmission       inet    n       -       n       -       -       smtpd\n  ........\n  -o cleanup_service_name=subcleanup\n\nsmtps     inet  n       -       -       -       -       smtpd\n  ........\n  -o cleanup_service_name=subcleanup\n\namavis           unix    -       -       -       -       2       smtp\n  ........\n  -o cleanup_service_name=subcleanup\n```\n\nthen at the end of the config file we add\n\n```bash\nsubcleanup unix n       -       -       -       0       cleanup\n    -o header_checks=pcre:/etc/postfix/smtp_header_checks.pcre\n```\n\nand that's it! We now restart Postfix as always, and if everything is correct, our mail server is perfectly up\u0026running!\n\nWe are going to add some security on the next story, using our favorite intrusion prevention software, [Fail2Ban](https://www.fail2ban.org/wiki/index.php/Main_Page)\n\n# FAIL2BAN\n\nIf you like stuff as set up your own server at home, or this is your field of work, you must probably know really well Fail2Ban software, but if you're not, well, it's time you catch up the good stuff, because intrusion detection systems, nowadays, are more than necessaries.\n\nMore of the same, install it\n\n```bash\nsudo apt-get install fail2ban\n```\n\nmake a local copy of the configuration file\n\n```bash\ncp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local\n```\n\nand edit your local copy\n\n```bash\nnano /etc/fail2ban/jail.local\n```\n\nthen go down to the _[JAILS]_ section and search for _# Mail servers_ line.\n\nOk, we need now to activate jails for Postfix, Dovecot y SASL:\n\n```bash\n[postfix]\n\nenabled  = true\nport     = smtp,ssmtp,submission\nfilter   = postfix\nlogpath  = /var/log/mail.log\n\n\n[sasl]\n\nenabled  = true\nport     = smtp,ssmtp,submission,imap2,imap3,imaps,pop3,pop3s\nfilter   = postfix-sasl\n# You might consider monitoring /var/log/mail.warn instead if you are\n# running postfix since it would provide the same log lines at the\n# \"warn\" level but overall at the smaller filesize.\nlogpath  = /var/log/mail.warn\nmaxretry = 1\nbantime  = 21600\n\n[dovecot]\n\nenabled = true\nport    = smtp,ssmtp,submission,imap2,imap3,imaps,pop3,pop3s\nfilter  = dovecot\nlogpath = /var/log/mail.log\n```\n\nPerfect, this is the end of the road, close the config fail and restart fail2ban\n\n```bash\nsudo /etc/init.d/fail2ban restart\n```\n\nNow that our server is running as we want it, properly configured and protected, we can now open the necessary ports on our firewall and on our router, to finally make our mail server visible to the Internet.\n\nNext short stories, _iptables_ and _router settings_\n\n# IPTABLES\n\nJust add this rules to your firewall, adjust them if your not using _iptables_\n\n```bash\nsudo iptables -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT\nsudo iptables -A INPUT -p tcp -m tcp --dport 587 -j ACCEPT\nsudo iptables -A INPUT -p tcp -m tcp --dport 993 -j ACCEPT\n\nsudo iptables -A OUTPUT -p tcp -m tcp --dport 25 -m state --state NEW -j ACCEPT\nsudo iptables -A OUTPUT -p tcp -m tcp --sport 25 -m state --state ESTABLISHED -j ACCEPT\nsudo iptables -A OUTPUT -p tcp -m tcp --dport 587 -m state --state NEW -j ACCEPT\nsudo iptables -A OUTPUT -p tcp -m tcp --sport 587 -m state --state ESTABLISHED -j ACCEPT\n```\n\n# ROUTER SETTINGS\n\nopen router port 25 (SMTP, to instantiate servers communication), 587 (SUBMISSION, secure send), and 993 (IMAPS over TLS/SSL, secure receive)\n\n\n# Testing\n\nThis is the end of another journey, we can enjoy it, ask ourselves if was worth the hours of work, and if we learned something, important or not, learning can only make us better.\n\nBut before let's try a test, a comprehensive one about the spammyness of our mail server, plus a lot of info on how to improve the quality of the service, is [mail tester](https://www.mail-tester.com/), i'll let you try yours then, and here you have the result for my mail server, the one identically configured as the tutorial itself\n\n![End-to-end (E2EE) Encrypted Email Server](https://github.com/d3cod3/EndtoEndEncryptedMailServer/blob/master/img/mail_score.jpg)\n\nand just for having something to compare, this is another test from a gmail account (you can try with one if you have it, and you will see the same result)\n\n![Gmail](https://github.com/d3cod3/EndtoEndEncryptedMailServer/blob/master/img/gmail_score.jpg)\n\n\n# Conclusions\n\n...soon\n","funding_links":["https://github.com/sponsors/d3cod3","https://ko-fi.com/d3cod3"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd3cod3%2Fendtoendencryptedmailserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fd3cod3%2Fendtoendencryptedmailserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd3cod3%2Fendtoendencryptedmailserver/lists"}