Following on from part 1, this article discusses how to configure Postfix to act as Jira's mail transfer agent. We discuss choice of mail relay, provide relayed and non-relayed Postfix sample configurations, and discuss how Postfix can be configured on reroute email on sandbox/staging servers. |
As suggested in part 1, your best plan is to get emails out of Jira and into a competent mail transfer agent (MTA) as early as possible. We will use Postfix, installed on localhost port 25, and then configure Jira to send email to it:
Jira will hand off to Postfix fast (because it's localhost) and with almost guaranteed success. Postfix is left with the hard task of actually delivering the emails.
If you have installed things on Debian/Ubuntu before, you know that the apt-get install
process asks you some configuration questions. So before diving in, we need to decide how Postfix is going to deliver emails. Typical options are:
Larger, more established companies often have existing SMTP relays through which all outgoing SMTP must flow, enforced by a firewall.
Smaller companies, your may choose to relay through you regular email provider, i.e. smtp.yourhostingprovider.com
.
If so, please review this list of email sending limits per provider to see if you will hit sending limits. Jira sends lots of email.
For GSuite in particular, the relay limit is 10,000 per day (documented here).
This is probably the way to go for larger (50+ user) installations if you don't want to have problems or deal with mail infrastructure. I have personally used sendgrid and had no problems.
This wasn't even an option when Jira sent emails directly, but..
Rather than send email via someone, why not just have our mail server send to their mail server directly? You know, like email was designed to work - decentralized, peer-to-peer, not beholden on giant gatekeeping corporations?
Well, you can do that, and it is in fact my favourite option. Email is the last great decentralized protocol that still more or less works. The problem is spam, and more specifically the countermeasures that large email providers take to avoid spam. Your little mail server, sending legitimate Jira emails, may not be trusted when it connects directly to a recipient's mail server, and your Jira notifications may not be delivered. You can increase the deliverability odds by following best practices, but you may be unlucky, e.g. if you unknowningly share a IP netblock with a known spammer.
Despite legitimate worries over deliverability, sending directly is my favourite option because:
Run apt-get install postfix
to install Postfix. You will be prompted to enter values in a series of 'debconf' setup screens. The first screen confronts us with the relay-or-send-directly choice discussed above:
The options mean:
Option | Incoming Mail | Outgoing Mail |
---|---|---|
Internet Site | Accepts outside connections, delivering to local mailboxes | Sent directly |
Internet with smarthost | Accepts outside connections, delivering to local mailboxes | Relayed |
Satellite system | Accepts connections from localhost, delivering to local mailboxes | Relayed |
Local only | Accepts connections from localhost, delivered to local mailboxes | No sending or relaying |
Pick Satellite system if you wish to relay through a mail service provider, or Internet Site if you wish to send email directly. Then accept the defaults.
The debconf setup sets Debian-specific paths for parameters like |
Now proceed to the relaying or send-directly section below, in which you will properly customize /etc/postfix/main.cf
.
To have Postfix send emails directly, choose 'Internet Site':
Because we will only accept incoming SMTP connections from Jira and Confluence locally, limit Postfix to listen on localhost:
# postconf inet_interfaces=loopback-only |
With Postfix configuration, the
Other useful commands:
When reading the docs, keep very clear in your head whether you're currently debugging Postfix receiving email ( |
This is not a Postfix tutorial, so I'll just paste a fuill /etc/postfix/main.cf
from a live server, suitably hyperlinked (via the mantools/postlink
perl script in the Postfix source):
# 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 (Ubuntu) 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 2 on # fresh installs. compatibility_level = 2 # Incoming SMTP (smtpd): TLS parameters smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_tls_security_level=may smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
# $myhostname must have a PTR record. myhostname = jiraconfserver.example.com alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydestination = $myhostname, example-jiraconf, localhost.localdomain, localhost mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = loopback-only inet_protocols = all # Outgoing SMTP # Opportunistic TLS authentication. http://www.postfix.org/postconf.5.html#smtp_tls_security_level smtp_tls_security_level = may
# On the advice of https://www.linode.com/docs/email/postfix/configure-postfix-to-send-mail-using-gmail-and-google-apps-on-debian-or-ubuntu/
# Without this we get 'Untrusted TLS connection established to smtp.gmail.com'
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt # Reuse TLS connections to hopefully delay GMail breaking with "4.7.0 Too many login attempts, please try again later". http://www.postfix.org/TLS_README.html#client_tls_reuse smtp_tls_connection_reuse = yes #tlsproxy_client_loglevel = 2 smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_scache smtp_tls_session_cache_timeout = 3600s smtp_tls_loglevel = 1 # Re-route emails to a catch-all address for testing or on sandbox #virtual_alias_maps = regexp:/etc/postfix/virtual_alias #disable_vrfy_command = yes # Increase the maximum email size from 10Mb to 50Mb. https://www.redradishtech.com/pages/viewpage.action?pageId=19103782 message_size_limit = 50240000
Some notes:
inet_protocols = all
is boilerplate 'Satellite system' content.loopback
, and our only SMTP client will be Jira/Confluence, so the smtpd_tls
lines are not strictly necessary, unless you tick the 'TLS' box in Jira's Outgoing Mail config.myhostname
is the EHLO address the server identifies itself as to others, and as such:..with a PTR record for ipv4 and (assuming inet_protocols = all
) ipv6. Here is a handy function for testing:
validate_spf() { a=${1}. # Add trailing dot ip=$(dig +short A $a) ptr=$(dig +short -x $ip) [[ $a == $ptr ]] && echo "ipv4 correct" || echo "$1 ipv4 resolved to $ip, whose PTR is $ptr, not $a" ip6=$(dig +short AAAA $1) ptr=$(dig +short -x $ip6) [[ $a == $ptr ]] && echo "ipv6 correct" || echo "$1 ipv6 resolved to $ip, whose PTR is $ptr, not $a" } validate_spf jiraconfserver.example.com |
The address (or IP) must be listed in your SPF record, e.g:
dig TXT example.com | grep spf example.com. 284 IN TXT "v=spf1 include:_spf.google.com a:jiraconfserver.example.com ~all" |
smtp_tls_connection_reuse tells Postfix to cache and reuse its outgoing TLS connection to SMTP servers. If you enable this, also ensure master.cf
has the line:
tlsproxy unix - - y - 0 tlsproxy |
The hope is that when sending dozens of emails to one provider (like smtp.gmail.com), we could make them all over just one TLS connection, and hopefully avoid "Too many login attempts" errors.
With this flag you'll see /var/log/mail.log
lines like:
Jul 22 21:45:19 example-jiraconf postfix/smtp[650976]: Trusted TLS connection reused to smtp.gmail.com[2404:6800:4003:c03::6d]:587: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) |
you'll see a new conn_use=
field indicating how much connection reuse you're getting:
Jul 22 21:45:21 example-jiraconf postfix/smtp[650805]: 25839197990: to=<joe.bloggs@example.com>, relay=smtp.gmail.com[74.125.130.109]:587, conn_use=5, delay=27, delays=0/22/0.26/5.3, dsn=2.0.0, status=sent (250 2.0.0 OK 1595418321 c139sm23292217pfb.65 - gsmtp) |
The most reuse I've seen is conn_use=9
. I have also not verified whether TLS connection reuse actually affects GSuite's "Too many login attempts" at all.
message_size_limit
is, as the comment states, increases the size limit for giant Jira attachments from JETI.virtual_alias_maps
With this setup I can successfully deliver emails to a variety of domains, e.g:
Jul 29 17:21:36 example-jiraconf postfix/qmgr[420220]: 4E843197944: from=<root@jiraconfserver.example.com>, size=469, nrcpt=1 (queue active) root@example-jiraconf:/etc/postfix# Jul 29 17:21:37 example-jiraconf postfix/tlsproxy[420459]: CONNECT to [2404:6800:4003:c04::1a]:25 Jul 29 17:21:37 example-jiraconf postfix/tlsproxy[420459]: Trusted TLS connection established to aspmx.l.google.com[2404:6800:4003:c04::1a]:25: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256 Jul 29 17:21:37 example-jiraconf postfix/smtp[420416]: Trusted TLS connection established to aspmx.l.google.com[2404:6800:4003:c04::1a]:25: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256 Jul 29 17:21:38 example-jiraconf postfix/smtp[420416]: 4E843197944: to=<jeff@redradishtech.com>, relay=aspmx.l.google.com[2404:6800:4003:c04::1a]:25, delay=58, delays=56/0.02/1.3/0.77, dsn=2.0.0, status=sent (250 2.0.0 OK 1596007298 mw16si68538pjb.75 - gsmtp) Jul 29 17:21:38 example-jiraconf postfix/tlsproxy[420459]: DISCONNECT [2404:6800:4003:c04::1a]:25 Jul 29 17:21:38 example-jiraconf postfix/qmgr[420220]: 4E843197944: removed |
Things to notice in the log above:
tlsproxy
logs and The relay=aspmx.l.google.com
reflects the first MX entry for domain redradishtech.com.To configure Postfix for relaying, start with a 'Satellite system':
then choose the defaults until the setup process ends.
Now follow the Postfix docs on enabling SASL authentication in the Postfix SMTP/LTMP client. Essentially we need to specify a relayhost
and enable SASL authentication:
/etc/postfix/main.cf: smtp_sasl_auth_enable = yes smtp_tls_security_level = encrypt smtp_sasl_tls_security_options = noanonymous relayhost = [mail.isp.example] # Alternative form: # relayhost = [mail.isp.example]:submission smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
If you intend to relay through GSuite, read this important page: SMTP relay: Route outgoing non-Gmail messages through Google.
If you are using a GSuite 'legacy free edition', as I am with redradishtech.com, the documentation starts with a warning:
Don't worry, you can still relay through GSuite, but:
|
Go to https://admin.google.com logged in as a GSuite administrator.
Finally, log out and back into GSuite as the 'jira' account to perform these steps:
Now for the Postfix configuration. Google helpfully provides some documentation on how to configure Postfix:
As a reminder, we are following the guide enabling SASL authentication in the Postfix SMTP/LTMP client, which basically wants you to add these parameters:
/etc/postfix/main.cf: smtp_sasl_auth_enable = yes smtp_tls_security_level = encrypt smtp_sasl_tls_security_options = noanonymous relayhost = [mail.isp.example] # Alternative form: # relayhost = [mail.isp.example]:587 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
GMail always displays email as coming 'from' the user that authenticated, rather than what is in the From:
header. In my case I have a Jira and Confluence (and an invoice system) all sending mail from one server via Postfix. Thus I actually want authentication to vary depending on sender:
From: jira@example.com
) then I want to authenticate as jira@example.com
.From: confluence@example.com
) then I want to authenticate as confluence@example.com
.From: billing@example.com
) , then I want to authenticate as billing@example.com
.www@example.com
This can be done by following the Sender-Dependent SASL Authentication instructions, which adds a parameter to make authentication dependent on the From:
header:
/etc/postfix/main.cf: smtp_sender_dependent_authentication = yes
sasl_passwd
then has credentials mapped per sender, plus a default fallback mapping for the relay:
/etc/postfix/sasl_passwd: # Per-sender authentication user1example.com username1:password1 user2example.net username2:password2 # Login information for the default relayhost. [mail.isp.example] username:password # Alternative form: # [mail.isp.example]:587 username:password
Here is a sample /etc/postfix/main.cf
that does all of the above:
# 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 (Ubuntu) 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 2 on # fresh installs. compatibility_level = 2 # Incoming SMTP (smtpd): TLS parameters smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_tls_security_level=may smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination # Outgoing SMTP # Location of CA certificates # On the advice of https://www.linode.com/docs/email/postfix/configure-postfix-to-send-mail-using-gmail-and-google-apps-on-debian-or-ubuntu/ # Without a valid CAfile we would get 'Untrusted TLS connection established to smtp.gmail.com' smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt # Require TLS when relaying to Google smtp_tls_security_level=encrypt smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # Enable SASL auth when connecting to other SMTP servers. http://www.postfix.org/SASL_README.html#client_sasl smtp_sasl_auth_enable=yes smtp_sasl_security_options = noanonymous, noplaintext # Override smtp_sasl_tls_security_options to remove 'noplaintext' so we can use PLAIN auth for gmail. https://toroid.org/postfix-smtp-relay-gmail smtp_sasl_tls_security_options = noanonymous # SASL login credentials smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd # Allow auth to outgoing SMTP to vary based on sender 'From' address. # This ensures credentials matching the 'From' header are used. smtp_sender_dependent_authentication = yes smtp_tls_loglevel = 1 myhostname = example alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname mydestination = example, example, localhost.lxd, localhost relayhost = [smtp.gmail.com]:587 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = loopback-only inet_protocols = ipv4 mynetworks = 127.0.0.1/32 10.20.46.187/32 [::1]/128 [fd42:f93a:8612:865e:216:3eff:fe35:f5ef]/128 [fe80::216:3eff:fe35:f5ef]/128 default_transport = smtp relay_transport = smtp
/etc/postfix/sasl_passwd
contains:
# Per-sender authentication; see also /etc/postfix/sender_relay.
# Optional Postfix SMTP client lookup tables with one username:password entry per sender, remote hostname or next-hop domain. Per-sender lookup is done only when sender-dependent authentication is enabled. If no username:password entry is found, then the Postfix SMTP client will not attempt to authenticate to the remote host.
# The Postfix SMTP client opens the lookup table before going to chroot jail, so you can leave the password file in /etc/postfix.
# Specify zero or more "type:name" lookup tables, separated by whitespace or comma. Tables will be searched in the specified order until a match is found.
jira@example.com jira@example.com:<APP PASSWORD>
confluence@example.com confluence@example.com:<APP PASSWORD>
billing@example.com billing@example.com:<APP PASSWORD>
[smtp.gmail.com]:587 www@example.com:<APP PASSWORD>
You will need to 'compile' sasl_passwd
into a sasl_passwd.db
file with postmap
. Paste the following into /etc/postfix/Makefile
:
all: $(wildcard *.db) sasl_passwd.db %.db: % postmap $< |
(Note: there must be a tab character before postmap
not spaces)
Then run make
in /etc/postfix
to generate sasl_passwd.db
Other notes:
An anecdote: I recently configured a 25-user Jira instance to relay through GSuite. Having only a 'legacy free edition', I relayed through smtp.gmail.com rather than Little did I know this meant the 2000-email sender limit applied, instead of the paid edition's 10,000 email limit. The projects used a fairly typical notification scheme, notifying the reporter, assignee, watchers and a certain user: Only about 3 users were typically active. Surely GMail could handle this? No, actually. All it took was one user bulk-updating a few hundred issues, forgetting to turn off the notifications. 500 issues, with about 4 notifications per issue (per above scheme) equals 2000 outgoing emails. GMail worked for a bit, then starts returning:
and later:
Jira cares not, and keeps trying to flush the queue. Eventually GMail turns nasty:
And finally smtp.gmail.com stops responding at all:
The server was then unable to send mail for 12-24 hours. At this point I decided to RTFM and discovered most of the information above. |
To test your Postfix configuration, it's handy to be able to generate test emails with a particular From:
header, to a particular user.
The mail
command is good for this. Specifically, the form:
mail - r <from_address> -s <subject> <to_address> <<< mail_body
You will need to apt-get install mailutils
first.
My Postfix debugging loop is basically:
/etc/postfix/main.cf
postfix reload
because who knows if what you edited is one of the few magically-reloading settings.tail -f /var/log/mail.log
in a terminalmail -r jira@redradishtech.com -s "Testing from jira@" jeff@redradishtech.com <<< Hello
postfix flush
to resend the mail queue.status=sent
deliveriesIf you need more verbose logs (SMTP protocol logs), add -v
to the smtpd
or smtp
command in /etc/postfix/master.cf
, and reload postfix. Sometimes trivial-rewrite
needs a -v too.
At this point you should have a functional Postfix. You should now configure your monitoring software to ensure that it stays functional.
I use this Nagios script to detect mail queue anomalies like bounces or a backed-up defer queue. It is the best I could find for free, but still not hugely flexible or great. If you know of a better monitoring solution please let me know in the comments.
Perhaps it is just me, but a few times now I've had GSuite 'app passwords' just mysteriously stop working. I have to regenerate them. To my knowledge GSuite app passwords don't expire, so I don't know what is going on. Without monitoring this failure would be quite damaging, e.g. on servers that sends important emails like invoices. |
In serious installations, one generally has a 'sandbox' (or 'staging') Jira for experiments and testing. The sandbox Jira data is periodically refreshed from production.
One requirement of sandbox Jira is that it must not be allowed to email real users. People get really confused if they receive notifications "from Jira" that were actually just experiments on sandbox.
Say we had our SMTP details configured directly in Jira:
This would be a problem: the credentials come with the data refreshes. What is to stop our sandbox Jira sending emails through smtp.gmail.com just like production?
The usual answer is: you set the -Datlassian.mail.senddisabled=true
flag to prevent emails being sent, and/or by blocking outgoing connects to 25/465/587 at the firewall (since plugins might send email directly).
But now things are different: Jira no longer embeds the SMTP host details:
So on sandbox we can configure Postfix to re-route all outgoing emails to a local mailbox (regardless of true destination).
Then even if you forget -Datlassian.mail.senddisabled=true
you don't have to worry. In fact, you might deliberately leave it unset so you can inspect JIRA's mail delivery, without the risk of spamming real users.
To do this:
Create a system account whose mailbox will accumulate catch-all emails:
useradd --no-create-home --comment "Postfix may re-route to this user mailbox" usercatchall |
(or skip this step and just use root
below)
Add a virtual_alias_maps parameter to your /etc/postfix/main.cf
:
# Map *@monitoring.redradishtech.com to jeff@redradishtech.com virtual_alias_maps = hash:/etc/postfix/virtual_alias |
and postfix reload
Create /etc/postfix/virtual_alias
containing:
# Reroute all email, sending it to root's local mailbox #/.*@.*/ mailcatchall |
Then make virtual_alias.db
(assuming you installed the Makefile shown earlier)
You should then see outgoing email be rerouted:
Jul 30 12:06:57 mycontainer postfix/qmgr[694107]: D94DDA70F: from=<billing@redradishtech.com>, size=357, nrcpt=1 (queue active) root@mycontainer:/etc/postfix# Jul 30 12:06:57 mycontainer postfix/local[694832]: D94DDA70F: to=<mailcatchall@mycontainer.lxd>, orig_to=<jeff@redradishtech.com>, relay=local, delay=0.03, delays=0.02/0/0/0, dsn=2.0.0, status=sent (delivered to mailbox) Jul 30 12:06:57 mycontainer postfix/qmgr[694107]: D94DDA70F: removed |
Use the best tool for the job! For delivering email, that's Postfix.
Assuming you started off just letting Jira relay your mail, let's review the benefits:
/var/log/mail.log
Feel free to add a comment below or contact me at jeff@redradishtech.com with any feedback or questions.