openec2 Article Description
Debian 12, Php8.2, Nginx, Dovecot / Postfix / Opendkim / spamtest, virustest/ spamassassin (spamd) on an Akamai/Linode Server
Debian 12, Php8.2, Nginx, Dovecot / Postfix / Opendkim / spamtest, virustest/ spamassassin (spamd) on an Akamai/Linode Server
EC2 Menu
EC2 Menu
UNDER CONSTRUCTION – the remaining piece of work is sieve filter, anti-virus, and anti-spam, which I have working, but not documented just yet. Should be a couple of weeks to finish this doco.
This content has taken a couple of years to explore, finally reaching a working service. All work shown uses an iMac terminal, and the vi editor.
There are so many pieces to install, and they keep being removed, updated etc. Some pieces are not recommended, or have poor documentation.
Also, dovecot may have security fixes that you can’t add until the Debian OS includes the latest version.
iMac NEW Outlook requests the password sign in all the time, so you go to system settings and Internet Accounts and add the account there. Fine after that.
My configurations are for Inbox IMAPS SSL port 993, and Sending STARTTLS Port 587 (Not SSL on 465).
I set the email up to sync directly with my Linode server, rather than Outlook. This way it updates correctly on Linode.
Debian 12, Php8.2, Nginx, Dovecot / Postfix / Opendkim / spamtest, virustest / spamassassin (spamd) on an Akamai/Linode Server
High Level Notes Is it worth having your own email server?
As a standalone service for one domain and personal use, the cost is higher than using a professional service such as MS Exchange. This is because we choose a basic Linode with backups – AUD $140/year compared to MS Exchange Plan 1 of $80.
If we include our website(s) on the same Linode running Debian 12, email is FREE. I believe that costs are like shopping dockets. They add up significantly over a five year period. Most people do not think this way.
If we wish to use iCloud.com for email, it is about AUD $20 per year to have your own domain configured, but in MS NEW Outlook make sure yu set it up NOT to sync directly with iCloud, or you won;t be able to copy or move, or view source code.
VentraIP cPanel provides email on Axigen, but you have no way of configuring the Axigen options that admin would have. The question on Axigen is performance – it may be too slow as I have noticed for some using cPanel with Axigen behind the scenes, and yet the product itself works fast. I personally think companies have had decades to improve email structure and services. It needs a complete re-write, just like IP6 is a rewrite over IP4. There is appalling structure within emails that you can’t work with, let alone terrible problems with configurations and errors, bounces, security, filtering. I mean, by this time there should have been an amazing email system in place with simple easy to follow GUI configuration screens, but it does not exist. Why has technology gone this way?
MS Exchange is good value, but AUD $80 a year. It has lots of admin panels you have to work through, let alone remember, and to work with WordPress SMTP email, you have to call their help desk to learn how to remove some security settings and permit smtp, again via a number of admin panels. THis is absurd for regular people. But there it is.
If using Amazon, the SES/Lambda/IAM/S3 Bucket system is impractical for me since they removed Node16.x javascript, as no one wrote working programs for filtering and forwarding emails correctly. So, we don’t know what is going on with email under these services. To use an email server on Amazon AWS requires you to remove port restrictions for your region, and if using SES as a relay instead, for that regioun – usually Oregon for Australia. Amazon Workmail is about the same cost as MS Exchange Plan 1, but MS provides more functionality – refer to reviews by users on forums. If you develop your own server, you have to ensure no bad reputation. It is all a lot of work overall.
Akamai/Linode clearly states the problems which I can certainly say has been true for my testing over the last two+ years: https://www.linode.com/docs/guides/email/email-services/
What I am showing here is not for business use, multiple domains and users. Why get involved in potential risks and headaches?
I would suggest that critical emails for financials and government communications be on a professional and hence different email service. For instance, a free account with proton mail has half a Gigabyte available. It is however slower than local email processing and viewing.
But then we have the problem of were data centers are located. Why would I want any risk of email on a server in South Korea. It makes no sense from a best practice point of view. Why would I choose an email provider regardless of the advertising, in China? Why would I want servers with connections through countries with typhoons? Why would I want to pay $15 a month for any number of services where costs should be a couple of dollars? Why would I use any service at all that has adds drop into the inbox. It is complete nonsense that these things are happening.
There is no concise or accurate article or book I am aware of that shows how to install dovecot as an end-to-end solution, with robust/reliable/performance configurations, for your own email server and domain name, integrating the above plugins. I have a working version I would like to share – one domain name, using the TLD (domain.com), not using mail.domain.com, and a low cost Positive DV SSL certificate.
DNS records need CAA entries. If your registrar does not provide this, you would have to move your domain name to other name servers that do, or transfer to another registrar. Akamai/Linode is not a registrar, and their instructions for name servers are too complex, so we will assume you have access elsewhere to CAA records. If I work out these details later, I will add them in post-article notes.
I make use of the Dovecot pigeonhole/seive filter for anti-virus testing, followed by Spamassassin (spamd), followed by Dovecot’s anti-spam – all three processes. We may add filter rules such as whitelisting or blacklisting, but further configurations are up to yourself to explore via Internet articles.
One may configure Amazon AWS SES to receive emails if the Dovecot server goes down.
Dovecot and other services have many options, so on the whole, we configure what works, not spend time figuring out why – it would be too labor intensive and never-ending. We already follow this approach for websites – for instance, we use a limited understanding of Nginx configurations, or for Apache, limited .htaccess configurations.
We must emphasise that technology changes. If the notes here do not work, I cannot help to fix issues. You need good Unix/Linux and problem solving skills. There are many components with their integrations involved – postfix, dovecot, opendkim, SSL, spamassassin, sieve, spamtest, virustest, database, and for web services (if we use them) nginx, php, opcache, memcached, database. We assert you can install Debian 12, and make use of Amazon SES for backup to emails that fail if the system is down. This is a lot.
No development should ever be done on a live server.
Disk swap space and resource use will be higher if co-located with a web server on the same Linode. You can restart website services separately to email services, but I like to refresh disk swap space (swapoff/swapon) as well at midnight via a crontab script if resource use is “too high”. Restarting email services or a reboot of the server could lose emails, hence the need for Amazon SES as a backup. I have other extensive articles that show how to configure SES Email. However, in this case it is not to send email, only receive – meaning we only add DKIM, no use of dmarc, and add an Amazon MX record with lower priority to the DNS.
If we were to explore use of Amazon EC2 instances rather than Linode, we would need to request ports be opened for the region we are in, rather than for Oregon. If in Australia we probably use SES in Oregon. Dovecot would need open ports in Sydney. I have not as yet configured email on EC2 in this manner. When testing Dovecot through SES as the relay and use of S3 Buckets, Dovecot will not work correctly.
It is possible to restart web services without restarting email services, or a swapoff/swapon reset – but I would advise only doing so via Crontab at midnight. Once a service is running, you will not be able to reboot the server without email going offline. Due to any known or unkown risk, we need Amazon SES as a backup service.
I have no notes on major upgrades or added security or more complex email filtering.
You must not manually move or delete Dovecot files. “doveadm” has some administrative options, not explored in this article.
Please configure dmarc@, postmaster@, abuse@, noreply@, webmaster@, admin@ as standard procedure. Manage bounced emails. Do not use the service for marketing or bulk mailouts. Stay in line with Akamai/Linode policies. You can add a sieve filter to drop dmarc emails.
We will keep in mind that some configurations need to be enabled or compiled in order to work. We use log files to watch what happens as we build the system and test it. You would comment out various log files on a live system – up to you. Email clients should send and receive emails without delay – a touch delay as sieve/antispam/antivirus scripts execute.
If using MS Outlook as a client on iMac, the New MS Outlook for iMac will not work first off. You have to configure the email on the Old Outlook, then switch to the New, and then add the email service using the IMAP Sync option. Just going ahead without this option will not work. We will use ports 587 and 993. Currently, New Outlook has too many bugs (as confirmed by MS Support) to use on Windows.
Check you Linode IP address is not blacklisted.
See mxtoolbox.com to test all your configurations. Sometimes a blacklist is temporary.
Have Debian 12 with Nginx fully up and running before installing the email service.
The SSH details below are complicated, but worth knowing how to set up. They ensure your vi editor will work, and you can login in the same way as you would with an Amazon AWS EC2 Linux instance. A Linode root login will not go well with the editing you need to do. Hence why I take time to show the details below.
Your request should say you have read their policies, it is a personal email, not for marketing, advertising or bulk email, and is required for use with your website as well. Say you will check ongoing for any bounced emails, and you will configure admin@, postmaster@, webmaster@, abuse@, noreply@, and your primary address, e.g. contact@. This shows you are aware of good practice.
Create a Linode with Backups enabled using Debian 12. Check the IP address is not blacklisted (badly anyway).
Add the A and AAAA records to your DNS. We will do further DNS configs later.
See my other articles on Debian installations to assist in the basics. For disk swap space, add 256MB if you wish, which will go on top of the pre-configured 512MB swap space.
For help on basic installation steps, see https://openec2.com/article-items/debian-11-nginx-part-4-first-configurations/
This next bit is confusing. See how you go. The aim is to have another user for a root privileged login that you can log into and then execute a sudo su command from $myser: to #myuser: so that the vi editor works correctly. (I do all my examples with the vi editor: https://openec2.com/vi-editor/ )
Here is the detail to set up a proper SSH environment:
All examples are based on iMac terminal console and the vi editor.
You need to have already added root to your terminal login capability. (See forums on adding root)
Create Linode Instance, Debian 12 using your firewall rules and a password – lowest cost plan in your region.
Let’s say you assigned the name development. Example below is for Sydney, Australia.
Login to Linode instance using the LISH Console via SSH details from the Linode console from your iMac terminal: (e.g….)
$ su root
# set -o vi
(then use the ssh command here:)
ssh -t XXXX@lish-ap-southeast.linode.com development-ap-southeast
Enter your master Linode password.
Then login as root….
login: root
Enter your instance password. This is the one you assigned, not the master Linode password.
You need to enter these commands for the vi editor.
root@localhost:~# set -o vi
root@localhost:~# export EXINIT=’set noautoindent’
root@localhost:~# export VISUAL=vim
Let’s add a user called ec2-user, to mimick an Amazon Linxu2023 instance login. You can use any name.
If you get an error saying sss_cache…DB version too old, see the notes below this section, then come back.
adduser ec2-user
It asks for a password, and then use null for name details etc.
passwd ec2-user will change the password later if you need to.
cd /etc
vi sudoers
SHIFT-G to get to the end of the file
o to append below the last line
Press ENTER
Manually type: (we cannot cut and paste at this stage)
ec2-user ALL=(ALL:ALL) ALL
ESC SHIFT: w ! to force a write. ESC means the Escape key. SHIFT means the SHIFT key, not the word.
SHIFTZZ (that is, three key sequence of SHIFT key and ZZ characters)
CTRL D to logout
Login as ec2-user then issue the sudo su command. This will be our future pattern for all work.
localhost login: ec2-user
Password:
ec2-user@localhost:~$ sudo su[sudo] password for ec2-user:
root@localhost:/home/ec2-user#
set -o vi
export EXINIT=’set noautoindent’
export VISUAL=vim
vi /etc/vim/vimrc.local –> use i to insert
let skip_defaults_vim = 1
if has(‘mouse’)
set mouse=r
endif
ESC SHIFTZZ to save the file
Edit root and ec2-user’s .bashrc files: use your own domain.com entry.
My notes show where it is obvious that you save a file after editing it.
cd ~ vi .bashrc export EXINIT='set noautoindent' export VISUAL=vim export PS1="[\u@domain.com: \w]\\$ " alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' cd /home/ec2-user vi .bashrc export EXINIT='set noautoindent' export VISUAL=vim export PS1="[\u@domain.com: \w]\\$ " alias rm='rm -i' alias cp='cp -i' alias mv='mv -i'
vi /etc/sudoers
manually change the previous entry to read:
ec2-user ALL=(ALL) NOPASSWD:ALL
This will mean we do not have to use a password when loggin in with a .pem security file later on.
Add root privileges:
root@localhost:~# usermod -aG adm ec2-user
root@localhost:~# usermod -aG root ec2-user
(Note: linux2023 would use admin, not adm. You can see groups in the /etc/groups file)
We now have to add /home/ec2-user/.ssh and /home/ec2-user/.ssh/authorized_keys
cd /home/ec2-user
mkdir .ssh
ls -la
drwxr-xr-x 2 root root 4096 Sep 24 01:40 .ssh
chown ec2-user .ssh;chgrp ec2-user .ssh; chmod 700 .ssh
ls -la
drwx—— 2 ec2-user ec2-user 4096 Sep 23 16:17 .ssh
cd .ssh
You need to have a .pem file on your PC and corresponding data in the authorized_keys file.
Here is an example:
On your PC add this file: (no blank lines above or below)
vi instance.pem
-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA241I7RglQUumLHtbudmPVPvF7h4TbHDM+3aMzTeqVK3ktqN3 trvqyu3PX1W3p1T+taaE2pIHRgoBewpeX4zJAGUnhRjSbtgFCTtNDukhWgn8xyGN 8Yd/RBi2Ns62r7nr50QMvdMO0j4jMcYCcAUpH3hcyvW4TBqLVIWpTum4r78RFoJv KTCFGY3If00nHUovCfYU082SdD9yVSAEziw/suQGeFinZfJffPYr43+R+tOTrEFG XeonsZJ4gQML6N6mUN9iK3m4i1vEjOBsH5PPG5UZcKrwPkHzzETDxa97Oc2ke330 6BWoYC4i9vZN19r8xUX6gw4hFPJd36pRzF6m7QIDAQABAoIBAQDEPSqb0WMrU8S6 KaC5I377xPcLeRJ/cOunMDV2EHVMrwmpPb95M8oPjDQ40FMRW+U21BoXi0K5FaRx J7lmyl223rNOQfuFW8xUjg64bySCaD7qdlF7O1NDuJrVrjqYIyR6V9bTrNyoiyXG DWiMW/B/0oOOXcdXLrqu17GXeUy8t/Fbim3hD+uHBRkNycUcK29PPxNJb4pDDdgO /aaKXocI8SoYdW3muK7qoncV5/EpDg+FaFLDo6+GdprwtKckUdfTeLmVakTsk9md 8ib444KcMcdlKhjr/kmvbU/F7r2vHQGkYuSdTO3N5qCeBt7dmLGwAyQ210vOCRcf 7tuLakO5AoGBAPFGsxsMXaulhz+SrASORgH33407Jxm1Y3z2lkSYWyUv/yF2cKbe Iv1qOlUrSZrNN/DrjsZO1RLrhraUkOpaylCItLO+xUR3neWGya4f1yDJgCqVuSnj ZPqovYqhFEXHsQDVnUfty9KORCvSLmme1C5b7/tMHFTWAwSENAdzL3irAoGBAOjz M3ZKRCrON0EFcKgGrgyzoL/UkAt+4xywZxjLgGw6eDg37uoKY+3fFsRoGYuqdd2Y kgUUmrfRY/gVKShOoMJQwrVs1i50ma9K+3oCzYaqlhEUMCWDHdZ1MebxKpJkhrFm Pp0xCDT03X/MWwibezcVm3KkOpptkfykeuqam47HAoGAAJEj9ppO1gpriPC1SsVy 0KpechyDeQH+G9sQe1TIUgwM021N0naPKn1Hac/SOnTk/sHu8fRZd9Pp2x/6PzK7 avkHQ6zdFc4aJuHsM2aLwN34WSFE8B5CrHwdBehe/dL8TX4zAmpColoHOvshdMoU wY8hvztsjZ57H1WYMbRJlkUCgYBNTnBUoD7RCdByZpDFYjoegvokzzDib8CFA9Gi +93pjNnapk1GJB6XkUJn+bgIjpBA8uH6h6T0vat0Z+lZtAZLliuXs7+8ePuLndGo 4wc72p6kmlOED2g2hHwEDSK4pF3Cv5Cl7+CuWlirkbDgQWD5ndURjYjZOOWKtzcH ZjmbfwKBgQDbuj5wVVp7HXCmhrHXfDGLg2d09p6HGR3PVJspUUeJJ/OTg6rKF5kB i9xRlS7M5ApZY1r75VE2zJcHsvcgK763ohuNv+GVrUpyLvDwU6daq1v2KnfeOEe9 wvKfRBKzyhotebwB3gw3v8z4X1UdBbAnvZQhcnZgZPbO/WISbDGL0A== -----END RSA PRIVATE KEY-----
On the Linode instance, in .ssh:
vi authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbjUjtGCVBS6Yse1u52Y9U+8XuHhNscMz7dozNN6pUreS2o3e2u+rK7c9fVbenVP61poTakgdGCgF7Cl5fjMkAZSeFGNJu2AUJO00O6SFaCfzHIY3xh39EGLY2zravuevnRAy90w7SPiMxxgJwBSkfeFzK9bhMGotUhalO6bivvxEWgm8pMIUZjch/TScdSi8J9hTTzZJ0P3JVIATOLD+y5AZ4WKdl8l989ivjf5H605OsQUZd6iexkniBAwvo3qZQ32IrebiLW8SM4Gwfk88blRlwqvA+QfPMRMPFr3s5zaR7ffToFahgLiL29k3X2vzFRfqDDiEU8l3fqlHMXqbt instance
You can add multiple lines with similar entries. You can research the Internet on how to create the above data.
YOU NEED TO REPLACE THE ABOVE CODE – which works – WITH YOUR OWN ASAP. I provided the code so you can test and see what works. Refer to Linode SSH documentation.
ls -l
-rw-r–r– 1 root root 389 Sep 24 01:44 authorized_keys
chown ec2-user *;chgrp ec2-user *; chmod 600 *
ls -l
-rw——- 1 ec2-user ec2-user 792 Sep 23 16:17 authorized_keys
Now logout and destroy your terminal, open a new one on your iMac, and type in the ssh command where the instance.pem file is located. e.g…..
# ssh -i “instance.pem” ec2-user@xxx.xxx.xxx.xxx where xxx is your Linode IP address.
If you have conflict with your existing or previous ssh logins, use this command or as a script:
:>/var/root/.ssh/known_hosts
Then try ssh again from where the instance.pem file is located. (You can put this into a script)
# ssh -i “instance.pem” ec2-user@xxx.xxx.xxx.xxx
ec2-user@xxx.xxx.xxx.xxx’s password:
Linux localhost 6.1.0-25-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.106-3 (2024-08-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Sep 24 01:54:11 2024 from 144.6.125.35[ec2-user@shawlw.me: ~]$ sudo su[root@mydomain.com: /home/ec2-user]# set -o vi
At this stage we are still typing a password. Also note, the vi editor now works correctly with editing and mouse scrolling and ec2-user operates as root.
To fix this on yur Linode instance:
vi /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication no
systemctl restart sshd
It will now work when you log back in.
If issues around this, see Linode articles on using SSH.
——————————————————————————————————————————-
DB version too old error:
If you get this error:
——————————-[sss_cache] [sysdb_domain_cache_connect] (0x0010): DB version too old [0.22], expected [0.23] for domain implicit_files!
Higher version of database is expected!
In order to upgrade the database, you must run SSSD.
Removing cache files in /var/lib/sss/db should fix the issue, but note that removing cache files will also remove all of your cached credentials.
Could not open available domains
——————————–
To fix this, do the following:
cd /var/lib/sss/db
rm *
sss_cache -E
——————————————————————————————————————————-
THE ABOVE REPRESENTS IMPORTANT INITIAL LINODE CONFIGURATIONS
We can now commence work…
——————————————————————————————————————————-
22 (memcached), 80 (http), 443 (https), 53 (dns), 587 (smtpd), 993 (imaps), 11211 (memcached – for nginx)
We do not need to open 25, 465 – and possibly things will still work without 587 as that would be opened up from the Linode support team.
Add your own static IP address to Port 22 for SSH. e.g. xxx.xxx.xxx.xxx/32
Memcached uses 127.0.0.0/16.
We use port 53 (which may already be opened – not sure) for the Nameservers that find email service domains from 8.8.8.8, and 1.1.1.1 (Google and Cloudflare).
These are some first steps you may take for your DNS Records:
If you plan to use Amazon SES as a backup for the event your email goes down, you would have added an SES domain in Oregon (for Australia) with DKIM but no DMARC, as we are only receiving emails as a downtime backup. The DNS record would be: (don;t do this unless you ahve those setps in place along with the S3 bucket, as per my other articles)
mydomain.com MX inbound-smtp.us-west-2.amazonaws.com 3600 30 -> 30 gives a lower priority to the live service that we actually want.
Your actual MX record, as we are not using mail.mydomain.com, is:
mydomain.com MX mydomain.com 3600 10 -> 10 give the higher priority.
Add the SPF record:
mydomain.com TXT v=spf1 ip4:xxx.xxx.xxx.xxx ip6:yyyy:yyyy::yyyy:yyyy:yyyy:yyyy ~all 3600. -> where xxxx and yyyy are the IP4 and IP6 addresses under your Linode’s network tab. (You click on the Linode’s title in blue to get this menu of tabs – which includes backups.) Notice on this tab the Reverse DNS string which will user later in Postfix’s main.cf file.
Add the DMARC records:
*._report._dmarc.mydomain.com TXT v=DMARC1 3600
_dmarc.mydomaincom TXT v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarc@mydomain.com 3600
We will later configure a sieve filter in Dovecot to drop the dmarc emails, unless you really want to capture them to some other folder than the INBOX.
We will want dkim to be added by default to all of our outbound emails:
_adsp._domainkey TXT dkim=all 3600
The default._domainkey.mydomain.com TXT record will be configured when we use opendkim.
It will look like this:
v=DKIM1; h=sha256; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArlC/d8/83skStRTVz1POx…….. and so on.
Some DNS services have to break up the length of the p= record by using multiple lines. We will show how to test the opendkim DNS record later.
Your SSL certificates need a CAA record. If using sectigo:
mydomain.com CAA 0 issue sectigo.com
mydomain.com CAA 0 issue digicert.com
I have not tested Let’s Encrypt with the SSL processes that Dovecot uses. If these fail due to incompatibility, you will need a low cost SSL certificate. The entry for Let’s Encrypt is 0 issue letsencrypt.org
When using Amazon SES as the backup, you will have three CNAME records provided by SES.
They look like this:
6hl7melvoc3lxxxxxxxxxxxxxxxxxxxx._domainkey.mydomain.com CNAME 6hl7melvoc3lxxxxxxxxxxxxxxxxxxxx.dkim.amazonses.com
If using a website, you would have a CNAME record pointing www.mydomain.com to mydomain.com
Sectigo allows you to add an authorisation key rather than using admin@mydomain.com, which looks something like this:
_ab28c553cbf6xxxxxxxxxxxxxxx.mydomain.com CNAME xxxxxxxxxxx0494B75456DE54ED2.o5U5855xxxxxxxxxxx.sectigo.com (This sort of thing)
You can check DNS records are pulished with “dns checker” but sometimes your PC needs more time, or at least clear the browser cache.
Note:
It is best to see my other articles for installing Debian, nginx, and memcached in case some parts are missing below. It is a little bit all over the place below, but you should be familiar with installing Debian anyway.
If /var/www or /var/www/html does not exist after installing, just make the directories with chmod 2775 and nginx permissions, and add a dummy /var/www/html/index.html file with any wording in it, with chmod 664. If phpMyAdmin does not work, go to root and check no files with ownership apache exist that may need changing to nginx. e.g. cd /; ls -lRa | grep apache | more
Debian also requires:
apt install libsasl2-modules
First we install the basics for a web service on the same Linode instance. I am installing php8.2.
As a reminder, –> comments and [comments] may be included in the coding that I show. All commands unless otherwise shown are from the root shell, or in this case ec2-user who has root access.
[We add another 256MB to the pre-built 512MB swap space] echo "vm.swappiness=10" >> /etc/sysctl.conf echo "vm.vfs_cache_pressure=200" >> /etc/sysctl.conf sysctl -w vm.swappiness=10 sysctl -w vm.vfs_cache_pressure=200 free -m dd if=/dev/zero of=/swapfile bs=1024 count=262144 mkswap /swapfile chmod 0600 /swapfile swapon /swapfile echo "/swapfile swap swap defaults 0 0" >> /etc/fstab free -m [Use your own Country/City. See /usr/share/zoneinfo] a="Australia/Brisbane";export a;echo $a ln -sf /usr/share/zoneinfo/$a /etc/localtime date apt update apt upgrade apt install software-properties-common ca-certificates lsb-release gnupg2 mariadb-server apt install php8.2-cli php8.2-mbstring php8.2-xml php8.2-common php8.2-curl php8.2-imap php8.2-bz2 apt install php8.2-mysqli php8.2-fpm gcc libjpeg* zip php8.2-zip [Can be useful for PDF documents:] apt install php8.2-gd [For certbot/lets encrypt:] [If having installation issues, you should probably "sync;sync;reboot" your instance or at least reboot at some stage.] apt install python3-venv apt install php8.2-xmlrpc php8.2-soap php8.2-intl python3 -m venv /opt/certbot/ /opt/certbot/bin/pip install --upgrade pip /opt/certbot/bin/pip install certbot ln -s /opt/certbot/bin/certbot /usr/bin/certbot apt install certbot apt install libgd-tools ipset net-tools
We should configure mariadb at this point. If mariadb is running, the following will execute. Otherwise, use the command “systemctl start mariadb”.
mysql_secure_installation "Enter current password for root" (enter for none): OK, successfully used password, moving on... "Switch to unix_socket authentication [Y/n]" n "Change the root password?" [Y/n] Y (nominate your database password) Y for the remaining questions] [Note that we now start and enable our mariadb and php8.2-fpm services. Enabling means they start at a reboot.] systemctl stop mariadb systemctl start mariadb systemctl enable mariadb
Install latest version of nginx: (I have other articles on Apache2 and Linux2023 httpd)
Refer to https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/
[Assuming we have ec2-user. Otherwise, /home/admin] cd /home/ec2-user apt install curl ca-certificates lsb-release debian-archive-keyring curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg [This is the verification output:] pub rsa2048 2011-08-19 [SC] [expires: 2024-06-14] 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 uid nginx signing key <signing-key@nginx.com> echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/debian `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx sudo apt update sudo apt install nginx cd /etc/nginx ls systemctl enable nginx [remove apache packages if they are hanging around:] apt remove *apache*
This would be a good point at which to reboot the instance.
Notes: php.ini’s Dynamic section should not need an entry to use memcached.so as we did in Linux2023 configurations.
Systemctl status -l commands are useful to check all services.
Remember you have to enable and start php8.2-fpm for nginx to work, and we use systemctl reload php8.2-fpm rather than a restart,
and systemctl enable memcached with a restart to start using it.
A useful script:
cd /home/ec2-user vi restart.sh #!/bin/sh free -m systemctl stop nginx systemctl stop mariadb systemctl stop memcached systemctl reload php8.2-fpm systemctl start mariadb systemctl start memcached systemctl start nginx swapoff -a swapon -a free -m systemctl status -l nginx systemctl status -l mariadb systemctl status -l php8.2-fpm systemctl status -l memcached exit [save and exit] chmod 777 restart.sh [test it:] ./restart.sh
If you have an SSL certificate, please install into /etc/ssl/certs and /etc/ssl/private. Set your DNS CAA record(s).
If using Let’s Encrypt, please see my articles on that method. That can’t be setup without nginx installed with a CAA record for letsencrypt.org.
Please see my next article for the php, phpMyAdmin, memcached, opcache configurations.
Please modify php.ini, www.conf for use of memcached – see https://openec2.com/article-items/nginx-memcached/
Please see my article for installing ngix – see https://openec2.com/article-items/debian-11-nginx-part-7-nginx-ssl/
As you see, there is always a lot of pre-planning.
WE ARE NOW READY TO CONFIGURE DOVECOT
We will configure the basics before adding filters and anti-virus/anti-spam configurations.
You should not need to do this step, but in case: (use your ip4 address and domain name – we are not using mail.domain.com in our example. If your intended MX record were to be mail.domain.com you’d need an SSL certificate for that and modify the installation details.)
vi /etc/hosts
xxx.xxx.xxx.xxx domain.com
I don’t use pop3, but I have installed it. All logins via root.
apt-get install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql [Select "Internet Site" when prompted - ENTER key will let the install continue] [Change localhost to your domain (email domain)]
You previously did the database and phpMyAdmin installation, so you can use that service to create the database, admin user, password, and tables.
Otherwise, see:
https://www.linode.com/docs/guides/email-with-postfix-dovecot-and-mysql/
I use utf8mb4_general_ci and utfgmb4 – you can choose otherwise if you wish.
Create a database called mailserver
Create a user called mailuser
Add full permissions for mailuser and root to mailserver
If using utfbmb4, modify the settings shown in the URL above, and you must change password` varchar(106) to (150) for a larger password.
Use these settings (from the URL above) in the phpMyAdmin SQL section (highlight the mailserver database first)
CREATE TABLE `virtual_domains` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; [Once done, do the next SQL command:] CREATE TABLE `virtual_users` ( `id` int(11) NOT NULL auto_increment, `domain_id` int(11) NOT NULL, `password` varchar(150) NOT NULL, `email` varchar(100) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; [And then the next:] CREATE TABLE `virtual_aliases` ( `id` int(11) NOT NULL auto_increment, `domain_id` int(11) NOT NULL, `source` varchar(100) NOT NULL, `destination` varchar(100) NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; [Next SQL: replace domain.com with your mail server domain, e.g. in my example, domain.com, remembering I am not going a step further and configuring mail.domain.com in my examples] INSERT INTO mailserver.virtual_domains (name) VALUES ('domain.com'); [Now create a password and paste it into an editor so yu can use it further below] doveadm pw -s SHA512-CRYPT [Next SQL: replace the word hash with your password, and user@domain.com with your. primary email user address] INSERT INTO mailserver.virtual_users (domain_id, password , email) VALUES ('1', 'hash', 'user@domain.com'); [Next SQL: add an alias. You can repeat for more, or use phpMyAdmin to copy and edit] INSERT INTO mailserver.virtual_aliases (domain_id, source, destination) VALUES ('1', 'admin@domain.com', 'user@domain.com'); [You can test all this worked as follows: with your own user, alias, domain names] SELECT * FROM mailserver.virtual_domains; [You should see the number 1 - our example are not dealing with multiple domains at this stage] SELECT * FROM mailserver.virtual_users; SELECT * FROM mailserver.virtual_aliases;
Initially we will get a plain version of Dovecot and Postfix working. Then we will add Opendkim for dkim records.
As we add features, like dkim and anti-spam, we will revisit various configuration files. Always replace domain.com with your own domain, and users or IP addresses.
**** IMPORTANT *****
Please use the critical fixes section below for the main.cf master.cf and opendkim.conf files rather than the content shown here for these fiiles.
When you have spf, dkim/dmarc working, then modify files below to add sieve and spamassassin. I know this is a bit of a mess, but it works.
You can’t use New Outlook to view all the headers as it does not show them all.
********************
Please copy the following to your /etc/postfix/main.cf file (always make an original backup first – e.g. cp -p FILE FILE.o
Ensure there are no repeated lines. If so, the log files will pick this up. You check settings with systemctl restart postfix or whatever service you test, followed by systemctl status -l postfix. You need to widen the terminal screen to see the full results.
cd /etc/postfix cp -p main.cf main.cf.o cp -p master.cf master.cf.o [Here is the content for main.cf. Do not use repeat lines - the log files will show if there is an error. You check with systemctl restart postfix and systemctl status -l postfix. Widen your terminal screen to see the output. I have put domain.com in CAPS so you can replace. The opendkim entries are initially commented out. LINODE_SMTPBANNER is the reverse pointer address in the network tab in Linode. e.g. xxx-xxx-xxx-xxx.ip.linodeusercontent.com. For instance, xxx-xxx-xxx-xxx.ip.linodeusercontent.com ESMTP snotbat.com postfix -> you could test replacing postfix with another word. Use your own values for: smtpd_tls_cert_file= and smtpd_tls_key_file=. I have not tested Let's Encrypt certificates for any incompatibiltites.] # See /usr/share/postfix/main.cf.dist for a commented, more complete version # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. #myorigin = /etc/mailname # smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) smtpd_banner = LINODE_SMTPBANNER ESMTP DOMAIN.COM postfix biff = no # appending .domain is the MUA's job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings #delay_warning_time = 4h readme_directory = no # See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on # fresh installs. compatibility_level = 3.6 # TLS parameters - USE YOUR OWN SSL CERTIFICATE AND DIRECTORY LOCATIONS. E.G. /etc/letsecnrypt/live/domain.com/fullchain.pem and ..../privkey.pem, or /etc/ssl/certs/... and /etc/ssl/private/... smtpd_tls_cert_file=/etc/ssl/certs/DOMAIN_COM.crt smtpd_tls_key_file=/etc/ssl/private/DOMAIN_COM.key smtpd_use_tls=yes smtpd_tls_auth_only = yes smtp_tls_security_level = may smtpd_tls_security_level = may smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_tls_security_options = noanonymous # Authentication smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. # Restrictions smtpd_helo_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unlisted_recipient, reject_unauth_destination, check_policy_service unix:private/policyd-spf smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_sender, reject_unknown_sender_domain smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. myhostname = DOMAIN.COM alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydomain = DOMAIN.COM myorigin = $mydomain mydestination = localhost relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all inet_protocols = all # Handing off local delivery to Dovecot's LMTP, and telling it where to store mail virtual_transport = lmtp:unix:private/dovecot-lmtp # Virtual domains, users, and aliases virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf, mysql:/etc/postfix/mysql-virtual-email2email.cf # Even more Restrictions and MTA params disable_vrfy_command = yes strict_rfc821_envelopes = yes #smtpd_etrn_restrictions = reject #smtpd_reject_unlisted_sender = yes #smtpd_reject_unlisted_recipient = yes smtpd_delay_reject = yes smtpd_helo_required = yes smtp_always_send_ehlo = yes #smtpd_hard_error_limit = 1 smtpd_timeout = 30s smtp_helo_timeout = 15s smtp_rcpt_timeout = 15s smtpd_recipient_limit = 40 minimal_backoff_time = 180s maximal_backoff_time = 3h # Reply Rejection Codes invalid_hostname_reject_code = 550 non_fqdn_reject_code = 550 unknown_address_reject_code = 550 unknown_client_reject_code = 550 unknown_hostname_reject_code = 550 unverified_recipient_reject_code = 550 unverified_sender_reject_code = 550 # original smtp_tls_CApath=/etc/ssl/certs smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache myhostname = DOMAIN.COM alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases # myorigin = /etc/mailname. WE DO NOT USE THIS # mydestination = $myhostname, DOMAIN.COM, localhost.localdomain, localhost.localdomain, localhost WE DO NOT USE THIS relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all inet_protocols = all # opendkim policyd-spf_time_limit = 3600 # Milter configuration # OpenDKIM -> WE WILL UNCOMMENT THESE LINES AFTER OPENDKIM IS INSTALLED AND READY FOR TESTING # milter_default_action = accept # milter_protocol = 6 # smtpd_milters = inet:localhost:8891 # non_smtpd_milters = $smtpd_milters maillog_file = /var/log/mail.log smtpd_tls_loglevel = 3 smtp_tls_loglevel = 3 # mail_home = WE DO NOT USE THIS VARIABLE
Configure master.cf (we will add more to this later)
cd /etc/postfix [These are the contents of master.cf. We will uncomment milter and spamassassin later. Options always have two space characters before them, so when uncomenting, do so.] # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master" or # on-line: http://www.postfix.org/master.5.html). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (no) (never) (100) # ========================================================================== smtp inet n - y - - smtpd -o content_filter=spamassassin submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_reject_unlisted_recipient=no -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING # -o content_filter=spamassassin smtps inet n - - - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING # -o content_filter=spamassassin pickup unix n - y 60 1 pickup cleanup unix n - y - 0 cleanup qmgr unix n - n 300 1 qmgr #qmgr unix n - n 300 1 oqmgr tlsmgr unix - - y 1000? 1 tlsmgr rewrite unix - - y - - trivial-rewrite bounce unix - - y - 0 bounce defer unix - - y - 0 bounce trace unix - - y - 0 bounce verify unix - - y - 1 verify flush unix n - y 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - y - - smtp relay unix - - y - - smtp -o syslog_name=postfix/$service_name showq unix n - y - - showq error unix - - y - - error retry unix - - y - - error discard unix - - y - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - y - - lmtp anvil unix - - y - 1 anvil scache unix - - y - 1 scache postlog unix-dgram n - n - 1 postlogd # ==================================================================== # # maildrop. See the Postfix MAILDROP_README file for details. # Also specify in main.cf: maildrop_destination_recipient_limit=1 # maildrop unix - n n - - pipe flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient} # # ==================================================================== # uucp unix - n n - - pipe flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) # # Other external delivery methods. # ifmail unix - n n - - pipe flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) bsmtp unix - n n - - pipe flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient scalemail-backend unix - n n - 2 pipe flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension} mailman unix - n n - - pipe flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user} dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient} policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf # spamassassin unix - n n - - pipe # user=spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
Create the virtual mailbox postfix entries:
cd /etc/postfix [Replace MAILUSER_PASSWORD with your own mailuser database password - this is visible plain text, and not encrypted] vi mysql-virtual-mailbox-domains.cf user = mailuser password = MAILUSER_PASSWORD hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_domains WHERE name='%s' [save and exit] vi mysql-virtual-mailbox-maps.cf user = mailuser password = MAILUSER_PASSWORD hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_users WHERE email='%s' [save and exit] vi mysql-virtual-alias-maps.cf user = mailuser password = MAILUSER_PASSWORD hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM virtual_aliases WHERE source='%s' [save and exit] vi mysql-virtual-email2email.cf user = mailuser password = MAILUSER_PASSWORD hosts = 127.0.0.1 dbname = mailserver query = SELECT email FROM virtual_users WHERE email='%s' [save and exit]
The above file permissions should be okay. e.g. -rw-r–r– 1 root root 143 Sep 26 10:17 mysql-virtual-alias-maps.cf
We should not need to alter /etc/postfix permissions. If you get errors, you can run “postfix set permissions” -> good to keep this in your install notes.
We will configure dovecot before we restart and test services.
[We are of course logged in with root permission # in the shell] cd /etc/dovecot cp -p /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig cp -p /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.orig cp -p /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig cp -p /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext.orig cp -p /etc/dovecot/conf.d/10-master.conf /etc/dovecot/conf.d/10-master.conf.orig cp -p /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.orig [I am only showing the uncommented lines. Replace with your own domain name:] vi /etc/dovecot/dovecot.conf import_environment = $import_environment PR_SET_DUMPABLE=1 !include_try /usr/share/dovecot/protocols.d/*.protocol protocols = imap lmtp postmaster_address = postmaster at DOMAIN.COM listen = *, :: dict { #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext } !include conf.d/*.conf !include_try local.conf [save and exit] vi /etc/dovecot/conf.d/10-mail.conf # mail_location = mbox:~/mail:INBOX=/var/mail/%u mail_location = maildir:/var/mail/vhosts/%d/%n/ mail_privileged_group = mail [Save and Exit] [Make the vhosts email directory for yuor primary email address and domain: e.g. /var/mail/vhosts/snotbat.com/fred] cd /var/mail mkdir vhosts mkdir domain.com cd domain.com mkdir PRIMARY_USER groupadd -g 5000 vmail useradd -g vmail -u 5000 vmail -d /var/mail cd /var/mail sudo chown -R vmail:vmail /var/mail chown vmail vhosts;chgrp vmail vhosts For user, I had: drwx--S--- 16 vmail vmail 4096 Sep 26 09:15 info (for info@.....) but while installing for this article, I had drwxr-sr-x 2 vmail vmail 4096 Sep 26 14:24 laurie cd /etc/dovecot/conf.d /etc/dovecot/conf.d/10-auth.conf disable_plaintext_auth = yes auth_mechanisms = plain login !include auth-system.conf.ext !include auth-sql.conf.ext [save and exit] vi 20-imap.conf mail_location = maildir:/var/mail/vhosts/%d/%n/ [save and exit]
More configurations:
cd /etc/dovecot/conf.d vi /etc/dovecot/conf.d/auth-sql.conf.ext passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } #userdb { # driver = prefetch #} # userdb { # driver = sql # args = /etc/dovecot/dovecot-sql.conf.ext # } userdb { driver = static #args = uid=vmail gid=vmail home=/var/vmail/%u args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n } [save and exit] cd /etc/dovecot [Use your database mailuser password below - not the encrypted version, and the password_query is shown exactly as needed] vi /etc/dovecot/dovecot-sql.conf.ext driver = mysql connect = host=127.0.0.1 dbname=mailserver user=mailuser password=MAILUSER_PASSWORD default_pass_scheme = SHA512-CRYPT password_query = SELECT email as user, password FROM virtual_users WHERE email='%u'; [save and exit] cd /etc chown -R vmail:dovecot /etc/dovecot chmod -R o-rwx /etc/dovecot ls -l|grep dovecot drwxr-x--- 4 vmail dovecot 4096 Sep 26 14:47 dovecot
We now get into the dovecot services configurations.
cd /etc/dovecot/conf.d [Be careful to configure this correctly. Obviously the ... dots are not to be in the config file. You don't have to enable POP3 if not using pop. I see no reason to use it.] vi /etc/dovecot/conf.d/10-master.conf ... service imap-login { inet_listener imap { port = 0 } inet_listener imaps { port = 993 ssl = yes } ... } ... service pop3-login { inet_listener pop3 { port = 0 } inet_listener pop3s { port = 995 ssl = yes } } ... service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { #mode = 0666i mode = 0600 user = postfix group = postfix } ... } service auth { ... unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } # Postfix smtp-auth unix_listener auth-userdb { mode = 0600 user = vmail } # Auth process is run as this user. # user = $default_internal_user user = dovecot } ... service auth-worker { # Auth worker process is run as root by default, so that it can access # /etc/shadow. If this isn't necessary, the user should be changed to # $default_internal_user. #user = root user = vmail } [save and exit] [If using Let's Encrypt/certbot, you would use /etc/letsencrypt/live/domain.com/fullchain.pem and private.pem. Use your own domain name and SSL file name for other certificates.] vi /etc/dovecot/conf.d/10-ssl.conf ssl = required # ssl_cert = </etc/dovecot/private/dovecot.pem # ssl_key = </etc/dovecot/private/dovecot.key ssl_cert = </etc/ssl/certs/domain_com.crt ssl_key = </etc/ssl/private/domain_com.key [save and exit] The certificate names and path must exist and be the same in the postfix main.cf file. [We have postfix logging already in /var/log/mail.log. We can always benefit by using a terminal shell with "tail -f /var/log/mail.log" for testing. You can empty it with ":> /var/log/mail.log" to see only new entries] [We will now set dovecot logging. REMEMBER - you have to revisit main.cf and 10-logging.conf to turn these off/on or to change the level of details in the logged events.] vi /var/log/dovecot.log [Enter a bank line] [save and exit] cd /etc/dovecot/conf.d vi /etc/dovecot/conf.d/10-logging.conf mail_debug = yes log_path = /var/log/dovecot.log info_log_path = /var/log/dovecot.log debug_log_path = /var/log/dovecot.log [save and exit] [If you want more extensive logging, add: (it is a bit much)] auth_verbose = yes auth_verbose_passwords = no auth_debug = yes auth_debug_passwords = yes verbose_ssl = yes [Install test emailing softeware:] apt-get install mailutils systemctl restart postfix systemctl restart dovecot systemctl status -l postfix systemctl status -l dovecot [If you have an error, the status command will let you now what you mispelt or did wrong.] [We do not enable the services just yet as we have no blacklisting or open port security installed as yet. If you read my articles with the blacklist.sh and ports, that can help. I'll show the main shell script for open ports here:] cd /home/ec2-user (or whatever is your shell login with root access) vi ports.sh #!/bin/sh iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 2 -j DROP iptables -A INPUT -p tcp --syn --dport 587 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 993 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 465 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 143 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 25 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 53 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 7000 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 8000 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 9443 -m connlimit --connlimit-above 5 -j DROP iptables -A INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 5 -j DROP iptables -L -vn exit [save and exit] chmod 777 /home/ec2-user/ports.sh ./ports.sh [This script can clean it all out so yu can start over again. These settings will be absent after a reboot.] vi firewall.sh #!/bin/bash iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT exit [save and exit] chmod 777 firewall.sh
Now we send a test email. I have given some dummy names. Use your vhosts email address and domain, and send to another email service like gmail.
You must have the DNS records set as per the early part of this article, and gmail does require the AAAA record with Linode instances.
[You can set up additional terminal shells and use the tail -f command rather than "cat" as shown below] [From 2 other terminals:] tail -f /var/log/mail.log tail -f /var/log/dovecot.log echo "Email body text" | sudo mail -s "Email subject line" ME@gmail.com -aFrom:fred@snotbat.com
This test email takes a while for Gmail to register it. Future emails are quicker. The email has sparse content as we do not have our opendkim set up as yet.
NOTE: At this stage spf is not working. The next installation steps will do this.
I added a Firefox recommended configuration /etc/dovecot/conf.d:
vi 15-ssl-intermediate.conf ## ## Dovecot SSL settings with Intermediate compatibility ## Follows Mozilla's Security/Server Side TLS guidelines ## https://wiki.mozilla.org/Security/Server_Side_TLS ## ## ## Optionial: ## Disable 3DES ciphersuites to prevent CVE-2016-2183 ## by appending ":!3DES" to the ssl_cipher_list ## Disable TLSv1 for PCI compliance as of June 30, 2018 ## by appending " !TLSv1" to the ssl_protocols for versions < 2.3 ## by changing ssl_min_protocol to "TLSv1.1" for versions >= 2.3 ## # ciphersuites ssl_cipher_list = ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS # for dovecot >= 2.3 # ssl_min_protocol = TLSv1 # for dovecot >= 2.3 ssl_min_protocol = TLSv1.2 # DH parameter size # for dovecot < 2.3 #ssl_dh_parameters_length = 2048 # for dovecot >= 2.3 # ssl_dh =
There are other settings that are no longer used in the above file. You can check for errors even when dovecot starts ok, by looking at the dovecot.log file.
You have to run postfix set-permissions to see errors.
*** IMPORTANT ***
MS New Outlook will not show all headers. You have to use another client, or use iCloud on the web to view headers.
These will show if the opendkim is working.
*****************
The final main.cf file is shown here where there were some errors to comment out.
These errors stopped port 587 working.
You must use:
netstat -tulpn|grep 587
tcp 0 0 0.0.0.0:587 0.0.0.0:* LISTEN 11023/master
tcp6 0 0 :::587 :::* LISTEN 11023/master
to verify port 587 is open. If it should be, but Amazon blocks it, take it out of sandbox mode for your region.The master.cf file:
/etc/postfix/main.cf: # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. #myorigin = /etc/mailname # smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) smtpd_banner = Lec2-54-79-232-121.ap-southeast-2.compute.amazonaws.com ESMTP shawlw.me postfix biff = no # appending .domain is the MUA's job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings #delay_warning_time = 4h readme_directory = no # See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on # fresh installs. compatibility_level = 3.6 # TLS parameters - USE YOUR OWN SSL CERTIFICATE AND DIRECTORY LOCATIONS. E.G. /etc/letsecnrypt/live/domain.com/fullchain.pem and ..../privkey.pem, or /etc/ssl/certs/... and /etc/ssl/private/... smtpd_tls_cert_file=/etc/ssl/certs/shawlw_me.crt smtpd_tls_key_file=/etc/ssl/private/shawlw_me.key smtpd_use_tls=yes smtpd_tls_auth_only = yes smtp_tls_security_level = may smtpd_tls_security_level = may smtpd_sasl_security_options = noanonymous, noplaintext smtpd_sasl_tls_security_options = noanonymous # Authentication smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. # Restrictions smtpd_helo_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unlisted_recipient, reject_unauth_destination, check_policy_service unix:private/policyd-spf smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_sender, reject_unknown_sender_domain smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. myhostname = shawlw.me alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydomain = shawlw.me myorigin = $mydomain mydestination = localhost # relayhost = # mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 # mailbox_size_limit = 0 # recipient_delimiter = + inet_interfaces = all inet_protocols = all # Handing off local delivery to Dovecot's LMTP, and telling it where to store mail virtual_transport = lmtp:unix:private/dovecot-lmtp # Virtual domains, users, and aliases virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf, mysql:/etc/postfix/mysql-virtual-email2email.cf # Even more Restrictions and MTA params disable_vrfy_command = yes strict_rfc821_envelopes = yes #smtpd_etrn_restrictions = reject #smtpd_reject_unlisted_sender = yes #smtpd_reject_unlisted_recipient = yes smtpd_delay_reject = yes smtpd_helo_required = yes smtp_always_send_ehlo = yes #smtpd_hard_error_limit = 1 smtpd_timeout = 30s smtp_helo_timeout = 15s smtp_rcpt_timeout = 15s smtpd_recipient_limit = 40 minimal_backoff_time = 180s maximal_backoff_time = 3h # Reply Rejection Codes invalid_hostname_reject_code = 550 non_fqdn_reject_code = 550 unknown_address_reject_code = 550 unknown_client_reject_code = 550 unknown_hostname_reject_code = 550 unverified_recipient_reject_code = 550 unverified_sender_reject_code = 550 # original smtp_tls_CApath=/etc/ssl/certs smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases # myorigin = /etc/mailname. WE DO NOT USE THIS # mydestination = $myhostname, DOMAIN.COM, localhost.localdomain, localhost.localdomain, localhost WE DO NOT USE THIS relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + # opendkim # 3600 line does not work: # policyd-spf_time_limit = 3600 # Milter configuration # OpenDKIM -> WE WILL UNCOMMENT THESE LINES AFTER OPENDKIM IS INSTALLED AND READY FOR TESTING milter_default_action = accept milter_protocol = 6 # smtpd_milters = inet:localhost:8891 smtpd_milters = local:opendkim/opendkim.sock non-smtpd_milters = local:opendkim/opendkim.sock # non_smtpd_milters = $smtpd_milters maillog_file = /var/log/mail.log smtpd_tls_loglevel = 3 smtp_tls_loglevel = 3 # mail_home = WE DO NOT USE THIS VARIABLE
master.cf:
/etc/postfix/master.cf # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master" or # on-line: http://www.postfix.org/master.5.html). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (no) (never) (100) # ========================================================================== smtp inet n - y - - smtpd # -o content_filter=spamassassin submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_reject_unlisted_recipient=no -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING # -o content_filter=spamassassin smtps inet n - - - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING # -o content_filter=spamassassin pickup unix n - y 60 1 pickup cleanup unix n - y - 0 cleanup qmgr unix n - n 300 1 qmgr #qmgr unix n - n 300 1 oqmgr tlsmgr unix - - y 1000? 1 tlsmgr rewrite unix - - y - - trivial-rewrite bounce unix - - y - 0 bounce defer unix - - y - 0 bounce trace unix - - y - 0 bounce verify unix - - y - 1 verify flush unix n - y 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - y - - smtp relay unix - - y - - smtp -o syslog_name=postfix/$service_name showq unix n - y - - showq error unix - - y - - error retry unix - - y - - error discard unix - - y - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - y - - lmtp anvil unix - - y - 1 anvil scache unix - - y - 1 scache postlog unix-dgram n - n - 1 postlogd # ==================================================================== # # maildrop. See the Postfix MAILDROP_README file for details. # Also specify in main.cf: maildrop_destination_recipient_limit=1 # maildrop unix - n n - - pipe flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient} # # ==================================================================== # uucp unix - n n - - pipe flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) # # Other external delivery methods. # ifmail unix - n n - - pipe flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) bsmtp unix - n n - - pipe flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient scalemail-backend unix - n n - 2 pipe flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension} mailman unix - n n - - pipe flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user} dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient} policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf # spamassassin unix - n n - - pipe # user=spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
Other things to check:
The previous listing of main.cf and master.cf should be ok. /etc/dovecot/dovecot.conf already has protocols but still put in the protocols line: # Enable installed protocols !include_try /usr/share/dovecot/protocols.d/*.protocol protocols = imap lmtp Install the opendkim software: apt-get install opendkim opendkim-tools postfix-policyd-spf-python postfix-pcre adduser postfix opendkim This will already be configured: vi /etc/postfix/master.cf policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf [We should also previously have added to the last two lines of the main.cf "smtpd_recipient_restrictions =" stanza:] reject_unauth_destination, check_policy_service unix:private/policyd-spf main.cf: (we do not do sieve or spamassassin at this point) milter_default_action = accept milter_protocol = 6 # NOT USING JUST YET - IT MAY WORK IF /etc/opendkin.conf has the same line for Socket: smtpd_milters = inet:localhost:8891 AND non-smtpd_milters = inet:localhost:8891 smtpd_milters = local:opendkim/opendkim.sock non-smtpd_milters = local:opendkim/opendkim.sock do the opendkim.conf file as per my article. No TrustedChain. /etc/default/opendkim - comment out everything: RUNDIR=/run/opendkim SOCKET=local:$RUNDIR/opendkim.sock USER=opendkim GROUP=opendkim PIDFILE=$RUNDIR/$NAME.pid EXTRAAFTER= /etc/opendkim.conf: Syslog yes SyslogSuccess yes LogWhy no Canonicalization relaxed/simple Mode sv SubDomains no OversignHeaders From Domain YOUR_DOMAIN (e.g. snotbat.com) KeyFile /etc/opendkim/keys/default.private KeyTable /etc/opendkim/key.table SigningTable refile:/etc/opendkim/signing.table ExternalIgnoreList /etc/opendkim/trusted.hosts InternalHosts /etc/opendkim/trusted.hosts AutoRestart yes AutoRestartRate 10/1M Background yes DNSTimeout 5 SignatureAlgorithm rsa-sha256 # Socket inet:8891@localhost Socket local:/var/spool/postfix/opendkim/opendkim.sock PidFile /run/opendkim/opendkim.pid Nameservers 8.8.8.8,1.1.1.1 #Selector 2020 #KeyFile /etc/dkimkeys/example.private UserID opendkim UMask 002 #Socket local:/run/opendkim/opendkim.sock #Socket inet:8891@localhost #Socket inet:8891 #Socket local:/var/spool/postfix/opendkim/opendkim.sock #PidFile /run/opendkim/opendkim.pid # Hosts for which to sign rather than verify, default is 127.0.0.1. See the # OPERATION section of opendkim(8) for more information. #InternalHosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12 # The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided # by the package dns-root-data. # TrustAnchorFile /usr/share/dns/root.key #Nameservers 127.0.0.1 [save and exit] create /etc/opendkim/keys: drwx------ 2 opendkim opendkim 4096 Oct 25 14:09 keys create keys.table signing trusted and so on as per my article. NO: vi /etc/systemd/system/multi-user.target.wants/opendkim.service PIDFile=/var/run/opendkim/opendkim.pid I don't think this would work so not done. Make /var/spool/postfix/opendkim drwxrwxr-- 2 opendkim opendkim 4096 Oct 25 14:13 opendkim I don't think drwxr-xr-x 2 opendkim postfix 4096 Sep 20 10:35 opendkim is correct ? cd /etc/opendkim/keys opendkim-genkey -b 2048 -h rsa-sha256 -r -s default -d domain.com -v [e.g. opendkim-genkey -b 2048 -h rsa-sha256 -r -s default -d snotbat.com -v] ls cat default.key default._domainkey IN TXT ( "v=DKIM1; h=rsa-sha256; k=rsa; s=email; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1P1dFPOOKzQp14icgeNbl7WeqUiks7JZUpxTbNz8ofV3O8iKb6xX3HFXcWhyeAi1OyYx0WhaLX0H8mhZGXomwawFJe9O3JbWN0LM+Z1GUG6i+K0El4qUrt8Ox2Gk0K377A07mXjjdKwJKQs6siIygbxpzbojaPYKDHz4PaYORann27vukObfp1nEXrdKtiOmUpNoBqqW6d5hk/" "iM6tozBa2AzzBoZ9rNz+ysl+ttn AND SO ON...... DKIM key default for domai Add to the mx record with the correct p= value. systemctl daemon-reload systemctl restart postfix systemctl restart dovecot systemctl restart opendkim PORT 587 is not knocked out ! ??? netstat -tulpn|grep 587 fix main.cf - remove duplicate inet_interfaces and no such thing as inet_protocols so remove it. postfix set-permissions can show problems and /var/log/mail.log Should fix port 587. Check with netstat -tulpn|grep 587 chown -R opendkim:opendkim /etc/opendkim chmod go-rw /etc/opendkim/keys I don't think we use chown opendkim:postfix /var/spool/postfix/opendkim as postfix is added to opendkim Recreate in old MS outlook, then New outlook Old Outlook will show the dkim, spf, dmarc headers working. iCloud Webmail can also show the headers fully. New Outlook will not (!)
WE HAVE COMPLETED THE BASIC DOVECOT SETUPS.
Next we look at opendkim, then the filtering/anti-virus/anti-spam setups.
Use https://www.linode.com/docs/guides/configure-spf-and-dkim-in-postfix-on-debian-9/ as your reference. I think the notes here will work though.
What we want is to use all the configs for chown, chgrp, chmod.
Otherwise we get errors from “journalctl –follow –unit postfix.service –unit opendkim.service” when we send emails.
Also, using the DNS Checker website, you should see port 993 open, but on Amazon AWS while in sandbox mode, port 587 will time out.
Then, here are the final configurations for sockets and various config files you need to change. This is why you must return back to this section of my notes to tidy up the configs.
vi /etc/postfix/main.cf # policyd-spf_time_limit = 3600 milter_default_action = accept milter_protocol = 6 smtpd_milters = local:opendkim/opendkim.sock non_smtpd_milters = local:opendkim/opendkim.sock [save and exit] The two entries for masgter.cf -o milter_macro_daemon_name=ORIGINATING must be uncomments and remember the two shite spaces before -o cd /etc/default [everything to be commented except the following:] vi opendkim RUNDIR=/run/opendkim USER=opendkim GROUP=opendkim PIDFILE=$RUNDIR/$NAME.pid EXTRAAFTER= [save and exit] cd /etc [Everything else to be commented out:] vi opendkim.conf Syslog yes SyslogSuccess yes LogWhy no Mode sv OversignHeaders From Domain YOUR_DOMAIN (e.g. domain.com) KeyFile /etc/opendkim/keys/default.private KeyTable /etc/opendkim/key.table SigningTable refile:/etc/opendkim/signing.table ExternalIgnoreList /etc/opendkim/trusted.hosts InternalHosts /etc/opendkim/trusted.hosts AutoRestart yes AutoRestartRate 10/1M Background yes DNSTimeout 5 SignatureAlgorithm rsa-sha256 Socket local:/var/spool/postfix/opendkim/opendkim.sock PidFile /run/opendkim/opendkim.pid Nameservers 8.8.8.8,1.1.1.1 UserID opendkim UMask 002 PidFile /run/opendkim/opendkim.pid [saave and exit]
chmod u=rw,go=r /etc/opendkim.conf chown -R opendkim:opendkim /etc/opendkim chmod go-rw /etc/opendkim/keys chown -R opendkim:opendkim /etc/opendkim chmod -R go-rwx /etc/opendkim/keys cd /etc chown -R opendkim:opendkim /etc/opendkim chmod -R go-rw /etc/opendkim/keys chown opendkim:postfix /var/spool/postfix/opendkim postfix set-permissions
Good idea to reboot, or at least restart opendkim, postfix, dovecot.
When sending an email from an existing account to your server, use two terminal shells with:
cd /var/log tail -f mail.log AND on 2nd terminal journalctl --follow --unit postfix.service --unit opendkim.service
You can check the service file is ok:
cat /usr/lib/systemd/system/opendkim.service [Unit] Description=OpenDKIM Milter Documentation=man:opendkim(8) man:opendkim.conf(5) man:opendkim-lua(3) man:opendkim-genkey(8) man:opendkim-genzone(8) man:opendkim-testkey(8) http://www.opendkim.org/docs.html After=network-online.target nss-lookup.target Wants=network-online.target [Service] Type=forking PIDFile=/run/opendkim/opendkim.pid ExecStart=/usr/sbin/opendkim ExecReload=/bin/kill -USR1 $MAINPID Restart=on-failure [Install] WantedBy=multi-user.target
I found too many issues trying to install on Linux 2023. The above is all on Debian 12 ARM.
/etc/postfix/main.cf has the line “policyd-spf_time_limit = 3600” but we have not installed software for it.
Also make sure your DNS Records are configured as shown at the top of this article.
apt-get install opendkim opendkim-tools postfix-policyd-spf-python postfix-pcre adduser postfix opendkim [Add the following to the end of master.cf and have two spaces before user=policyd-spf IF NOT ALREADY ADDED - it should be there from previous steps] vi /etc/postfix/master.cf policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf [We should also previously have added to the last two lines of the main.cf "smtpd_recipient_restrictions =" stanza:] reject_unauth_destination, check_policy_service unix:private/policyd-spf systemctl restart postfix systemctl restart dovecot [Send an email to an existing address you own, using your new primary user@domain.com address:] echo "Email body text" | sudo mail -s "Email subject line" me@myemail.com -aFrom:info@domain.com [This is a good point at which to tail -f the mail.log and dovecot.log files. Check your Inbox, view the raw code, and you should see:] Received-SPF: Pass
Important note: we see some documentation saying to use /var/run/opendkim/opendkim.pid as the PidFile or PIDFile.
/var/run is a softlink to /run, so do not configure this way. Only use /run/opendkim/opendkim.pid.
Opendkim and Milter will not work unless you ensure these couple of steps:
THESE MUST BE CHECKED AFTER THE CONFIGURATIONS BELOW cd /etc/opendkim/keys chown opendkim:opendkim * ls -l Make sure /run/opendkim/opendkim.pid is used in: /etc/systemd/system/multi-user.target.wants/opendkim.service which I think is a soft link to /usr/lib/systemd/system/opendkim.service -> make sure it uses /run and not /var/run cd cd /run/opendkim chown opendkim:opendkim * ls -l and check /etc/opendkim.conf uses: PidFile /run/opendkim/opendkim.pid I believe -rw-r--r-- 1 root root 5 Sep 28 14:08 opendkim.pid can remain as root. IF EMAILS GET no DKIM signing, it means milter is not being executed.
If the first attempt to a gmail address did not work, but the logs show it should have, send again. Google may be determining if a new address is safe.
Your gmail raw source code should show: “received-spf: pass (google.com: domain of ….”
Next – opendkim. This was messy, but I got there in the end.
vi /etc/default/opendkim and comment out everything. We want none of it. e.g. RUNDIR, SOCKET, USER – comment all of these out.
cd /etc cp -p opendkim.conf opendkim.conf.o [Uncomment and modfiy various values as shown, and use your domain name. Recall, I am not doing mail.domain.com configurations in this article.] [We use 8891 as shown for the socket, and /var/run in the PidFile for this Debian12 installation] vi /etc/opendkim.conf LogWhy no Mode sv SubDomains no Domain domain.com KeyFile /etc/opendkim/keys/default.private KeyTable /etc/opendkim/key.table SigningTable refile:/etc/opendkim/signing.table ExternalIgnoreList /etc/opendkim/trusted.hosts InternalHosts /etc/opendkim/trusted.hosts AutoRestart yes AutoRestartRate 10/1M Background yes DNSTimeout 5 SignatureAlgorithm rsa-sha256 Socket inet:8891@localhost PidFile /run/opendkim/opendkim.pid Nameservers 8.8.8.8,1.1.1.1 [save and exit] [You need to create a directory /etc/opendkim, with the permissions shown here...] cd /etc mkdir opendkim chmod 755 opendkim; chown opendkim opendkim; chgrp opendkim opendkim; drwx------ 2 opendkim opendkim 4096 Sep 27 10:20 dkimkeys drwxr-xr- 2 opendkim opendkim 4096 Sep 27 11:52 opendkim -rw-r--r-- 1 root root 2529 Sep 27 11:50 opendkim.conf -rw-r--r-- 1 root root 2104 Nov 26 2023 opendkim.conf.o Under /etc/opendkim, create these: drwx------ 2 opendkim opendkim 4096 Sep 20 10:23 keys -rw-r--r-- 1 opendkim opendkim 67 Sep 20 10:22 key.table -rw-r--r-- 1 opendkim opendkim 26 Sep 20 10:21 signing.table -rw-r--r-- 1 opendkim opendkim 58 Sep 20 10:22 trusted.hosts [IMPORTANT: the word default is what we will use for dmarc values, e.g. default._domainkey.domain.com and the default.private key in the DNS Records. You can have other names though if it matches the tables, DNS and key names you create] vi key.table default domain.com:default:/etc/opendkim/keys/default.private [save and exit] [Again, use your own domain name, and the use of "default" as mentioned above:] vi signing.table *@domain.com default [save and exit] [Of course, if you work out how to do multiple domains, or mail.domain.com, these values may differ: (use your own domain name)] vi trusted.hosts 127.0.0.1 ::1 localhost domain.com *.domain.com [save and exit]
We will create default.private and default.txt under /etc/opendkim/keys. One could use /etc/opendkim/…./keys or anything really, so long as it is referenced correctly elsewhere, which you will see during these configurations.
[Do this fix:] vi /etc/systemd/system/multi-user.target.wants/opendkim.service PIDFile=/var/run/opendkim/opendkim.pid [save and exit] [I'm not sure why I did the next step...] mkdir /var/spool/postfix/opendkim [Make permissions:] drwxr-xr-x 2 opendkim postfix 4096 Sep 20 10:35 opendkim [Create the default keys for your domain.com name - use your own domain.] cd /etc/opendkim/keys pwd opendkim-genkey -b 2048 -h rsa-sha256 -r -s default -d domain.com -v [e.g. opendkim-genkey -b 2048 -h rsa-sha256 -r -s default -d snotbat.com -v] ls cat default.key default._domainkey IN TXT ( "v=DKIM1; h=rsa-sha256; k=rsa; s=email; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1P1dFPOOKzQp14icgeNbl7WeqUiks7JZUpxTbNz8ofV3O8iKb6xX3HFXcWhyeAi1OyYx0WhaLX0H8mhZGXomwawFJe9O3JbWN0LM+Z1GUG6i+K0El4qUrt8Ox2Gk0K377A07mXjjdKwJKQs6siIygbxpzbojaPYKDHz4PaYORann27vukObfp1nEXrdKtiOmUpNoBqqW6d5hk/" "iM6tozBa2AzzBoZ9rNz+ysl+ttn AND SO ON...... DKIM key default for domain.com
In the above key, I have shown domain.com, but it must be your own domain.
We take this content and place in your DNS record…
[Add your own record, e.g. default._domainkey.snotbat.com TXT and so on. There must be no double quotes in the p=section, but some registrars require you to make multiple lines for the p value. See your own registrar's instructions, and it they need quotes.] [Sample:] default._domainkey.domain.com TXT v=DKIM1; h=sha256; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1P1dFPOOKzQp14icgeNblasdjhss7JZUpxTbNz8ofV3O8iKb6xX3HFXcWhyeAi1OyYx0WhaLX0H8mhZGXomwawFJe9O3JbWN0LM+Z1GUG6i+K0El4qUrt8Ox2Gk0K377A07mXjjdKwJKQs6siIygbxpzbojaPYKDHz4PaYORann27vukObf AND SO ON...
Now uncomment the lines we previously used in /etc/postfix/main.cf
milter_default_action = accept milter_protocol = 6 smtpd_milters = inet:localhost:8891 non_smtpd_milters = $smtpd_milters
Uncomment the lines we placed previously in /etc/postfix/master.cf
[Under submission inet n - y - - smtpd, and use twp space characters before it:] -o milter_macro_daemon_name=ORIGINATING [Under smtps inet n - - - - smtpd:] -o milter_macro_daemon_name=ORIGINATING [We still have spamassasin commented out] [Save and exit] [Now run these commands...] systemctl daemon-reload systemctl restart postfix systemctl restart dovecot systemctl restart opendkim ps -ef | grep opendkim opendkim 25872 1 0 12:31 ? 00:00:00 /usr/sbin/opendkim opendkim 25873 25872 0 12:31 ? 00:00:00 /usr/sbin/opendkim systemctl status -l opendkim [Test your opendkim key: use your own domain name and dmarc id - in this case I have configured for "default._domainkey.domain.com] opendkim-testkey -d domain.com -s default default.private -vvv opendkim-testkey: using default configfile /etc/opendkim.conf opendkim-testkey: key loaded from /etc/opendkim/keys/default.private opendkim-testkey: checking key 'default._domainkey.shawlw.me' opendkim-testkey: key not secure opendkim-testkey: key OK [The key not secure above is meant to be this way.]
We will not be too concerned with systemctl status -l opendkim just yet, but do run the command.
** FIXES **
Below I mentioned user_prefs to stop DNS warnings in spamd.log, but it seems it is to do with nameservers 8.8.8.8 and 1.1.1.1 and so on.
So I tried removing all of them, and then no error message, and removing 1.1.1.1 which I think is Cloudflare, and was ok.
WE HAVE COMPLETED THE SPF DKIM DMARC SETUPS.
Next we configure basic filtering/anti-virus/anti-spam setups – we use both the dovecot spam and the spamassassin setups.
[Install sieve filters] apt-get install dovecot-sieve apt-get install dovecot-managesieved apt install dovecot-antispam [We will create a simplistci default:] cd /var/mail/vhosts vi default.sieve require ["fileinto"]; require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Junk"; } [save and exit] ls -l -rw-r--r-- 1 vmail vmail 107 Sep 21 11:44 default.sieve [You always need to compile sieve...] sievec ./default.sieve ls -l -rw-r--r-- 1 root vmail 256 Oct 2 09:19 default.svbin [Create sieve rules under the user's email. Replace dmarc@domain.com with your own. There are too many rules to go over this. See internet forums. Note the logic needs to know when to stop processing, or you get multiples of the same email in yur folders. You will be able to block bad users. I have not looked into IP address blocking, but your blacklisted ports in nginx can take of that.] cd /var/mail/vhosts/domain.com/USER vi .dovecot.sieve require ["fileinto", "envelope", "subaddress", "comparator-i;ascii-numeric","relational", "spamtestplus", "virustest"]; /* Other messages get filed into INBOX */ /* Not scanned ? */ if virustest :value "eq" :comparator "i;ascii-numeric" "0" { /* fileinto "Unclassified"; */ /* fileinto "Quarantine"; */ /* stop; */ /* Infected with high probability (value range in 1-5) */ } elsif virustest :value "eq" :comparator "i;ascii-numeric" "4" { /* Quarantine it in special folder (still somewhat dangerous) */ fileinto "Quarantine"; stop; /* Definitely infected */ } elsif virustest :value "eq" :comparator "i;ascii-numeric" "5" { /* Just get rid of it */ /* discard; */ fileinto "Quarantine"; stop; } if header :contains "X-Spam-Flag" "YES" { fileinto "Junk"; stop; } if header :contains "X-Spam-Level" "**********" { discard; stop; } if address :is "from" "dmarc@mydomain.com" { discard; /* fileinto "History"; */ stop; } if envelope :detail "to" "spam"{ fileinto "Junk"; stop; } /* If the spamtest fails for some reason, e.g. spam header is missing, file * file it in a special folder. */ if spamtest :value "eq" :comparator "i;ascii-numeric" "0" { /* fileinto "Unclassified"; */ fileinto "Inbox"; stop; /* If the spamtest score (in the range 1-10) is larger than or equal to 3, * file it into the spam folder: */ } elsif spamtest :value "ge" :comparator "i;ascii-numeric" "9" { fileinto "Junk"; stop; /* For more fine-grained score evaluation, the :percent tag can be used. The * following rule discards all messages with a percent score * (relative to maximum) of more than 85 %: */ } elsif spamtest :value "gt" :comparator "i;ascii-numeric" :percent "95" { /* discard; */ fileinto "Quarantine"; stop; } [save and exit] At this point it will not compile, as we have other software to install and configure. We will need to do it later with this command: sievec ./.dovecot.sieve ls -la
Let’s configure some files:
[Under the plugin section, comment out and add the lines as shown:] cd /etc/dovecot/conf.d vi 90-sieve.conf sieve = ~/.dovecot.sieve sieve_global_dir = /var/mail/vhosts # sieve_global_path = /var/mail/vhosts --> did not work, had to use _dir sieve_dir = ~/sieve sieve_default = /var/mail/vhosts/default.sieve # sieve = file:~/sieve;active=~/.dovecot.sieve [Then at the bottom of the file before the last } bracket, add these lines: there are various versions of what to do here - you'd have to research it. These confis are for dovecot's virust and spam testing. We will also install spamassassin as first line of defence further below.] sieve_extensions = +spamtest +spamtestplus +virustest sieve_spamtest_status_type = score sieve_spamtest_status_header = \ X-Spam-Score: score=(-?[[:digit:]]+\.[[:digit:]]).* sieve_spamtest_max_header = \ X-Spam-Score: score=-?[[:digit:]]+\.[[:digit:]] required=([[:digit:]]+\.[[:digit:]]) sieve_virustest_status_type = text sieve_virustest_status_header = X-Virus-Scan: Found to be (.+)\. sieve_virustest_text_value1 = clean sieve_virustest_text_value5 = infected [save and exit] [Add the sieve plugin in the lmtp stanza] [antispam below is if dovecot-antivirus is installed - perhaps can go in 20-imap.conf if not here] vi 20-lmtp.conf protocol lmtp { mail_plugins = $mail_plugins sieve antispam } [save and exit] [We previously added logging to 10-logging.conf] [Add postmaster to 15-lda.conf. Use your won domain. We have to do this.] vi 15-lda.conf postmaster_address = postmaster@domain.com [save and exit] [We need to create an sieve directory under the user's email location] cd /var/mail/vhosts/domain.com/USER mkdir sieve chmod 2777 sieve chgrp vmail sieve ls -l drwxrwsrwx 2 root vmail 4096 Sep 21 10:34 sieve
We will install spamassassin.
After installing spamassassin, some ***** CRITICAL FIXES ****
Remove pyzor: apt remove pyzor (Otherwise errors on spamd.log) Add "imap4flags" to the required list in .dovecot.sieve and recompile it. Then you can use: addflag "\\Seen"; for emails you want marked as read. /usr/share/spamassasin: 60_whitelist cannot use wildcards. Not sure yyet how we block domains??? local.cf: use: required_score 10.0 Do the same for /etc/spamassassin/local.cf vi /etc/dovecot/conf.d/90-sieve.conf In the plugin section: # sieve = file:~/sieve;active=~/.dovecot.sieve sieve_extensions = +spamtest +spamtestplus +virustest +virustestplus sieve = ~/.dovecot.sieve sieve_global_path = /var/mail/vhosts sieve_dir = ~/sieve sieve_default = /var/mail/vhosts/default.sieve Before the last curly bracket at the end: (I'm not sure why we have a difference to .dovecot.sieve as yet) sieve_extensions = +spamtest +spamtestplus +virustest sieve_spamtest_status_type = score sieve_spamtest_status_header = \ X-Spam-Score: score=(-?[[:digit:]]+\.[[:digit:]]).* sieve_spamtest_max_header = \ X-Spam-Score: score=-?[[:digit:]]+\.[[:digit:]] required=([[:digit:]]+\.[[:digit:]]) sieve_virustest_status_type = text sieve_virustest_status_header = X-Virus-Scan: Found to be (.+)\. sieve_virustest_text_value1 = clean sieve_virustest_text_value5 = infected # sieve_spamtest_max_value = 10.0 We can't use the sieve_spamtest_max_value = 10.0 line with the above configs. /etc/dovecot/conf.d/20-lmtp.conf: protocol lmtp { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins mail_plugins = $mail_plugins sieve #postmaster_address = postmaster@shawlw.me } I don't yet have the spamassasin cron timer file working. It is disabled by default.
apt insall libgssapi-perl razor pyzor libencode-detect-perl libgeoip2-perl libnet-patricia-perl libbsd-resource-perl apt-get --install-recommends install spamassassin [Check spamc is installed after this] groupadd spamd useradd -g spamd -s /bin/false -d /var/log/spamassassin spamd mkdir /var/log/spamassassin chown spamd:spamd /var/log/spamassassin vi /etc/default/spamd CRON=1 ENABLED=1 SAHOME="/var/log/spamassassin/" OPTIONS="--create-prefs --max-children=5 --helper-home-dir=/var/lib/spamassassin --username=spamd --syslog=/var/log/spamassassin/spamd.log" PIDFILE="/run/spamd.pid" [save and exit] [There are variations on the above] sudo mkdir /var/log/spamassassin && sudo chown spamd:spamd /var/log/spamassassin vi /etc/logrotate.d/spamassassin /var/log/spamassassin/spamd.log { copytruncate rotate 12 weekly compress missingok postrotate /bin/systemctl restart spamd.service > /dev/null endscript } [save and exit] [Change spamassassin to spamd:] vi /etc/cron.daily/spamassassin test -f /etc/default/spamd && . /etc/default/spamd [save and exit] [For convenience:] cd /var/log ln -s /var/log/spamassassin/spamd.log spamd.log ls -l systemctl restart spamd systemctl status -l spamd systemctl enable spamd [This takes a while"] sa-update :>/var/log/spamassassin/spamd.log systemctl restart spamd [Wait a moment...] cat /var/log/spamd.log [Append to the postfix master.cf file:] vi /etc/postfix/master.cf spamassassin unix - n n - - pipe user=spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient} [save and exit] [Change spam rating from 5 to 10.] cd /usr/share/spamassassin vi local.cf # required_score 5.0 required_score 10.0 [save and exit] [Examples of white and black listing, but also add the pyzor line as shown. This is a new file] vi 60_whitelist.cf # whitelist_auth me@gmail.com you@gmail.com # blacklist_from ..... .... use_pyzor 0 [save and exit] [Now we return to /var/mail/vhosts/domain.com/user:] sievec .dovecot.sieve [Restart all services and check the status with -l. Here is a helpful script:] vi /home/ec2-user/post.sh #!/bin/sh systemctl stop opendkim systemctl stop dovecot systemctl stop postfix systemctl stop spamd systemctl start opendkim systemctl start spamd systemctl start postfix systemctl start dovecot systemctl status -l opendkim > tmp.txt systemctl status -l postfix >> tmp.txt systemctl status -l dovecot >> tmp.txt systemctl status -l spamd >> tmp.txt cat tmp.txt exit [save and exit] chmod 777 /home/ec2-user/post.sh cd /home/ec2-user ./post.sh
After more use of spamassassin, it is /etc/spamassassin/local.cf you need to configure for changes to spam level 10.0
Also, your spamd.log may show a warning about pyzor if you have not removed pyzor.
And, it may show these messages which you can remove:
Sat Oct 26 12:55:40 2024 [7639] warn: check: dns_block_rule RCVD_IN_ZEN_BLOCKED_OPENDNS hit, creating /var/log/spamassassin/.spamassassin/dnsblock_zen.spamhaus.org (This means DNSBL blocked you due to too many queries. Set all affected rules score to 0, or use “dns_query_restriction deny zen.spamhaus.org” to disable queries)
Sat Oct 26 12:55:40 2024 [7639] warn: check: dns_block_rule RCVD_IN_DNSWL_BLOCKED hit, creating /var/log/spamassassin/.spamassassin/dnsblock_list.dnswl.org (This means DNSBL blocked you due to too many queries. Set all affected rules score to 0, or use “dns_query_restriction deny list.dnswl.org” to disable queries)
To remove this, either whitelist your domain with the above entities, or use a “score” of 0 as follows:
cd /var/log/spamassassin/.spamassassin cp -p user_prefs /var/mail/vhosts/YOUR_DOMAIN/YOUR_USER/user_prefs You can of course just manuallu edit a new file instead of copying vi /var/mail/vhosts/YOUR_DOMAIN/YOUR_USER/user_prefs score RCVD_IN_ZEN_BLOCKED_OPENDNS 0 score RCVD_IN_DNSWL_BLOCKED 0 [save and exit]
You will not longer see those spamd.log messages.
[save and exit]As usual, online documentation is really thin, without good explanations of what is going on, or applicable to current software.
After reviewing this, I found no ability to install TNEF. It is available in Axigen.
TNEF mime attachments need to be converted back to normal formats.
These are the apt packages:
apt-get install libconvert-tnef-perl libfile-mmagic-perl libmime-tools-perl
apt install clamav clamav-daemon
https://wiki.debian.org/ClamAV
https://medium.com/@wingsuitist/set-up-clamav-for-osx-1-the-open-source-virus-scanner-82a927b60fa3
https://vpsie.com/knowledge-base/how-to-install-clamav-on-debian-12/
https://askubuntu.com/questions/1413657/clamav-make-freeze-to-laptop
Try removing apt antispam, and see if a received Email still has a Virus line in the raw source code, which means you don;t need the package.
MS TNEF Decoder / and check emails are base 64, not open html.
quota in imap and lmtp .conf files?
Fix .dovecot.sieve to put spam etc. into junk folder instead of dropping, for testing.
Where to block *@domain.com entries?
use of mail.domain.com, not domain.com
Multi Domains – how, as the above configs have some specific references to postmaster, and domain.com
Dovecot dictionary ??
Dovecot cleanups
Can we use a soft link ln -s on say the archive or a new History folder where emails would be encrypted on the Amazon EFS disk?
The .time cleanups not tested as yet.
Keep an eye on swap space.
Can we monitor and drop bad IP addresses from spam?
How to implement grey listing? (ClamAV not acceptable) – Denial of Service to look more into
If system down, using S3 bucket as the backup with a generic S3 email notifying of an email
MS New Outlook – still asking for password if dovecot is restarted – what about exiting the app and coming back in ? Not an issue in Old Outlook
CHeck deleted emails still show the body text extract