Haraka 3.1.6 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -1
- package/CONTRIBUTORS.md +8 -8
- package/Plugins.md +99 -99
- package/address.js +53 -0
- package/bin/haraka +1 -1
- package/config/smtp_forward.ini +10 -0
- package/config/smtp_proxy.ini +10 -0
- package/connection.js +28 -11
- package/docs/Outbound.md +1 -1
- package/docs/Transaction.md +1 -1
- package/docs/plugins/queue/smtp_forward.md +19 -3
- package/docs/plugins/queue/smtp_proxy.md +10 -2
- package/docs/plugins/status.md +21 -5
- package/haraka.js +1 -1
- package/outbound/hmail.js +41 -41
- package/outbound/index.js +5 -5
- package/outbound/queue.js +1 -1
- package/outbound/tls.js +2 -43
- package/package.json +48 -48
- package/plugins/auth/auth_base.js +9 -3
- package/plugins/auth/auth_proxy.js +14 -11
- package/plugins/block_me.js +6 -4
- package/plugins/prevent_credential_leaks.js +3 -1
- package/plugins/process_title.js +6 -6
- package/plugins/queue/qmail-queue.js +15 -19
- package/plugins/queue/smtp_forward.js +14 -6
- package/plugins/queue/smtp_proxy.js +14 -3
- package/plugins/rcpt_to.host_list_base.js +1 -1
- package/plugins/record_envelope_addresses.js +2 -2
- package/plugins/status.js +34 -5
- package/plugins/tls.js +13 -5
- package/plugins/xclient.js +3 -1
- package/server.js +5 -3
- package/smtp_client.js +20 -11
- package/test/config/block_me.recipient +1 -0
- package/test/config/block_me.senders +1 -0
- package/test/connection.js +25 -1
- package/test/fixtures/util_hmailitem.js +1 -1
- package/test/outbound/bounce_net_errors.js +3 -2
- package/test/outbound/index.js +2 -2
- package/test/plugins/auth/auth_base.js +1 -1
- package/test/plugins/auth/auth_bridge.js +80 -0
- package/test/plugins/auth/flat_file.js +128 -0
- package/test/plugins/block_me.js +157 -0
- package/test/plugins/data.signatures.js +114 -0
- package/test/plugins/delay_deny.js +263 -0
- package/test/plugins/prevent_credential_leaks.js +178 -0
- package/test/plugins/process_title.js +135 -0
- package/test/plugins/queue/deliver.js +99 -0
- package/test/plugins/queue/discard.js +79 -0
- package/test/plugins/queue/lmtp.js +138 -0
- package/test/plugins/queue/qmail-queue.js +99 -0
- package/test/plugins/queue/quarantine.js +81 -0
- package/test/plugins/queue/smtp_bridge.js +154 -0
- package/test/plugins/queue/smtp_forward.js +43 -7
- package/test/plugins/queue/smtp_proxy.js +139 -0
- package/test/plugins/rcpt_to.host_list_base.js +1 -1
- package/test/plugins/rcpt_to.in_host_list.js +1 -1
- package/test/plugins/record_envelope_addresses.js +2 -2
- package/test/plugins/reseed_rng.js +34 -0
- package/test/plugins/status.js +71 -0
- package/test/plugins/tarpit.js +91 -0
- package/test/plugins/tls.js +25 -0
- package/test/plugins/toobusy.js +21 -0
- package/test/plugins/xclient.js +14 -0
- package/test/server.js +59 -0
- package/test/smtp_client.js +46 -13
- package/test/tls_socket.js +82 -0
- package/tls_socket.js +50 -0
package/test/tls_socket.js
CHANGED
|
@@ -239,4 +239,86 @@ test('tls_socket', async (t) => {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
})
|
|
242
|
+
|
|
243
|
+
await t.test('load_plugin_tls_options', async (t) => {
|
|
244
|
+
// Point haraka-config at test/config so tls.ini fixtures load.
|
|
245
|
+
const origConfig = tls_socket.config
|
|
246
|
+
const origCfg = tls_socket.cfg
|
|
247
|
+
const test_config = require('haraka-config').module_config(path.resolve(__dirname))
|
|
248
|
+
|
|
249
|
+
t.beforeEach(() => {
|
|
250
|
+
tls_socket.config = test_config
|
|
251
|
+
tls_socket.cfg = undefined // bust load_tls_ini cache between cases
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
t.after(() => {
|
|
255
|
+
tls_socket.config = origConfig
|
|
256
|
+
tls_socket.cfg = origCfg
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
await t.test('inherits tls.ini [main] when plugin cfg is empty', () => {
|
|
260
|
+
const opts = tls_socket.load_plugin_tls_options({})
|
|
261
|
+
// From test/config/tls.ini [main]
|
|
262
|
+
assert.equal(opts.rejectUnauthorized, false)
|
|
263
|
+
assert.equal(opts.minVersion, 'TLSv1')
|
|
264
|
+
assert.equal(opts.honorCipherOrder, true)
|
|
265
|
+
assert.ok(opts.ciphers && opts.ciphers.length)
|
|
266
|
+
assert.ok(Buffer.isBuffer(opts.key), 'key resolved to Buffer')
|
|
267
|
+
assert.ok(Buffer.isBuffer(opts.cert), 'cert resolved to Buffer')
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
await t.test('plugin cfg overrides [main]', () => {
|
|
271
|
+
const opts = tls_socket.load_plugin_tls_options({
|
|
272
|
+
rejectUnauthorized: true,
|
|
273
|
+
minVersion: 'TLSv1.3',
|
|
274
|
+
ciphers: 'ECDHE-RSA-AES256-GCM-SHA384',
|
|
275
|
+
})
|
|
276
|
+
assert.equal(opts.rejectUnauthorized, true)
|
|
277
|
+
assert.equal(opts.minVersion, 'TLSv1.3')
|
|
278
|
+
assert.equal(opts.ciphers, 'ECDHE-RSA-AES256-GCM-SHA384')
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
await t.test('resolves key/cert/dhparam file refs to Buffers', () => {
|
|
282
|
+
const opts = tls_socket.load_plugin_tls_options({
|
|
283
|
+
key: 'outbound_tls_key.pem',
|
|
284
|
+
cert: 'outbound_tls_cert.pem',
|
|
285
|
+
dhparam: 'dhparams.pem',
|
|
286
|
+
})
|
|
287
|
+
assert.ok(Buffer.isBuffer(opts.key) && opts.key.length > 0)
|
|
288
|
+
assert.ok(Buffer.isBuffer(opts.cert) && opts.cert.length > 0)
|
|
289
|
+
assert.ok(Buffer.isBuffer(opts.dhparam) && opts.dhparam.length > 0)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
await t.test('drops missing dhparam rather than leaving null', () => {
|
|
293
|
+
const opts = tls_socket.load_plugin_tls_options({
|
|
294
|
+
dhparam: 'does_not_exist.pem',
|
|
295
|
+
})
|
|
296
|
+
assert.equal(opts.dhparam, undefined)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
await t.test('normalises no_tls_hosts / force_tls_hosts to arrays', () => {
|
|
300
|
+
const opts = tls_socket.load_plugin_tls_options({
|
|
301
|
+
no_tls_hosts: '10.0.0.5',
|
|
302
|
+
force_tls_hosts: ['a.example.com', 'b.example.com'],
|
|
303
|
+
})
|
|
304
|
+
assert.deepEqual(opts.no_tls_hosts, ['10.0.0.5'])
|
|
305
|
+
assert.deepEqual(opts.force_tls_hosts, ['a.example.com', 'b.example.com'])
|
|
306
|
+
|
|
307
|
+
const opts2 = tls_socket.load_plugin_tls_options({})
|
|
308
|
+
assert.deepEqual(opts2.no_tls_hosts, [])
|
|
309
|
+
assert.deepEqual(opts2.force_tls_hosts, [])
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
await t.test('does not set servername', () => {
|
|
313
|
+
const opts = tls_socket.load_plugin_tls_options({})
|
|
314
|
+
assert.equal(opts.servername, undefined)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
await t.test('does not mutate the input plugin cfg', () => {
|
|
318
|
+
const input = { rejectUnauthorized: true, no_tls_hosts: '10.0.0.5' }
|
|
319
|
+
const before = JSON.stringify(input)
|
|
320
|
+
tls_socket.load_plugin_tls_options(input)
|
|
321
|
+
assert.equal(JSON.stringify(input), before)
|
|
322
|
+
})
|
|
323
|
+
})
|
|
242
324
|
})
|
package/tls_socket.js
CHANGED
|
@@ -251,6 +251,56 @@ exports.load_tls_ini = (opts) => {
|
|
|
251
251
|
return cfg
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// Build a client tls_options, merges a consumers own [tls] section
|
|
255
|
+
// over tls.ini [main].
|
|
256
|
+
exports.load_plugin_tls_options = (plugin_tls_cfg = {}) => {
|
|
257
|
+
const tls_cfg = exports.load_tls_ini({ role: 'client' })
|
|
258
|
+
const cfg = JSON.parse(JSON.stringify(plugin_tls_cfg))
|
|
259
|
+
|
|
260
|
+
// Inheritance from tls.ini [main] deliberately omits no_tls_hosts: the
|
|
261
|
+
// [main].no_tls_hosts list is documented as inbound-only; outbound and
|
|
262
|
+
// queue plugins should opt in explicitly via their own section.
|
|
263
|
+
const inheritable_opts = [
|
|
264
|
+
'key',
|
|
265
|
+
'cert',
|
|
266
|
+
'ciphers',
|
|
267
|
+
'minVersion',
|
|
268
|
+
'dhparam',
|
|
269
|
+
'requestCert',
|
|
270
|
+
'honorCipherOrder',
|
|
271
|
+
'rejectUnauthorized',
|
|
272
|
+
'force_tls_hosts',
|
|
273
|
+
]
|
|
274
|
+
for (const opt of inheritable_opts) {
|
|
275
|
+
if (cfg[opt] !== undefined) continue // set in plugin [tls]
|
|
276
|
+
if (tls_cfg.main[opt] === undefined) continue // unset in tls.ini [main]
|
|
277
|
+
cfg[opt] = tls_cfg.main[opt]
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Resolve key/cert/dhparam file references to buffers. Drop empty results
|
|
281
|
+
// so we never pass null to tls.connect.
|
|
282
|
+
for (const k of ['key', 'cert', 'dhparam']) {
|
|
283
|
+
if (!cfg[k]) {
|
|
284
|
+
delete cfg[k]
|
|
285
|
+
continue
|
|
286
|
+
}
|
|
287
|
+
const ref = Array.isArray(cfg[k]) ? cfg[k][0] : cfg[k]
|
|
288
|
+
const bin = exports.config.get(ref, 'binary')
|
|
289
|
+
if (bin) cfg[k] = bin
|
|
290
|
+
else delete cfg[k]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
for (const k of ['no_tls_hosts', 'force_tls_hosts']) {
|
|
294
|
+
if (!cfg[k]) {
|
|
295
|
+
cfg[k] = []
|
|
296
|
+
continue
|
|
297
|
+
}
|
|
298
|
+
if (!Array.isArray(cfg[k])) cfg[k] = [cfg[k]]
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return cfg
|
|
302
|
+
}
|
|
303
|
+
|
|
254
304
|
exports.applySocketOpts = (name) => {
|
|
255
305
|
// https://nodejs.org/api/tls.html#tls_new_tls_tlssocket_socket_options
|
|
256
306
|
const TLSSocketOptions = [
|