Haraka 3.0.1 → 3.0.3
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/Changes.md +56 -0
- package/Dockerfile +3 -3
- package/Plugins.md +5 -4
- package/README.md +4 -4
- package/TODO +1 -24
- package/config/access.domains +1 -1
- package/config/auth_flat_file.ini +1 -0
- package/config/auth_vpopmaild.ini +4 -2
- package/config/helo.checks.ini +1 -1
- package/config/outbound.ini +1 -1
- package/config/rabbitmq_amqplib.ini +8 -1
- package/connection.js +32 -10
- package/docs/Connection.md +1 -1
- package/docs/Outbound.md +6 -15
- package/docs/Plugins.md +46 -39
- package/docs/Transaction.md +1 -1
- package/docs/{plugins → deprecated}/connect.rdns_access.md +1 -1
- package/docs/{plugins → deprecated}/mail_from.access.md +1 -1
- package/docs/{plugins → deprecated}/rcpt_to.access.md +1 -1
- package/docs/plugins/auth/auth_vpopmaild.md +15 -19
- package/docs/plugins/auth/flat_file.md +23 -30
- package/docs/plugins/clamd.md +1 -1
- package/docs/plugins/queue/rabbitmq_amqplib.md +7 -0
- package/docs/plugins/queue/smtp_forward.md +16 -38
- package/docs/plugins/queue/smtp_proxy.md +9 -11
- package/docs/plugins/relay.md +2 -2
- package/outbound/hmail.js +2 -2
- package/outbound/queue.js +5 -0
- package/outbound/tls.js +1 -1
- package/package.json +31 -31
- package/plugins/auth/auth_base.js +27 -11
- package/plugins/auth/auth_vpopmaild.js +29 -19
- package/plugins/auth/flat_file.js +17 -12
- package/plugins/clamd.js +1 -0
- package/plugins/dns_list_base.js +3 -3
- package/plugins/helo.checks.js +15 -7
- package/plugins/queue/rabbitmq_amqplib.js +1 -1
- package/plugins/queue/smtp_forward.js +21 -15
- package/plugins/tls.js +1 -1
- package/plugins.js +1 -0
- package/tests/config/helo.checks.ini +52 -0
- package/tests/plugins/dns_list_base.js +41 -31
- package/tests/plugins/helo.checks.js +212 -239
- package/tests/plugins/queue/smtp_forward.js +36 -7
- package/tests/queue/multibyte +0 -0
- package/tests/queue/plain +0 -0
- package/transaction.js +1 -1
- package/config/lookup_rdns.strict.ini +0 -12
- package/config/lookup_rdns.strict.timeout +0 -1
- package/config/lookup_rdns.strict.whitelist +0 -1
- package/config/lookup_rdns.strict.whitelist_regex +0 -5
- package/config/rcpt_to.blocklist +0 -1
- package/config/rdns.allow_regexps +0 -0
- package/config/rdns.deny_regexps +0 -0
- package/config.js +0 -6
- package/coverage/lcov.info +0 -13863
- package/coverage/tmp/coverage-42958-1658373250585-0.json +0 -1
- package/coverage/tmp/coverage-42961-1658373250529-0.json +0 -1
- package/docs/plugins/relay_acl.md +0 -29
- package/docs/plugins/relay_all.md +0 -15
- package/docs/plugins/relay_force_routing.md +0 -33
- package/plugins/data.headers.js +0 -4
- package/plugins/relay_all.js +0 -13
- /package/docs/{plugins → deprecated}/rcpt_to.routes.md +0 -0
|
@@ -1,47 +1,40 @@
|
|
|
1
|
-
auth/flat\_file
|
|
2
|
-
==============
|
|
1
|
+
# auth/flat\_file
|
|
3
2
|
|
|
4
|
-
The `auth/flat_file` plugin allows you to create a file containing username
|
|
5
|
-
and password combinations, and have relaying users authenticate from that
|
|
6
|
-
file.
|
|
3
|
+
The `auth/flat_file` plugin allows you to create a file containing username and password combinations, and have relaying users authenticate from that file.
|
|
7
4
|
|
|
8
|
-
Note that passwords are stored in clear-text, so this may not be a great idea
|
|
9
|
-
for large scale systems. However the plugin would be a good start for someone
|
|
10
|
-
looking to implement authentication using some other form of auth.
|
|
5
|
+
Note that passwords are stored in clear-text, so this may not be a great idea for large scale systems. However the plugin would be a good start for someone looking to implement authentication using some other form of auth.
|
|
11
6
|
|
|
12
|
-
**Security** - it is recommended to switch to [auth-encfile][url-authencflat]
|
|
13
|
-
to protect your user credentials.
|
|
7
|
+
**Security** - it is recommended to switch to [auth-encfile][url-authencflat] to protect your user credentials.
|
|
14
8
|
|
|
15
|
-
**IMPORANT NOTE** - this plugin requires that STARTTLS be used via the tls plugin
|
|
16
|
-
|
|
17
|
-
improve security out-of-the-box. Localhost and any IP in RFC1918 ranges
|
|
18
|
-
are automatically exempt from this rule.
|
|
9
|
+
**IMPORANT NOTE** - this plugin requires that STARTTLS be used via the tls plugin before it will advertise AUTH capabilities by the EHLO command. Localhost and IPs in RFC1918 ranges
|
|
10
|
+
are exempt from this rule.
|
|
19
11
|
|
|
20
|
-
Configuration
|
|
21
|
-
-------------
|
|
12
|
+
## Configuration
|
|
22
13
|
|
|
23
|
-
Configuration is stored in `config/auth_flat_file.ini
|
|
24
|
-
style formatting.
|
|
14
|
+
Configuration is stored in `config/auth_flat_file.ini`.
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
parameter. Lists of authentification methods are comma separated. Currently
|
|
28
|
-
supported methods are: `CRAM-MD5`, `PLAIN` and `LOGIN`. The `PLAIN`
|
|
29
|
-
and `LOGIN` methods are not secure. That is why TLS is required before AUTH is
|
|
30
|
-
offered.
|
|
16
|
+
* [core]methods
|
|
31
17
|
|
|
32
|
-
|
|
18
|
+
Authentication methods are listed in the `[core]methods` parameter. Authentification methods are comma separated. Currently supported methods are: `CRAM-MD5`, `PLAIN` and `LOGIN`. The `PLAIN` and `LOGIN` methods are insecure and require TLS to be enabled.
|
|
19
|
+
|
|
20
|
+
* [core]constrain_sender: (default: true). For outbound messages (due to successful AUTH), constrain the envelope sender (MAIL FROM) to the same domain as the authenticated user. This setting, combined with `rate_rcpt_sender` in the [limit](https://github.com/haraka/haraka-plugin-limit) plugin can dramatically reduce the amount of backscatter and spam sent when an email account is compromised.
|
|
33
21
|
|
|
34
|
-
|
|
35
|
-
methods=PLAIN,LOGIN,CRAM-MD5
|
|
22
|
+
Example:
|
|
36
23
|
|
|
24
|
+
```ini
|
|
25
|
+
[core]
|
|
26
|
+
methods=PLAIN,LOGIN,CRAM-MD5
|
|
27
|
+
constrain_sender=true
|
|
28
|
+
```
|
|
37
29
|
|
|
38
30
|
Users are stored in the `[users]` section.
|
|
39
31
|
|
|
40
32
|
Example:
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
```ini
|
|
35
|
+
[users]
|
|
36
|
+
user1=password1
|
|
37
|
+
user@domain.com=password2
|
|
38
|
+
```
|
|
46
39
|
|
|
47
40
|
[url-authencflat]: https://github.com/AuspeXeu/haraka-plugin-auth-enc-file
|
package/docs/plugins/clamd.md
CHANGED
|
@@ -133,7 +133,7 @@ meeting following criteria.
|
|
|
133
133
|
via regexp by enclosing the pattern in //.
|
|
134
134
|
|
|
135
135
|
To negate a match (e.g. reject if it matches), prefix the match with !.
|
|
136
|
-
Negative matches are always tested
|
|
136
|
+
Negative matches are always tested first.
|
|
137
137
|
|
|
138
138
|
Example:
|
|
139
139
|
|
|
@@ -36,5 +36,12 @@ Configuration
|
|
|
36
36
|
durable = true
|
|
37
37
|
autoDelete = false
|
|
38
38
|
|
|
39
|
+
; Optional queue arguments
|
|
40
|
+
; More information about x-arguments can be found at https://www.rabbitmq.com/queues.html#optional-arguments
|
|
41
|
+
[queue_args]
|
|
42
|
+
x-dead-letter-exchange =
|
|
43
|
+
x-dead-letter-routing-key = emails_dlq
|
|
44
|
+
x-overflow = reject-publish
|
|
45
|
+
x-queue-type = quorum
|
|
39
46
|
|
|
40
47
|
More information about RabbitMQ can be found at https://www.rabbitmq.com/
|
|
@@ -1,27 +1,18 @@
|
|
|
1
|
-
queue/smtp\_forward
|
|
1
|
+
# queue/smtp\_forward
|
|
2
2
|
==================
|
|
3
3
|
|
|
4
|
-
This plugin delivers to another mail server. This is a common setup when you
|
|
5
|
-
want to have a mail server with a solid pedigree of outbound delivery to
|
|
6
|
-
other hosts, and inbound delivery to users.
|
|
4
|
+
This plugin delivers to another mail server. This is a common setup when you want to have a mail server with a solid pedigree of outbound delivery to other hosts, and inbound delivery to users.
|
|
7
5
|
|
|
8
|
-
In comparison to `queue/smtp_proxy`, this plugin waits until queue time to
|
|
9
|
-
attempt the ongoing connection. This can be a benefit in reducing connections
|
|
10
|
-
to your inbound mail server when you have content filtering (such as
|
|
11
|
-
spamassassin) enabled. A possible downside is that it also delays recipient
|
|
12
|
-
validation that the ongoing mail server may provide until queue time.
|
|
6
|
+
In comparison to `queue/smtp_proxy`, this plugin waits until queue time to attempt the ongoing connection. This can be a benefit in reducing connections to your inbound mail server when you have content filtering (such as spamassassin) enabled. A possible downside is that it also delays recipient validation that the ongoing mail server may provide until queue time.
|
|
13
7
|
|
|
14
|
-
Configuration
|
|
8
|
+
## Configuration
|
|
15
9
|
-------------
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Configuration is stored in this file in the following keys:
|
|
11
|
+
Configuration is stored in smtp\_forward.ini in the following keys:
|
|
20
12
|
|
|
21
13
|
* enable\_outbound=[true]
|
|
22
14
|
|
|
23
|
-
SMTP forward outbound messages (set to false to enable Haraka's separate
|
|
24
|
-
Outbound mail routing (MX based delivery)).
|
|
15
|
+
SMTP forward outbound messages (set to false to enable Haraka's separate Outbound mail routing (MX based delivery)).
|
|
25
16
|
|
|
26
17
|
* host=HOST
|
|
27
18
|
|
|
@@ -33,14 +24,11 @@ Configuration
|
|
|
33
24
|
|
|
34
25
|
* connect\_timeout=SECONDS
|
|
35
26
|
|
|
36
|
-
The maximum amount of time to wait when creating a new connection
|
|
37
|
-
to the host. Default: 30 seconds.
|
|
27
|
+
The maximum amount of time to wait when creating a new connection to the host. Default: 30 seconds.
|
|
38
28
|
|
|
39
29
|
* timeout=SECONDS
|
|
40
30
|
|
|
41
|
-
The amount of seconds to let a backend connection live idle in the
|
|
42
|
-
connection pool. This should always be less than the global plugin
|
|
43
|
-
timeout, which should in turn be less than the connection timeout.
|
|
31
|
+
The amount of seconds to let a backend connection live idle in the connection pool. This should always be less than the global plugin timeout, which should in turn be less than the connection timeout.
|
|
44
32
|
|
|
45
33
|
* max\_connections=NUMBER
|
|
46
34
|
|
|
@@ -48,12 +36,9 @@ Configuration
|
|
|
48
36
|
|
|
49
37
|
* enable\_tls=[true]
|
|
50
38
|
|
|
51
|
-
Enable TLS with the forward host (if supported). TLS uses options
|
|
52
|
-
from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin,
|
|
53
|
-
that certificate will be used as a TLS Client Certificate.
|
|
39
|
+
Enable TLS with the forward host (if supported). TLS uses options from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin, that certificate will be used as a TLS Client Certificate.
|
|
54
40
|
|
|
55
|
-
This option controls the use of TLS via `STARTTLS`. This plugin does not work with
|
|
56
|
-
SMTP over TLS.
|
|
41
|
+
This option controls the use of TLS via `STARTTLS`. This plugin does not work with SMTP over TLS.
|
|
57
42
|
|
|
58
43
|
* auth\_type=[plain\|login]
|
|
59
44
|
|
|
@@ -69,9 +54,7 @@ Configuration
|
|
|
69
54
|
|
|
70
55
|
* queue
|
|
71
56
|
|
|
72
|
-
Which queue plugin to use. Default: undefined. The default bahavior is to
|
|
73
|
-
use smtp_forward for inbound connections and outbound for relaying
|
|
74
|
-
connections. This option is used for complex mail routes.
|
|
57
|
+
Which queue plugin to use. Default: undefined. The default bahavior is to use smtp_forward for inbound connections and outbound for relaying connections. This option is used for complex mail routes.
|
|
75
58
|
|
|
76
59
|
* check_sender=false
|
|
77
60
|
|
|
@@ -86,18 +69,13 @@ Configuration
|
|
|
86
69
|
|
|
87
70
|
# Per-Domain Configuration
|
|
88
71
|
|
|
89
|
-
More specific forward routes for domains can be defined. The domain is
|
|
90
|
-
chosen based on the value of the `domain_selector` config variable.
|
|
72
|
+
More specific forward routes for domains can be defined. The domain is chosen based on the value of the `domain_selector` config variable.
|
|
91
73
|
|
|
92
|
-
When `domain_selector` is set to `rcpt_to` (the default), more specific
|
|
93
|
-
routes are only honored for SMTP connections with a single recipient or SMTP
|
|
94
|
-
connections where every recipient host is identical.
|
|
74
|
+
When `domain_selector` is set to `rcpt_to` (the default), more specific routes are only honored for SMTP connections with a single recipient or SMTP connections where every recipient host is identical.
|
|
95
75
|
|
|
96
|
-
When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM
|
|
97
|
-
address is used.
|
|
76
|
+
When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM address is used.
|
|
98
77
|
|
|
99
|
-
enable\_outbound can be set or unset on a per-domain level to enable or disable
|
|
100
|
-
forwarding for specific domains.
|
|
78
|
+
enable\_outbound can be set or unset on a per-domain level to enable or disable forwarding for specific domains.
|
|
101
79
|
|
|
102
80
|
# default SMTP host
|
|
103
81
|
host=1.2.3.4
|
|
@@ -122,4 +100,4 @@ forwarding for specific domains.
|
|
|
122
100
|
|
|
123
101
|
# Split host forward routing
|
|
124
102
|
|
|
125
|
-
When an incoming email transaction has multiple recipients with different forward routes,
|
|
103
|
+
When an incoming email transaction has multiple recipients with different forward routes, recipients to subsequent forward routes are deferred. Example: an incoming email transaction has recipients user@example1.com, user@example2.com, and user@example3.com. The first two recipients will be accepted (they share the same forward destination) and the latter will be deferred. It will arrive in a future delivery attempt by the remote.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
queue/smtp\_proxy
|
|
1
|
+
# queue/smtp\_proxy
|
|
2
2
|
================
|
|
3
3
|
|
|
4
4
|
This plugin delivers to another mail server. This is a common setup when you
|
|
@@ -12,14 +12,12 @@ particular one important facility to some setups is recipient filtering.
|
|
|
12
12
|
However be aware that other than connect and HELO-time filtering, you will
|
|
13
13
|
have as many connections to your ongoing SMTP server as you have to Haraka.
|
|
14
14
|
|
|
15
|
-
Configuration
|
|
15
|
+
## Configuration
|
|
16
16
|
-------------
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Configuration is stored in this file in the following keys:
|
|
18
|
+
Configuration is stored in smtp\_proxy.ini in the following keys:
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
* enable\_outbound=[true]
|
|
23
21
|
|
|
24
22
|
SMTP proxy outbound messages (set to false to enable Haraka's
|
|
25
23
|
separate Outbound mail routing (MX based delivery)).
|
|
@@ -27,9 +25,9 @@ Configuration
|
|
|
27
25
|
* host=HOST
|
|
28
26
|
|
|
29
27
|
The host to connect to.
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
* port=PORT
|
|
32
|
-
|
|
30
|
+
|
|
33
31
|
The port to connect to.
|
|
34
32
|
|
|
35
33
|
* connect\_timeout=SECONDS
|
|
@@ -38,17 +36,17 @@ Configuration
|
|
|
38
36
|
to the host. Default if unspecified is 30 seconds.
|
|
39
37
|
|
|
40
38
|
* timeout=SECONDS
|
|
41
|
-
|
|
39
|
+
|
|
42
40
|
The amount of seconds to let a backend connection live idle in the
|
|
43
41
|
proxy pool. This should always be less than the global plugin timeout,
|
|
44
42
|
which should in turn be less than the connection timeout.
|
|
45
43
|
|
|
46
44
|
* max\_connections=NUMBER
|
|
47
|
-
|
|
45
|
+
|
|
48
46
|
Maximum number of connections to create at any given time.
|
|
49
47
|
|
|
50
48
|
* enable\_tls=[true|yes|1]
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
Enable TLS with the forward host (if supported). TLS uses options from
|
|
53
51
|
the tls plugin.
|
|
54
52
|
|
package/docs/plugins/relay.md
CHANGED
|
@@ -8,7 +8,7 @@ This **relay** plugin provides Haraka with options for managing relay permission
|
|
|
8
8
|
|
|
9
9
|
## Authentication
|
|
10
10
|
|
|
11
|
-
One way to enable relaying is [
|
|
11
|
+
One way to enable relaying is authentication via the [auth plugins](http://haraka.github.io/plugins). Successful authentication enables relaying during _that_ SMTP connection. To securely offer SMTP AUTH, the [tls](http://haraka.github.io/plugins/tls) plugin and at least one auth plugin must be enabled and properly configured. When that requirement is met, the AUTH SMTP extension will be advertised to SMTP clients.
|
|
12
12
|
|
|
13
13
|
% nc mail.example.com 587
|
|
14
14
|
220 mail.example.com ESMTP Haraka 2.4.0 ready
|
|
@@ -118,7 +118,7 @@ Example:
|
|
|
118
118
|
[domains]
|
|
119
119
|
test.com = { "action": "accept" }
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Think of *accept* as the equivalent of qmail's *rcpthosts*, or a misplaced Haraka `rcpt_to.*` plugin. The *accept* mechanism is another way to tell Haraka that a particular domain is one we accept mail for. The difference between this and the [rcpt_to.in_host_list](http://haraka.github.io/plugins/rcpt_to.in_host_list) plugin is that this one also enables relaying.
|
|
122
122
|
|
|
123
123
|
* continue (mails are subject to further checks)
|
|
124
124
|
|
package/outbound/hmail.js
CHANGED
|
@@ -103,7 +103,7 @@ class HMailItem extends events.EventEmitter {
|
|
|
103
103
|
|
|
104
104
|
this._stream_bytes_from(this.path, {start: 4, end: todo_len + 3}, (err2, todo_bytes) => {
|
|
105
105
|
if (todo_bytes.length !== todo_len) {
|
|
106
|
-
const wrongLength = `Didn't find right amount of data in todo!: ${err2}`;
|
|
106
|
+
const wrongLength = `Didn't find right amount of data in todo!: ${err2} ${this.path}`;
|
|
107
107
|
this.logcrit(wrongLength);
|
|
108
108
|
fs.rename(this.path, path.join(queue_dir, `error.${this.filename}`), (err3) => {
|
|
109
109
|
if (err3) {
|
|
@@ -1062,7 +1062,7 @@ class HMailItem extends events.EventEmitter {
|
|
|
1062
1062
|
"\r": '#10',
|
|
1063
1063
|
"\n": '#13'
|
|
1064
1064
|
};
|
|
1065
|
-
const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join()}]`, 'g');
|
|
1065
|
+
const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join('')}]`, 'g');
|
|
1066
1066
|
|
|
1067
1067
|
bounce_msg_html_.forEach(line => {
|
|
1068
1068
|
line = line.replace(/\{(\w+)\}/g, (i, word) => {
|
package/outbound/queue.js
CHANGED
|
@@ -103,6 +103,11 @@ exports.read_parts = file => {
|
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
if (file.startsWith('error.')) {
|
|
107
|
+
logger.logwarn(`[outbound] 'Skipping' error file in queue folder: ${file}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
106
111
|
const parts = _qfile.parts(file);
|
|
107
112
|
if (!parts) {
|
|
108
113
|
logger.logerror(`[outbound] Unrecognized file in queue folder: ${file}`);
|
package/outbound/tls.js
CHANGED
|
@@ -95,7 +95,7 @@ class OutboundTLS {
|
|
|
95
95
|
|
|
96
96
|
logger.lognotice(this, `TLS connection failed. Marking ${host} as non-TLS for ${expiry} seconds`);
|
|
97
97
|
|
|
98
|
-
this.db.
|
|
98
|
+
this.db.setEx(dbkey, expiry, (new Date()).toISOString())
|
|
99
99
|
.then(cb)
|
|
100
100
|
.catch(err => {
|
|
101
101
|
logger.logerror(this, `Redis returned error: ${err}`);
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"server",
|
|
10
10
|
"email"
|
|
11
11
|
],
|
|
12
|
-
"version": "3.0.
|
|
12
|
+
"version": "3.0.3",
|
|
13
13
|
"homepage": "http://haraka.github.io",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -20,28 +20,28 @@
|
|
|
20
20
|
"node": ">=16"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"address-rfc2821": "^2.
|
|
23
|
+
"address-rfc2821": "^2.1.1",
|
|
24
24
|
"address-rfc2822": "^2.1.0",
|
|
25
|
-
"async": "^3.2.
|
|
25
|
+
"async": "^3.2.5",
|
|
26
26
|
"daemon": "~1.1.0",
|
|
27
|
-
"ipaddr.js": "~2.0
|
|
28
|
-
"node-gyp": "^
|
|
29
|
-
"nopt": "~7.
|
|
27
|
+
"ipaddr.js": "~2.1.0",
|
|
28
|
+
"node-gyp": "^10.0.1",
|
|
29
|
+
"nopt": "~7.2.0",
|
|
30
30
|
"npid": "~0.4.0",
|
|
31
|
-
"semver": "~7.
|
|
32
|
-
"sprintf-js": "~1.1.
|
|
31
|
+
"semver": "~7.6.0",
|
|
32
|
+
"sprintf-js": "~1.1.3",
|
|
33
33
|
"haraka-config": "^1.1.0",
|
|
34
34
|
"haraka-constants": "^1.0.6",
|
|
35
35
|
"haraka-dsn": "^1.0.4",
|
|
36
36
|
"haraka-email-message": "^1.2.0",
|
|
37
37
|
"haraka-message-stream": "^1.2.0",
|
|
38
|
-
"haraka-net-utils": "^1.5.
|
|
38
|
+
"haraka-net-utils": "^1.5.3",
|
|
39
39
|
"haraka-notes": "^1.0.6",
|
|
40
40
|
"haraka-plugin-attachment": "^1.0.7",
|
|
41
|
-
"haraka-plugin-spf": "1.2.
|
|
42
|
-
"haraka-plugin-redis": "^2.0.
|
|
43
|
-
"haraka-results": "^2.2.
|
|
44
|
-
"haraka-tld": "^1.
|
|
41
|
+
"haraka-plugin-spf": "1.2.4",
|
|
42
|
+
"haraka-plugin-redis": "^2.0.6",
|
|
43
|
+
"haraka-results": "^2.2.3",
|
|
44
|
+
"haraka-tld": "^1.2.0",
|
|
45
45
|
"haraka-utils": "^1.0.3",
|
|
46
46
|
"openssl-wrapper": "^0.3.4",
|
|
47
47
|
"sockaddr": "^1.0.1"
|
|
@@ -49,36 +49,36 @@
|
|
|
49
49
|
"optionalDependencies": {
|
|
50
50
|
"haraka-plugin-access": "^1.1.5",
|
|
51
51
|
"haraka-plugin-aliases": "^1.0.1",
|
|
52
|
-
"haraka-plugin-asn": "^2.0.
|
|
53
|
-
"haraka-plugin-auth-ldap": "^1.0
|
|
54
|
-
"haraka-plugin-dcc": "^1.0.
|
|
55
|
-
"haraka-plugin-elasticsearch": "^
|
|
52
|
+
"haraka-plugin-asn": "^2.0.2",
|
|
53
|
+
"haraka-plugin-auth-ldap": "^1.1.0",
|
|
54
|
+
"haraka-plugin-dcc": "^1.0.2",
|
|
55
|
+
"haraka-plugin-elasticsearch": "^8.0.2",
|
|
56
56
|
"haraka-plugin-fcrdns": "^1.1.0",
|
|
57
57
|
"haraka-plugin-graph": "^1.0.5",
|
|
58
58
|
"haraka-plugin-geoip": "^1.0.17",
|
|
59
59
|
"haraka-plugin-headers": "^1.0.3",
|
|
60
|
-
"haraka-plugin-karma": "^2.1.
|
|
61
|
-
"haraka-plugin-limit": "^1.1.
|
|
60
|
+
"haraka-plugin-karma": "^2.1.2",
|
|
61
|
+
"haraka-plugin-limit": "^1.1.1",
|
|
62
62
|
"haraka-plugin-p0f": "^1.0.9",
|
|
63
|
-
"haraka-plugin-qmail-deliverable": "^1.
|
|
64
|
-
"haraka-plugin-known-senders": "^1.0.
|
|
65
|
-
"haraka-plugin-rcpt-ldap": "^1.
|
|
66
|
-
"haraka-plugin-recipient-routes": "^1.0
|
|
67
|
-
"haraka-plugin-rspamd": "^1.
|
|
68
|
-
"haraka-plugin-syslog": "^1.0.
|
|
69
|
-
"haraka-plugin-uribl": "^1.0.
|
|
63
|
+
"haraka-plugin-qmail-deliverable": "^1.2.1",
|
|
64
|
+
"haraka-plugin-known-senders": "^1.0.9",
|
|
65
|
+
"haraka-plugin-rcpt-ldap": "^1.1.0",
|
|
66
|
+
"haraka-plugin-recipient-routes": "^1.2.0",
|
|
67
|
+
"haraka-plugin-rspamd": "^1.3.1",
|
|
68
|
+
"haraka-plugin-syslog": "^1.0.5",
|
|
69
|
+
"haraka-plugin-uribl": "^1.0.8",
|
|
70
70
|
"haraka-plugin-watch": "^2.0.2",
|
|
71
71
|
"ocsp": "~1.2.0",
|
|
72
|
-
"redis": "~4.
|
|
72
|
+
"redis": "~4.6.11",
|
|
73
73
|
"tmp": "~0.2.1"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"nodeunit-x": "^0.
|
|
77
|
-
"haraka-test-fixtures": "^1.
|
|
76
|
+
"nodeunit-x": "^0.16.0",
|
|
77
|
+
"haraka-test-fixtures": "^1.3.3",
|
|
78
78
|
"mock-require": "^3.0.3",
|
|
79
|
-
"eslint": "^8.
|
|
79
|
+
"eslint": "^8.56.0",
|
|
80
80
|
"eslint-plugin-haraka": "^1.0.15",
|
|
81
|
-
"nodemailer": "^6.
|
|
81
|
+
"nodemailer": "^6.9.9"
|
|
82
82
|
},
|
|
83
83
|
"bugs": {
|
|
84
84
|
"mail": "haraka.mail@gmail.com",
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// Note: You can disable setting `connection.notes.auth_passwd` by `plugin.blankout_password = true`
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
const tlds = require('haraka-tld')
|
|
10
|
+
const utils = require('haraka-utils');
|
|
11
|
+
|
|
9
12
|
const AUTH_COMMAND = 'AUTH';
|
|
10
13
|
const AUTH_METHOD_CRAM_MD5 = 'CRAM-MD5';
|
|
11
14
|
const AUTH_METHOD_PLAIN = 'PLAIN';
|
|
@@ -15,7 +18,7 @@ const LOGIN_STRING2 = 'UGFzc3dvcmQ6'; //Password: base64 coded
|
|
|
15
18
|
|
|
16
19
|
exports.hook_capabilities = (next, connection) => {
|
|
17
20
|
// Don't offer AUTH capabilities unless session is encrypted
|
|
18
|
-
if (!connection.tls.enabled)
|
|
21
|
+
if (!connection.tls.enabled) return next();
|
|
19
22
|
|
|
20
23
|
const methods = [ 'PLAIN', 'LOGIN', 'CRAM-MD5' ];
|
|
21
24
|
connection.capabilities.push(`AUTH ${methods.join(' ')}`);
|
|
@@ -47,9 +50,7 @@ exports.hook_unrecognized_command = function (next, connection, params) {
|
|
|
47
50
|
|
|
48
51
|
exports.check_plain_passwd = function (connection, user, passwd, cb) {
|
|
49
52
|
function callback (plain_pw) {
|
|
50
|
-
|
|
51
|
-
if (plain_pw !== passwd) return cb(false);
|
|
52
|
-
cb(true);
|
|
53
|
+
cb(plain_pw === null ? false : plain_pw === passwd);
|
|
53
54
|
}
|
|
54
55
|
if (this.get_plain_passwd.length == 2) {
|
|
55
56
|
this.get_plain_passwd(user, callback);
|
|
@@ -71,7 +72,7 @@ exports.check_cram_md5_passwd = function (connection, user, passwd, cb) {
|
|
|
71
72
|
|
|
72
73
|
if (hmac.digest('hex') === passwd) return cb(true);
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
cb(false);
|
|
75
76
|
}
|
|
76
77
|
if (this.get_plain_passwd.length == 2) {
|
|
77
78
|
this.get_plain_passwd(user, callback);
|
|
@@ -117,7 +118,7 @@ exports.check_user = function (next, connection, credentials, method) {
|
|
|
117
118
|
connection.auth_results(`auth=pass (${method.toLowerCase()})`);
|
|
118
119
|
connection.notes.auth_user = credentials[0];
|
|
119
120
|
if (!plugin.blankout_password) connection.notes.auth_passwd = credentials[1];
|
|
120
|
-
|
|
121
|
+
next(OK);
|
|
121
122
|
});
|
|
122
123
|
return;
|
|
123
124
|
}
|
|
@@ -125,9 +126,7 @@ exports.check_user = function (next, connection, credentials, method) {
|
|
|
125
126
|
if (!connection.notes.auth_fails) connection.notes.auth_fails = 0;
|
|
126
127
|
|
|
127
128
|
connection.notes.auth_fails++;
|
|
128
|
-
connection.results.add({name: 'auth'}, {
|
|
129
|
-
fail:`${plugin.name}/${method}`,
|
|
130
|
-
});
|
|
129
|
+
connection.results.add({name: 'auth'}, { fail:`${plugin.name}/${method}` });
|
|
131
130
|
|
|
132
131
|
let delay = Math.pow(2, connection.notes.auth_fails - 1);
|
|
133
132
|
if (plugin.timeout && delay >= plugin.timeout) {
|
|
@@ -230,7 +229,7 @@ exports.auth_cram_md5 = function (next, connection, params) {
|
|
|
230
229
|
return this.check_user(next, connection, credentials, AUTH_METHOD_CRAM_MD5);
|
|
231
230
|
}
|
|
232
231
|
|
|
233
|
-
const ticket = `<${this.hexi(Math.floor(Math.random() * 1000000))}
|
|
232
|
+
const ticket = `<${this.hexi(Math.floor(Math.random() * 1000000))}.${this.hexi(Date.now())}@${connection.local.host}>`;
|
|
234
233
|
|
|
235
234
|
connection.loginfo(this, `ticket: ${ticket}`);
|
|
236
235
|
connection.respond(334, utils.base64(ticket), () => {
|
|
@@ -240,3 +239,20 @@ exports.auth_cram_md5 = function (next, connection, params) {
|
|
|
240
239
|
}
|
|
241
240
|
|
|
242
241
|
exports.hexi = number => String(Math.abs(parseInt(number)).toString(16))
|
|
242
|
+
|
|
243
|
+
exports.constrain_sender = function (next, connection, params) {
|
|
244
|
+
const au = connection.results.get('auth')?.user
|
|
245
|
+
if (!au) return next()
|
|
246
|
+
|
|
247
|
+
const ad = /@/.test(au) ? au.split('@').pop() : au
|
|
248
|
+
const ed = params[0].host
|
|
249
|
+
|
|
250
|
+
if (!ad || !ed) return next()
|
|
251
|
+
|
|
252
|
+
const auth_od = tlds.get_organizational_domain(ad)
|
|
253
|
+
const envelope_od = tlds.get_organizational_domain(ed)
|
|
254
|
+
|
|
255
|
+
if (auth_od === envelope_od) return next()
|
|
256
|
+
|
|
257
|
+
next(DENY, `Envelope domain '${envelope_od}' doesn't match AUTH domain '${auth_od}'`)
|
|
258
|
+
}
|
|
@@ -4,25 +4,36 @@ const net = require('net');
|
|
|
4
4
|
|
|
5
5
|
exports.register = function () {
|
|
6
6
|
this.inherits('auth/auth_base');
|
|
7
|
-
this.
|
|
7
|
+
this.blankout_password=true
|
|
8
|
+
|
|
9
|
+
this.load_vpopmaild_ini();
|
|
10
|
+
|
|
11
|
+
if (this.cfg.main.constrain_sender) {
|
|
12
|
+
this.register_hook('mail', 'constrain_sender')
|
|
13
|
+
}
|
|
8
14
|
}
|
|
9
15
|
|
|
10
|
-
exports.
|
|
11
|
-
this.cfg = this.config.get('auth_vpopmaild.ini',
|
|
12
|
-
|
|
16
|
+
exports.load_vpopmaild_ini = function () {
|
|
17
|
+
this.cfg = this.config.get('auth_vpopmaild.ini', {
|
|
18
|
+
booleans: [
|
|
19
|
+
'+main.constrain_sender',
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
() => {
|
|
23
|
+
this.load_vpopmaild_ini();
|
|
13
24
|
});
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
exports.hook_capabilities = function (next, connection) {
|
|
17
|
-
if (!connection.tls.enabled)
|
|
28
|
+
if (!connection.tls.enabled) return next();
|
|
18
29
|
|
|
19
30
|
const methods = [ 'PLAIN', 'LOGIN' ];
|
|
20
|
-
if (this.cfg.main.sysadmin)
|
|
31
|
+
if (this.cfg.main.sysadmin) methods.push('CRAM-MD5');
|
|
21
32
|
|
|
22
33
|
connection.capabilities.push(`AUTH ${methods.join(' ')}`);
|
|
23
34
|
connection.notes.allowed_auth_methods = methods;
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
next();
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
exports.check_plain_passwd = function (connection, user, passwd, cb) {
|
|
@@ -49,11 +60,12 @@ exports.check_plain_passwd = function (connection, user, passwd, cb) {
|
|
|
49
60
|
}
|
|
50
61
|
socket.end(); // disconnect
|
|
51
62
|
}
|
|
52
|
-
})
|
|
63
|
+
})
|
|
64
|
+
|
|
53
65
|
socket.on('end', () => {
|
|
54
66
|
connection.loginfo(this, `AUTH user="${user}" success=${auth_success}`);
|
|
55
|
-
|
|
56
|
-
})
|
|
67
|
+
cb(auth_success);
|
|
68
|
+
})
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
exports.get_sock_opts = function (user) {
|
|
@@ -66,13 +78,11 @@ exports.get_sock_opts = function (user) {
|
|
|
66
78
|
|
|
67
79
|
const domain = (user.split('@'))[1];
|
|
68
80
|
let sect = this.cfg.main;
|
|
69
|
-
if (domain && this.cfg[domain])
|
|
70
|
-
sect = this.cfg[domain];
|
|
71
|
-
}
|
|
81
|
+
if (domain && this.cfg[domain]) sect = this.cfg[domain];
|
|
72
82
|
|
|
73
|
-
if (sect.port)
|
|
74
|
-
if (sect.host)
|
|
75
|
-
if (sect.sysadmin)
|
|
83
|
+
if (sect.port) this.sock_opts.port = sect.port;
|
|
84
|
+
if (sect.host) this.sock_opts.host = sect.host;
|
|
85
|
+
if (sect.sysadmin) this.sock_opts.sysadmin = sect.sysadmin;
|
|
76
86
|
|
|
77
87
|
this.logdebug(`sock: ${this.sock_opts.host}:${this.sock_opts.port}`);
|
|
78
88
|
return this.sock_opts;
|
|
@@ -89,14 +99,14 @@ exports.get_vpopmaild_socket = function (user) {
|
|
|
89
99
|
socket.on('timeout', () => {
|
|
90
100
|
this.logerror("vpopmaild connection timed out");
|
|
91
101
|
socket.end();
|
|
92
|
-
})
|
|
102
|
+
})
|
|
93
103
|
socket.on('error', err => {
|
|
94
104
|
this.logerror(`vpopmaild connection failed: ${err}`);
|
|
95
105
|
socket.end();
|
|
96
|
-
})
|
|
106
|
+
})
|
|
97
107
|
socket.on('connect', () => {
|
|
98
108
|
this.logdebug('vpopmail connected');
|
|
99
|
-
})
|
|
109
|
+
})
|
|
100
110
|
return socket;
|
|
101
111
|
}
|
|
102
112
|
|
|
@@ -3,26 +3,32 @@
|
|
|
3
3
|
exports.register = function () {
|
|
4
4
|
this.inherits('auth/auth_base');
|
|
5
5
|
this.load_flat_ini();
|
|
6
|
+
|
|
7
|
+
if (this.cfg.core.constrain_sender) {
|
|
8
|
+
this.register_hook('mail', 'constrain_sender')
|
|
9
|
+
}
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
exports.load_flat_ini = function () {
|
|
9
|
-
this.cfg = this.config.get('auth_flat_file.ini',
|
|
13
|
+
this.cfg = this.config.get('auth_flat_file.ini', {
|
|
14
|
+
booleans: [
|
|
15
|
+
'+core.constrain_sender',
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
() => {
|
|
10
19
|
this.load_flat_ini();
|
|
11
20
|
});
|
|
21
|
+
|
|
22
|
+
if (this.cfg.users === undefined) this.cfg.users = {}
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
exports.hook_capabilities = function (next, connection) {
|
|
15
|
-
// don't allow AUTH unless private IP or encrypted
|
|
16
26
|
if (!connection.remote.is_private && !connection.tls.enabled) {
|
|
17
|
-
connection.logdebug(this,
|
|
18
|
-
"Auth disabled for insecure public connection");
|
|
27
|
+
connection.logdebug(this, "Auth disabled for insecure public connection");
|
|
19
28
|
return next();
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
if (this.cfg.core?.methods ) {
|
|
24
|
-
methods = this.cfg.core.methods.split(',');
|
|
25
|
-
}
|
|
31
|
+
const methods = this.cfg.core?.methods ? this.cfg.core.methods.split(',') : null
|
|
26
32
|
if (methods && methods.length > 0) {
|
|
27
33
|
connection.capabilities.push(`AUTH ${methods.join(' ')}`);
|
|
28
34
|
connection.notes.allowed_auth_methods = methods;
|
|
@@ -31,8 +37,7 @@ exports.hook_capabilities = function (next, connection) {
|
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
exports.get_plain_passwd = function (user, connection, cb) {
|
|
34
|
-
if (this.cfg.users[user])
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return cb();
|
|
40
|
+
if (this.cfg.users[user]) return cb(this.cfg.users[user].toString());
|
|
41
|
+
|
|
42
|
+
cb();
|
|
38
43
|
}
|