Haraka 3.1.1 → 3.1.2
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/.prettierignore +4 -0
- package/CONTRIBUTORS.md +5 -5
- package/Changes.md +62 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -479
- package/config/outbound.ini +3 -0
- package/connection.js +1072 -1108
- package/docs/Connection.md +29 -30
- package/docs/CoreConfig.md +38 -39
- package/docs/CustomReturnCodes.md +0 -1
- package/docs/HAProxy.md +2 -2
- package/docs/Header.md +1 -1
- package/docs/Logging.md +29 -5
- package/docs/Outbound.md +93 -78
- package/docs/Plugins.md +103 -108
- package/docs/Transaction.md +49 -51
- package/docs/Tutorial.md +127 -143
- package/docs/deprecated/access.md +0 -1
- package/docs/deprecated/backscatterer.md +2 -3
- package/docs/deprecated/connect.rdns_access.md +18 -27
- package/docs/deprecated/data.headers.md +0 -1
- package/docs/deprecated/data.nomsgid.md +1 -2
- package/docs/deprecated/data.noreceived.md +1 -2
- package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
- package/docs/deprecated/dkim_sign.md +13 -17
- package/docs/deprecated/dkim_verify.md +9 -17
- package/docs/deprecated/dnsbl.md +36 -38
- package/docs/deprecated/dnswl.md +41 -43
- package/docs/deprecated/lookup_rdns.strict.md +21 -34
- package/docs/deprecated/mail_from.access.md +17 -25
- package/docs/deprecated/mail_from.blocklist.md +9 -12
- package/docs/deprecated/mail_from.nobounces.md +1 -2
- package/docs/deprecated/rcpt_to.access.md +20 -27
- package/docs/deprecated/rcpt_to.blocklist.md +10 -13
- package/docs/deprecated/rcpt_to.routes.md +0 -1
- package/docs/deprecated/rdns.regexp.md +13 -15
- package/docs/plugins/aliases.md +89 -89
- package/docs/plugins/auth/auth_bridge.md +5 -7
- package/docs/plugins/auth/auth_ldap.md +11 -14
- package/docs/plugins/auth/auth_proxy.md +10 -12
- package/docs/plugins/auth/auth_vpopmaild.md +5 -6
- package/docs/plugins/auth/flat_file.md +4 -4
- package/docs/plugins/block_me.md +3 -3
- package/docs/plugins/data.signatures.md +1 -2
- package/docs/plugins/delay_deny.md +3 -4
- package/docs/plugins/max_unrecognized_commands.md +4 -4
- package/docs/plugins/prevent_credential_leaks.md +6 -6
- package/docs/plugins/process_title.md +18 -18
- package/docs/plugins/queue/deliver.md +2 -3
- package/docs/plugins/queue/discard.md +4 -4
- package/docs/plugins/queue/lmtp.md +1 -3
- package/docs/plugins/queue/qmail-queue.md +7 -9
- package/docs/plugins/queue/quarantine.md +16 -21
- package/docs/plugins/queue/rabbitmq.md +8 -11
- package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
- package/docs/plugins/queue/smtp_bridge.md +7 -10
- package/docs/plugins/queue/smtp_forward.md +42 -34
- package/docs/plugins/queue/smtp_proxy.md +30 -29
- package/docs/plugins/queue/test.md +1 -3
- package/docs/plugins/rcpt_to.in_host_list.md +6 -6
- package/docs/plugins/rcpt_to.max_count.md +1 -1
- package/docs/plugins/record_envelope_addresses.md +3 -3
- package/docs/plugins/reseed_rng.md +6 -6
- package/docs/plugins/status.md +9 -8
- package/docs/plugins/tarpit.md +7 -11
- package/docs/plugins/tls.md +12 -17
- package/docs/plugins/toobusy.md +4 -4
- package/docs/plugins/xclient.md +3 -3
- package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
- package/docs/tutorials/SettingUpOutbound.md +6 -9
- package/endpoint.js +35 -38
- package/eslint.config.mjs +22 -19
- package/haraka.js +42 -47
- package/host_pool.js +75 -79
- package/http/html/404.html +45 -49
- package/http/html/index.html +39 -28
- package/http/package.json +2 -4
- package/line_socket.js +27 -28
- package/logger.js +182 -201
- package/outbound/client_pool.js +33 -33
- package/outbound/config.js +64 -59
- package/outbound/fsync_writestream.js +24 -25
- package/outbound/hmail.js +888 -835
- package/outbound/index.js +194 -187
- package/outbound/qfile.js +49 -52
- package/outbound/queue.js +197 -190
- package/outbound/timer_queue.js +41 -43
- package/outbound/tls.js +68 -61
- package/outbound/todo.js +11 -11
- package/package.json +32 -32
- package/plugins/.eslintrc.yaml +0 -1
- package/plugins/auth/auth_base.js +123 -127
- package/plugins/auth/auth_bridge.js +7 -7
- package/plugins/auth/auth_proxy.js +121 -126
- package/plugins/auth/auth_vpopmaild.js +84 -85
- package/plugins/auth/flat_file.js +18 -17
- package/plugins/block_me.js +31 -31
- package/plugins/data.signatures.js +13 -13
- package/plugins/delay_deny.js +65 -61
- package/plugins/prevent_credential_leaks.js +23 -23
- package/plugins/process_title.js +125 -128
- package/plugins/profile.js +5 -5
- package/plugins/queue/deliver.js +3 -3
- package/plugins/queue/discard.js +13 -14
- package/plugins/queue/lmtp.js +16 -17
- package/plugins/queue/qmail-queue.js +54 -55
- package/plugins/queue/quarantine.js +68 -70
- package/plugins/queue/rabbitmq.js +80 -87
- package/plugins/queue/rabbitmq_amqplib.js +75 -54
- package/plugins/queue/smtp_bridge.js +16 -16
- package/plugins/queue/smtp_forward.js +175 -179
- package/plugins/queue/smtp_proxy.js +69 -71
- package/plugins/queue/test.js +9 -9
- package/plugins/rcpt_to.host_list_base.js +30 -34
- package/plugins/rcpt_to.in_host_list.js +19 -19
- package/plugins/record_envelope_addresses.js +4 -4
- package/plugins/reseed_rng.js +4 -4
- package/plugins/status.js +90 -97
- package/plugins/tarpit.js +25 -14
- package/plugins/tls.js +68 -68
- package/plugins/toobusy.js +21 -23
- package/plugins/xclient.js +51 -53
- package/plugins.js +276 -293
- package/rfc1869.js +30 -35
- package/server.js +308 -299
- package/smtp_client.js +244 -228
- package/test/.eslintrc.yaml +0 -1
- package/test/connection.js +127 -134
- package/test/endpoint.js +53 -47
- package/test/fixtures/line_socket.js +12 -12
- package/test/fixtures/util_hmailitem.js +89 -85
- package/test/host_pool.js +90 -92
- package/test/installation/plugins/base_plugin.js +2 -2
- package/test/installation/plugins/folder_plugin/index.js +2 -3
- package/test/installation/plugins/inherits.js +3 -3
- package/test/installation/plugins/load_first.js +2 -3
- package/test/installation/plugins/plugin.js +1 -3
- package/test/installation/plugins/tls.js +2 -4
- package/test/logger.js +135 -116
- package/test/outbound/hmail.js +49 -35
- package/test/outbound/index.js +118 -101
- package/test/outbound/qfile.js +51 -53
- package/test/outbound_bounce_net_errors.js +84 -69
- package/test/outbound_bounce_rfc3464.js +235 -165
- package/test/plugins/auth/auth_base.js +420 -279
- package/test/plugins/auth/auth_vpopmaild.js +38 -39
- package/test/plugins/queue/smtp_forward.js +126 -104
- package/test/plugins/rcpt_to.host_list_base.js +85 -67
- package/test/plugins/rcpt_to.in_host_list.js +159 -112
- package/test/plugins/status.js +71 -64
- package/test/plugins/tls.js +37 -34
- package/test/plugins.js +97 -92
- package/test/rfc1869.js +19 -26
- package/test/server.js +293 -272
- package/test/smtp_client.js +180 -176
- package/test/tls_socket.js +62 -66
- package/test/transaction.js +159 -160
- package/tls_socket.js +331 -333
- package/transaction.js +129 -137
package/docs/plugins/tarpit.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# tarpit
|
|
2
2
|
|
|
3
3
|
This plugin is designed to introduce deliberate delays on the response
|
|
4
|
-
of every hook in order to slow down a connection.
|
|
4
|
+
of every hook in order to slow down a connection. It has no
|
|
5
5
|
configuration and is designed to be used only by other plugins.
|
|
6
6
|
|
|
7
7
|
It must be loaded early in config/plugins (e.g. before any plugins
|
|
8
|
-
that accept recipients or that return OK) but must be loaded
|
|
8
|
+
that accept recipients or that return OK) but must be loaded _after_
|
|
9
9
|
any plugins that wish to use it.
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
## Usage
|
|
13
12
|
|
|
14
13
|
To use this plugin in another plugin set:
|
|
@@ -19,26 +18,24 @@ or
|
|
|
19
18
|
|
|
20
19
|
connection.transaction.notes.tarpit = <seconds to delay>;
|
|
21
20
|
|
|
22
|
-
|
|
23
21
|
## Configuration
|
|
24
22
|
|
|
25
23
|
The configuration file for tarpit is config/tarpit.ini.
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
- hooks_to_delay - a list of hooks to delay at. This setting can be used to
|
|
28
26
|
override the default list in the plugin. For example, if you notice that
|
|
29
|
-
malware is disconnecting after delaying
|
|
27
|
+
malware is disconnecting after delaying rcpt_ok, you can remove just that
|
|
30
28
|
hook from the list:
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
hooks_to_delay=connect,helo,ehlo,mail,rcpt,data,data_post,queue,unrecognized_command,vrfy,noop,rset,quit
|
|
34
31
|
|
|
35
32
|
## Plugin Timeout
|
|
36
33
|
|
|
37
34
|
config/tarpit.timeout (Default: 0)
|
|
38
35
|
|
|
39
|
-
All Haraka plugins can configure a
|
|
36
|
+
All Haraka plugins can configure a _name_.timeout file. The timeout specifies
|
|
40
37
|
how long Haraka lets the plugin do nothing before it times out. When zero,
|
|
41
|
-
there is no timeout. When non-zero and
|
|
38
|
+
there is no timeout. When non-zero and _seconds to delay_ is longer than
|
|
42
39
|
tarpit.timeout (default: 1s), you'll get errors like this in your log files:
|
|
43
40
|
|
|
44
41
|
[core] Plugin tarpit timed out on hook rcpt - make sure it calls the callback
|
|
@@ -47,7 +44,6 @@ tarpit.timeout (default: 1s), you'll get errors like this in your log files:
|
|
|
47
44
|
The solution is to set the contents of config/tarpit.timeout to zero or
|
|
48
45
|
**seconds to delay** + 1.
|
|
49
46
|
|
|
50
|
-
|
|
51
47
|
## Logging
|
|
52
48
|
|
|
53
49
|
When tarpitting a command it will log 'tarpitting response for Ns' to
|
package/docs/plugins/tls.md
CHANGED
|
@@ -32,21 +32,21 @@ An example [acme.sh](https://acme.sh) deployment [script](https://github.com/msi
|
|
|
32
32
|
|
|
33
33
|
### Wild Wild West
|
|
34
34
|
|
|
35
|
-
PEM encoded TLS certificates and keys can be stored in files in `config/tls`. The certificate loader is recursive, so TLS files can be in subdirs like `config/tls/mx1.example.com`. The certificate names are parsed from the 1st cert in each file and indexed by the certs Common Name(s). Subject Alternate Names are supported. The file name containing the certificates does
|
|
35
|
+
PEM encoded TLS certificates and keys can be stored in files in `config/tls`. The certificate loader is recursive, so TLS files can be in subdirs like `config/tls/mx1.example.com`. The certificate names are parsed from the 1st cert in each file and indexed by the certs Common Name(s). Subject Alternate Names are supported. The file name containing the certificates does _not_ matter. Additional certificates within each file are presumed to be CA chain (intermediate) certificates.
|
|
36
36
|
|
|
37
37
|
If the TLS key is stored in the same file as the matching certificate, then the name of the file does not matter. If the TLS key is alone in a file, the file MUST be named with the keys Common Name. The file extension does not matter, `.pem` and `.key` are common. If the key is used for multiple CNs, the key must be stored in a file name matching each CN. Examples of working TLS key/cert file pairs for the Common Name mx1.example.com:
|
|
38
38
|
|
|
39
39
|
1. certificate bundle (see above), key & cert in same file
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
- config/tls/mx1.example.com.pem (recommended)
|
|
41
|
+
- config/tls/any-unique-name.pem (CN is extracted from 1st cert)
|
|
42
42
|
2. files in TLS dir
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
- config/tls/mx1.example.com.crt
|
|
44
|
+
- config/tls/mx1.example.com.key
|
|
45
45
|
3. files in subdir
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
4. wildcard bundle on Windows platform (
|
|
49
|
-
|
|
46
|
+
- config/tls/example.com/mx1.cert
|
|
47
|
+
- config/tls/example.com/mx1.example.com.key
|
|
48
|
+
4. wildcard bundle on Windows platform (\* is not allowed in file names)
|
|
49
|
+
- config/tls/\_.example.com.pem
|
|
50
50
|
|
|
51
51
|
## Purchased Certificate
|
|
52
52
|
|
|
@@ -129,7 +129,7 @@ no_starttls_ports[]=2525
|
|
|
129
129
|
|
|
130
130
|
### force_tls_hosts
|
|
131
131
|
|
|
132
|
-
For known good TLS hosts, it's possible to force that the outbound mailer will only connect via secure sockets. This makes Haraka use
|
|
132
|
+
For known good TLS hosts, it's possible to force that the outbound mailer will only connect via secure sockets. This makes Haraka use _forced TLS_ instead of _opportunistic TLS_. For forced TLS, the STARTTLS upgrade must succeed with a valid certificate (overriding `rejectUnauthorized`). The list is matched both against the host (MX record or `nexthop` in `relay_dest_domains.ini`), and the domain name of the email address.
|
|
133
133
|
|
|
134
134
|
Note: unlike `no_tls_hosts`, this feature is implemented as an array:
|
|
135
135
|
|
|
@@ -153,11 +153,11 @@ See also: [Mozilla SSL configuration generator](https://ssl-config.mozilla.org/)
|
|
|
153
153
|
|
|
154
154
|
Specifies minimum allowable TLS protocol version to use. Example:
|
|
155
155
|
|
|
156
|
-
minVersion=TLSv1.1
|
|
156
|
+
minVersion=TLSv1.1
|
|
157
157
|
|
|
158
158
|
If unset, the default is node's tls.DEFAULT_MIN_VERSION constant.
|
|
159
159
|
|
|
160
|
-
(**Node.js 11.4+ required**, for older instances you can use
|
|
160
|
+
(**Node.js 11.4+ required**, for older instances you can use _secureProtocol_ settings)
|
|
161
161
|
|
|
162
162
|
### honorCipherOrder
|
|
163
163
|
|
|
@@ -178,14 +178,12 @@ Whether Haraka should request a certificate from a connecting client.
|
|
|
178
178
|
|
|
179
179
|
requestCert=[true|false] (default: true)
|
|
180
180
|
|
|
181
|
-
|
|
182
181
|
### rejectUnauthorized
|
|
183
182
|
|
|
184
183
|
Reject connections from clients without a CA validated TLS certificate.
|
|
185
184
|
|
|
186
185
|
rejectUnauthorized=[true|false] (default: false)
|
|
187
186
|
|
|
188
|
-
|
|
189
187
|
### requireAuthorized
|
|
190
188
|
|
|
191
189
|
When `rejectUnauthorized=false`, require validated TLS certificates on just the specified ports.
|
|
@@ -195,7 +193,6 @@ requireAuthorized[]=465
|
|
|
195
193
|
;requireAuthorized[]=587
|
|
196
194
|
```
|
|
197
195
|
|
|
198
|
-
|
|
199
196
|
### secureProtocol
|
|
200
197
|
|
|
201
198
|
Specifies the OpenSSL API function used for handling the TLS session. Choose
|
|
@@ -203,7 +200,6 @@ one of the methods described at the
|
|
|
203
200
|
[OpenSSL API page](https://www.openssl.org/docs/manmaster/ssl/ssl.html).
|
|
204
201
|
The default is `SSLv23_method`.
|
|
205
202
|
|
|
206
|
-
|
|
207
203
|
### requestOCSP
|
|
208
204
|
|
|
209
205
|
Specifies that OCSP Stapling should be enabled, according to RFC 6066.
|
|
@@ -218,7 +214,6 @@ they are valid, and get refreshed after that time. A server restart
|
|
|
218
214
|
requires the OCSP responses to be fetched again upon the first client
|
|
219
215
|
connection.
|
|
220
216
|
|
|
221
|
-
|
|
222
217
|
## Inbound Specific Configuration
|
|
223
218
|
|
|
224
219
|
By default the above options are shared with outbound mail (either
|
package/docs/plugins/toobusy.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# toobusy
|
|
2
2
|
|
|
3
|
-
This plugin will stop Haraka accepting new connections when the event loop
|
|
3
|
+
This plugin will stop Haraka accepting new connections when the event loop
|
|
4
4
|
latency is too high.
|
|
5
5
|
|
|
6
6
|
See https://github.com/STRML/node-toobusy for details.
|
|
@@ -8,13 +8,13 @@ See https://github.com/STRML/node-toobusy for details.
|
|
|
8
8
|
To use this plugin you have to install the 'toobusy-js' module by running
|
|
9
9
|
'npm install toobusy-js' in your Haraka configuration directory.
|
|
10
10
|
|
|
11
|
-
This plugin should be listed at the top of your config/plugins file so that
|
|
12
|
-
it runs before any other plugin that hooks
|
|
11
|
+
This plugin should be listed at the top of your config/plugins file so that
|
|
12
|
+
it runs before any other plugin that hooks lookup_rdns.
|
|
13
13
|
|
|
14
14
|
## Configuration
|
|
15
15
|
|
|
16
16
|
If you wish to override the default maxLag value of 70ms then add the desired
|
|
17
|
-
value to config/toobusy.maxlag.
|
|
17
|
+
value to config/toobusy.maxlag. This can be set and changed at runtime and
|
|
18
18
|
no restart is required.
|
|
19
19
|
|
|
20
20
|
Note that if you set the maxLag value to <10 then this will cause the toobusy
|
package/docs/plugins/xclient.md
CHANGED
|
@@ -4,7 +4,7 @@ Implements the [XCLIENT](http://www.postfix.org/XCLIENT_README.html) protocol.
|
|
|
4
4
|
|
|
5
5
|
## configuration
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- xclient.hosts
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
A list of IP addresses, one per line that should be allowed to use the
|
|
10
|
+
XCLIENT protocol. Localhost (127.0.0.1 or ::1) is allowed implicitly.
|
|
@@ -1,47 +1,26 @@
|
|
|
1
|
-
Migrating from Haraka v1.x to v2.x
|
|
2
|
-
==================================
|
|
1
|
+
# Migrating from Haraka v1.x to v2.x
|
|
3
2
|
|
|
4
|
-
Haraka v2.x contains two significant changes to the v1.x API related to
|
|
5
|
-
streams.
|
|
3
|
+
Haraka v2.x contains two significant changes to the v1.x API related to streams.
|
|
6
4
|
|
|
7
|
-
Streams are an abstraction over a data flow that is provided by Node core
|
|
8
|
-
and is used throughout node to "pipe" data between two places or more. This
|
|
9
|
-
makes programming very easy, and is hence why we started using them in Haraka
|
|
10
|
-
starting with version 2.0.0.
|
|
5
|
+
Streams are an abstraction over a data flow that is provided by Node core and is used throughout node to "pipe" data between two places or more. This makes programming very easy, and is hence why we started using them in Haraka starting with version 2.0.0.
|
|
11
6
|
|
|
12
|
-
For more information about the Stream API, see
|
|
13
|
-
http://nodejs.org/api/stream.html
|
|
7
|
+
For more information about the Stream API, see http://nodejs.org/api/stream.html
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
it's very unlikely you will need to change anything. Though you may want
|
|
17
|
-
to configure `spool_dir` and `spool_after` in `config/smtp.ini`. However if
|
|
18
|
-
you have written custom plugins, continue reading.
|
|
9
|
+
Note that when using bundled Haraka plugins, it's very unlikely you will need to change anything. Though you may want to configure `spool_dir` and `spool_after` in `config/smtp.ini`. If you have custom plugins, continue reading.
|
|
19
10
|
|
|
20
|
-
Changes To Look For
|
|
21
|
-
-------------------
|
|
11
|
+
## Changes To Look For
|
|
22
12
|
|
|
23
|
-
Firstly, the incoming data in an email (the email body) is now stored in an
|
|
24
|
-
object which you can treat as a ReadableStream. To find if this is relevant
|
|
25
|
-
for you, look for instances of `data_lines` in your plugins.
|
|
13
|
+
Firstly, the incoming data in an email (the email body) is now stored in an object which you can treat as a ReadableStream. To find if this is relevant for you, look for instances of `data_lines` in your plugins.
|
|
26
14
|
|
|
27
|
-
Secondly, if you parse the mail body, attachments are now provided as a
|
|
28
|
-
stream, rather than custom start/data/end events. To find if this is relevant
|
|
29
|
-
for you, look for instances of `attachment_hooks` in your plugins.
|
|
15
|
+
Secondly, if you parse the mail body, attachments are now provided as a stream, rather than custom start/data/end events. To find if this is relevant for you, look for instances of `attachment_hooks` in your plugins.
|
|
30
16
|
|
|
31
|
-
Fixing
|
|
32
|
-
-------------------------
|
|
17
|
+
## Fixing data_lines plugins
|
|
33
18
|
|
|
34
|
-
Any plugins now working on each line of data will need to change to using a
|
|
35
|
-
stream. The stream is called `transaction.message_stream`.
|
|
19
|
+
Any plugins now working on each line of data will need to change to using a stream. The stream is called `transaction.message_stream`.
|
|
36
20
|
|
|
37
|
-
These changes may be complicated if you are iterating over each line and
|
|
38
|
-
doing something with the strings therein. However if you are piping the data
|
|
39
|
-
to an application or over a network, your code will become significantly
|
|
40
|
-
simpler (and a lot faster).
|
|
21
|
+
These changes may be complicated if you are iterating over each line and doing something with the strings therein. However if you are piping the data to an application or over a network, your code will become significantly simpler (and a lot faster).
|
|
41
22
|
|
|
42
|
-
In v1.x Haraka populated the `transaction.data_lines` array for each line of
|
|
43
|
-
data received. If you were writing the data to a socket then you had to handle backpressure manually by checking the return of `write()` and adding
|
|
44
|
-
`on('drain')` handlers like so:
|
|
23
|
+
In v1.x Haraka populated the `transaction.data_lines` array for each line of data received. If you were writing the data to a socket then you had to handle backpressure manually by checking the return of `write()` and adding `on('drain')` handlers like so:
|
|
45
24
|
|
|
46
25
|
var data_marker = 0;
|
|
47
26
|
var in_data = false;
|
|
@@ -70,18 +49,17 @@ data received. If you were writing the data to a socket then you had to handle
|
|
|
70
49
|
|
|
71
50
|
In v2.x this now becomes:
|
|
72
51
|
|
|
73
|
-
connection.transaction.message_stream.pipe(socket, {
|
|
52
|
+
connection.transaction.message_stream.pipe(socket, {ending_dot: true});
|
|
74
53
|
|
|
75
|
-
This automatically chunks the data, handles backpressure and will apply any
|
|
76
|
-
necessary format changes.
|
|
54
|
+
This automatically chunks the data, handles backpressure and will apply any
|
|
55
|
+
necessary format changes. See `docs/Transaction.md` for the full details.
|
|
77
56
|
|
|
78
|
-
If you need to handle the input data by line, then you will need to create
|
|
79
|
-
your own writable stream and then pipe the message to the stream and then
|
|
80
|
-
extract the lines from the stream of data.
|
|
57
|
+
If you need to handle the input data by line, then you will need to create
|
|
58
|
+
your own writable stream and then pipe the message to the stream and then
|
|
59
|
+
extract the lines from the stream of data. See the `dkim` plugin for
|
|
81
60
|
an example.
|
|
82
61
|
|
|
83
|
-
Fixing
|
|
84
|
-
-------------------------------
|
|
62
|
+
## Fixing attachment_hooks plugins
|
|
85
63
|
|
|
86
64
|
For v1.x you passed in functions to `transaction.attachment_hooks()` as
|
|
87
65
|
follows:
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
Configuring Haraka For Outbound Email
|
|
2
|
-
=====================================
|
|
1
|
+
# Configuring Haraka For Outbound Email
|
|
3
2
|
|
|
4
3
|
It is trivially easy to configure Haraka as an outbound email server. But
|
|
5
4
|
first there are external things you may want to sort out:
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
- Get your DNS PTR record working - make sure it matches the A record of the
|
|
8
7
|
host you are sending from.
|
|
9
|
-
|
|
8
|
+
- Consider implementing an SPF record. I don't personally do this, but some
|
|
10
9
|
people seem to think it helps.
|
|
11
10
|
|
|
12
11
|
There's lots of information elsewhere on the internet about getting these
|
|
13
12
|
things working, and they are specific to your network and your DNS hosting.
|
|
14
13
|
|
|
15
|
-
First Some Background
|
|
16
|
-
---------------------
|
|
14
|
+
## First Some Background
|
|
17
15
|
|
|
18
16
|
Sending outbound mail through Haraka is called "relaying", and that is the
|
|
19
17
|
term the internals use. The process is simple - if a plugin in Haraka tells
|
|
@@ -25,8 +23,7 @@ address used when connecting to Haraka. If that address also bounces then
|
|
|
25
23
|
it is considered a "double bounce" and Haraka will log an error and drop it
|
|
26
24
|
on the floor.
|
|
27
25
|
|
|
28
|
-
The Setup
|
|
29
|
-
---------
|
|
26
|
+
## The Setup
|
|
30
27
|
|
|
31
28
|
Outbound mail servers should run on port 587 and enforce authentication. This
|
|
32
29
|
is slightly different from the "old" model where there would simply be a
|
|
@@ -52,7 +49,7 @@ and password:
|
|
|
52
49
|
|
|
53
50
|
vi config/auth_flat_file.ini
|
|
54
51
|
|
|
55
|
-
See the documentation in docs/plugins/auth/
|
|
52
|
+
See the documentation in docs/plugins/auth/flat_file.md for information about
|
|
56
53
|
what can go in that file.
|
|
57
54
|
|
|
58
55
|
Now you can start Haraka. That's all the configuration you need.
|
package/endpoint.js
CHANGED
|
@@ -1,69 +1,66 @@
|
|
|
1
|
-
'use strict'
|
|
1
|
+
'use strict'
|
|
2
2
|
// Socket address parser/formatter and server binding helper
|
|
3
3
|
|
|
4
|
-
const fs = require('node:fs/promises')
|
|
5
|
-
const sockaddr = require('sockaddr')
|
|
4
|
+
const fs = require('node:fs/promises')
|
|
5
|
+
const sockaddr = require('sockaddr')
|
|
6
6
|
|
|
7
|
-
module.exports = function endpoint
|
|
7
|
+
module.exports = function endpoint(addr, defaultPort) {
|
|
8
8
|
try {
|
|
9
9
|
if ('string' === typeof addr || 'number' === typeof addr) {
|
|
10
|
-
addr = sockaddr(addr, {defaultPort})
|
|
11
|
-
const match = /^(.*):([0-7]{3})$/.exec(addr.path || '')
|
|
10
|
+
addr = sockaddr(addr, { defaultPort })
|
|
11
|
+
const match = /^(.*):([0-7]{3})$/.exec(addr.path || '')
|
|
12
12
|
if (match) {
|
|
13
|
-
addr.path = match[1]
|
|
14
|
-
addr.mode = match[2]
|
|
13
|
+
addr.path = match[1]
|
|
14
|
+
addr.mode = match[2]
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
17
|
+
} catch (err) {
|
|
19
18
|
// Return the parse exception instead of throwing it
|
|
20
|
-
return err
|
|
19
|
+
return err
|
|
21
20
|
}
|
|
22
|
-
return new Endpoint(addr)
|
|
21
|
+
return new Endpoint(addr)
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
class Endpoint {
|
|
26
|
-
|
|
27
|
-
constructor (addr) {
|
|
25
|
+
constructor(addr) {
|
|
28
26
|
if (addr.path) {
|
|
29
|
-
this.path = addr.path
|
|
30
|
-
if (addr.mode) this.mode = addr.mode
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
27
|
+
this.path = addr.path
|
|
28
|
+
if (addr.mode) this.mode = addr.mode
|
|
29
|
+
} else {
|
|
33
30
|
// Handle server.address() return as well as parsed host/port
|
|
34
|
-
const host = addr.address || addr.host || '::0'
|
|
31
|
+
const host = addr.address || addr.host || '::0'
|
|
35
32
|
// Normalize '::' to '::0'
|
|
36
|
-
this.host =
|
|
37
|
-
this.port = parseInt(addr.port, 10)
|
|
33
|
+
this.host = '::' === host ? '::0' : host
|
|
34
|
+
this.port = parseInt(addr.port, 10)
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
toString
|
|
42
|
-
if (this.mode) return `${this.path}:${this.mode}
|
|
43
|
-
if (this.path) return this.path
|
|
44
|
-
if (this.host.includes(':')) return `[${this.host}]:${this.port}
|
|
45
|
-
return `${this.host}:${this.port}
|
|
38
|
+
toString() {
|
|
39
|
+
if (this.mode) return `${this.path}:${this.mode}`
|
|
40
|
+
if (this.path) return this.path
|
|
41
|
+
if (this.host.includes(':')) return `[${this.host}]:${this.port}`
|
|
42
|
+
return `${this.host}:${this.port}`
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
// Make server listen on this endpoint, w/optional options
|
|
49
|
-
async bind
|
|
50
|
-
opts = {...opts}
|
|
46
|
+
async bind(server, opts) {
|
|
47
|
+
opts = { ...opts }
|
|
51
48
|
|
|
52
|
-
const mode = this.mode ? parseInt(this.mode, 8) : false
|
|
49
|
+
const mode = this.mode ? parseInt(this.mode, 8) : false
|
|
53
50
|
if (this.path) {
|
|
54
|
-
opts.path = this.path
|
|
55
|
-
await fs.rm(this.path, { force: true })
|
|
51
|
+
opts.path = this.path
|
|
52
|
+
await fs.rm(this.path, { force: true }) // errors are ignored when force is true
|
|
56
53
|
} else {
|
|
57
|
-
opts.host = this.host
|
|
58
|
-
opts.port = this.port
|
|
54
|
+
opts.host = this.host
|
|
55
|
+
opts.port = this.port
|
|
59
56
|
}
|
|
60
|
-
|
|
57
|
+
|
|
61
58
|
return new Promise((resolve, reject) => {
|
|
62
59
|
server.listen(opts, async (err) => {
|
|
63
|
-
if(err) return reject(err)
|
|
64
|
-
if (mode) await fs.chmod(opts.path, mode)
|
|
60
|
+
if (err) return reject(err)
|
|
61
|
+
if (mode) await fs.chmod(opts.path, mode)
|
|
65
62
|
resolve()
|
|
66
|
-
})
|
|
67
|
-
})
|
|
63
|
+
})
|
|
64
|
+
})
|
|
68
65
|
}
|
|
69
66
|
}
|
package/eslint.config.mjs
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import globals from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import js from
|
|
5
|
-
import { FlatCompat } from
|
|
1
|
+
import globals from 'globals'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import js from '@eslint/js'
|
|
5
|
+
import { FlatCompat } from '@eslint/eslintrc'
|
|
6
6
|
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
-
const __dirname = path.dirname(__filename)
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = path.dirname(__filename)
|
|
9
9
|
const compat = new FlatCompat({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
})
|
|
10
|
+
baseDirectory: __dirname,
|
|
11
|
+
recommendedConfig: js.configs.recommended,
|
|
12
|
+
allConfig: js.configs.all,
|
|
13
|
+
})
|
|
14
14
|
|
|
15
|
-
export default [
|
|
15
|
+
export default [
|
|
16
|
+
...compat.extends('@haraka'),
|
|
17
|
+
{
|
|
16
18
|
languageOptions: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
globals: {
|
|
20
|
+
...globals.node,
|
|
21
|
+
},
|
|
20
22
|
},
|
|
21
23
|
|
|
22
24
|
rules: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
'prefer-template': 'warn',
|
|
26
|
+
'no-unneeded-ternary': 1,
|
|
27
|
+
'no-unused-vars': 0,
|
|
26
28
|
},
|
|
27
|
-
}
|
|
29
|
+
},
|
|
30
|
+
]
|
package/haraka.js
CHANGED
|
@@ -1,79 +1,74 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
'use strict'
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const makePathJoin = () => path.join(process.env.HARAKA, 'node_modules')
|
|
3
|
+
'use strict'
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const makePathJoin = () => path.join(process.env.HARAKA, 'node_modules')
|
|
6
6
|
|
|
7
7
|
if (!process.env.HARAKA) {
|
|
8
|
-
console.warn(
|
|
8
|
+
console.warn('WARNING: Not running installed Haraka - command line arguments ignored')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// this must be set before "server.js" is loaded
|
|
12
|
-
process.env.HARAKA = process.env.HARAKA || path.resolve('.')
|
|
12
|
+
process.env.HARAKA = process.env.HARAKA || path.resolve('.')
|
|
13
13
|
try {
|
|
14
|
-
require.paths.push(makePathJoin())
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
(`${process.env.NODE_PATH}:${makePathJoin()}`) :
|
|
19
|
-
(makePathJoin());
|
|
20
|
-
require('module')._initPaths(); // Horrible hack
|
|
14
|
+
require.paths.push(makePathJoin())
|
|
15
|
+
} catch (e) {
|
|
16
|
+
process.env.NODE_PATH = process.env.NODE_PATH ? `${process.env.NODE_PATH}:${makePathJoin()}` : makePathJoin()
|
|
17
|
+
require('module')._initPaths() // Horrible hack
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
const utils = require('haraka-utils')
|
|
24
|
-
const logger = require('./logger')
|
|
25
|
-
const server = require('./server')
|
|
20
|
+
const utils = require('haraka-utils')
|
|
21
|
+
const logger = require('./logger')
|
|
22
|
+
const server = require('./server')
|
|
26
23
|
|
|
27
24
|
exports.version = utils.getVersion(__dirname)
|
|
28
25
|
|
|
29
|
-
process.on('uncaughtException', err => {
|
|
26
|
+
process.on('uncaughtException', (err) => {
|
|
30
27
|
if (err.stack) {
|
|
31
|
-
err.stack.split(
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
logger.crit(`Caught exception: ${JSON.stringify(err)}`);
|
|
28
|
+
err.stack.split('\n').forEach((line) => logger.crit(line))
|
|
29
|
+
} else {
|
|
30
|
+
logger.crit(`Caught exception: ${JSON.stringify(err)}`)
|
|
35
31
|
}
|
|
36
|
-
logger.dump_and_exit(1)
|
|
37
|
-
})
|
|
32
|
+
logger.dump_and_exit(1)
|
|
33
|
+
})
|
|
38
34
|
|
|
39
|
-
let shutting_down = false
|
|
40
|
-
const signals = ['SIGINT']
|
|
35
|
+
let shutting_down = false
|
|
36
|
+
const signals = ['SIGINT']
|
|
41
37
|
|
|
42
38
|
if (process.pid === 1) signals.push('SIGTERM')
|
|
43
39
|
|
|
44
40
|
for (const sig of signals) {
|
|
45
41
|
process.on(sig, () => {
|
|
46
|
-
if (shutting_down) return process.exit(1)
|
|
47
|
-
shutting_down = true
|
|
48
|
-
const [, filename] = process.argv
|
|
49
|
-
process.title = path.basename(filename, '.js')
|
|
42
|
+
if (shutting_down) return process.exit(1)
|
|
43
|
+
shutting_down = true
|
|
44
|
+
const [, filename] = process.argv
|
|
45
|
+
process.title = path.basename(filename, '.js')
|
|
50
46
|
|
|
51
|
-
logger.notice(`${sig} received`)
|
|
47
|
+
logger.notice(`${sig} received`)
|
|
52
48
|
logger.dump_and_exit(() => {
|
|
53
49
|
if (server.cluster?.isMaster) {
|
|
54
|
-
server.performShutdown()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
server.performShutdown();
|
|
50
|
+
server.performShutdown()
|
|
51
|
+
} else if (!server.cluster) {
|
|
52
|
+
server.performShutdown()
|
|
58
53
|
}
|
|
59
|
-
})
|
|
60
|
-
})
|
|
54
|
+
})
|
|
55
|
+
})
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
process.on('SIGHUP', () => {
|
|
64
|
-
logger.notice('Flushing the temp fail queue')
|
|
65
|
-
server.flushQueue()
|
|
66
|
-
})
|
|
59
|
+
logger.notice('Flushing the temp fail queue')
|
|
60
|
+
server.flushQueue()
|
|
61
|
+
})
|
|
67
62
|
|
|
68
|
-
process.on('exit', code => {
|
|
69
|
-
if (shutting_down) return
|
|
70
|
-
const [, filename] = process.argv
|
|
71
|
-
process.title = path.basename(filename, '.js')
|
|
63
|
+
process.on('exit', (code) => {
|
|
64
|
+
if (shutting_down) return
|
|
65
|
+
const [, filename] = process.argv
|
|
66
|
+
process.title = path.basename(filename, '.js')
|
|
72
67
|
|
|
73
|
-
logger.notice('Shutting down')
|
|
74
|
-
logger.dump_logs()
|
|
75
|
-
})
|
|
68
|
+
logger.notice('Shutting down')
|
|
69
|
+
logger.dump_logs()
|
|
70
|
+
})
|
|
76
71
|
|
|
77
|
-
logger.log('NOTICE', `Starting up Haraka version ${exports.version}`)
|
|
72
|
+
logger.log('NOTICE', `Starting up Haraka version ${exports.version}`)
|
|
78
73
|
|
|
79
|
-
server.createServer()
|
|
74
|
+
server.createServer()
|