ANNOUNCE: Haraka 2.8.2

Note: We shortly after this released 2.8.3 which fixes config merging, a feature we added in 2.8.0. It didn’t seem to really deserve a blog post.

This is a minor bugfix release, mostly to fix the broken config/plugins file we accidentally shipped with v2.8.0.


  • Added Node v6 to travis tests

New Features

  • Added bin/haraka –qunstick <domain> to flush all mails for that domain (#1460)


  • Make bin/haraka –qlist show much more information (#1452)
  • Allow CIDR ranges in no_tls_hosts (#1450)

Bug Fixes

  • 2.8.0 was shipped with a broken config/plugins. (#1453)
  • Stop haraka dying when ldap connections fail (#1456)
  • Pick up domain specific config correctly in ldap (#1456)

Announce: Haraka v2.8.0

This release represents a huge leap forwards for Haraka users everywhere. We came very close to calling this v3, but since we didn’t break any APIs we stuck with v2.8.

This release contains work from 15 contributors around the world, and we thank them all for their time and effort.

Upgrading is simply a matter of running: npm install -g Haraka. Following an upgrade to 2.8 we recommend deleting any config files in your personal Haraka config folder that you have not modified. The reason for this is that Haraka will now load non-modified config data from the core Haraka folder. This change makes future upgrades much easier.

The major new features in this release are:

  • The ability to write plugins as npm packages
  • The merging of config data to allow minimal config in your local config folder
  • Many TLS fixes including the ability to use outbound TLS without a certificate, meaning we now enable outbound TLS by default

The full list of changes are:

  • Changes
    • updated dependency versions (#1426, #1425)
    • use utf8 encoding for body filters (#1429)
    • remove spameatingmonkey from tests (#1421)
    • replace ./constants.js with haraka-constants (#1353)
    • Document HMail and TODO items (#1343)
    • Copy only a minimal config/* by default (#1341).
    • cfreader/* removed to haraka/haraka-config (#1350)
    • outbound and smtp_client honor tls.ini settings (#1350)
    • outbound TLS defaults to enabled
    • lint: remove all unused variables (#1358)
    • replace ./address.js with address-rfc2181 (#1359)
  • New Features
    • smtp_forward: accepts a list of backend hosts, thanks @kgeoss (#1333)
    • config: add array[] syntax to INI files (#1345)
    • plugins.js: support require(‘./config’) in plugins
    • Load plugin config from own folder and merge (#1335)
    • Allow original email’s Subject to be included in bounce message (#1337)
    • new queue/smtp_bridge plugin, thanks @jesucarr (#1351)
  • Improvements
    • early_talker: supports IP whitelisting (#1423)
    • loading plugins as packages (#1278)
    • removed TLD stuff to haraka/haraka-tld (#1301)
    • removed unused ‘require(‘redis’) in plugins/karma (#1348)
    • improved MIME header support per rfc2231 (#1344)
    • tls options can be defined for outbound and smtp_* (#1357)
    • explicitly disable SSLv2 (#1395)
    • cache STUN results
    • xclient plugin improvements (#1405)
    • tls: Set verify=NO correctly when no certificate presented (#1400)
    • improved message header decoding (#1403, #1406)
    • bounce: skip single_recipient check for relays/private_ips (#1385)
    • rspamd docs: Clarify usage of check.private_ip (#1383)
    • if rcpt_to returns DSN in msg, log it properly (#1375)
  • Bug Fixes
    • fix out-of-range errors from banner insertion (#1334)
    • dkim_verify: Call next only after message_stream ended (#1330)
    • outbound: remove type check from pid match (#1322)
    • lint: enable no-shadown and remove all shadow variables (#1349)
    • spf: fix log_debug syntax (#1416)
    • auto_proxy: fix a starttls loop (#1392)
    • fcrdns: corrected err variable name (#1391)
    • rspamd: Fix undefined variable (#1396)
    • dkim_verify: Fix header handling (#1371)
    • smtp_client: fix remote_ip (#1362)

Happy Haraking!


Express, Lost Sessions, and Windows

One of the harder bugs I’ve ever had to track down.

Most of our office uses Macs. One developer alone uses Windows. He would regularly see the weirdest issue where he couldn’t log into our app – he’d try time and time again, and maybe on the 4th or 5th try it would work.

He even hacked in some delays via Javascript’s setTimeout() function to try and fix it, knowing it was a weird bug with lost sessions.

After helping him debug a problem on his machine, and seeing him go through this over and over, it was finally time to get down to the meat of this issue. Why were sessions not getting saved for Windows users, but were just fine for Mac users?

Every problem related to a call to res.redirect(). First problem located.

So why would this work fine for Mac and not Windows? It wasn’t even browser specific.

Thankfully I have a large back-story in various networking related activities. Windows and Macs have different networking stacks, and different ways they cut apart packets and so on.

I also knew quite well how Express (the Node.js HTTP server stack we use) saved the session, due to various unrelated deep dives into the code.

Express saves the session by hijacking res.end(). It turns out that when you do a res.redirect(), on Windows it will likely get the headers in a single packet, and the body in another, but perform the redirect before even seeing the body (because it’s empty and irrelevant). But res.end() isn’t called until the HTTP request is completed. This means the Windows boxes can get the redirect, request the redirected URL, and get access to an unsaved session before res.end() has time to completely save the session.

The fix? Now in our code we hijack res.redirect() to perform req.session.save() before performing the actual redirect.

This fix should probably go into express-session. I will work on a pull request.

Code for the curious (put this in some middleware):

 var redirect = res.redirect;
 res.redirect = function (path) {
 res.redirect = redirect;

 if (req.session) {
 req.session.save(function (err) {
 if (err) console.error(err);
 else {

 function _finish () {
 if (/\&utm_/.test(path)) {
 return res.redirect(path);
 if (req.query.utm_campaign && req.query.utm_medium && req.query.utm_source) {
 var extras = qs.stringify({
 utm_campaign: req.query.utm_campaign,
 utm_medium: req.query.utm_medium,
 utm_source: req.query.utm_source,
 if (/\?./.test(path)) {
 path = path + '&' + extras;
 else {
 path = path + '?' + extras;
 return res.redirect(path); 

Announcing Haraka v2.7.3

The Haraka core team are happy to announce Haraka v2.7.3.

  • Changes
    • smtp_proxy & qmail-queue: default to enabled for outbound deliveries (previously used Outbound), to better matches user expectations.
  • New Features
    • outbound: allow passing notes to send_email (#1295)
  • Improvements
    • logging: emit log message queue before shutting down (#1296)
    • result_store: permit redis pub/sub to work when host != localhost (#1277)
    • tests: quiet the extremely verbose messages (#1282)
    • rspamd: add timeout error handling (#1276)
    • watch: fix display of early_talker results (#1281)
    • spamassassin: publish results to result_store (#1280)
    • karma: can now connect to redis on hosts other than localhost (#1275)
    • geoip & p0f: don’t log empty/null values from RFC 1918 connects (#1267)
    • redis: make plugin params match docs (#1273)
    • mailbody: small refactoring (#1315)
    • smtp_proxy & qmail-queue: default to enabled for outbound (#1308)
  • Bug Fixes
    • redis: use correct path for db.select (#1273)
    • count errors correctly (#1274)
    • logger: ignore null arguments (#1299)
    • connection: pause for hook_reset_transaction (#1303)
    • rcpt_to.routes: update redis usage for compat with redis plugin (#1302)
    • smtp_forward: use correct config path to auth settings (#1327)
    • messagestream: correctly pass options parameter to get_data (#1316)
    • spf: honour configuration for mfrom scope (#1322)
    • outbound: Add missing dash to ‘Final-Recipient’ header name (#1320)

Announcing Haraka v2.7.1

Update: This is now 2.7.2. We broke something. We’re sorry.

The Haraka core team is very proud to announce Haraka v2.7.1.

This is a bug fix release.

Fixes and other changes are:

  • New Features
    • added debian init.d file (#1255) @slattery
  • Improvements
    • smtp_forward auth settings now work (#430)
    • better handling of broken messages (#1234)
    • Docker: use latest Phusion image && stdout (#1238, #1239)
    • Clean up plugin loading a tiny bit (#1242)
    • make dkim keydir case insensitive (1251)
    • ignore DNS errors that aren’t errors (#1247)
    • outbound doc updates (#1258) @Currerius
    • outbound: return DENYSOFT on queue error (#1264)
    • smtp_client: if enable_tls is set and TLS files missing, warn (#1266)
  • Bug Fixes
    • Don’t sent empty headers to rspamd (#1230)
    • Fix auth_base.js key need to be a string – number.toString() (#1228)
    • fix bug with empty charset= on mime parts <E2><80><A6> (#1225)
    • Fix “passwd” check crash with numeric password. (#1254)
    • result_store: show arrays when not empty (#1261)

Upgrading is a simple matter of running:

    npm install -g Haraka

And then copying the following config files to your haraka runtime directory:


Creating nice looking Slack bot Messages with Hubot

At Ideal Candidate we finally had enough of HipChat. After several days of messages not being sent, which was really affecting our ability to communicate (even though we sit next to each other – chat is a better interface for keeping everyone involved).

We spent a week evaluating Slack, which many of my friends/colleagues around the world had recommended. Despite their operational status boards looking very similar, we had zero issues with slack messages getting delayed, and the people doing the eval preferred the interface.

We had a few things we needed from Slack which we wanted to keep from the HipChat days:

  • Jira Integration (this is what I’ll talk most about here).
  • Posting to channels a few bits of data from our application (customer signup, customer login).
  • Rainforest messages about running tests.
  • A few other “fun” plugins

Fun plugins were easy. I’ll pass on that here for now.

Rainforest support was a supported plugin. Done.

Posting to channels required a bit of custom code, which we had already written for HipChat, but our experience (below) made us improve what we had.

So let’s get to Jira integration with Slack.

Jira Slack Integration

Let’s be honest about this: The default Jira-Slack integration is total shit.

With HipChat we could control what types of events could generate a message to a channel. The Slack built-in Jira integration only sends a message for the following events:

  • An issue is created or re-opened
  • An issue is closed (unless it was previously resolved)
  • An issue is updated to done or resolved
  • An issue is updated to in progress or to do after previously being marked as done

We wanted much more nuance than that:

  • A bug is created on the current sprint
  • An issue changes status
  • An issue goes from “Ready For Test” back to “ToDo”, we want the last comment (i.e. the reason)
  • An issue is marked “Done”, we want the last comment
  • An issue is mentioned in the channel, we want the summary

We found a number of Hubot Jira plugins on npm that we hoped could work, but none really fit the bill, so we ended up writing our own. The main hubot-jira plugin linked above just dumps plain text into the channel, and we knew we could do better.

So we set about seeing how to create good looking messages with Hubot. The thing to understand about Hubot is it is designed for multiple systems (HipChat, Slack, IRC, etc), so we kind of have to be a bit devious to create a nice looking message. Slack supports a way to do this, calling them attachments (which is a stupid name – but all software has historical shit). First we forked the hubot-jira plugin to get issues in a pretty format. The hubot-jira plugin only responds to mentions of tickets (doing an API query based on the regexp /\b(\w+-\d+)\b/), so first step was to format that nicely.

The code in hubot-jira does a plain msg.send, which just sends a plain text message. To get a nicely formatted message we constructed an attachment object:

              attachment =
                title: "[#{key}] #{json.fields.summary}"
                title_link: jiraUrl + "/browse/#{key}"
                fields: [
                    title: "Status"
                    value: json.fields.status.name
                    short: true
                color: "#003366"
                author_icon: "https://developer.atlassian.com/imgs/jira.png"

And then can post it using the a bit of a hack on hubot directly accessing the Slack object:

                channel: msg.envelope.room
                username: msg.robot.name
                attachments: [attachment]

This created a message in Slack that looks like this:


Note: There are other bits of code to add in certain fields there. See the Slack Attachment docs for more info.

Finally we adding in webhook support, which allowed us to support all sorts of status transitions we wanted:

Screen Shot 2015-11-19 at 21.16.09


We added various other things to our Slack account later, such as posting when new customers sign up, using similar code, but in all it has been a good transition. We haven’t had any times with Slack yet where messages don’t make it through. We hope that continues.



Announcing Haraka v2.7.0

After several months of frenzied development work I’m extremely proud to announce Haraka v2.7.0.

This release consists of 455 commits, 232 files changed, and contributions from 15 people. We also added a new core committer to the team: Josef Fröhle – welcome to the team! Josef has contributed significantly to this release and we look forward to his contributions in the future.

One other change – we moved the repository on github from baudehlo/Haraka to haraka/Haraka – please update your links – thought github will issue redirects on any older links.

Full list of changes in this release:

New Features

  • SPF bounce check
  • rspamd plugin (@fatalbanana)
  • watch plugin
  • limit plugin (connection concurrency, errors, unrecognized commands)
  • plugins can now be npm packages (see also #946)
  • built-in HTTP server (Express backed)
  • ESETS AV plugin
  • DCC plugin (incomplete)
  • Add LOGIN support to XCLIENT
  • backscatterer plugin
  • full IPv4 & IPv6 compatibility inbound #1120, #1123, #1154 (@Dexus)
  • Early talker #1075 (@smfreegard, @msimerson)
  • permit loading of plugins in node_modules #1056 (@msimerson)


  • Fix anti_spoof by use config #1171
  • Add license clause #1170
  • package.json dependencies and travis update #1147, #1168 (@Dexus)
  • logging: remove node-syslog and strong-fork-syslog with modern-syslog #1145 (@Dexus)
  • aliases: support for email, user and host aliases #1149 (@Dexus)
  • add docs for use private key with TLS #1130 (@Dexus)
  • outbound: ENOENT on dotfile – compatibility for windows #1129 (@Dexus)
  • plugin/attachment: block more attachment file types #1191 (@Dexus)
  • remove double functions #1126 (@Dexus)
  • Outbound Bounce messages according to RFC3464 #1189 (@hatsebutz)
  • toobusy: only run checks if toobusy.js installed and loads
  • HAProxy: set local_ip, local_port and remote_port
  • save auth pass/fail/user to result_store
  • ini files no longer require values (useful for storing lists)
  • connection: add MAIL and RCPT to results
  • results_store: enable ’emit’ feature for .push()
  • add support for custom Outbound Received header value (@zombified)
  • save smtp_forward result to result_store
  • auth_base: permit a return message (@DarkSorrow)
  • add DSN.create() and RFC 4954 support
  • enhanced pipelining support
  • added config/access.domains with some tips (@EyePulp)
  • Add SSL detection over plain-text socket
  • earlytalker: store results
  • bounce: make it safe to check non_local_msgid
  • AVG: store results, added defer options
  • tls: change createCredentials to tls.createSecureContext (@DarkSorrow)
  • update dependency versions (esp async 0.2.9 -> 1.0.0)
  • ASN docs: add FTP download note for routeviews
  • karma: removed concurrency limits (see limit plugin) and penalty feature
  • added utils.elapsed()
  • deny message includes hostname
  • Add Fisher-Yates shuffle to randomize lookup order in data.uribl
  • change default message size limit to 25mb
  • auth_base: save auth results
  • upgrade toobusy plugin to toobusy-js (@alexkavon)
  • configfile: permit / char in ini keys
  • added utils.node_min()
  • added result_store.get_all()
  • updated ubuntu upstart script
  • plugin/rate_limit: return in no custom default is set 0 = unlimited #1186, #1185
  • Outbound.send_email: added dot-stuffing #1176, #1165 (@hatsebutz)
  • make sure server object is availabe to plugins loaded from node_modules #1162 (@bmonty)
  • Net_utils.get_ips_by_host #1160 (@msimerson)
  • fcrdns: don’t log error for ENODATA #1140 (@msimerson)
  • improve MUA detection #1137 (@msimerson)
  • tls: tmp disable for hosts that fail STARTTLS #1136 (@msimerson)
  • karma: skip deny on outbound hooks #1100 (@msimerson)
  • Store HAProxy IP in connection object #1097 (@smfreegard)
  • Remove UUID from queued message #1092 (@smfreegard)

Bug Fixes

  • fix windows build and test failures #1076 (@msimerson)
  • Fix plugin ordering #1081 (@smfreegard)
  • Fix distance reporting to X-Haraka-GeoIP for geoip-lite #1086 (@smfreegard)
  • uribl: prevent calling next() more than 1x #1138 (@msimerson)
  • Fix so constants are imported when plugin is loaded from node_modules. #1133 (@bmonty)
  • Include STMP-code in bounce-reason string for upstream 5XX responses #1117 (@hatsebutz)
  • TLS fixes: add timed_out flag and karma should not run deny hook on it. #1109 (@smfreegard)
  • Fix port to number instead of string for HAProxy #1108 (@DarkSorrow)
  • Plugin dcc: fixed syntax error #1164 (@hatsebutz)
  • config: fix flat files if \r\n lines #1187 (@Dexus)
  • corrected hook_rcpt log code hook_rcpt_ok returns CONT
  • fix crash bug when loglevel = LOGDEBUG
  • corrected pathname in rcpt.ldap plugin (@abhas)
  • added helo.checks boolean for proto_mismatch
  • make rate_limit redis keys always expire @celesteking
  • dkim_sign: Buffer.concat expects an array of buffers
  • transaction: check discard_data before adding line end (@DarkSorrow)
  • fix 8-bit msg not displayed properly in gmail
  • fcrdns: always init results
  • TLS timer on error
  • dkim_verify: fixed timeout issue
  • smtp_[proxy|forward]: correct authentication example
  • Fork child workers after init_master hook
  • connection: return 450/550 for plugin DENY* (was 452/552)
  • spamassassin: don’t call next() when transaction gone
  • outbound: fix crash when sending bounce mail
  • auth_base: fix bad protocol in auth_base.js #1121 (@Dexus)
  • outbound: Fix HELO/rDNS issue while using multiple outbound ip #1128 (@Dexus)
  • connection: Fix bug when client disconnect after sending data #1193
  • Fix connect.geoip bug #1144 (@smfreegard)
  • Fix tiny bug in messagesniffer #1198 (@smfreegard)

When upgrading, please ensure to copy over the new public suffix lists from the installation directory.