facebook youtube pinterest twitter reddit whatsapp instagram

Setting Up Send-Only Mail Server From Scratch (With Haraka)

In this guide, I would walk you through the steps of setting up an email server that can be used as a send-only mail server or just an outgoing only mail server, we would not be dealing with receiving mails, we only care about sending emails

There are a lot of use cases for this, from newsletters to personal email, to providing email service for your clients, and so on.

Before I get started, let me give you a bit of backstory of what led me to use Haraka:

I am working on my CMS (which is powering this website), and I constantly find it hard to integrate some form of email service into the project, and no, I don't want to use postfix, or any old program. I want something I can extend using a language I am familiar with, be easy to integrate into my CMS, and should be highly modular which led me to Haraka.

Haraka is a  SMPT sever project writen in JavaScript (Node.js) which provides extremely high performance coupled with a flexible plugin system allowing Javascript programmers full access to change the behaviour of the server

Requirements To Follow The Guide:

  • Basic JavaScript Knowledge
  • Your Way Around The Terminal (Not Necessary as you can mimic the guide instruction)
  • Debian-Based VPS Distro (Ubuntu, Devuan, POP OS, etc)

Getting Started (Installing Haraka)

Haraka won't work without gcc installed, install it like so:

apt install build-essential -y

Ensure you have the latest version of node and npm installed,

I am using Debian, so, I would be installing node and npm like so:

apt install curl -y && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &&\
apt update && apt-get install -y nodejs

Once that is installed, install haraka globally (note that Haraka starts with Capital H and not h):

npm install -g Haraka

If you don't want to install haraka globally, remove -g. if you don't know what that means, then use the above command as is.

If the command gives "nobody" errors run the next command

npm -g config set user root

npm install -g Haraka

Running Haraka

First, check if haraka is installed:

haraka -v

If you get: bash: haraka: command not found run the following:

npm -g config set user root

npm install -g Haraka

Once haraka is installed, you need to create an instance or a copy of the haraka service config:

haraka -i devsrealm_email_service

The above would create the directory devsrealm_email_service with config and plugin directories within. It also sets the hostname used by Haraka to the output of the hostname.

The devsrealm_email_service contains the following:

  • config: Directory containing various configuration files.
  • docs: Directory containing documentation.
  • package.json: An empty package.json, you could use this in case you want to install node dependencies
  • plugins: Directory containing the custom plugins.

DNS Configuration

Note: you would replace website.com with the name of your website and 111.11.1.111 should be replaced with the IP of your mail server.

; an A record is declared to name your mail server
mail.website.com  A       111.11.1.111

; an MX record is declared to let the world know that mail.website.com handles mail for website.com
website.com.   MX 10    mail.website.com.

The above would be set in your DNS records. If you have a backup mail server should in case something goes wrong with your primary mail server, you can set it below the MX record, e.g:

; an A record is declared to name your mail server
mail.website.com  A       111.11.1.111

; an MX record is declared to let the world know that mail.website.com handles mail for website.com
website.com.   MX 10    mail.website.com.
website.com.   MX 20   mail-backup.website.com.

Instead of setting website.com or mail.website.com, you can use @ and mail which is equivalent to website.com and mail.website.com, here is an example:

; an A record is declared to name your mail server
mail  A       111.11.1.111
; add smtp if you want to make it fancy, this way you would be able to connect to your server with smtp.website.com
smtp A 111.11.1.111

; an MX record is declared to let the world know that mail.website.com handles mail for website.com
@   MX 10    mail.website.com.
@   MX 20   mail-backup.website.com.

Note: Since we are only using haraka for sending, the mx portion is useless since it is more or less used for handling incoming email for your domain, so, for the above, just do:

; an A record is declared to name your mail server
mail.website.com  A       111.11.1.111

or 

; an A record is declared to name your mail server
mail  A       111.11.1.111
; add smtp if you want to make it fancy, this way you would be able to connect to your server with smtp.website.com
smtp A 111.11.1.111

Once you have added the DNS records, you can confirm:

$ host mail.website.com
mail.website.com has address 111.11.1.111

$ dig -t MX website.com +short
10 mail.website.com.

Still, in your DNS records, add the below:

website.com.      IN TXT  "v=spf1 mx -all"

SPF makes it possible for destination mail servers to determine if a machine is allowed to send mail on behalf of a domain.

The above SPF is saying only the mail server is allowed to send mail on behalf of the domain.

Lastly, set your rDNS in your VPS control panel to mail.website.com, if you do not know how to do it, please ask your VPS customer agent.

Generate a Letencrypt Certificate

Install certbot:

apt-get -y install certbot

then generate cert-only this way:

certbot certonly --standalone -d "mail.website.com" -m "mail@website.com" --agree-tos --hsts --staple-ocsp --non-interactive

Once that is generated, it would be saved at:

/etc/letsencrypt/live/website.com/fullchain.pem

 and the private key would be at

/etc/letsencrypt/live/website.com/privkey.pem

Setting Up Haraka

tls

open the config/tls.ini

nano /path/to/haraka/config/tls.ini

and refer to the new certificate:

key=/etc/letsencrypt/live/website.com/privkey.pem
cert=/etc/letsencrypt/live/website.com/fullchain.pem

me

Here you can set the name to use of the server, it would be used in received lines and elsewhere, by default it is set to your server hostname, 

open config/me, and add mail.website.com on the first line.

host_list

Here, you add the host you want to accept mail for, this is not important for a send-only mail server as it is used when you want to receive an email, so you can skip this.

To accept emails for your domain,  you need to add them to the config/host_list file. for example, to accept emails for website.com (i.e., mail@website.com, user@website.com) you need to add website.com to the host_list file as a single line.

In my case, I added the following:

localhost
mail.webiste.com
webiste.com

Set to accept mail for localhost, mail.website.com, and website.com, again, this option can be skipped since we are only using haraka as a send-only, I just wanted to point it out

SMTP

Open config/smtp.ini, uncomment listen, and use it as follows:

listen=111.11.1.111:587

Meaning we are listening on port 587, you can as well change it to 465, but I would stick with 587

uncomment spool_dir, it is like this initially:

;spool_dir=/var/spool/haraka

change it to

spool_dir=/var/spool/haraka

close the file

auth_enc_file

When connecting to the SMTP server, we need a way to authenticate with our user and pass, by default, haraka has a flat_file which stores password in clear text, so we would not use it, instead, install auth_enc_file using the following:

npm install haraka-plugin-auth-enc-file

once that is installed, open the config/auth_enc_file.ini file using:

nano config/auth_enc_file.ini

and use the following format, you can have multiple user:pass, just make sure they are on a separate line:

mail={SHA512-CRYPT}xxxxxx
otherUser={SHA512-CRYPT}xxxxxx

You would replace xxxxxx with a SHA512-CRYPT hash, let's say you want your password to be, my_mum_is_lovely, you would generate it this way:

mkpasswd -m sha-512 -s my_mum_is_lovely

which gives the output

$6$LP5cw5oGVvl4xFPI$ZOC4hSFVpCT/r5hwACfyeh5ENU53zVGoht6cx/4f5Pzr.NABk1jipJRsDan5hd8NtwBJf38vVpt49ZhHJrQP71

Now, you can just change the xxxxxx to the above, and repeat the step for another user in the auth_enc_file.ini.

With that, it means you would be able to connect your SMTP server with the following settings:

For the mail user:

  • Host: mail.website.com
  • User: mail
  • Pass: my_mum_is_lovely
  • Port: 587
  • Encryption: tls

For the other user:

  • Host: mail.website.com
  • User: otherUser
  • Pass: your_choosen_pass
  • Port: 587
  • Encryption: tls

You won't be able to send anything yet as we are still setting up haraka

dkim

DomainKeys Identified Mail allows the sending server to use a cryptographic signature, storing the public decryption key in a DNS record. The receiver can then verify the signing server has a key for that domain.

cd into the dkim directory:

cd /path/to/haraka/dkim
chmod 744 dkim_key_gen.sh

generate a key for website.com

./dkim_key_gen.sh website.com

When that is done, haraka would have created 4 files in config/dkim/website.com:

dns private public selector

The selector file contains the DNS label where the DKIM public key is published. The private and public files contain the DKIM keys.

The DNS file contains a formatted record of the public key suitable for copying/pasting into your domain's zone file. It also has suggestions for DKIM, SPF, and DMARC policy records.

use cat to view the DNS (I am only highlighting the important bit):

Add this TXT record to the website.com DNS zone.

....
....

BIND zone file formatted:

feb2023._domainkey    IN   TXT (
      "v=DKIM1;p=MIIBIjANBgkxxxxxxxxxxxxxxxxxxxxxxxxxxxuHDDuMexuecP6JiHVzdZppTmANfB1Ak91s0Y1Ev+OW"
      "k/QMsrJ+bCeTOIocXXX6+WqZedddddddddddddddddddSk/e5zjR7ikXRbnsdx2sf5YmtJjv6yDpe1PUCnHR4zjryr2U"
      "FVp69FbiZMDM9PT0lWedfAM3wV+t3Udddddddddddd9ihpMnSLXX6vnCwe349VKQqrt+BS/hwZZhyLWfYdx"
      "0mtOQdDAQAB")

The instruction on how to add it to the txt record would be there. Once you are done with that, you can also add a dmarc:

_dmarc  IN      TXT "v=DMARC1; p=reject; adkim=s; aspf=r; rua=mailto:dmarc-feedback@website.com; ruf=mailto:dmarc-feedback@website.com; pct=100"

Now, open /path/to/haraka_instance/config/dkim_sign.ini and add the following:

headers_to_sign=Subject,From,To

Enable plugins

Open /path/to/haraka_instance/config/plugins and uncomment (by uncomment I mean, remove the pre-pended # symbol) the following plugin:

  • tls
  • spf
  • dkim_sign
  • haraka-plugin-auth-enc-file

If haraka-plugin-auth-enc-file is not in that file, just add it at the very last line, then close.

Manage Haraka With SystemD

This way you would be able to start haraka automatically on boot, disable, and easily manage haraka instances

Create a new service file for Haraka. You can create it in the /etc/systemd/system directory with the following command:

sudo nano /etc/systemd/system/haraka_instance_1.service

add the below:

[Unit]
Description=Haraka Mail Server
After=network.target

[Service]
ExecStart=/usr/bin/haraka -c .
WorkingDirectory=/path/to/haraka_instance_1
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target

Save the File

Reload the Systemd daemon to make the changes effective:

sudo systemctl daemon-reload

Start the Haraka service using the following command:

sudo systemctl start haraka_instance_1

Enable the new haraka service using the following command:

sudo systemctl enable haraka_instance_1

To check the status of the Haraka service, run:

sudo systemctl status haraka_instance_1

Haraka full logs would be logged at Syslog

Testing Smtp Server

If everything went well, you can test your SMTP server, for example, for the mail user, you can use the following details:

For the mail user:

  • Host: mail.website.com
  • User: mail
  • Pass: my_mum_is_lovely
  • Port: 587
  • Encryption: tls

Good luck.

If you get stuck or you want to hire me for consultation, then reach out to me: faruq [at] devsrealm.com

Troubleshooting

Here are some common issue and their fixes

Couldn't reach server. Please double-check the server and port number

The "Couldn't reach server. Please double-check the server and port number" error is issued by Gmail when using the "Send mail as" feature. The error is because Google could not reach the server, and the likely culprit is a malformed or incorrect DNS setting, check the following:

  • Your mail hostname is pointing to the IP of the server hosting the domain
  • If you are using AAAA records, cross-check that you are using the correct IPV6, Gmail would try to connect to IPV6 and if it fails, it halts, you'll think it would try to also reach the IP of the A record, but that isn't the case, so ensure the IPV6 is correct, or just delete it altogether, this way, Gmail would use the A record IP