Haraka 3.0.5 → 3.1.1
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/CONTRIBUTORS.md +6 -5
- package/Changes.md +179 -27
- package/LICENSE +1 -1
- package/Plugins.md +3 -3
- package/bin/haraka +3 -1
- package/config/connection.ini +63 -0
- package/config/smtp.ini +0 -18
- package/connection.js +53 -63
- package/docs/CoreConfig.md +17 -78
- package/docs/HAProxy.md +10 -28
- package/docs/Outbound.md +8 -9
- package/docs/plugins/aliases.md +4 -6
- package/docs/plugins/block_me.md +2 -4
- package/docs/plugins/data.signatures.md +2 -4
- package/docs/plugins/max_unrecognized_commands.md +2 -4
- package/docs/plugins/prevent_credential_leaks.md +2 -5
- package/docs/plugins/process_title.md +1 -2
- package/docs/plugins/queue/test.md +9 -0
- package/docs/plugins/rcpt_to.in_host_list.md +1 -2
- package/docs/plugins/rcpt_to.max_count.md +2 -10
- package/docs/plugins/record_envelope_addresses.md +3 -6
- package/docs/plugins/relay.md +1 -155
- package/docs/plugins/reseed_rng.md +1 -2
- package/docs/plugins/tarpit.md +7 -10
- package/docs/plugins/toobusy.md +2 -4
- package/docs/plugins/xclient.md +1 -2
- package/eslint.config.mjs +27 -0
- package/logger.js +1 -8
- package/outbound/client_pool.js +8 -1
- package/outbound/hmail.js +8 -1
- package/package.json +61 -53
- package/plugins/auth/auth_base.js +0 -1
- package/plugins/queue/test.js +5 -3
- package/server.js +0 -14
- package/test/connection.js +18 -7
- package/.eslintrc.yaml +0 -11
- package/config/connection_close_message +0 -1
- package/config/databytes +0 -1
- package/config/dhparams.pem +0 -8
- package/config/early_talker.ini +0 -11
- package/config/mail_from.is_resolvable.ini +0 -6
- package/config/max_unrecognized_commands +0 -1
- package/config/smtp.json +0 -17
- package/docs/plugins/early_talker.md +0 -22
- package/docs/plugins/mail_from.is_resolvable.md +0 -29
- package/plugins/early_talker.js +0 -155
- package/plugins/mail_from.is_resolvable.js +0 -132
- package/plugins/relay.js +0 -207
- package/test/plugins/early_talker.js +0 -104
- package/test/plugins/mail_from.is_resolvable.js +0 -35
- package/test/plugins/relay.js +0 -303
- /package/docs/{plugins → deprecated}/access.md +0 -0
- /package/docs/{plugins → deprecated}/backscatterer.md +0 -0
- /package/docs/{plugins → deprecated}/data.headers.md +0 -0
package/connection.js
CHANGED
|
@@ -24,33 +24,33 @@ const outbound = require('./outbound');
|
|
|
24
24
|
|
|
25
25
|
const states = constants.connection.state;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
const cfg = config.get('connection.ini', {
|
|
28
|
+
booleans: [
|
|
29
|
+
'-main.strict_rfc1869',
|
|
30
|
+
'+main.smtputf8',
|
|
31
|
+
'+headers.add_received',
|
|
32
|
+
'+headers.show_version',
|
|
33
|
+
'+headers.clean_auth_results',
|
|
34
|
+
]
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const haproxy_hosts_ipv4 = [];
|
|
38
|
+
const haproxy_hosts_ipv6 = [];
|
|
39
|
+
|
|
40
|
+
for (const ip of cfg.haproxy.hosts) {
|
|
41
|
+
if (!ip) continue;
|
|
42
|
+
if (net.isIPv6(ip.split('/')[0])) {
|
|
43
|
+
haproxy_hosts_ipv6.push([ipaddr.IPv6.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 64)]);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
haproxy_hosts_ipv4.push([ipaddr.IPv4.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 32)]);
|
|
43
47
|
}
|
|
44
|
-
haproxy_hosts_ipv4 = new_ipv4_hosts;
|
|
45
|
-
haproxy_hosts_ipv6 = new_ipv6_hosts;
|
|
46
48
|
}
|
|
47
|
-
loadHAProxyHosts();
|
|
48
49
|
|
|
49
50
|
class Connection {
|
|
50
|
-
constructor (client, server,
|
|
51
|
+
constructor (client, server, smtp_cfg) {
|
|
51
52
|
this.client = client;
|
|
52
53
|
this.server = server;
|
|
53
|
-
this.cfg = cfg;
|
|
54
54
|
|
|
55
55
|
this.local = {
|
|
56
56
|
ip: null,
|
|
@@ -97,10 +97,6 @@ class Connection {
|
|
|
97
97
|
this.transaction = null;
|
|
98
98
|
this.tran_count = 0;
|
|
99
99
|
this.capabilities = null;
|
|
100
|
-
this.ehlo_hello_message = config.get('ehlo_hello_message') || 'Haraka is at your service.';
|
|
101
|
-
this.connection_close_message = config.get('connection_close_message') || 'closing connection. Have a jolly good day.';
|
|
102
|
-
this.banner_includes_uuid = !!config.get('banner_includes_uuid');
|
|
103
|
-
this.deny_includes_uuid = config.get('deny_includes_uuid') || null;
|
|
104
100
|
this.early_talker = false;
|
|
105
101
|
this.pipelining = false;
|
|
106
102
|
this._relaying = false;
|
|
@@ -109,8 +105,6 @@ class Connection {
|
|
|
109
105
|
this.hooks_to_run = [];
|
|
110
106
|
this.start_time = Date.now();
|
|
111
107
|
this.last_reject = '';
|
|
112
|
-
this.max_bytes = parseInt(config.get('databytes')) || 0;
|
|
113
|
-
this.max_mime_parts = parseInt(config.get('max_mime_parts')) || 1000;
|
|
114
108
|
this.totalbytes = 0;
|
|
115
109
|
this.rcpt_count = {
|
|
116
110
|
accept: 0,
|
|
@@ -122,13 +116,11 @@ class Connection {
|
|
|
122
116
|
tempfail: 0,
|
|
123
117
|
reject: 0,
|
|
124
118
|
};
|
|
125
|
-
this.max_line_length = parseInt(config.get('max_line_length')) || 512;
|
|
126
|
-
this.max_data_line_length = parseInt(config.get('max_data_line_length')) || 992;
|
|
127
119
|
this.results = new ResultStore(this);
|
|
128
120
|
this.errors = 0;
|
|
129
121
|
this.last_rcpt_msg = null;
|
|
130
122
|
this.hook = null;
|
|
131
|
-
if (
|
|
123
|
+
if (cfg.headers.show_version) {
|
|
132
124
|
this.local.info += `/${utils.getVersion(__dirname)}`;
|
|
133
125
|
}
|
|
134
126
|
Connection.setupClient(this);
|
|
@@ -200,7 +192,6 @@ class Connection {
|
|
|
200
192
|
});
|
|
201
193
|
|
|
202
194
|
const ha_list = net.isIPv6(self.remote.ip) ? haproxy_hosts_ipv6 : haproxy_hosts_ipv4;
|
|
203
|
-
|
|
204
195
|
if (ha_list.some((element, index, array) => {
|
|
205
196
|
return ipaddr.parse(self.remote.ip).match(element[0], element[1]);
|
|
206
197
|
})) {
|
|
@@ -220,7 +211,7 @@ class Connection {
|
|
|
220
211
|
this.set('hello', 'host', undefined);
|
|
221
212
|
this.set('tls', 'enabled', true);
|
|
222
213
|
for (const t of ['cipher','verified','verifyError','peerCertificate']) {
|
|
223
|
-
if (obj[t] === undefined)
|
|
214
|
+
if (obj[t] === undefined) continue;
|
|
224
215
|
this.set('tls', t, obj[t]);
|
|
225
216
|
}
|
|
226
217
|
// prior to 2017-07, authorized and verified were both used. Verified
|
|
@@ -401,10 +392,10 @@ class Connection {
|
|
|
401
392
|
|
|
402
393
|
let maxlength;
|
|
403
394
|
if (this.state === states.PAUSE_DATA || this.state === states.DATA) {
|
|
404
|
-
maxlength =
|
|
395
|
+
maxlength = cfg.max.data_line_length;
|
|
405
396
|
}
|
|
406
397
|
else {
|
|
407
|
-
maxlength =
|
|
398
|
+
maxlength = cfg.max.line_length;
|
|
408
399
|
}
|
|
409
400
|
|
|
410
401
|
let offset;
|
|
@@ -535,10 +526,10 @@ class Connection {
|
|
|
535
526
|
|
|
536
527
|
if (code >= 400) {
|
|
537
528
|
this.last_reject = `${code} ${messages.join(' ')}`;
|
|
538
|
-
if (
|
|
529
|
+
if (cfg.uuid.deny_chars) {
|
|
539
530
|
uuid = (this.transaction || this).uuid;
|
|
540
|
-
if (
|
|
541
|
-
uuid = uuid.substr(0,
|
|
531
|
+
if (cfg.uuid.deny_chars > 1) {
|
|
532
|
+
uuid = uuid.substr(0, cfg.uuid.deny_chars);
|
|
542
533
|
}
|
|
543
534
|
}
|
|
544
535
|
}
|
|
@@ -649,7 +640,7 @@ class Connection {
|
|
|
649
640
|
}
|
|
650
641
|
init_transaction (cb) {
|
|
651
642
|
this.reset_transaction(() => {
|
|
652
|
-
this.transaction = trans.createTransaction(this.tran_uuid(),
|
|
643
|
+
this.transaction = trans.createTransaction(this.tran_uuid(), cfg);
|
|
653
644
|
// Catch any errors from the message_stream
|
|
654
645
|
this.transaction.message_stream.on('error', (err) => {
|
|
655
646
|
this.logcrit(`message_stream error: ${err.message}`);
|
|
@@ -792,19 +783,20 @@ class Connection {
|
|
|
792
783
|
});
|
|
793
784
|
break;
|
|
794
785
|
default: {
|
|
795
|
-
let greeting
|
|
796
|
-
if (greeting
|
|
786
|
+
let greeting;
|
|
787
|
+
if (cfg.message.greeting?.length) {
|
|
797
788
|
// RFC5321 section 4.2
|
|
798
789
|
// Hostname/domain should appear after the 220
|
|
790
|
+
greeting = [...cfg.message.greeting];
|
|
799
791
|
greeting[0] = `${this.local.host} ESMTP ${greeting[0]}`;
|
|
800
|
-
if (
|
|
801
|
-
greeting[0] += ` (${this.uuid})`;
|
|
792
|
+
if (cfg.uuid.banner_chars) {
|
|
793
|
+
greeting[0] += ` (${this.uuid.substr(0, cfg.uuid.banner_chars)})`;
|
|
802
794
|
}
|
|
803
795
|
}
|
|
804
796
|
else {
|
|
805
797
|
greeting = `${this.local.host} ESMTP ${this.local.info} ready`;
|
|
806
|
-
if (
|
|
807
|
-
greeting += ` (${this.uuid})`;
|
|
798
|
+
if (cfg.uuid.banner_chars) {
|
|
799
|
+
greeting += ` (${this.uuid.substr(0, cfg.uuid.banner_chars)})`;
|
|
808
800
|
}
|
|
809
801
|
}
|
|
810
802
|
this.respond(220, msg || greeting);
|
|
@@ -850,7 +842,7 @@ class Connection {
|
|
|
850
842
|
default:
|
|
851
843
|
// RFC5321 section 4.1.1.1
|
|
852
844
|
// Hostname/domain should appear after 250
|
|
853
|
-
this.respond(250, `${this.local.host} Hello ${this.get_remote('host')}, ${
|
|
845
|
+
this.respond(250, `${this.local.host} Hello ${this.get_remote('host')}, ${cfg.message.helo}`);
|
|
854
846
|
}
|
|
855
847
|
}
|
|
856
848
|
ehlo_respond (retval, msg) {
|
|
@@ -883,16 +875,14 @@ class Connection {
|
|
|
883
875
|
// Hostname/domain should appear after 250
|
|
884
876
|
|
|
885
877
|
const response = [
|
|
886
|
-
`${this.local.host} Hello ${this.get_remote('host')}, ${
|
|
878
|
+
`${this.local.host} Hello ${this.get_remote('host')}, ${cfg.message.helo}`,
|
|
887
879
|
"PIPELINING",
|
|
888
880
|
"8BITMIME",
|
|
889
881
|
];
|
|
890
882
|
|
|
891
|
-
if (
|
|
892
|
-
response.push("SMTPUTF8");
|
|
893
|
-
}
|
|
883
|
+
if (cfg.main.smtputf8) response.push("SMTPUTF8");
|
|
894
884
|
|
|
895
|
-
response.push(`SIZE ${
|
|
885
|
+
response.push(`SIZE ${cfg.max.bytes}`);
|
|
896
886
|
|
|
897
887
|
this.capabilities = response;
|
|
898
888
|
|
|
@@ -905,7 +895,7 @@ class Connection {
|
|
|
905
895
|
this.respond(250, this.capabilities);
|
|
906
896
|
}
|
|
907
897
|
quit_respond (retval, msg) {
|
|
908
|
-
this.respond(221, msg || `${this.local.host} ${
|
|
898
|
+
this.respond(221, msg || `${this.local.host} ${cfg.message.close}`, () => {
|
|
909
899
|
this.disconnect();
|
|
910
900
|
});
|
|
911
901
|
}
|
|
@@ -1314,7 +1304,7 @@ class Connection {
|
|
|
1314
1304
|
|
|
1315
1305
|
let results;
|
|
1316
1306
|
try {
|
|
1317
|
-
results = rfc1869.parse('mail', line, this.cfg.main.strict_rfc1869
|
|
1307
|
+
results = rfc1869.parse('mail', line, (!this.relaying && cfg.main.strict_rfc1869));
|
|
1318
1308
|
}
|
|
1319
1309
|
catch (err) {
|
|
1320
1310
|
this.errors++;
|
|
@@ -1356,7 +1346,7 @@ class Connection {
|
|
|
1356
1346
|
|
|
1357
1347
|
// Handle SIZE extension
|
|
1358
1348
|
if (params?.SIZE && params.SIZE > 0) {
|
|
1359
|
-
if (
|
|
1349
|
+
if (cfg.max.bytes > 0 && params.SIZE > cfg.max.bytes) {
|
|
1360
1350
|
return this.respond(550, 'Message too big!');
|
|
1361
1351
|
}
|
|
1362
1352
|
}
|
|
@@ -1378,7 +1368,7 @@ class Connection {
|
|
|
1378
1368
|
|
|
1379
1369
|
let results;
|
|
1380
1370
|
try {
|
|
1381
|
-
results = rfc1869.parse('rcpt', line,
|
|
1371
|
+
results = rfc1869.parse('rcpt', line, cfg.main.strict_rfc1869 && !this.relaying);
|
|
1382
1372
|
}
|
|
1383
1373
|
catch (err) {
|
|
1384
1374
|
this.errors++;
|
|
@@ -1512,7 +1502,7 @@ class Connection {
|
|
|
1512
1502
|
return this.respond(503, "RCPT required first");
|
|
1513
1503
|
}
|
|
1514
1504
|
|
|
1515
|
-
if (
|
|
1505
|
+
if (cfg.headers.add_received) {
|
|
1516
1506
|
this.accumulate_data(`Received: ${this.received_line()}\r\n`);
|
|
1517
1507
|
}
|
|
1518
1508
|
plugins.run_hooks('data', this);
|
|
@@ -1577,11 +1567,11 @@ class Connection {
|
|
|
1577
1567
|
}
|
|
1578
1568
|
|
|
1579
1569
|
// Stop accumulating data as we're going to reject at dot.
|
|
1580
|
-
if (
|
|
1570
|
+
if (cfg.max.bytes && this.transaction.data_bytes > cfg.max.bytes) {
|
|
1581
1571
|
return;
|
|
1582
1572
|
}
|
|
1583
1573
|
|
|
1584
|
-
if (this.transaction.mime_part_count >=
|
|
1574
|
+
if (this.transaction.mime_part_count >= cfg.max.mime_parts) {
|
|
1585
1575
|
this.logcrit("Possible DoS attempt - too many MIME parts");
|
|
1586
1576
|
this.respond(554, "Transaction failed due to too many MIME parts", () => {
|
|
1587
1577
|
this.disconnect();
|
|
@@ -1596,13 +1586,13 @@ class Connection {
|
|
|
1596
1586
|
this.totalbytes += this.transaction.data_bytes;
|
|
1597
1587
|
|
|
1598
1588
|
// Check message size limit
|
|
1599
|
-
if (
|
|
1600
|
-
this.lognotice(`Incoming message exceeded
|
|
1589
|
+
if (cfg.max.bytes && this.transaction.data_bytes > cfg.max.bytes) {
|
|
1590
|
+
this.lognotice(`Incoming message exceeded max size of ${cfg.max.bytes}`);
|
|
1601
1591
|
return plugins.run_hooks('max_data_exceeded', this);
|
|
1602
1592
|
}
|
|
1603
1593
|
|
|
1604
1594
|
// Check max received headers count
|
|
1605
|
-
if (this.transaction.header.get_all('received').length >
|
|
1595
|
+
if (this.transaction.header.get_all('received').length > cfg.headers.max_received) {
|
|
1606
1596
|
this.logerror("Incoming message had too many Received headers");
|
|
1607
1597
|
this.respond(550, "Too many received headers - possible mail loop", () => {
|
|
1608
1598
|
this.reset_transaction();
|
|
@@ -1611,11 +1601,11 @@ class Connection {
|
|
|
1611
1601
|
}
|
|
1612
1602
|
|
|
1613
1603
|
// Warn if we hit the maximum parsed header lines limit
|
|
1614
|
-
if (this.transaction.header_lines.length >=
|
|
1615
|
-
this.logwarn(`Incoming message reached maximum parsing limit of ${
|
|
1604
|
+
if (this.transaction.header_lines.length >= cfg.headers.max_lines) {
|
|
1605
|
+
this.logwarn(`Incoming message reached maximum parsing limit of ${cfg.headers.max_lines} header lines`);
|
|
1616
1606
|
}
|
|
1617
1607
|
|
|
1618
|
-
if (
|
|
1608
|
+
if (cfg.headers.clean_auth_results) {
|
|
1619
1609
|
this.auth_results_clean(); // rename old A-R headers
|
|
1620
1610
|
}
|
|
1621
1611
|
const ar_field = this.auth_results(); // assemble new one
|
package/docs/CoreConfig.md
CHANGED
|
@@ -11,10 +11,6 @@ If either of these files exist then they are loaded first.
|
|
|
11
11
|
This file is designed to use the JSON/YAML file overrides documented in
|
|
12
12
|
[haraka-config](https://github.com/haraka/haraka-config) to optionally provide the entire configuration in a single file.
|
|
13
13
|
|
|
14
|
-
* databytes
|
|
15
|
-
|
|
16
|
-
Contains the maximum SIZE of an email that Haraka will receive.
|
|
17
|
-
|
|
18
14
|
* plugins
|
|
19
15
|
|
|
20
16
|
The list of plugins to load
|
|
@@ -40,35 +36,29 @@ The list of plugins to load
|
|
|
40
36
|
specify -1 to disable spooling completely or 0 to force all messages to be spooled to disk.
|
|
41
37
|
* graceful\_shutdown - (default: false) enable this to wait for sockets on shutdown instead of closing them quickly
|
|
42
38
|
* force_shutdown_timeout - (default: 30) number of seconds to wait for a graceful shutdown
|
|
43
|
-
* smtputf8 - (default: true) advertise support for SMTPUTF8
|
|
44
|
-
* strict\_rfc1869 - (default: false) Requires senders to conform to RFC 1869 and RFC 821 when sending the MAIL FROM and RCPT TO commands. In particular,
|
|
45
|
-
the inclusion of spurious spaces or missing angle brackets will be rejected.
|
|
46
39
|
|
|
47
40
|
* me
|
|
48
41
|
|
|
49
42
|
A name to use for this server. Used in received lines and elsewhere. Setup
|
|
50
43
|
by default to be your hostname.
|
|
51
44
|
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
Each connection and mail in Haraka includes a UUID which is also in most log
|
|
55
|
-
messages. If you put a `1` in this file then every denied mail (either via
|
|
56
|
-
DENY/5xx or DENYSOFT/4xx return codes) will include the uuid at the start
|
|
57
|
-
of each line of the deny message in brackets, making it easy to track
|
|
58
|
-
problems back to the logs.
|
|
59
|
-
|
|
60
|
-
Because UUIDs are long, if you put a number greater than 1 in the config
|
|
61
|
-
file, it will be truncated to that length. We recommend a 6 as a good
|
|
62
|
-
balance of finding in the logs and not making lines too long.
|
|
63
|
-
|
|
64
|
-
* banner\_includes\_uuid
|
|
65
|
-
|
|
66
|
-
This will add the full UUID to the first line of the SMTP greeting banner.
|
|
45
|
+
* connection.ini
|
|
67
46
|
|
|
68
|
-
|
|
47
|
+
See inline comments in connection.ini for the following settings:
|
|
69
48
|
|
|
70
|
-
|
|
71
|
-
|
|
49
|
+
* haproxy.hosts\_ipv4
|
|
50
|
+
* haproxy.hosts\_ipv6
|
|
51
|
+
* headers.\*
|
|
52
|
+
* max.bytes
|
|
53
|
+
* max.line\_length
|
|
54
|
+
* max.data\_line\_length
|
|
55
|
+
* max.mime\_parts
|
|
56
|
+
* message.greeting
|
|
57
|
+
* message.close
|
|
58
|
+
* smtputf8
|
|
59
|
+
* strict\_rfc1869
|
|
60
|
+
* uuid.deny\_chars
|
|
61
|
+
* uuid.banner\_bytes
|
|
72
62
|
|
|
73
63
|
* plugin\_timeout
|
|
74
64
|
|
|
@@ -81,59 +71,8 @@ The list of plugins to load
|
|
|
81
71
|
If the plugin is in a sub-directory of plugins, then you must create this file
|
|
82
72
|
in the equivalent path e.g. the queue/smtp_forward would need a timeout file in `config/queue/smtp_forward.timeout`
|
|
83
73
|
|
|
84
|
-
*
|
|
85
|
-
|
|
86
|
-
The greeting line used when a client connects. This can be multiple lines
|
|
87
|
-
if required (this may cause some connecting machines to fail - though
|
|
88
|
-
usually only spam-bots).
|
|
89
|
-
|
|
90
|
-
* max\_received\_count
|
|
91
|
-
|
|
92
|
-
The maximum number of "Received" headers allowed in an email. This is a
|
|
93
|
-
simple protection against mail loops. Defaults to 100.
|
|
94
|
-
|
|
95
|
-
* max\_line\_length
|
|
96
|
-
|
|
97
|
-
The maximum length of lines in SMTP session commands (e.g. RCPT, HELO etc).
|
|
98
|
-
Defaults to 512 (bytes) which is mandated by RFC 5321 §4.5.3.1.4. Clients
|
|
99
|
-
exceeding this limit will be immediately disconnected with a "521 Command
|
|
100
|
-
line too long" error.
|
|
101
|
-
|
|
102
|
-
* max\_data\_line\_length
|
|
103
|
-
|
|
104
|
-
The maximum length of lines in the DATA section of emails. Defaults to 992
|
|
105
|
-
(bytes) which is the limit set by Sendmail. When this limit is exceeded the
|
|
106
|
-
three bytes "\r\n " (0x0d 0x0a 0x20) are inserted into the stream to "fix"
|
|
107
|
-
it. This has the potential to "break" some email, but makes it more likely
|
|
108
|
-
to be accepted by upstream/downstream services, and is the same behaviour
|
|
109
|
-
as Sendmail. Also when the data line length limit is exceeded
|
|
110
|
-
`transaction.notes.data_line_length_exceeded` is set to `true`.
|
|
111
|
-
|
|
112
|
-
* outbound.concurrency\_max
|
|
113
|
-
|
|
114
|
-
Maximum concurrency to use when delivering mails outbound. Defaults to 100.
|
|
115
|
-
|
|
116
|
-
* outbound.disabled
|
|
117
|
-
|
|
118
|
-
Put a `1` in this file to temporarily disable outbound delivery. Useful
|
|
119
|
-
while figuring out network issues or testing.
|
|
74
|
+
* outbound.ini
|
|
120
75
|
|
|
121
76
|
* outbound.bounce\_message
|
|
122
77
|
|
|
123
|
-
The bounce message if delivery of the message fails. The default is normally fine. Bounce messages contain a number of template
|
|
124
|
-
replacement values which are best discovered by looking at the source code.
|
|
125
|
-
|
|
126
|
-
* haproxy\_hosts
|
|
127
|
-
|
|
128
|
-
A list of HAProxy hosts that Haraka should enable the PROXY protocol from.
|
|
129
|
-
See [HAProxy.md](HAProxy.md)
|
|
130
|
-
|
|
131
|
-
* max_mime_parts
|
|
132
|
-
|
|
133
|
-
Defaults to 1000. There's a potential denial of service in large numbers of
|
|
134
|
-
MIME parts in carefully crafted emails. If this limit is too low for some
|
|
135
|
-
reason you can increase it by setting a value in this file.
|
|
136
|
-
|
|
137
|
-
* connection\_close\_message
|
|
138
|
-
|
|
139
|
-
Defaults to `closing connection. Have a jolly good day.` can be overrridden with custom text
|
|
78
|
+
The bounce message if delivery of the message fails. The default is normally fine. Bounce messages contain a number of template replacement values which are best discovered by looking at the source code.
|
package/docs/HAProxy.md
CHANGED
|
@@ -1,34 +1,20 @@
|
|
|
1
|
-
HAProxy PROXY protocol extension support
|
|
2
|
-
========================================
|
|
1
|
+
# HAProxy PROXY protocol extension support
|
|
3
2
|
|
|
4
|
-
Haraka
|
|
3
|
+
Haraka supports PROXY protocol [1].
|
|
5
4
|
|
|
6
|
-
This allows an upstream proxy to pass IP address and port of the client which
|
|
7
|
-
Haraka will use instead of the socket IP address (which is of the proxy).
|
|
8
|
-
This allows DNSBLs and access control lists to operate on the proxied address.
|
|
5
|
+
This allows an upstream proxy to pass the IP address and port of the remote client. Haraka will use the remote IP instead of the socket IP address (which is the proxy). This allows DNSBLs and access control lists to use the correct source address.
|
|
9
6
|
|
|
10
|
-
Support is disabled by default
|
|
11
|
-
PROXY command then Haraka will return a DENYSOFTDISCONNECT error.
|
|
12
|
-
DENYSOFT is used to prevent configuration errors from rejecting valid mail.
|
|
7
|
+
Support is disabled by default. Attempts to send a PROXY command will return a DENYSOFTDISCONNECT error. DENYSOFT is used to prevent configuration errors from rejecting valid mail.
|
|
13
8
|
|
|
14
|
-
To enable support for PROXY you must
|
|
15
|
-
file which should contain a list of IP addresses of the HAProxy hosts
|
|
16
|
-
that should be allowed to send the PROXY command. A range of IP
|
|
17
|
-
addresses can be specified by it's CIDR network address.
|
|
9
|
+
To enable support for PROXY you must populate connection.ini[haproxy]hosts[] with the IP addresses of the HAProxy hosts that MUST send the PROXY command. Ranges can be specified with CIDR notation.
|
|
18
10
|
|
|
19
|
-
When a host connects to Haraka
|
|
20
|
-
`haproxy_hosts` file - a banner is not sent, instead Haraka waits for the
|
|
21
|
-
PROXY command to be sent before proceeding. The connection will timeout
|
|
22
|
-
with `421 PROXY timed out` if the command is not sent within 30 seconds.
|
|
11
|
+
When a proxy host connects to Haraka, a banner is not sent. Instead Haraka awaits the PROXY command. The connection will timeout with `421 PROXY timed out` if the command is not sent within 30 seconds.
|
|
23
12
|
|
|
24
|
-
NOTE: because Haraka does not send a banner when a listed HAProxy host
|
|
25
|
-
connects you must set check-send-proxy to ensure that the service checks
|
|
26
|
-
send a PROXY command before they run.
|
|
13
|
+
NOTE: because Haraka does not send a banner when a listed HAProxy host connects you must set check-send-proxy to ensure that the service checks send a PROXY command before they run.
|
|
27
14
|
|
|
28
15
|
[1] http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
|
|
29
16
|
|
|
30
|
-
HAProxy supports the PROXY protocol in version 1.5 or later
|
|
31
|
-
are patches available to add support for 1.4.
|
|
17
|
+
HAProxy supports the PROXY protocol in version 1.5 or later.
|
|
32
18
|
|
|
33
19
|
Here is an example listener section for haproxy.cfg:
|
|
34
20
|
|
|
@@ -45,13 +31,9 @@ listen smtp :25
|
|
|
45
31
|
server smtp5 ip.of.haraka.server5:25 check-send-proxy check inter 10s send-proxy
|
|
46
32
|
```
|
|
47
33
|
|
|
48
|
-
The important part is `send-proxy` which causes HAProxy to send the PROXY
|
|
49
|
-
extension on connection.
|
|
34
|
+
The important part is `send-proxy` which causes HAProxy to send the PROXY extension on connection.
|
|
50
35
|
|
|
51
|
-
When using `option smtpchk` you will see CONNRESET errors reported in the Haraka logs as
|
|
52
|
-
smtpchk drops the connection before the HELO response is still being written.
|
|
53
|
-
You can use the `option tcp-check` instead to provide a better service check by having
|
|
54
|
-
the check wait for the banner, send QUIT and then check the response:
|
|
36
|
+
When using `option smtpchk` you will see CONNRESET errors reported in the Haraka logs as smtpchk drops the connection before the HELO response is still being written. You can use the `option tcp-check` instead to provide a better service check by having the check wait for the banner, send QUIT and then check the response:
|
|
55
37
|
|
|
56
38
|
```
|
|
57
39
|
option tcp-check
|
package/docs/Outbound.md
CHANGED
|
@@ -51,8 +51,7 @@ Default: false. By default, outbound to a local IP is disabled, to avoid creatin
|
|
|
51
51
|
|
|
52
52
|
* `temp_fail_intervals`
|
|
53
53
|
|
|
54
|
-
Set this to specify the delay intervals to use between trying to re-send an email that has a temporary failure condition.
|
|
55
|
-
`[60,300,300,3600,3600,3600]` in seconds. The email will be bounced when the array runs out of intervals (the 7th failure in this case). Set this to `none` to bounce the email on the first temporary failure.
|
|
54
|
+
Set this to specify the delay intervals to use between trying to re-send an email that has a temporary failure condition. The setting is a comma separated list of time spans and multipliers. The time span is a number followed by `s`, `m`, `h`, or `d` to represent seconds, minutes, hours, and days, respectively. The multiplier is an asterisk followed by an integer representing the number of times to repeat the interval. For example, the entry `1m, 5m*2, 1h*3` results in an array of delay times of `[60,300,300,3600,3600,3600]` in seconds. The email will be bounced when the array runs out of intervals (the 7th failure in this case). Set this to `none` to bounce the email on the first temporary failure.
|
|
56
55
|
|
|
57
56
|
### outbound.bounce\_message
|
|
58
57
|
|
|
@@ -137,11 +136,11 @@ Parameters: `next, hmail, params`
|
|
|
137
136
|
Params is a list of: `[host, ip, response, delay, port, mode, ok_recips, secured]`
|
|
138
137
|
|
|
139
138
|
When mails are successfully delivered to the remote end then the `delivered` hook is called. The return codes from this hook have no effect, so it is only useful for logging the fact that a successful delivery occurred.
|
|
140
|
-
|
|
139
|
+
|
|
141
140
|
* `host` - Hostname of the MX that the message was delivered to,
|
|
142
141
|
* `ip` - IP address of the host that the message was delivered to,
|
|
143
142
|
* `response` - Variable contains the SMTP response text returned by the host that received the message and will typically contain the remote queue ID and
|
|
144
|
-
* `delay` - Time taken between the queue file being created and the
|
|
143
|
+
* `delay` - Time taken between the queue file being created and the message being delivered.
|
|
145
144
|
* `port` - Port number that the message was delivered to.
|
|
146
145
|
* `mode` - Shows whether SMTP or LMTP was used to deliver the mail.
|
|
147
146
|
* `ok_recips` - an `Address`<sup>[1](#fn1)</sup> array containing all of the recipients that were successfully delivered to.
|
|
@@ -149,7 +148,7 @@ When mails are successfully delivered to the remote end then the `delivered` hoo
|
|
|
149
148
|
|
|
150
149
|
## Outbound IP address
|
|
151
150
|
|
|
152
|
-
Normally the OS will decide which IP address will be used for outbound
|
|
151
|
+
Normally the OS will decide which IP address will be used for outbound connections using the IP routing table.
|
|
153
152
|
|
|
154
153
|
There are instances where you may want to separate outbound traffic on different IP addresses based on sender, domain or some other identifier. To do this, the IP address that you want to use *must* be bound to an interface (or alias) on the local system.
|
|
155
154
|
|
|
@@ -235,9 +234,9 @@ Where `options` is a Object that may contain the following keys:
|
|
|
235
234
|
|------------------------|-------------|
|
|
236
235
|
| `dot_stuffed: true` | Use this if you are passing your content dot-stuffed (a dot at the start of a line is doubled, like it is in SMTP conversation, see [RFC 2821][url-rfc2821].|
|
|
237
236
|
| `notes: { key: value}` | In case you need notes in the new transaction that `send_email()` creates. |
|
|
238
|
-
| `remove_msgid: true` | Remove any Message-Id header found in the message.
|
|
239
|
-
| `remove_date: true` | Remove any Date header found in the message.
|
|
240
|
-
| `origin: Object` | Adds object as argument to logger.log calls inside outbound.send_email. Useful for tracking which Plugin/Connection/HMailItem object generated email. |
|
|
237
|
+
| `remove_msgid: true` | Remove any Message-Id header found in the message. If you are reading a message in from the filesystem and you want to ensure that a generated Message-Id header is used in preference over the original. This is useful if you are releasing mail from a quarantine. |
|
|
238
|
+
| `remove_date: true` | Remove any Date header found in the message. If you are reading a message in from the filesystem and you want to ensure that a generated Date header is used in preference over the original. This is useful if you are releasing mail from a quarantine. |
|
|
239
|
+
| `origin: Object` | Adds object as argument to logger.log calls inside outbound.send_email. Useful for tracking which Plugin/Connection/HMailItem object generated email. |
|
|
241
240
|
|
|
242
241
|
|
|
243
242
|
```js
|
|
@@ -248,4 +247,4 @@ outbound.send_email(from, to, contents, outnext, { notes: transaction.notes });
|
|
|
248
247
|
|
|
249
248
|
[url-tls]: https://haraka.github.io/plugins/tls
|
|
250
249
|
[url-harakamx]: https://github.com/haraka/haraka-net-utils?tab=readme-ov-file#harakamx
|
|
251
|
-
[url-rfc2821]: https://tools.ietf.org/html/rfc2821#section-4.5.2
|
|
250
|
+
[url-rfc2821]: https://tools.ietf.org/html/rfc2821#section-4.5.2
|
package/docs/plugins/aliases.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
aliases
|
|
2
|
-
=======
|
|
1
|
+
# aliases
|
|
3
2
|
|
|
4
3
|
This plugin allows one to configure aliases that may perform an action or
|
|
5
4
|
change the RCPT address in a number of ways. All aliases are specified in
|
|
@@ -12,8 +11,7 @@ that run on hook_rcpt
|
|
|
12
11
|
|
|
13
12
|
WARNING: DO NOT USE THIS PLUGIN WITH queue/smtp\_proxy.
|
|
14
13
|
|
|
15
|
-
Configuration
|
|
16
|
-
-------------
|
|
14
|
+
## Configuration
|
|
17
15
|
|
|
18
16
|
* aliases
|
|
19
17
|
|
|
@@ -135,8 +133,8 @@ Configuration
|
|
|
135
133
|
aliases on a single domain that map to other local parts at the
|
|
136
134
|
same domain.
|
|
137
135
|
|
|
138
|
-
Example Configuration
|
|
139
|
-
|
|
136
|
+
### Example Configuration
|
|
137
|
+
|
|
140
138
|
{
|
|
141
139
|
"test1" : { "action" : "drop" },
|
|
142
140
|
"test2" : { "action" : "drop" },
|
package/docs/plugins/block_me.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
block\_me
|
|
2
|
-
========
|
|
1
|
+
# block\_me
|
|
3
2
|
|
|
4
3
|
This plugin allows you to configure an address which mail sent to will be
|
|
5
4
|
parsed for a From: address in the body of the message, and will add that
|
|
@@ -10,8 +9,7 @@ particular mailbox to block them in the future.
|
|
|
10
9
|
|
|
11
10
|
Note that this is a system-wide block, and not per-user. Be careful with this.
|
|
12
11
|
|
|
13
|
-
Configuration
|
|
14
|
-
-------------
|
|
12
|
+
## Configuration
|
|
15
13
|
|
|
16
14
|
* `config/block_me.recipient` - a file containing the address to email to
|
|
17
15
|
get something blocked. For example: **spam@domain.com**.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
data.signatures
|
|
2
|
-
===============
|
|
1
|
+
# data.signatures
|
|
3
2
|
|
|
4
3
|
This plugin allows you to add string signatures to a configuration file and
|
|
5
4
|
have this plugin scan the body text of an email for those strings. Mails
|
|
6
5
|
matching these signatures will be blocked.
|
|
7
6
|
|
|
8
|
-
Configuration
|
|
9
|
-
-------------
|
|
7
|
+
## Configuration
|
|
10
8
|
|
|
11
9
|
* data.signatures
|
|
12
10
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
max\_unrecognized\_commands
|
|
2
|
-
=========================
|
|
1
|
+
# max\_unrecognized\_commands
|
|
3
2
|
|
|
4
3
|
This plugin places a maximum limit on the number of unrecognized commands
|
|
5
4
|
allowed before recognising that the connection is bad.
|
|
@@ -13,8 +12,7 @@ runs after any plugins that use the unrecognized_command hook to implement
|
|
|
13
12
|
other SMTP verbs and extensions (such as the auth/* plugins), otherwise
|
|
14
13
|
commands valid for these plugins will be counted as unknown by this plugin.
|
|
15
14
|
|
|
16
|
-
Configuration
|
|
17
|
-
-------------
|
|
15
|
+
## Configuration
|
|
18
16
|
|
|
19
17
|
* max\_unrecognized\_commands
|
|
20
18
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
prevent\_credential\_leaks
|
|
2
|
-
========
|
|
1
|
+
# prevent\_credential\_leaks
|
|
3
2
|
|
|
4
3
|
This plugin prevents an authenticated user (via SMTP AUTH) from sending
|
|
5
4
|
their username and password out in a message (e.g. like replying to a
|
|
@@ -16,9 +15,7 @@ Note that if the username is qualified e.g. user@domain.com - then the
|
|
|
16
15
|
plugin will search for both `user` and `user@domain.com` for maximum
|
|
17
16
|
effectiveness.
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
Configuration
|
|
21
|
-
-------------
|
|
18
|
+
## Configuration
|
|
22
19
|
|
|
23
20
|
No configuration is required. Simply add the plugin to your `config/plugins`
|
|
24
21
|
file. It should be added before any other plugins that run on hook_data_post
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
queue/test
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
This plugin saves incoming E-Mail to your temporary directory, as `mail_{message_id}.eml`, where message_id is a UUID.
|
|
5
|
+
|
|
6
|
+
This plugin can be useful to quickly test if you're able to receive incoming E-Mail and just dump them to disk.
|
|
7
|
+
|
|
8
|
+
The temporary directory is determined using Node's [`os.tmpdir()`](https://nodejs.org/api/os.html#ostmpdir), which respects standard platform configurations.
|
|
9
|
+
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
rcpt\_to.max\_count
|
|
2
|
-
=================
|
|
1
|
+
# rcpt\_to.max\_count
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Configuration
|
|
7
|
-
-------------
|
|
8
|
-
|
|
9
|
-
* rcpt\_to.max\_count
|
|
10
|
-
|
|
11
|
-
The maximum number of recipients. Default: 40.
|
|
3
|
+
The functionality of this plugin was integrated in to [haraka-plugin-limit](https://github.com/haraka/haraka-plugin-limit).
|